@ibgib/core-gib 0.1.38 → 0.1.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/common/other/graph-helper.d.mts.map +1 -1
  2. package/dist/common/other/graph-helper.mjs +18 -3
  3. package/dist/common/other/graph-helper.mjs.map +1 -1
  4. package/dist/sync/graft-info/graft-info-helpers.mjs +1 -1
  5. package/dist/sync/graft-info/graft-info-helpers.mjs.map +1 -1
  6. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs +321 -235
  7. package/dist/sync/sync-conflict-adv-multitimelines.respec.mjs.map +1 -1
  8. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs +6 -6
  9. package/dist/sync/sync-conflict-basic-multitimelines.respec.mjs.map +1 -1
  10. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs +1 -1
  11. package/dist/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs.map +1 -1
  12. package/dist/sync/sync-saga-coordinator.d.mts.map +1 -1
  13. package/dist/sync/sync-saga-coordinator.mjs +49 -12
  14. package/dist/sync/sync-saga-coordinator.mjs.map +1 -1
  15. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts +4 -0
  16. package/dist/sync/sync-saga-message/sync-saga-message-types.d.mts.map +1 -1
  17. package/dist/test-helpers.d.mts +104 -47
  18. package/dist/test-helpers.d.mts.map +1 -1
  19. package/dist/test-helpers.mjs +335 -131
  20. package/dist/test-helpers.mjs.map +1 -1
  21. package/dist/test-types.d.mts +25 -7
  22. package/dist/test-types.d.mts.map +1 -1
  23. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs +2 -2
  24. package/dist/witness/space/reconciliation-space/reconciliation-space-helper.mjs.map +1 -1
  25. package/package.json +1 -1
  26. package/src/common/other/graph-helper.mts +14 -2
  27. package/src/sync/SYNC_TESTING.md +135 -70
  28. package/src/sync/graft-info/graft-info-helpers.mts +1 -1
  29. package/src/sync/sync-conflict-adv-multitimelines.respec.mts +332 -245
  30. package/src/sync/sync-conflict-basic-multitimelines.respec.mts +5 -5
  31. package/src/sync/sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mts +1 -1
  32. package/src/sync/sync-saga-coordinator.mts +58 -14
  33. package/src/sync/sync-saga-message/sync-saga-message-types.mts +4 -0
  34. package/src/test-helpers.mts +429 -152
  35. package/src/test-types.mts +30 -7
  36. package/src/witness/space/reconciliation-space/reconciliation-space-helper.mts +2 -2
  37. package/tmp.md +205 -1160
@@ -7,26 +7,24 @@
7
7
  import { respecfully, iReckon, ifWeMight } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
8
8
  const maam = `[${import.meta.url}]`, sir = maam;
9
9
  import { clone, delay, extractErrorMsg, pretty } from '@ibgib/helper-gib/dist/helpers/utils-helper.mjs';
10
- import { getIbGibAddr } from '@ibgib/ts-gib/dist/helper.mjs';
10
+ import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
11
+ import { TestTransformer, getTestKeystoneServiceHelper } from '../test-helpers.mjs';
11
12
  import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
12
13
  import { getFromSpace } from '../witness/space/space-helper.mjs';
13
14
  import { Metaspace_Innerspace } from '../witness/space/metaspace/metaspace-innerspace/metaspace-innerspace.mjs';
14
15
  import { InnerSpace_V1 } from '../witness/space/inner-space/inner-space-v1.mjs';
15
- import { createTimelineRootTestHelper, getTestKeystoneServiceHelper } from '../test-helpers.mjs';
16
- import { appendToTimeline, mut8Timeline } from '../timeline/timeline-api.mjs';
17
16
  import { SyncPeerInnerspace_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-v1.mjs';
18
17
  import { DEFAULT_INNER_SPACE_DATA_V1 } from '../witness/space/inner-space/inner-space-types.mjs';
19
- import { getIbGibsFromCache_fallbackToSpaces, getTjpAddr } from '../common/other/ibgib-helper.mjs';
20
- import { fnObs } from '../common/pubsub/observer/observer-helper.mjs';
18
+ import { getIbGibsFromCache_fallbackToSpaces, getTjpAddr, } from '../common/other/ibgib-helper.mjs';
21
19
  import { getDependencyGraph, graphsAreEquivalent } from '../common/other/graph-helper.mjs';
22
20
  import { SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1 } from './sync-peer/sync-peer-innerspace/sync-peer-innerspace-constants.mjs';
23
21
  import { SyncConflictStrategy } from './sync-constants.mjs';
24
- import { GRAFT_BASE_REL8N_NAME, GRAFT_INFO_REL8N_NAME, GRAFT_ORPHAN_REL8N_NAME } from './graft-info/graft-info-constants.mjs';
25
- const logalot = false;
22
+ import { getHistory } from '../timeline/timeline-api.mjs';
23
+ const logalot = GLOBAL_LOG_A_LOT;
26
24
  const lc = sir;
27
25
  const TEST_REL8N_NAME = 'testrel8n';
28
- await respecfully(sir, `Two different fields and rel8d`, async () => {
29
- // 1. Setup Spaces
26
+ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
27
+ // #region Init/Setup
30
28
  const metaspace = new Metaspace_Innerspace(undefined);
31
29
  await metaspace.initialize({
32
30
  getFnAlert: () => async ({ title, msg }) => { console.log(`[Alert] ${title}: ${msg}`); },
@@ -52,287 +50,325 @@ await respecfully(sir, `Two different fields and rel8d`, async () => {
52
50
  description: 'dest test space',
53
51
  });
54
52
  await destSpace.initialized;
55
- // 2. Seed common History (v0 -> v1)
56
- console.log(`${lc} Creating common History...`);
57
- const alpha_v0 = await createTimelineRootTestHelper({
58
- /**
59
- * greek letters are distinct timelines
60
- */
61
- ib: 'alpha',
62
- data: { type: 'alpha', },
63
- space: sourceSpace,
64
- });
65
- // Create v1 (common)
53
+ const testTransformer = new TestTransformer(metaspace, sourceSpace, destSpace);
54
+ if (logalot) {
55
+ console.log(`${lc} Setting up Coordinators...`);
56
+ }
57
+ const mockKeystone = await getTestKeystoneServiceHelper();
58
+ const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
59
+ const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
60
+ async function newTestPeer() {
61
+ const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
62
+ await peer.initialized;
63
+ await peer.initializeSender({
64
+ senderSpace: sourceSpace,
65
+ receiverSpace: destSpace,
66
+ receiverCoordinator: receiverCoordinator,
67
+ receiverMetaspace: metaspace,
68
+ });
69
+ return peer;
70
+ }
71
+ // #endregion Init/Setup
72
+ // #region Round 1: Seed common history
66
73
  /**
67
- * **created** on source, but sync will take it to dest
74
+ * not sure if we're going to do anything with the round info itself
68
75
  */
69
- const alpha_v1_common_onSource = await mut8Timeline({
70
- timeline: alpha_v0,
71
- mut8Opts: {
72
- mut8Ib: alpha_v0.ib + '_common',
73
- dataToAddOrPatch: { commonField: 'common yo' }
74
- },
76
+ const _r1 = testTransformer.newRound({
77
+ name: 'r1_seedCommonHistory',
78
+ description: 'Create alpha v0 and v1 on source, sync to dest so both v0 and v1 are established as synced.',
79
+ });
80
+ // #region r1 setup
81
+ if (logalot) {
82
+ console.log(`${lc} Creating common History...`);
83
+ }
84
+ const r1_alpha_v0_source = await testTransformer.create({
85
+ atom: 'alpha',
86
+ in: 'source',
87
+ name: 'r1_alpha_v0_source', // should be auto-calculated?
88
+ });
89
+ // Create v1 (common) - will be synced to dest
90
+ const r1_alpha_v1_source_common = await testTransformer.mut8({
91
+ stepInfo: r1_alpha_v0_source,
92
+ strField: 'commonField',
93
+ name: 'r1_alpha_v1_source_common'
94
+ });
95
+ const alpha_tjpAddr = getTjpAddr({ ibGib: r1_alpha_v0_source.ibGib, defaultIfNone: 'incomingAddr' });
96
+ // #endregion r1 setup
97
+ await respecfully(sir, `r1 verify pre`, async () => {
98
+ await ifWeMight(sir, 'dest should NOT have alpha', async () => {
99
+ const resGet = await getFromSpace({
100
+ space: destSpace,
101
+ addr: alpha_tjpAddr,
102
+ });
103
+ // When timeline doesn't exist, getFromSpace returns success:false
104
+ iReckon(sir, resGet.success).asTo('getFromSpace should return false for missing timeline').isGonnaBeFalse();
105
+ iReckon(sir, resGet.ibGibs?.length ?? 0).asTo('Dest should NOT have alpha timeline yet').isGonnaBe(0);
106
+ });
107
+ });
108
+ const r1_syncSaga = await senderCoordinator.sync({
109
+ peer: await newTestPeer(),
110
+ localSpace: sourceSpace,
75
111
  metaspace,
76
- space: sourceSpace,
112
+ domainIbGibs: [r1_alpha_v1_source_common.ibGib],
113
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
114
+ useSessionIdentity: false,
77
115
  });
78
- const tjpAddr = getTjpAddr({ ibGib: alpha_v0, defaultIfNone: 'incomingAddr' }) ??
79
- getIbGibAddr({ ibGib: alpha_v0 });
80
- // Sync alpha to dest immediately so they start synced
81
- // must get the entire dependency graph for alpha
82
- const depGraph_alpha_v1Andcommon = await getDependencyGraph({
83
- ibGibAddrs: [tjpAddr],
84
- space: sourceSpace,
85
- live: true,
86
- }) ?? {};
87
- console.log(`${lc} depGraph_testRootAndV1Common: ${pretty(depGraph_alpha_v1Andcommon)}`);
88
- if (Object.keys(depGraph_alpha_v1Andcommon).length === 0) {
89
- throw new Error(`(UNEXPECTED) depGraph_testRootAndV1Common empty? (E: 39b4d855ffa65476084b4123786da826)`);
116
+ await r1_syncSaga.done;
117
+ if (logalot) {
118
+ console.log(`${lc} r1_syncSaga Complete.`);
90
119
  }
91
- // put the entire graph into the destspace, but...
92
- await metaspace.put({ ibGibs: Object.values(depGraph_alpha_v1Andcommon), space: destSpace });
93
- const resGetDest = await getFromSpace({ space: destSpace, addr: getIbGibAddr({ ibGib: alpha_v1_common_onSource }) });
120
+ await respecfully(sir, `r1 verify post`, async () => {
121
+ await ifWeMight(sir, 'dest should have alpha', async () => {
122
+ // alpha's full dep graph should exist on dest
123
+ const [alpha_dest] = await getIbGibsFromCache_fallbackToSpaces({
124
+ addrs: [r1_alpha_v0_source.addr],
125
+ space: destSpace,
126
+ });
127
+ iReckon(sir, alpha_dest).asTo('alpha exists in dest').isGonnaBeTruthy();
128
+ if (alpha_dest) {
129
+ const depGraph_alpha_source = await getDependencyGraph({ ibGib: r1_alpha_v0_source.ibGib, space: sourceSpace });
130
+ const depGraph_alpha_dest = await getDependencyGraph({ ibGib: alpha_dest, space: destSpace });
131
+ const alphaDepsAreEqual = graphsAreEquivalent({
132
+ graphA: depGraph_alpha_source, graphB: depGraph_alpha_dest
133
+ });
134
+ iReckon(sir, alphaDepsAreEqual).asTo('alpha got synced and graphs are equal').isGonnaBeTrue();
135
+ }
136
+ });
137
+ });
138
+ // Get alpha_v1_common from destSpace for Round 2
139
+ const resGetDest = await getFromSpace({ space: destSpace, addr: alpha_tjpAddr });
94
140
  if (!resGetDest.success || !resGetDest.ibGibs || resGetDest.ibGibs.length === 0) {
95
- throw new Error(`Failed to retrieve v1_Common_onDest from destSpace. (E: 40c67811eba14f729880a46dcbb65126)`);
141
+ throw new Error(`Failed to retrieve alpha from destSpace after sync. (E: 40c67811eba14f729880a46dcbb65126)`);
96
142
  }
97
- /**
98
- * same as v1_common
99
- */
100
143
  const alpha_v1_common_onDest = resGetDest.ibGibs[0];
101
- // ...only register the main ibgibs
102
- await metaspace.registerNewIbGib({ ibGib: alpha_v0, space: destSpace });
103
- await metaspace.registerNewIbGib({ ibGib: alpha_v1_common_onDest, space: destSpace }); // it's the "on source" one, but only because
104
- // 3. Create Divergence(s) (v2source vs v2dest), i.e. "simultaneous" parallel edits
105
- // NOTE: source vs dest here is to indicate the location the edit is made.
106
- // syncing will obviously blur this line.
107
- console.log(`${lc} Creating Divergence...`);
108
- // #region edit on source
109
- // Source: v1 (common) -> v2source (Edit Field A and rel8 beta)
110
- const beta_v0_source = await createTimelineRootTestHelper({
111
- ib: 'beta',
112
- data: {
113
- fieldA: 'beta field A created on source',
114
- },
115
- space: sourceSpace,
144
+ // #endregion Round 1: Seed common history
145
+ // #region Round 2: Create Divergence (v2source vs v2dest) - "simultaneous" parallel edits
146
+ const _r2 = testTransformer.newRound({
147
+ name: 'r2_divergent_edits',
148
+ description: 'Create conflicts on alpha, add beta relation on source, separate edit on dest'
116
149
  });
117
- const betaAddr_v0 = getIbGibAddr({ ibGib: beta_v0_source });
118
- const alpha_v2_source_sansBeta = await mut8Timeline({
119
- timeline: alpha_v1_common_onSource,
120
- mut8Opts: {
121
- mut8Ib: alpha_v0.ib + '_v2source',
122
- dataToAddOrPatch: { fieldA: 'source_edit' }
123
- },
124
- metaspace,
125
- space: sourceSpace,
150
+ // #region r2 source edits
151
+ if (logalot) {
152
+ console.log(`${lc} Creating Divergence...`);
153
+ }
154
+ // Source: Create beta, mutate alpha fieldA, add beta relation
155
+ const r2_beta_v0_source = await testTransformer.create({
156
+ atom: 'beta',
157
+ in: 'source',
158
+ data: { fieldA: 'beta field A created on source' },
159
+ name: 'r2_beta_v0_source'
126
160
  });
127
- const alpha_v3_source_rel8beta = await appendToTimeline({
128
- timeline: alpha_v2_source_sansBeta,
129
- metaspace,
130
- space: sourceSpace,
131
- rel8nInfos: [
132
- { rel8nName: TEST_REL8N_NAME, ibGibs: [beta_v0_source] }
133
- ],
161
+ const r2_alpha_v2_source_arbitraryMut8 = await testTransformer.mut8({
162
+ ibGib: r1_alpha_v1_source_common.ibGib,
163
+ in: 'source',
164
+ strField: 'fieldA',
165
+ name: 'r2_alpha_v2_source_arbitraryMut8',
166
+ comments_snake_plus_CamelCase: 'editedOnSource',
134
167
  });
135
- /**
136
- * since we're doing this first, this will be the base in our graft
137
- */
138
- const baseAddr = getIbGibAddr({ ibGib: alpha_v3_source_rel8beta });
139
- // #endregion edit on source
140
- /**
141
- * timestamp/timestampMs drives orphan/base timeline selection in
142
- * graftTimelines
143
- */
144
- await delay(16);
145
- // #region edit on dest
146
- // Dest: v1 (common) -> alpha_v2_dest (Edit Field B)
147
- const alpha_v2_dest = await mut8Timeline({
148
- timeline: alpha_v1_common_onDest,
149
- mut8Opts: {
150
- mut8Ib: alpha_v0.ib + '_v2dest',
151
- dataToAddOrPatch: { fieldB: 'dest_edit' }
152
- },
153
- metaspace,
154
- space: destSpace,
168
+ const r2_alpha_v3_source_rel8dBeta = await testTransformer.rel8({
169
+ stepInfo: r2_alpha_v2_source_arbitraryMut8,
170
+ in: 'source',
171
+ targetIbGibs: [r2_beta_v0_source.ibGib],
172
+ rel8nName: TEST_REL8N_NAME,
173
+ name: 'r2_alpha_v3_source_rel8dBeta'
155
174
  });
156
- /**
157
- * since we're doing this second (i.e., later temporally), this will be
158
- * the orphan in our graft
159
- */
160
- const orphanAddr = getIbGibAddr({ ibGib: alpha_v2_dest });
161
- // Verify Divergence
162
- console.log(`${lc} Divergence created.`);
163
- // 4. Run Sync (Optimistic)
164
- console.log(`${lc} Setting up Coordinators...`);
165
- const mockKeystone = await getTestKeystoneServiceHelper();
166
- const senderCoordinator = new SyncSagaCoordinator(mockKeystone);
167
- const receiverCoordinator = new SyncSagaCoordinator(mockKeystone);
168
- const peer = new SyncPeerInnerspace_V1(clone(SYNC_PEER_INNERSPACE_DEFAULT_DATA_V1));
169
- await peer.initialized;
170
- await peer.initializeSender({
171
- senderSpace: sourceSpace, // "Client"
172
- receiverSpace: destSpace, // "Server"
173
- receiverCoordinator,
174
- receiverMetaspace: metaspace,
175
+ // #endregion r2 source edits
176
+ // #region r2 dest edits
177
+ // Dest: Mutate alpha fieldB (different field than source)
178
+ const r2_alpha_v2_dest_mut8FieldB = await testTransformer.mut8({
179
+ ibGib: alpha_v1_common_onDest,
180
+ in: 'dest',
181
+ strField: 'fieldB',
182
+ name: 'r2_alpha_v2_dest_mut8FieldB',
183
+ comments_snake_plus_CamelCase: 'editedOnDest',
175
184
  });
185
+ if (logalot) {
186
+ console.log(`${lc} Divergence created.`);
187
+ }
188
+ // #endregion r2 dest edits
176
189
  // Verify Receiver has correct KV (Pre-Sync Check)
177
190
  // This ensures the conflict precondition exists.
178
- await ifWeMight(sir, 'verify receiver KV pre-sync', async () => {
179
- try {
180
- const destKV = await receiverCoordinator.getKnowledgeMap({
181
- space: destSpace,
182
- metaspace,
183
- domainIbGibs: [alpha_v1_common_onDest]
184
- });
185
- const v1TjpAddr = getTjpAddr({ ibGib: alpha_v1_common_onDest, defaultIfNone: 'incomingAddr' }) ?? getIbGibAddr({ ibGib: alpha_v1_common_onDest });
186
- // Actually v1 has tjp rel8n.
187
- const destTip = destKV[v1TjpAddr];
188
- iReckon(sir, !!destTip).asTo(`Dest KV has timeline tip for ${v1TjpAddr}`).isGonnaBeTruthy();
189
- if (!destTip) {
190
- throw new Error(`Test Setup Fail: Dest Space does not have index for timeline ${v1TjpAddr}. Seeding failed. (E: c194a80b4e4877b77826c37a1753b826)`);
191
+ await respecfully(sir, `r2 verify pre`, async () => {
192
+ await ifWeMight(sir, 'dest has alpha v1 common', async () => {
193
+ try {
194
+ const destKV = await receiverCoordinator.getKnowledgeMap({
195
+ space: destSpace,
196
+ metaspace,
197
+ domainIbGibs: [alpha_v1_common_onDest]
198
+ });
199
+ const alpha_v1_common_tjpAddr_onDest = getTjpAddr({ ibGib: alpha_v1_common_onDest, defaultIfNone: 'incomingAddr' });
200
+ const destTip = destKV[alpha_v1_common_tjpAddr_onDest];
201
+ iReckon(sir, !!destTip).asTo(`Dest KV has timeline tip for ${alpha_v1_common_tjpAddr_onDest}`).isGonnaBeTruthy();
202
+ if (!destTip) {
203
+ throw new Error(`Test Setup Fail: Dest Space does not have index for timeline ${alpha_v1_common_tjpAddr_onDest}. Seeding failed. (E: c194a80b4e4877b77826c37a1753b826)`);
204
+ }
191
205
  }
192
- }
193
- catch (error) {
194
- console.error(`${lc} ${extractErrorMsg(error)}`);
195
- iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
196
- }
206
+ catch (error) {
207
+ console.error(`${lc} ${extractErrorMsg(error)}`);
208
+ iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
209
+ }
210
+ });
197
211
  });
198
- console.log(`${lc} Running Sync (ConflictStrategy: optimistic)...`);
199
- let resSync;
212
+ if (logalot) {
213
+ console.log(`${lc} Running Sync (ConflictStrategy: optimistic)...`);
214
+ }
215
+ let r2_syncSaga;
200
216
  try {
201
- console.dir(sourceSpace);
202
- console.dir(destSpace);
203
- resSync = await senderCoordinator.sync({
204
- peer,
217
+ if (logalot) {
218
+ console.dir(sourceSpace);
219
+ }
220
+ if (logalot) {
221
+ console.dir(destSpace);
222
+ }
223
+ r2_syncSaga = await senderCoordinator.sync({
224
+ peer: await newTestPeer(),
205
225
  localSpace: sourceSpace,
206
226
  metaspace,
207
- domainIbGibs: [alpha_v3_source_rel8beta],
227
+ domainIbGibs: [r2_alpha_v3_source_rel8dBeta.ibGib],
208
228
  conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
209
229
  useSessionIdentity: false,
210
230
  });
211
- const sublc = `${lc}[updates$]`;
212
- /**
213
- * I have added this so you can see how to subscribe to an ibgib
214
- * observable using {@link fnObs}.
215
- */
216
- const subscription = await resSync.updates$.subscribe(fnObs({
217
- next: async (ctxIbGib) => {
218
- // console.log(`${sublc} next fired. ${JSON.stringify(ctxIbGib)}`);
219
- console.log(`${sublc} next fired. (I: e68d8894bac8800f9f3430e8a38d6626)`);
220
- },
221
- error: async (e) => {
222
- if (e.data) {
223
- console.error(`${sublc} error fired. error: ${JSON.stringify(e.data)} (E: eddf17f76a486b9c5a2f4ee86ed38b26)`);
224
- }
225
- else {
226
- console.dir(e);
227
- console.error(`${sublc} error fired. error: ${extractErrorMsg(e)} (E: af9c3b6f1c88befeff77ca46111b3826)`);
228
- }
229
- },
230
- complete: async () => {
231
- console.log(`${sublc} complete fired`);
232
- },
233
- }));
234
- console.log(`${lc} awaiting resSync.done`);
235
- await resSync.done;
236
- console.log(`${lc} Sync Complete.`);
231
+ if (logalot) {
232
+ console.log(`${lc} awaiting r2_syncSaga.done`);
233
+ }
234
+ await r2_syncSaga.done;
235
+ if (logalot) {
236
+ console.log(`${lc} r2_syncSaga Complete.`);
237
+ }
237
238
  }
238
239
  catch (e) {
239
240
  console.error(`${lc} Sync Failed with Error:`, e);
240
241
  iReckon(sir, false).asTo(`Sync failed with error: ${e}`).isGonnaBeTruthy();
241
242
  return; // Exit test early
242
243
  }
243
- // 5. Verification
244
244
  // Expectation: Both Spaces should now have a NEW tip (V3) that merges v2source and v2dest.
245
- const resSourceTip = await getFromSpace({ space: sourceSpace, addr: getIbGibAddr({ ibGib: alpha_v3_source_rel8beta }) });
246
- await respecfully(sir, `verify merge`, async () => {
245
+ await respecfully(sir, `r2 verify post`, async () => {
247
246
  // Retrieve updated KV from Source (or check what happened to v2source)
248
247
  // Ideally, we just check the source space for the timeline tip
249
248
  // The timeline tip for alpha/v0 should now be NEW.
250
249
  try {
251
250
  // Get the KV for the Source Space
252
- const sourceKV_afterSync = await senderCoordinator.getKnowledgeMap({
251
+ const alpha_sourceKV_afterSync = await senderCoordinator.getKnowledgeMap({
253
252
  space: sourceSpace,
254
253
  metaspace,
255
- domainIbGibs: [alpha_v0] // We want to know the tip of this timeline
254
+ domainIbGibs: [r1_alpha_v0_source.ibGib] // We want to know the tip of this timeline
255
+ });
256
+ if (logalot) {
257
+ console.log(`${lc} getKnowledgeMap returned. sourceKV_afterSync: ${pretty(alpha_sourceKV_afterSync)} (I: e8780cda37c8b2a46eeb85786874e926)`);
258
+ }
259
+ const alpha_source_tipAddr = alpha_sourceKV_afterSync[alpha_tjpAddr];
260
+ if (logalot) {
261
+ console.log(`${lc} alpha_source_tipAddr: ${alpha_source_tipAddr} (I: 34590f29c9b706ba04a54b0f633a6426)`);
262
+ }
263
+ if (!alpha_source_tipAddr) {
264
+ throw new Error(`Source Space missing timeline tip for ${alpha_tjpAddr} (E: ec95980b9c980c5c5870812e15e43826)`);
265
+ }
266
+ const alpha_destKV_afterSync = await senderCoordinator.getKnowledgeMap({
267
+ space: destSpace,
268
+ metaspace,
269
+ domainIbGibs: [r1_alpha_v0_source.ibGib] // We want to know the tip of this timeline
256
270
  });
257
- const tjpAddr = getTjpAddr({ ibGib: alpha_v0, defaultIfNone: 'incomingAddr' }) ??
258
- getIbGibAddr({ ibGib: alpha_v0 });
259
271
  if (logalot) {
260
- console.log(`${lc} getKnowledgeMap returned. sourceKV_afterSync: ${pretty(sourceKV_afterSync)} (I: e8780cda37c8b2a46eeb85786874e926)`);
272
+ console.log(`${lc} getKnowledgeMap returned. destKV_afterSync: ${pretty(alpha_destKV_afterSync)} (I: e8780cda37c8b2a46eeb85786874e926)`);
273
+ }
274
+ const alpha_dest_tipAddr = alpha_destKV_afterSync[alpha_tjpAddr];
275
+ if (logalot) {
276
+ console.log(`${lc} alpha_dest_tipAddr: ${alpha_dest_tipAddr} (I: afac71eba4e6d3f3c47952680ad8b826)`);
261
277
  }
262
- const sourceTipAddr = sourceKV_afterSync[tjpAddr];
263
- if (!sourceTipAddr) {
264
- throw new Error(`Source Space missing timeline tip for ${tjpAddr} (E: ec95980b9c980c5c5870812e15e43826)`);
278
+ if (!alpha_dest_tipAddr) {
279
+ throw new Error(`dest Space missing timeline tip for ${alpha_tjpAddr} (E: 30b018c6349917aa28c9f538fa567826)`);
265
280
  }
281
+ await ifWeMight(sir, 'tip addrs', async () => {
282
+ iReckon(sir, alpha_source_tipAddr).asTo('source/dest have same tip addrs').isGonnaBe(alpha_dest_tipAddr);
283
+ });
266
284
  // Fetch the new tip
267
- const resTip = await getFromSpace({ space: sourceSpace, addr: sourceTipAddr });
268
- const newTip = resTip.ibGibs[0];
269
- await ifWeMight(sir, 'basics of alpha merge', async () => {
270
- iReckon(sir, sourceTipAddr).asTo(`Source Tip (${sourceTipAddr}) should NOT be alpha_v3_source_rel8beta`).not.isGonnaBe(getIbGibAddr({ ibGib: alpha_v3_source_rel8beta }));
271
- iReckon(sir, sourceTipAddr).asTo(`Source Tip (${sourceTipAddr}) should NOT be v2dest`).not.isGonnaBe(getIbGibAddr({ ibGib: alpha_v2_dest }));
272
- // Check Data: Should have BOTH edits
273
- iReckon(sir, newTip.data.fieldA).asTo('New Tip Data Field A').isGonnaBe('source_edit');
274
- iReckon(sir, newTip.data.fieldB).asTo('New Tip Data Field B').isGonnaBe('dest_edit');
285
+ const resGet_alpha_source_tipAddr = await getFromSpace({ space: sourceSpace, addr: alpha_source_tipAddr });
286
+ const gotten_alpha_source_tipIbGib = resGet_alpha_source_tipAddr.ibGibs[0];
287
+ if (logalot || true) {
288
+ console.log(`${lc} gotten_alpha_source_tipIbGib:\n${pretty(gotten_alpha_source_tipIbGib)} (I: e9c608c1fcb85f08d17d4af319d62726)`);
289
+ }
290
+ // #region DEBUG SANITY CHECK
291
+ const resGetTjp = await getFromSpace({ space: sourceSpace, addr: alpha_tjpAddr });
292
+ const gottenTjp = resGetTjp.ibGibs[0];
293
+ debugger;
294
+ const history = await getHistory({
295
+ timeline: gotten_alpha_source_tipIbGib,
296
+ space: sourceSpace,
297
+ metaspace,
298
+ includeDna: true,
275
299
  });
276
- // Check Graft Structure
277
- // Look for 'graftinfo' rel8n
278
- await ifWeMight(sir, 'graft info is valid', async () => {
279
- if (!newTip.rel8ns) {
280
- throw new Error(`(UNEXPECTED) newTip.rel8ns falsy? (E: eb4078712ea2f02c7ee84b983ed77826)`);
281
- }
282
- const graftInfoRel = newTip.rel8ns[GRAFT_INFO_REL8N_NAME];
283
- iReckon(sir, graftInfoRel).asTo('New Tip should have graftinfo rel8n').isGonnaBeTruthy();
284
- if (!graftInfoRel) {
285
- return; /* <<<< returns early, above reckoning fails */
286
- }
287
- iReckon(sir, graftInfoRel.length).asTo('Only 1 graftinfo').isGonnaBe(1);
288
- // Fetch Graft Info Stone
289
- const resGraft = await getFromSpace({ space: sourceSpace, addr: graftInfoRel[0] });
290
- const graftInfo = resGraft.ibGibs[0];
291
- // Check Graft Relations (graftbase, graftorphan)
292
- if (!graftInfo.rel8ns) {
293
- throw new Error(`(UNEXPECTED) graftInfo.rel8ns falsy? (E: 8e39e84e9e0813e09e8f6788ce488926)`);
294
- }
295
- const baseRel = graftInfo.rel8ns[GRAFT_BASE_REL8N_NAME];
296
- const orphanRel = graftInfo.rel8ns[GRAFT_ORPHAN_REL8N_NAME];
297
- iReckon(sir, baseRel).asTo('Graft Base exists').isGonnaBeTruthy();
298
- iReckon(sir, baseRel.length).asTo('Graft Base length is 2').isGonnaBe(2);
299
- iReckon(sir, orphanRel).asTo('Graft Orphan exists').isGonnaBeTruthy();
300
- iReckon(sir, orphanRel.length).asTo('Graft Orphan length is 1').isGonnaBe(1);
301
- iReckon(sir, baseRel.at(-1)).asTo('base rel8ns final addr will be baseAddr').isGonnaBe(baseAddr);
302
- iReckon(sir, orphanRel[0]).asTo('orphan rel8ns will be later addr').isGonnaBe(orphanAddr);
300
+ debugger;
301
+ // #endregion DEBUG SANITY CHECK
302
+ await ifWeMight(sir, 'r2 basics of alpha merge', async () => {
303
+ iReckon(sir, alpha_source_tipAddr)
304
+ .asTo(`Source Tip (${alpha_source_tipAddr}) should NOT be r2_alpha_v3_source_rel8dBeta`)
305
+ .not.isGonnaBe(r2_alpha_v3_source_rel8dBeta.addr);
306
+ iReckon(sir, alpha_source_tipAddr)
307
+ .asTo(`Source Tip (${alpha_source_tipAddr}) should NOT be r2_alpha_v2_dest_mut8FieldB`)
308
+ .not.isGonnaBe(r2_alpha_v2_dest_mut8FieldB.addr);
309
+ // Check Data: Should have BOTH edits (using testTransformer-generated values)
310
+ const r2_source_mut8Info = r2_alpha_v2_source_arbitraryMut8.infos[0];
311
+ const r2_dest_mut8Info = r2_alpha_v2_dest_mut8FieldB.infos[0];
312
+ const a = gotten_alpha_source_tipIbGib.data[r2_source_mut8Info.key];
313
+ const a2 = r2_source_mut8Info.value;
314
+ console.log(`${a} should be ${a2}`);
315
+ iReckon(sir, gotten_alpha_source_tipIbGib.data[r2_source_mut8Info.key]).asTo(`New Tip has source field ${r2_source_mut8Info.key}`).isGonnaBe(r2_source_mut8Info.value);
316
+ const b = gotten_alpha_source_tipIbGib.data[r2_dest_mut8Info.key];
317
+ const b2 = r2_dest_mut8Info.value;
318
+ console.log(`${b} should be ${b2}`);
319
+ iReckon(sir, gotten_alpha_source_tipIbGib.data[r2_dest_mut8Info.key]).asTo(`New Tip has dest field ${r2_dest_mut8Info.key}`).isGonnaBe(r2_dest_mut8Info.value);
303
320
  });
304
- await ifWeMight(sir, 'alpha and deps synced', async () => {
305
- // alpha's full dep graph should exist on dest
306
- const [beta_dest] = await getIbGibsFromCache_fallbackToSpaces({
307
- addrs: [betaAddr_v0],
321
+ await ifWeMight(sir, 'r2 alpha and deps synced', async () => {
322
+ // alpha's full dep graph should exist on dest, even though its
323
+ // timeline was grafted
324
+ const [alpha_dest] = await getIbGibsFromCache_fallbackToSpaces({
325
+ addrs: [r1_alpha_v0_source.addr],
308
326
  space: destSpace,
309
327
  });
310
- iReckon(sir, beta_dest).asTo('beta exists in dest').isGonnaBeTruthy();
311
- if (beta_dest) {
312
- const depGraph_beta_source = await getDependencyGraph({ ibGib: beta_v0_source, space: sourceSpace });
313
- if (Object.keys(depGraph_alpha_v1Andcommon).length === 0) {
314
- throw new Error(`(UNEXPECTED) depGraph_testRootAndV1Common empty? (E: 39b4d855ffa65476084b4123786da826)`);
328
+ iReckon(sir, alpha_dest).asTo('alpha exists in dest').isGonnaBeTruthy();
329
+ if (alpha_dest) {
330
+ try {
331
+ const depGraph_alpha_source = await getDependencyGraph({
332
+ ibGib: r1_alpha_v0_source.ibGib,
333
+ live: true,
334
+ space: sourceSpace
335
+ });
336
+ const depGraph_alpha_dest = await getDependencyGraph({
337
+ ibGib: alpha_dest,
338
+ live: true,
339
+ space: destSpace
340
+ });
341
+ const alphaDepsAreEqual = graphsAreEquivalent({
342
+ graphA: depGraph_alpha_source,
343
+ graphB: depGraph_alpha_dest,
344
+ slowButThorough: true,
345
+ });
346
+ iReckon(sir, alphaDepsAreEqual).asTo('alpha got synced and graphs are equal').isGonnaBeTrue();
347
+ }
348
+ catch (error) {
349
+ console.error(`${lc} ${extractErrorMsg(error)}`);
350
+ iReckon(sir, true).asTo('ERROR: alpha exists in dest').isGonnaBeFalse(); // fails
315
351
  }
316
- const depGraph_beta_dest = await getDependencyGraph({ ibGib: beta_dest, space: destSpace });
317
- const betaDepsAreEqual = graphsAreEquivalent({
318
- graphA: depGraph_beta_source, graphB: depGraph_beta_dest
319
- });
320
- iReckon(sir, betaDepsAreEqual).asTo('beta got synced and graphs are equal').isGonnaBeTrue();
321
352
  }
322
353
  });
323
- await ifWeMight(sir, 'beta and deps synced', async () => {
354
+ await ifWeMight(sir, 'r2 beta and deps synced', async () => {
324
355
  // beta's full dep graph should exist on dest
325
356
  const [beta_dest] = await getIbGibsFromCache_fallbackToSpaces({
326
- addrs: [betaAddr_v0],
357
+ addrs: [r2_beta_v0_source.addr],
327
358
  space: destSpace,
328
359
  });
329
360
  iReckon(sir, beta_dest).asTo('beta exists in dest').isGonnaBeTruthy();
330
361
  if (beta_dest) {
331
- const depGraph_beta_source = await getDependencyGraph({ ibGib: beta_v0_source, space: sourceSpace });
332
- if (Object.keys(depGraph_alpha_v1Andcommon).length === 0) {
333
- throw new Error(`(UNEXPECTED) depGraph_testRootAndV1Common empty? (E: 39b4d855ffa65476084b4123786da826)`);
334
- }
335
- const depGraph_beta_dest = await getDependencyGraph({ ibGib: beta_dest, space: destSpace });
362
+ const depGraph_beta_source = await getDependencyGraph({
363
+ ibGib: r2_beta_v0_source.ibGib,
364
+ live: true,
365
+ space: sourceSpace
366
+ });
367
+ const depGraph_beta_dest = await getDependencyGraph({
368
+ ibGib: beta_dest,
369
+ live: true,
370
+ space: destSpace
371
+ });
336
372
  const betaDepsAreEqual = graphsAreEquivalent({
337
373
  graphA: depGraph_beta_source,
338
374
  graphB: depGraph_beta_dest,
@@ -347,5 +383,55 @@ await respecfully(sir, `Two different fields and rel8d`, async () => {
347
383
  iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
348
384
  }
349
385
  });
386
+ // #endregion Round 2: Create Divergence (v2source vs v2dest) - "simultaneous" parallel edits
387
+ // #region Round 3: Multiple Timeline Conflicts
388
+ const _r3 = testTransformer.newRound({
389
+ name: 'r3_multiple_timeline_conflicts',
390
+ description: 'Both alpha AND beta have divergent edits on source vs dest - tests multiple simultaneous grafts'
391
+ });
392
+ // #region r3 source edits
393
+ if (logalot) {
394
+ console.log(`${lc} Creating r3 divergent edits on source...`);
395
+ }
396
+ // Source: Mutate alpha fieldC, mutate beta betaFieldA
397
+ // TODO: Add source edits here
398
+ // #endregion r3 source edits
399
+ // #region r3 dest edits
400
+ if (logalot) {
401
+ console.log(`${lc} Creating r3 divergent edits on dest...`);
402
+ }
403
+ // Dest: Mutate alpha fieldD, mutate beta betaFieldB
404
+ // TODO: Add dest edits here
405
+ // #endregion r3 dest edits
406
+ await respecfully(sir, `r3 verify pre`, async () => {
407
+ // TODO: Verify pre-sync state
408
+ await ifWeMight(sir, 'source has divergent alpha and beta', async () => {
409
+ // Verify source has its versions
410
+ });
411
+ await ifWeMight(sir, 'dest has divergent alpha and beta', async () => {
412
+ // Verify dest has its versions
413
+ });
414
+ });
415
+ if (logalot) {
416
+ console.log(`${lc} Running r3 Sync...`);
417
+ }
418
+ // TODO: Add sync operation here
419
+ // const r3_syncSaga = await senderCoordinator.sync({...});
420
+ await respecfully(sir, `r3 verify post`, async () => {
421
+ // TODO: Verify post-sync state
422
+ await ifWeMight(sir, 'r3 alpha merge has both edits', async () => {
423
+ // Verify alpha has fieldC (source) AND fieldD (dest)
424
+ });
425
+ await ifWeMight(sir, 'r3 beta merge has both edits', async () => {
426
+ // Verify beta has betaFieldA (source) AND betaFieldB (dest)
427
+ });
428
+ await ifWeMight(sir, 'r3 alpha and deps synced', async () => {
429
+ // Verify alpha's full dep graph on both spaces
430
+ });
431
+ await ifWeMight(sir, 'r3 beta and deps synced', async () => {
432
+ // Verify beta's full dep graph on both spaces
433
+ });
434
+ });
435
+ // #endregion Round 3: Multiple Timeline Conflicts
350
436
  });
351
437
  //# sourceMappingURL=sync-conflict-adv-multitimelines.respec.mjs.map