/ test / general.spec.js
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  }