general.spec.js
1 import { Data, same, Memory, fact, Collection, Task, $ } from './lib.js' 2 import * as Link from '../src/data/link.js' 3 import proofsDB from './proofs.db.js' 4 import moviesDB from './movie.db.js' 5 import * as Microshaft from './microshaft.db.js' 6 7 /** 8 * @type {import('entail').Suite} 9 */ 10 export const testDB = { 11 'capabilities across ucans': async (assert) => { 12 const UCAN = fact({ 13 the: 'ucan', 14 cid: String, 15 issuer: String, 16 audience: String, 17 expiration: Number, 18 capabilities: Object, 19 }) 20 21 const Capability = fact({ 22 the: 'capability', 23 can: String, 24 with: String, 25 }) 26 27 const Delegation = fact({ 28 this: Object, 29 cid: String, 30 can: String, 31 space: String, 32 }) 33 .with({ capabilities: Object, capability: Object }) 34 .where(({ this: ucan, cid, capabilities, capability, space, can }) => [ 35 UCAN.match({ this: ucan, cid, capabilities }), 36 Collection.match({ this: capabilities, of: capability }), 37 Capability.match({ this: capability, can, with: space }), 38 ]) 39 40 const Access = fact({ 41 space: String, 42 upload: String, 43 store: String, 44 }).where(({ space, upload, store }) => [ 45 Delegation.match({ space, can: 'upload/add', cid: upload }), 46 Delegation.match({ space, can: 'store/add', cid: store }), 47 Access.claim({ space, upload, store }), 48 ]) 49 50 const [...access] = await Access().query({ from: proofsDB }) 51 assert.deepEqual( 52 access.map((fact) => fact.toJSON()), 53 [ 54 Access.assert({ 55 upload: 'bafy...upload', 56 store: 'bafy...store', 57 space: 'did:key:zAlice', 58 }).toJSON(), 59 ] 60 ) 61 }, 62 63 'test basic': async (assert) => { 64 const db = Memory.create() 65 66 const Age = fact({ age: Number }) 67 const Likes = fact({ likes: String }) 68 69 const fred = Link.of('fred') 70 const sally = Link.of('sally') 71 const ethel = Link.of('ethel') 72 const marije = Link.of('marije') 73 74 Task.perform( 75 db.transact([ 76 ...Age.assert({ this: sally, age: 21 }), 77 ...Age.assert({ this: fred, age: 42 }), 78 ...Age.assert({ this: ethel, age: 42 }), 79 ...Likes.assert({ this: fred, likes: 'pizza' }), 80 ...Likes.assert({ this: sally, likes: 'opera' }), 81 ...Likes.assert({ this: ethel, likes: 'sushi' }), 82 ...Likes.assert({ this: marije, likes: 'opera' }), 83 ]) 84 ) 85 86 assert.deepEqual(await Age.match({ age: 42 }).query({ from: db }), [ 87 Age.assert({ this: fred, age: 42 }), 88 Age.assert({ this: ethel, age: 42 }), 89 ]) 90 91 assert.deepEqual(await Likes().query({ from: db }), [ 92 Likes.assert({ this: fred, likes: 'pizza' }), 93 Likes.assert({ this: sally, likes: 'opera' }), 94 Likes.assert({ this: ethel, likes: 'sushi' }), 95 Likes.assert({ this: marije, likes: 'opera' }), 96 ]) 97 98 assert.deepEqual( 99 await Likes.match({ likes: 'pizza' }).query({ from: db }), 100 [Likes.assert({ this: fred, likes: 'pizza' })] 101 ) 102 103 assert.deepEqual(await Likes.match({ this: ethel }).query({ from: db }), [ 104 Likes.assert({ this: ethel, likes: 'sushi' }), 105 ]) 106 107 const Alike = fact({ 108 as: Object, 109 likes: String, 110 }).where(({ this: self, likes, as }) => [ 111 Likes.match({ this: self, likes }), 112 Likes.match({ this: as, likes }), 113 same.not({ this: self, as }), 114 ]) 115 116 assert.deepEqual(await Alike().query({ from: db }), [ 117 Alike.assert({ this: sally, as: marije, likes: 'opera' }), 118 Alike.assert({ this: marije, as: sally, likes: 'opera' }), 119 ]) 120 }, 121 122 'sketch pull pattern': async (assert) => { 123 const Person = fact({ 124 the: 'person', 125 this: Object, 126 name: String, 127 }) 128 129 const Movie = fact({ 130 the: 'movie', 131 this: Object, 132 cast: Object, 133 director: Object, 134 title: String, 135 }) 136 137 const Cast = fact({ 138 title: String, 139 director: String, 140 actor: String, 141 }) 142 .with({ cast: Object, directedBy: Object }) 143 .where(({ title, cast, directedBy, director, actor }) => [ 144 Movie({ cast, director: directedBy, title }), 145 Person({ this: directedBy, name: director }), 146 Person({ this: cast, name: actor }), 147 Cast.claim({ title, actor, director }), 148 ]) 149 150 const arnold = 'Arnold Schwarzenegger' 151 assert.deepEqual( 152 await Cast.match({ actor: arnold }).query({ from: moviesDB }), 153 [ 154 Cast.assert({ 155 title: 'The Terminator', 156 director: 'James Cameron', 157 actor: arnold, 158 }), 159 Cast.assert({ 160 title: 'Terminator 2: Judgment Day', 161 director: 'James Cameron', 162 actor: arnold, 163 }), 164 Cast.assert({ 165 title: 'Predator', 166 director: 'John McTiernan', 167 actor: arnold, 168 }), 169 Cast.assert({ 170 title: 'Commando', 171 director: 'Mark L. Lester', 172 actor: arnold, 173 }), 174 Cast.assert({ 175 title: 'Terminator 3: Rise of the Machines', 176 director: 'Jonathan Mostow', 177 actor: arnold, 178 }), 179 ] 180 ) 181 182 const CastExceptCameron = Cast.where( 183 ({ this: $, title, director, actor }) => [ 184 Cast({ this: $, title, director, actor }), 185 same.not({ this: 'James Cameron', as: director }), 186 ] 187 ) 188 189 assert.deepEqual( 190 await CastExceptCameron.match({ actor: arnold }).query({ 191 from: moviesDB, 192 }), 193 [ 194 Cast.assert({ 195 director: 'John McTiernan', 196 title: 'Predator', 197 actor: arnold, 198 }), 199 Cast.assert({ 200 director: 'Mark L. Lester', 201 title: 'Commando', 202 actor: arnold, 203 }), 204 Cast.assert({ 205 director: 'Jonathan Mostow', 206 title: 'Terminator 3: Rise of the Machines', 207 actor: arnold, 208 }), 209 ] 210 ) 211 }, 212 213 'test facts': async (assert) => { 214 const Person = fact({ 215 the: 'person', 216 name: String, 217 }) 218 219 const Job = fact({ 220 the: 'job', 221 title: String, 222 salary: Number, 223 }) 224 225 const Employee = fact({ 226 name: String, 227 job: String, 228 }).where(({ this: $, name, job }) => [ 229 Person({ this: $, name }), 230 Job.match({ this: $, title: job }), 231 ]) 232 233 const Programmer = fact({ 234 name: String, 235 }).where(({ this: employee, name }) => [ 236 Employee({ this: employee, name: name, job: 'Computer programmer' }), 237 ]) 238 239 assert.deepEqual(await Programmer().query({ from: Microshaft.db }), [ 240 Programmer.assert({ 241 this: Link.of(Microshaft.alyssa), 242 name: 'Hacker Alyssa P', 243 }), 244 Programmer.assert({ 245 this: Link.of(Microshaft.cy), 246 name: 'Fect Cy D', 247 }), 248 ]) 249 }, 250 251 'test supervisor': async (assert) => { 252 const Person = fact({ 253 the: 'person', 254 name: String, 255 }) 256 257 const Job = fact({ 258 the: 'job', 259 title: String, 260 }) 261 262 const Supervisor = fact({ 263 the: 'job', 264 supervisor: Object, 265 }) 266 267 const Employee = fact({ 268 name: String, 269 }).where(({ this: $, name }) => [ 270 Person({ this: $, name }), 271 Job.match({ this: $ }), 272 ]) 273 274 const Manager = fact({ 275 employee: String, 276 manager: String, 277 }) 278 .with({ supervisor: Object, subordinate: Object }) 279 .where(({ supervisor, employee, subordinate, manager }) => [ 280 Employee({ this: subordinate, name: employee }), 281 Employee({ this: supervisor, name: manager }), 282 Supervisor({ this: subordinate, supervisor }), 283 Manager.claim({ employee, manager }), 284 ]) 285 286 assert.deepEqual(await Manager().query({ from: Microshaft.db }), [ 287 Manager.assert({ 288 employee: 'Scrooge Eben', 289 manager: 'Warbucks Oliver', 290 }), 291 Manager.assert({ 292 employee: 'Cratchet Robert', 293 manager: 'Scrooge Eben', 294 }), 295 Manager.assert({ 296 employee: 'Bitdiddle Ben', 297 manager: 'Warbucks Oliver', 298 }), 299 300 Manager.assert({ 301 employee: 'Hacker Alyssa P', 302 manager: 'Bitdiddle Ben', 303 }), 304 Manager.assert({ 305 employee: 'Fect Cy D', 306 manager: 'Bitdiddle Ben', 307 }), 308 Manager.assert({ 309 employee: 'Tweakit Lem E', 310 manager: 'Bitdiddle Ben', 311 }), 312 Manager.assert({ 313 employee: 'Reasoner Louis', 314 manager: 'Hacker Alyssa P', 315 }), 316 317 Manager.assert({ 318 employee: 'Aull DeWitt', 319 manager: 'Warbucks Oliver', 320 }), 321 ]) 322 }, 323 324 'test salary': async (assert) => { 325 const Person = fact({ 326 the: 'person', 327 name: String, 328 }) 329 330 const Job = fact({ 331 the: 'job', 332 salary: Number, 333 }) 334 335 const Employee = fact({ 336 name: String, 337 salary: Number, 338 }).where(({ this: employee, name, salary }) => [ 339 Person({ this: employee, name }), 340 Job({ this: employee, salary }), 341 ]) 342 343 const Above30K = Employee.where(({ this: employee, name, salary }) => [ 344 Employee({ this: employee, name, salary }), 345 Data.greater({ this: salary, than: 30_000 }), 346 ]) 347 348 assert.deepEqual(await Above30K().query({ from: Microshaft.db }), [ 349 Employee.assert({ 350 this: Link.of(Microshaft.oliver), 351 name: 'Warbucks Oliver', 352 salary: 150_000, 353 }), 354 Employee.assert({ 355 this: Link.of(Microshaft.eben), 356 name: 'Scrooge Eben', 357 salary: 75_000, 358 }), 359 Employee.assert({ 360 this: Link.of(Microshaft.ben), 361 name: 'Bitdiddle Ben', 362 salary: 60_000, 363 }), 364 Employee.assert({ 365 this: Link.of(Microshaft.alyssa), 366 name: 'Hacker Alyssa P', 367 salary: 40_000, 368 }), 369 Employee.assert({ 370 this: Link.of(Microshaft.cy), 371 name: 'Fect Cy D', 372 salary: 35_000, 373 }), 374 ]) 375 376 const Between30_100K = Above30K.where( 377 ({ this: employee, name, salary }) => [ 378 Above30K({ this: employee, name, salary }), 379 Data.less({ this: salary, than: 100_000 }), 380 ] 381 ) 382 383 assert.deepEqual(await Between30_100K().query({ from: Microshaft.db }), [ 384 Employee.assert({ 385 this: Link.of(Microshaft.eben), 386 name: 'Scrooge Eben', 387 salary: 75_000, 388 }), 389 Employee.assert({ 390 this: Link.of(Microshaft.ben), 391 name: 'Bitdiddle Ben', 392 salary: 60_000, 393 }), 394 Employee.assert({ 395 this: Link.of(Microshaft.alyssa), 396 name: 'Hacker Alyssa P', 397 salary: 40_000, 398 }), 399 Employee.assert({ 400 this: Link.of(Microshaft.cy), 401 name: 'Fect Cy D', 402 salary: 35_000, 403 }), 404 ]) 405 }, 406 407 'test disjunction': async (assert) => { 408 const Person = fact({ 409 the: 'person', 410 name: String, 411 }) 412 413 const Supervisor = fact({ 414 the: 'job', 415 supervisor: Object, 416 }) 417 418 const Manager = fact({ 419 employee: String, 420 manager: String, 421 }) 422 .with({ supervisor: Object, subordinate: Object }) 423 .where(({ employee, manager, supervisor, subordinate }) => [ 424 Person({ this: subordinate, name: employee }), 425 Person({ this: supervisor, name: manager }), 426 Supervisor({ this: subordinate, supervisor }), 427 Manager.claim({ employee, manager }), 428 ]) 429 430 const ReportingToBenOrAlyssa = Manager.when( 431 ({ this: fact, employee, manager }) => ({ 432 Ben: [ 433 Manager({ this: fact, employee, manager }), 434 same({ this: manager, as: 'Bitdiddle Ben' }), 435 ], 436 Alyssa: [ 437 Manager({ this: fact, employee, manager }), 438 same({ this: manager, as: 'Hacker Alyssa P' }), 439 ], 440 }) 441 ) 442 443 assert.deepEqual( 444 await ReportingToBenOrAlyssa().query({ from: Microshaft.db }), 445 [ 446 ReportingToBenOrAlyssa.assert({ 447 employee: 'Hacker Alyssa P', 448 manager: 'Bitdiddle Ben', 449 }), 450 ReportingToBenOrAlyssa.assert({ 451 employee: 'Fect Cy D', 452 manager: 'Bitdiddle Ben', 453 }), 454 ReportingToBenOrAlyssa.assert({ 455 employee: 'Tweakit Lem E', 456 manager: 'Bitdiddle Ben', 457 }), 458 ReportingToBenOrAlyssa.assert({ 459 employee: 'Reasoner Louis', 460 manager: 'Hacker Alyssa P', 461 }), 462 ] 463 ) 464 }, 465 466 'test negation': async (assert) => { 467 const Person = fact({ 468 the: 'person', 469 name: String, 470 }) 471 472 const Job = fact({ 473 the: 'job', 474 title: String, 475 }) 476 477 const Supervisor = fact({ 478 the: 'job', 479 supervisor: Object, 480 }) 481 482 const NonProgrammer = Person.with({ manager: Object }).where( 483 ({ this: employee, manager, name }) => [ 484 Person({ this: employee, name }), 485 Person({ this: manager, name: 'Bitdiddle Ben' }), 486 Job.not({ this: employee, title: 'Computer programmer' }), 487 Supervisor({ this: employee, supervisor: manager }), 488 ] 489 ) 490 491 assert.deepEqual(await NonProgrammer().query({ from: Microshaft.db }), [ 492 Person.assert({ this: Link.of(Microshaft.lem), name: 'Tweakit Lem E' }), 493 ]) 494 }, 495 }