UNPKG

@traxjs/trax

Version:

Reactive state management

660 lines (571 loc) 31.8 kB
import { beforeEach, describe, expect, it } from 'vitest'; import { createTraxEnv } from '../core'; import { Store, Trax } from '../types'; import { DictFamilyStore, printEvents } from './utils'; describe('Dictionaries', () => { let trax: Trax; beforeEach(() => { trax = createTraxEnv(); }); function printLogs(minCycleId = 0, ignoreCycleEvents = true): string[] { return printEvents(trax.log, ignoreCycleEvents, minCycleId); } describe('Primary', () => { function createFamilyStore(empty: boolean) { return trax.createStore("FStore", (store: Store<DictFamilyStore>) => { if (empty) { store.init({ familyName: "Simpson", members: {} }); } else { store.init({ familyName: "Simpson", members: { m1: { firstName: "Homer", lastName: "Simpson" }, m2: { firstName: "Marge", lastName: "Simpson" } } }); } let family = store.data; store.compute("Size", () => { family.size = trax.getObjectKeys(family.members).length; }); store.compute("Names", () => { const members = family.members; family.names = trax.getObjectKeys(members).map(id => members[id]?.firstName || "").join(", "); }); }); } it('should support creation as JSON + updates (empty)', async () => { const fs = createFamilyStore(true); const f = fs.data; const members = f.members; expect(f.size).toBe(0); expect(f.names).toBe(""); await trax.reconciliation(); expect(printLogs(0)).toMatchObject([ "0:1 !PCS - !StoreInit (FStore)", "0:2 !NEW - S: FStore", "0:3 !NEW - O: FStore/data", "0:4 !NEW - P: FStore#Size", "0:5 !PCS - !Compute #1 (FStore#Size) P1 Init - parentId=0:1", "0:6 !NEW - O: FStore/data*members", "0:7 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:8 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 0", "0:9 !SET - FStore/data.size = 0 (prev: undefined)", "0:10 !PCE - 0:5", "0:11 !NEW - P: FStore#Names", "0:12 !PCS - !Compute #1 (FStore#Names) P2 Init - parentId=0:1", "0:13 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:14 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 0", "0:15 !SET - FStore/data.names = '' (prev: undefined)", "0:16 !PCE - 0:12", "0:17 !PCE - 0:1", "0:18 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:19 !GET - FStore/data.size -> 0", "0:20 !GET - FStore/data.names -> ''", ]); trax.log.info("Add item"); members["m1"] = { firstName: "Homer", lastName: "Simpson" }; expect(trax.getTraxId(members["m1"])).toBe("FStore/data*members*m1"); await trax.reconciliation(); expect(f.size).toBe(1); expect(f.names).toBe("Homer"); expect(printLogs(1)).toMatchObject([ "1:1 !LOG - Add item", "1:2 !NEW - O: FStore/data*members*m1", "1:3 !SET - FStore/data*members.m1 = '[TRAX FStore/data*members*m1]' (prev: undefined)", "1:4 !DRT - FStore#Size <- FStore/data*members.☆trax.dictionary.size☆", "1:5 !DRT - FStore#Names <- FStore/data*members.☆trax.dictionary.size☆", "1:6 !GET - FStore/data*members.m1 -> '[TRAX FStore/data*members*m1]'", "1:7 !PCS - !Reconciliation #1 - 2 processors", "1:8 !PCS - !Compute #2 (FStore#Size) P1 Reconciliation - parentId=1:7", "1:9 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "1:10 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 1", "1:11 !SET - FStore/data.size = 1 (prev: 0)", "1:12 !PCE - 1:8", "1:13 !PCS - !Compute #2 (FStore#Names) P2 Reconciliation - parentId=1:7", "1:14 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "1:15 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 1", "1:16 !GET - FStore/data*members.m1 -> '[TRAX FStore/data*members*m1]'", "1:17 !GET - FStore/data*members*m1.firstName -> 'Homer'", "1:18 !SET - FStore/data.names = 'Homer' (prev: '')", "1:19 !PCE - 1:13", "1:20 !PCE - 1:7", "2:1 !GET - FStore/data.size -> 1", "2:2 !GET - FStore/data.names -> 'Homer'", ]); await trax.reconciliation(); trax.log.info("Set item undefined"); (members as any)["m1"] = undefined; await trax.reconciliation(); expect(f.names).toBe(""); expect(f.size).toBe(1); // because m1 is still a property await trax.reconciliation(); trax.log.info("Delete item"); delete members["m1"]; await trax.reconciliation(); expect(f.names).toBe(""); expect(f.size).toBe(0); expect(printLogs(5)).toMatchObject([ "5:1 !LOG - Delete item", "5:2 !DRT - FStore#Size <- FStore/data*members.☆trax.dictionary.size☆", "5:3 !DRT - FStore#Names <- FStore/data*members.☆trax.dictionary.size☆", "5:4 !PCS - !Reconciliation #3 - 2 processors", "5:5 !PCS - !Compute #3 (FStore#Size) P1 Reconciliation - parentId=5:4", "5:6 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "5:7 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 0", "5:8 !SET - FStore/data.size = 0 (prev: 1)", "5:9 !PCE - 5:5", "5:10 !PCS - !Compute #4 (FStore#Names) P2 Reconciliation - parentId=5:4", "5:11 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "5:12 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 0", "5:13 !PCE - 5:10", "5:14 !PCE - 5:4", "6:1 !GET - FStore/data.names -> ''", "6:2 !GET - FStore/data.size -> 0", ]); }); it('should support creation as JSON + updates (not empty)', async () => { const fs = createFamilyStore(false); const f = fs.data; const members = f.members; expect(f.size).toBe(2); expect(f.names).toBe("Homer, Marge"); await trax.reconciliation(); expect(printLogs(0)).toMatchObject([ "0:1 !PCS - !StoreInit (FStore)", "0:2 !NEW - S: FStore", "0:3 !NEW - O: FStore/data", "0:4 !NEW - P: FStore#Size", "0:5 !PCS - !Compute #1 (FStore#Size) P1 Init - parentId=0:1", "0:6 !NEW - O: FStore/data*members", "0:7 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:8 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 2", "0:9 !SET - FStore/data.size = 2 (prev: undefined)", "0:10 !PCE - 0:5", "0:11 !NEW - P: FStore#Names", "0:12 !PCS - !Compute #1 (FStore#Names) P2 Init - parentId=0:1", "0:13 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:14 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 2", "0:15 !NEW - O: FStore/data*members*m1", "0:16 !GET - FStore/data*members.m1 -> '[TRAX FStore/data*members*m1]'", "0:17 !GET - FStore/data*members*m1.firstName -> 'Homer'", "0:18 !NEW - O: FStore/data*members*m2", "0:19 !GET - FStore/data*members.m2 -> '[TRAX FStore/data*members*m2]'", "0:20 !GET - FStore/data*members*m2.firstName -> 'Marge'", "0:21 !SET - FStore/data.names = 'Homer, Marge' (prev: undefined)", "0:22 !PCE - 0:12", "0:23 !PCE - 0:1", "0:24 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:25 !GET - FStore/data.size -> 2", "0:26 !GET - FStore/data.names -> 'Homer, Marge'", ]); trax.log.info("Add item"); members["m3"] = { firstName: "Bart", lastName: "Simpson" }; expect(trax.getTraxId(members["m3"])).toBe("FStore/data*members*m3"); await trax.reconciliation(); expect(f.size).toBe(3); // Size changed expect(f.names).toBe("Homer, Marge, Bart"); await trax.reconciliation(); trax.log.info("Update item"); members["m1"].firstName = "HOMER"; await trax.reconciliation(); expect(f.size).toBe(3); expect(f.names).toBe("HOMER, Marge, Bart"); expect(printLogs(3)).toMatchObject([ "3:1 !LOG - Update item", "3:2 !GET - FStore/data*members.m1 -> '[TRAX FStore/data*members*m1]'", "3:3 !SET - FStore/data*members*m1.firstName = 'HOMER' (prev: 'Homer')", "3:4 !DRT - FStore#Names <- FStore/data*members*m1.firstName", // Size is not dirty "3:5 !PCS - !Reconciliation #2 - 2 processors", "3:6 !PCS - !Compute #3 (FStore#Names) P2 Reconciliation - parentId=3:5", "3:7 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "3:8 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 3", "3:9 !GET - FStore/data*members.m1 -> '[TRAX FStore/data*members*m1]'", "3:10 !GET - FStore/data*members*m1.firstName -> 'HOMER'", "3:11 !GET - FStore/data*members.m2 -> '[TRAX FStore/data*members*m2]'", "3:12 !GET - FStore/data*members*m2.firstName -> 'Marge'", "3:13 !GET - FStore/data*members.m3 -> '[TRAX FStore/data*members*m3]'", "3:14 !GET - FStore/data*members*m3.firstName -> 'Bart'", "3:15 !SET - FStore/data.names = 'HOMER, Marge, Bart' (prev: 'Homer, Marge, Bart')", "3:16 !PCE - 3:6", "3:17 !PCE - 3:5", "4:1 !GET - FStore/data.size -> 3", "4:2 !GET - FStore/data.names -> 'HOMER, Marge, Bart'", ]); }); }); describe('Computed', () => { function createFamilyStore(empty: boolean) { return trax.createStore("FStore", (store: Store<DictFamilyStore>) => { if (empty) { store.init({ familyName: "Simpson", members: {} }); } else { store.init({ familyName: "Simpson", members: { m1: { firstName: "Homer", lastName: "Simpson" }, m2: { firstName: "Marge", lastName: "Simpson" } } }); } const family = store.data; store.compute("Infos", () => { let infos = family.infos; if (!infos) { // create the dictionary infos = family.infos = {}; } const members = family.members; const content: DictFamilyStore["infos"] = {}; for (let k of trax.getObjectKeys(members)) { const m = members[k]; if (m === undefined) { // corner case - will not occur if types are not bypassed (content as any)[k] = undefined; } else { const info = store.add(["Info", m], { desc: "" }); info.desc = m.firstName + " " + m.lastName; content[k] = info; } } trax.updateDictionary(infos, content); }); }); } function printInfos(infos: DictFamilyStore["infos"]) { if (!infos) return ""; const arr: string[] = []; return trax.getObjectKeys(infos).map(k => { const info = infos[k]; if (info === undefined) return k + ":XXX"; return `${k}:${info.desc}`; }).join(" / "); } it('should compute a dictionary from another one (empty start)', async () => { const fs = createFamilyStore(true); const family = fs.data; const members = family.members; expect(printLogs(0)).toMatchObject([ "0:1 !PCS - !StoreInit (FStore)", "0:2 !NEW - S: FStore", "0:3 !NEW - O: FStore/data", "0:4 !NEW - P: FStore#Infos", "0:5 !PCS - !Compute #1 (FStore#Infos) P1 Init - parentId=0:1", "0:6 !GET - FStore/data.infos -> undefined", "0:7 !NEW - O: FStore/data*infos", "0:8 !SET - FStore/data.infos = '[TRAX FStore/data*infos]' (prev: undefined)", "0:9 !NEW - O: FStore/data*members", "0:10 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:11 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 0", "0:12 !PCS - !DictionaryUpdate - parentId=0:5", "0:13 !PCE - 0:12", "0:14 !PCE - 0:5", "0:15 !PCE - 0:1", "0:16 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", ]); expect(printInfos(family.infos)).toBe(""); await trax.reconciliation(); trax.log.info("Size Increase"); members["m1"] = { firstName: "Homer", lastName: "Simpson" }; members["m2"] = { firstName: "Bart", lastName: "Simpson" }; await trax.reconciliation(); expect(printInfos(family.infos)).toBe("m1:Homer Simpson / m2:Bart Simpson"); expect(printLogs(1)).toMatchObject([ "1:1 !LOG - Size Increase", "1:2 !NEW - O: FStore/data*members*m1", "1:3 !SET - FStore/data*members.m1 = '[TRAX FStore/data*members*m1]' (prev: undefined)", "1:4 !DRT - FStore#Infos <- FStore/data*members.☆trax.dictionary.size☆", "1:5 !NEW - O: FStore/data*members*m2", "1:6 !SET - FStore/data*members.m2 = '[TRAX FStore/data*members*m2]' (prev: undefined)", "1:7 !PCS - !Reconciliation #1 - 1 processor", "1:8 !PCS - !Compute #2 (FStore#Infos) P1 Reconciliation - parentId=1:7", "1:9 !GET - FStore/data.infos -> '[TRAX FStore/data*infos]'", "1:10 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "1:11 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 2", "1:12 !GET - FStore/data*members.m1 -> '[TRAX FStore/data*members*m1]'", "1:13 !NEW - O: FStore/Info:data*members*m1", "1:14 !GET - FStore/data*members*m1.firstName -> 'Homer'", "1:15 !GET - FStore/data*members*m1.lastName -> 'Simpson'", "1:16 !SET - FStore/Info:data*members*m1.desc = 'Homer Simpson' (prev: '')", "1:17 !GET - FStore/data*members.m2 -> '[TRAX FStore/data*members*m2]'", "1:18 !NEW - O: FStore/Info:data*members*m2", "1:19 !GET - FStore/data*members*m2.firstName -> 'Bart'", "1:20 !GET - FStore/data*members*m2.lastName -> 'Simpson'", "1:21 !SET - FStore/Info:data*members*m2.desc = 'Bart Simpson' (prev: '')", "1:22 !PCS - !DictionaryUpdate - parentId=1:8", "1:23 !GET - FStore/data*infos.☆trax.dictionary.size☆ -> 0", "1:24 !SET - FStore/data*infos.m1 = '[TRAX FStore/Info:data*members*m1]' (prev: undefined)", "1:25 !SET - FStore/data*infos.m2 = '[TRAX FStore/Info:data*members*m2]' (prev: undefined)", "1:26 !PCE - 1:22", "1:27 !PCE - 1:8", "1:28 !PCE - 1:7", "2:1 !GET - FStore/data.infos -> '[TRAX FStore/data*infos]'", "2:2 !GET - FStore/data*infos.☆trax.dictionary.size☆ -> 2", "2:3 !GET - FStore/data*infos.m1 -> '[TRAX FStore/Info:data*members*m1]'", "2:4 !GET - FStore/Info:data*members*m1.desc -> 'Homer Simpson'", "2:5 !GET - FStore/data*infos.m2 -> '[TRAX FStore/Info:data*members*m2]'", "2:6 !GET - FStore/Info:data*members*m2.desc -> 'Bart Simpson'", ]); await trax.reconciliation(); trax.log.info("Update with undefined"); (members as any)["m2"] = undefined; await trax.reconciliation(); expect(printInfos(family.infos)).toBe("m1:Homer Simpson / m2:XXX"); trax.log.info("Normal update"); members["m2"] = { firstName: "Lisa", lastName: "Simpson" }; await trax.reconciliation(); expect(printInfos(family.infos)).toBe("m1:Homer Simpson / m2:Lisa Simpson"); await trax.reconciliation(); trax.log.info("Delete"); delete members["m1"]; await trax.reconciliation(); expect(printLogs(6)).toMatchObject([ "6:1 !LOG - Delete", "6:2 !DRT - FStore#Infos <- FStore/data*members.☆trax.dictionary.size☆", "6:3 !PCS - !Reconciliation #4 - 1 processor", "6:4 !PCS - !Compute #5 (FStore#Infos) P1 Reconciliation - parentId=6:3", "6:5 !GET - FStore/data.infos -> '[TRAX FStore/data*infos]'", "6:6 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "6:7 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 1", "6:8 !GET - FStore/data*members.m2 -> '[TRAX FStore/data*members*m2$1]'", "6:9 !GET - FStore/data*members*m2$1.firstName -> 'Lisa'", "6:10 !GET - FStore/data*members*m2$1.lastName -> 'Simpson'", "6:11 !PCS - !DictionaryUpdate - parentId=6:4", "6:12 !GET - FStore/data*infos.☆trax.dictionary.size☆ -> 2", "6:13 !PCE - 6:11", "6:14 !PCE - 6:4", "6:15 !PCE - 6:3", ]); expect(printInfos(family.infos)).toBe("m2:Lisa Simpson"); }); it('should compute a dictionary from another one (non empty start)', async () => { const fs = createFamilyStore(false); const family = fs.data; const members = family.members; expect(printLogs(0)).toMatchObject([ "0:1 !PCS - !StoreInit (FStore)", "0:2 !NEW - S: FStore", "0:3 !NEW - O: FStore/data", "0:4 !NEW - P: FStore#Infos", "0:5 !PCS - !Compute #1 (FStore#Infos) P1 Init - parentId=0:1", "0:6 !GET - FStore/data.infos -> undefined", "0:7 !NEW - O: FStore/data*infos", "0:8 !SET - FStore/data.infos = '[TRAX FStore/data*infos]' (prev: undefined)", "0:9 !NEW - O: FStore/data*members", "0:10 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:11 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 2", "0:12 !NEW - O: FStore/data*members*m1", "0:13 !GET - FStore/data*members.m1 -> '[TRAX FStore/data*members*m1]'", "0:14 !NEW - O: FStore/Info:data*members*m1", "0:15 !GET - FStore/data*members*m1.firstName -> 'Homer'", "0:16 !GET - FStore/data*members*m1.lastName -> 'Simpson'", "0:17 !SET - FStore/Info:data*members*m1.desc = 'Homer Simpson' (prev: '')", "0:18 !NEW - O: FStore/data*members*m2", "0:19 !GET - FStore/data*members.m2 -> '[TRAX FStore/data*members*m2]'", "0:20 !NEW - O: FStore/Info:data*members*m2", "0:21 !GET - FStore/data*members*m2.firstName -> 'Marge'", "0:22 !GET - FStore/data*members*m2.lastName -> 'Simpson'", "0:23 !SET - FStore/Info:data*members*m2.desc = 'Marge Simpson' (prev: '')", "0:24 !PCS - !DictionaryUpdate - parentId=0:5", "0:25 !PCE - 0:24", "0:26 !PCE - 0:5", "0:27 !PCE - 0:1", "0:28 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", ]); expect(printInfos(family.infos)).toBe("m1:Homer Simpson / m2:Marge Simpson"); await trax.reconciliation(); trax.log.info("Size Increase"); members["m3"] = { firstName: "Bart", lastName: "Simpson" }; members["m4"] = { firstName: "Lisa", lastName: "Simpson" }; await trax.reconciliation(); expect(printInfos(family.infos)).toBe("m1:Homer Simpson / m2:Marge Simpson / m3:Bart Simpson / m4:Lisa Simpson"); expect(trax.getTraxId(family.infos!["m4"])).toBe("FStore/Info:data*members*m4"); trax.log.info("Size Increase 2"); members["m5"] = { firstName: "Maggie", lastName: "Simpson" }; await trax.reconciliation(); expect(printInfos(family.infos)).toBe("m1:Homer Simpson / m2:Marge Simpson / m3:Bart Simpson / m4:Lisa Simpson / m5:Maggie Simpson"); trax.log.info("Delete"); delete members["m2"]; delete members["m3"]; await trax.reconciliation(); expect(printInfos(family.infos)).toBe("m1:Homer Simpson / m4:Lisa Simpson / m5:Maggie Simpson"); trax.log.info("Update"); members["m1"].firstName = "HOMER"; await trax.reconciliation(); expect(printInfos(family.infos)).toBe("m1:HOMER Simpson / m4:Lisa Simpson / m5:Maggie Simpson"); members["m5"] = { firstName: "Maggie", lastName: "Simpson" }; // no changes }); describe('Errors', () => { it('should be raised in case of invalid updateDictionary arguments', async () => { trax.createStore("FStore", (store: Store<DictFamilyStore>) => { store.init({ familyName: "Simpson", members: {} }); const family = store.data; store.compute("Infos", () => { let infos = family.infos; if (!infos) { // create the dictionary infos = family.infos = {}; } const members = family.members; const content: DictFamilyStore["infos"] = {}; for (let k of trax.getObjectKeys(members)) { const m = members[k]; const info = store.add(["Info", m], { desc: "" }); info.desc = m.firstName + " " + m.lastName; content[k] = info; } trax.updateDictionary(12 as any, content); }); }); expect(printLogs(0)).toMatchObject([ "0:1 !PCS - !StoreInit (FStore)", "0:2 !NEW - S: FStore", "0:3 !NEW - O: FStore/data", "0:4 !NEW - P: FStore#Infos", "0:5 !PCS - !Compute #1 (FStore#Infos) P1 Init - parentId=0:1", "0:6 !GET - FStore/data.infos -> undefined", "0:7 !NEW - O: FStore/data*infos", "0:8 !SET - FStore/data.infos = '[TRAX FStore/data*infos]' (prev: undefined)", "0:9 !NEW - O: FStore/data*members", "0:10 !GET - FStore/data.members -> '[TRAX FStore/data*members]'", "0:11 !GET - FStore/data*members.☆trax.dictionary.size☆ -> 0", "0:12 !ERR - [TRAX] updateDictionary: Invalid argument (object expected)", "0:13 !PCE - 0:5", "0:14 !PCE - 0:1", ]); }); it('should be raised when a computed dict is updated by multiple processors (manual change)', async () => { const fs = trax.createStore("FStore", (store: Store<DictFamilyStore>) => { store.init({ familyName: "Simpson", members: { m1: { firstName: "Homer", lastName: "Simpson" } } }); const family = store.data; store.compute("Infos", () => { let infos = family.infos; if (!infos) { // create the dictionary infos = family.infos = {}; } const members = family.members; const content: DictFamilyStore["infos"] = {}; for (let k of trax.getObjectKeys(members)) { const m = members[k]; const info = store.add(["Info", m], { desc: "" }); info.desc = m.firstName + " " + m.lastName; content[k] = info; } trax.updateDictionary(infos, content); }); }); const infos = fs.data.infos; expect(printInfos(infos)).toBe("m1:Homer Simpson"); await trax.reconciliation(); trax.log.info("Manual update"); infos!["foo"] = { desc: "bar" }; expect(printLogs(1)).toMatchObject([ "1:1 !LOG - Manual update", "1:2 !ERR - [TRAX] Computed content conflict: FStore/data*infos.foo can only be set by FStore#Infos" ]); }); it('should be raised when a computed dict is updated by multiple processors (direct change)', async () => { const fs = trax.createStore("FStore", (store: Store<DictFamilyStore>) => { store.init({ familyName: "Simpson", members: { m1: { firstName: "Homer", lastName: "Simpson" } } }); const family = store.data; store.compute("Infos", () => { let infos = family.infos; if (!infos) { // create the dictionary infos = family.infos = {}; } const members = family.members; const content: DictFamilyStore["infos"] = {}; for (let k of trax.getObjectKeys(members)) { const m = members[k]; const info = store.add(["Info", m], { desc: "" }); info.desc = m.firstName + " " + m.lastName; content[k] = info; } trax.updateDictionary(infos, content); }); }); const infos = fs.data.infos; expect(printInfos(infos)).toBe("m1:Homer Simpson"); await trax.reconciliation(); trax.log.info("Direct processor update"); fs.compute("Invalid", () => { infos!["foo"] = { desc: "bar" }; }); expect(printLogs(1)).toMatchObject([ "1:1 !LOG - Direct processor update", "1:2 !NEW - P: FStore#Invalid", "1:3 !PCS - !Compute #1 (FStore#Invalid) P2 Init", "1:4 !ERR - [TRAX] Computed content conflict: FStore/data*infos.foo can only be set by FStore#Infos", "1:5 !PCE - 1:3", "1:6 !ERR - [TRAX] (FStore#Invalid) No dependencies found: processor will never be re-executed", ]); }); it('should be raised when a computed dict is updated by multiple processors (updateDictionary change)', async () => { const fs = trax.createStore("FStore", (store: Store<DictFamilyStore>) => { store.init({ familyName: "Simpson", members: { m1: { firstName: "Homer", lastName: "Simpson" } } }); const family = store.data; store.compute("Infos", () => { let infos = family.infos; if (!infos) { // create the dictionary infos = family.infos = {}; } const members = family.members; const content: DictFamilyStore["infos"] = {}; for (let k of trax.getObjectKeys(members)) { const m = members[k]; const info = store.add(["Info", m], { desc: "" }); info.desc = m.firstName + " " + m.lastName; content[k] = info; } trax.updateDictionary(infos, content); }); }); const infos = fs.data.infos; expect(printInfos(infos)).toBe("m1:Homer Simpson"); await trax.reconciliation(); trax.log.info("Direct processor update"); fs.compute("Invalid", () => { trax.updateDictionary(infos!, { x: { desc: "bar" } }) }); expect(printLogs(1)).toMatchObject([ "1:1 !LOG - Direct processor update", "1:2 !NEW - P: FStore#Invalid", "1:3 !PCS - !Compute #1 (FStore#Invalid) P2 Init", "1:4 !ERR - [TRAX] Computed content conflict: FStore/data*infos can only be changed by FStore#Infos", "1:5 !PCS - !DictionaryUpdate - parentId=1:3", "1:6 !GET - FStore/data*infos.☆trax.dictionary.size☆ -> 1", "1:7 !ERR - [TRAX] Computed content conflict: FStore/data*infos.x can only be set by FStore#Infos", "1:8 !PCE - 1:5", "1:9 !PCE - 1:3", ]); }); }); }); });