evaluation.spec.js
1 import { Memory, Task, Link, $, Analyzer } from './lib.js' 2 import * as Match from '../src/match.js' 3 4 /** 5 * @type {import('entail').Suite} 6 */ 7 export const testEvaluation = { 8 'plans negation last': async (assert) => { 9 const db = Memory.create({ alice }) 10 11 const plan = Analyzer.rule({ 12 match: { 13 manager: $.manager, 14 employee: $.employee, 15 managerName: $.managerName, 16 employeeName: $.employeeName, 17 }, 18 when: { 19 where: [ 20 { 21 match: { 22 name: $.managerName, 23 supervisor: $.manager, 24 report: $.employee, 25 }, 26 rule: { 27 match: { 28 name: $.name, 29 supervisor: $.supervisor, 30 report: $.report, 31 }, 32 when: { 33 where: [ 34 { 35 match: { 36 the: 'person/name', 37 of: $.supervisor, 38 is: $.name, 39 }, 40 }, 41 { 42 match: { 43 the: 'work/report', 44 of: $.supervisor, 45 is: $.report, 46 }, 47 }, 48 ], 49 }, 50 }, 51 }, 52 { 53 match: { 54 person: $.employee, 55 name: $.employeeName, 56 }, 57 rule: { 58 match: { 59 name: $.name, 60 person: $.person, 61 }, 62 when: { 63 where: [ 64 { 65 match: { 66 the: 'person/name', 67 of: $.person, 68 is: $.name, 69 }, 70 }, 71 ], 72 }, 73 }, 74 }, 75 ], 76 }, 77 }) 78 .apply({ 79 manager: $.$manager, 80 employee: $.$employee, 81 managerName: $.$managerName, 82 employeeName: $.$employeeName, 83 }) 84 .prepare() 85 86 const result = await Task.perform(plan.query({ from: db })) 87 88 assert.deepEqual( 89 [...result], 90 [ 91 Match.from([ 92 [$.$manager, Link.of(alice)], 93 [$.$employee, Link.of(bob)], 94 [$.$managerName, 'Alice'], 95 [$.$employeeName, 'Bob'], 96 ]), 97 Match.from([ 98 [$.$manager, Link.of(bob)], 99 [$.$employee, Link.of(mallory)], 100 [$.$managerName, 'Bob'], 101 [$.$employeeName, 'Mallory'], 102 ]), 103 ] 104 ) 105 }, 106 107 'same variable as different binding': async (assert) => { 108 const db = Memory.create({ alice }) 109 110 const rule = Analyzer.rule({ 111 match: { 112 a: $.a, 113 actual: $.aa, 114 b: $.b, 115 expect: $.bb, 116 }, 117 when: { 118 where: [ 119 { 120 match: { 121 the: 'person/name', 122 of: $.a, 123 is: $.aa, 124 }, 125 }, 126 { 127 match: { 128 the: 'person/name', 129 of: $.b, 130 is: $.bb, 131 }, 132 }, 133 ], 134 }, 135 }) 136 137 const plan = rule 138 .apply({ 139 a: $.subject, 140 actual: $.actual, 141 b: $.subject, 142 expect: $.expect, 143 }) 144 .prepare() 145 146 const result = await Task.perform(plan.query({ from: db })) 147 148 assert.deepEqual( 149 [...result], 150 [ 151 Match.from([ 152 [$.subject, Link.of(alice)], 153 [$.actual, 'Alice'], 154 [$.expect, 'Alice'], 155 ]), 156 Match.from([ 157 [$.subject, Link.of(bob)], 158 [$.actual, 'Bob'], 159 [$.expect, 'Bob'], 160 ]), 161 Match.from([ 162 [$.subject, Link.of(mallory)], 163 [$.actual, 'Mallory'], 164 [$.expect, 'Mallory'], 165 ]), 166 // { 167 // a: Link.of(alice), 168 // actual: 'Alice', 169 // b: Link.of(alice), 170 // expect: 'Alice', 171 // }, 172 // { 173 // a: Link.of(bob), 174 // actual: 'Bob', 175 // b: Link.of(bob), 176 // expect: 'Bob', 177 // }, 178 // { 179 // a: Link.of(mallory), 180 // actual: 'Mallory', 181 // b: Link.of(mallory), 182 // expect: 'Mallory', 183 // }, 184 ] 185 ) 186 }, 187 188 'test generate query': async (assert) => { 189 const mallory = { 'Person/name': 'Mallory' } 190 const bob = { 191 'Person/name': 'Bob', 192 'Manages/employee': mallory, 193 } 194 const alice = { 195 'Person/name': 'Alice', 196 'Manages/employee': bob, 197 } 198 const db = Memory.create({ alice }) 199 200 const rule = Analyzer.rule({ 201 match: { 202 this: $.this, 203 'name.the': $['name.the'], 204 'name.is': $['name.is'], 205 'name.of': $.this, 206 'manages.the': $['manages.the'], 207 'manages.is.name.the': $['manages.is.name.the'], 208 'manages.is.name.is': $['manages.is.name.is'], 209 'manages.of': $.this, 210 }, 211 when: { 212 where: [ 213 { 214 match: { the: $['name.the'], is: $['name.is'], of: $.this }, 215 rule: { 216 match: { the: $.the, is: $.is, of: $.of }, 217 when: { 218 where: [ 219 { match: { of: 'Person/name', is: $.the }, operator: '==' }, 220 { match: { the: $.the, of: $.of, is: $.is } }, 221 { match: { of: $.is, is: 'string' }, operator: 'data/type' }, 222 ], 223 }, 224 }, 225 }, 226 { 227 match: { 228 the: $['manages.the'], 229 'is.this': $['manages.is.this'], 230 'is.name.the': $['manages.is.name.the'], 231 'is.name.is': $['manages.is.name.is'], 232 of: $.this, 233 }, 234 rule: { 235 match: { 236 the: $.the, 237 'is.this': $['is.this'], 238 'is.name.the': $['is.name.the'], 239 'is.name.is': $['is.name.is'], 240 of: $.of, 241 }, 242 when: { 243 where: [ 244 { 245 match: { of: 'Manages/employee', is: $.the }, 246 operator: '==', 247 }, 248 { match: { the: $.the, of: $.of, is: $['is.this'] } }, 249 { 250 match: { 251 this: $['is.this'], 252 'name.the': $['is.name.the'], 253 'name.is': $['is.name.is'], 254 'name.of': $['is.this'], 255 }, 256 rule: { 257 match: { 258 this: $.this, 259 'name.the': $['name.the'], 260 'name.is': $['name.is'], 261 'name.of': $.this, 262 }, 263 when: { 264 where: [ 265 { 266 match: { 267 the: $['name.the'], 268 is: $['name.is'], 269 of: $.this, 270 }, 271 rule: { 272 match: { the: $.the, is: $.is, of: $.of }, 273 when: { 274 where: [ 275 { 276 match: { of: 'Person/name', is: $.the }, 277 operator: '==', 278 }, 279 { match: { the: $.the, of: $.of, is: $.is } }, 280 { 281 match: { of: $.is, is: 'string' }, 282 operator: 'data/type', 283 }, 284 ], 285 }, 286 }, 287 }, 288 ], 289 }, 290 }, 291 }, 292 ], 293 }, 294 }, 295 }, 296 ], 297 }, 298 }) 299 300 const result = await Task.perform(rule.apply().query({ from: db })) 301 302 assert.deepEqual( 303 [...result], 304 [ 305 Match.from([ 306 [$.this, Link.of(alice)], 307 [$['name.the'], 'Person/name'], 308 [$['name.is'], 'Alice'], 309 [$['manages.the'], 'Manages/employee'], 310 [$['manages.is.name.the'], 'Person/name'], 311 [$['manages.is.name.is'], 'Bob'], 312 ]), 313 Match.from([ 314 [$.this, Link.of(bob)], 315 [$['name.the'], 'Person/name'], 316 [$['name.is'], 'Bob'], 317 [$['manages.the'], 'Manages/employee'], 318 [$['manages.is.name.the'], 'Person/name'], 319 [$['manages.is.name.is'], 'Mallory'], 320 ]), 321 ] 322 ) 323 }, 324 } 325 326 const mallory = { 327 'person/name': 'Mallory', 328 } 329 330 const bob = { 331 'person/name': 'Bob', 332 'work/report': mallory, 333 } 334 335 const alice = { 336 'person/name': 'Alice', 337 'work/report': bob, 338 }