@ibgib/core-gib 0.1.40 → 0.1.42

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.
@@ -4,9 +4,10 @@
4
4
  * Verifies Conflict Resolution strategies in SyncSagaCoordinator.
5
5
  * Reproduces divergence scenarios and asserts resolution behavior.
6
6
  */
7
- import { respecfully, iReckon, ifWeMight } from '@ibgib/helper-gib/dist/respec-gib/respec-gib.mjs';
7
+ import { respecfully, ifWe, iReckon } 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
11
  import { GLOBAL_LOG_A_LOT } from '../core-constants.mjs';
11
12
  import { TestTransformer, getTestKeystoneServiceHelper } from '../test-helpers.mjs';
12
13
  import { SyncSagaCoordinator } from './sync-saga-coordinator.mjs';
@@ -86,16 +87,16 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
86
87
  in: 'source',
87
88
  name: 'r1_alpha_v0_source', // should be auto-calculated?
88
89
  });
90
+ const alpha_tjpAddr = getTjpAddr({ ibGib: r1_alpha_v0_source.ibGib, defaultIfNone: 'incomingAddr' });
89
91
  // Create v1 (common) - will be synced to dest
90
92
  const r1_alpha_v1_source_common = await testTransformer.mut8({
91
93
  stepInfo: r1_alpha_v0_source,
92
94
  strField: 'commonField',
93
95
  name: 'r1_alpha_v1_source_common'
94
96
  });
95
- const alpha_tjpAddr = getTjpAddr({ ibGib: r1_alpha_v0_source.ibGib, defaultIfNone: 'incomingAddr' });
96
97
  // #endregion r1 setup
97
98
  await respecfully(sir, `r1 verify pre`, async () => {
98
- await ifWeMight(sir, 'dest should NOT have alpha', async () => {
99
+ await ifWe(sir, 'dest should NOT have alpha', async () => {
99
100
  const resGet = await getFromSpace({
100
101
  space: destSpace,
101
102
  addr: alpha_tjpAddr,
@@ -118,7 +119,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
118
119
  console.log(`${lc} r1_syncSaga Complete.`);
119
120
  }
120
121
  await respecfully(sir, `r1 verify post`, async () => {
121
- await ifWeMight(sir, 'dest should have alpha', async () => {
122
+ await ifWe(sir, 'dest should have alpha', async () => {
122
123
  // alpha's full dep graph should exist on dest
123
124
  const [alpha_dest] = await getIbGibsFromCache_fallbackToSpaces({
124
125
  addrs: [r1_alpha_v0_source.addr],
@@ -136,7 +137,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
136
137
  });
137
138
  });
138
139
  // Get alpha_v1_common from destSpace for Round 2
139
- const resGetDest = await getFromSpace({ space: destSpace, addr: alpha_tjpAddr });
140
+ const resGetDest = await getFromSpace({ space: destSpace, addr: r1_alpha_v1_source_common.addr });
140
141
  if (!resGetDest.success || !resGetDest.ibGibs || resGetDest.ibGibs.length === 0) {
141
142
  throw new Error(`Failed to retrieve alpha from destSpace after sync. (E: 40c67811eba14f729880a46dcbb65126)`);
142
143
  }
@@ -189,7 +190,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
189
190
  // Verify Receiver has correct KV (Pre-Sync Check)
190
191
  // This ensures the conflict precondition exists.
191
192
  await respecfully(sir, `r2 verify pre`, async () => {
192
- await ifWeMight(sir, 'dest has alpha v1 common', async () => {
193
+ await ifWe(sir, 'dest has alpha v1 common', async () => {
193
194
  try {
194
195
  const destKV = await receiverCoordinator.getKnowledgeMap({
195
196
  space: destSpace,
@@ -278,7 +279,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
278
279
  if (!alpha_dest_tipAddr) {
279
280
  throw new Error(`dest Space missing timeline tip for ${alpha_tjpAddr} (E: 30b018c6349917aa28c9f538fa567826)`);
280
281
  }
281
- await ifWeMight(sir, 'tip addrs', async () => {
282
+ await ifWe(sir, 'tip addrs', async () => {
282
283
  iReckon(sir, alpha_source_tipAddr).asTo('source/dest have same tip addrs').isGonnaBe(alpha_dest_tipAddr);
283
284
  });
284
285
  // Fetch the new tip
@@ -299,7 +300,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
299
300
  });
300
301
  debugger;
301
302
  // #endregion DEBUG SANITY CHECK
302
- await ifWeMight(sir, 'r2 basics of alpha merge', async () => {
303
+ await ifWe(sir, 'r2 basics of alpha merge', async () => {
303
304
  iReckon(sir, alpha_source_tipAddr)
304
305
  .asTo(`Source Tip (${alpha_source_tipAddr}) should NOT be r2_alpha_v3_source_rel8dBeta`)
305
306
  .not.isGonnaBe(r2_alpha_v3_source_rel8dBeta.addr);
@@ -318,7 +319,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
318
319
  console.log(`${b} should be ${b2}`);
319
320
  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);
320
321
  });
321
- await ifWeMight(sir, 'r2 alpha and deps synced', async () => {
322
+ await ifWe(sir, 'r2 alpha and deps synced', async () => {
322
323
  // alpha's full dep graph should exist on dest, even though its
323
324
  // timeline was grafted
324
325
  const [alpha_dest] = await getIbGibsFromCache_fallbackToSpaces({
@@ -351,7 +352,7 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
351
352
  }
352
353
  }
353
354
  });
354
- await ifWeMight(sir, 'r2 beta and deps synced', async () => {
355
+ await ifWe(sir, 'r2 beta and deps synced', async () => {
355
356
  // beta's full dep graph should exist on dest
356
357
  const [beta_dest] = await getIbGibsFromCache_fallbackToSpaces({
357
358
  addrs: [r2_beta_v0_source.addr],
@@ -393,45 +394,632 @@ await respecfully(sir, `Multi-round/timeline permutations`, async () => {
393
394
  if (logalot) {
394
395
  console.log(`${lc} Creating r3 divergent edits on source...`);
395
396
  }
396
- // Source: Mutate alpha fieldC, mutate beta betaFieldA
397
- // TODO: Add source edits here
397
+ // Source: Get latest alpha and beta from source, then mutate both
398
+ // After R2, alpha has graft point on source. We'll mutate from that.
399
+ const resGet_r3_alpha_source = await getFromSpace({ space: sourceSpace, addr: alpha_tjpAddr });
400
+ if (!resGet_r3_alpha_source.success || !resGet_r3_alpha_source.ibGibs || resGet_r3_alpha_source.ibGibs.length === 0) {
401
+ throw new Error(`Failed to retrieve alpha from sourceSpace for R3. (E: 8e3a4f7c9b2d1a5e6f8c3d9e2b4a7c1f)`);
402
+ }
403
+ const r3_alpha_v3_graftPoint = resGet_r3_alpha_source.ibGibs[0];
404
+ const beta_tjpAddr = getTjpAddr({ ibGib: r2_beta_v0_source.ibGib, defaultIfNone: 'incomingAddr' });
405
+ const resGet_r3_beta_source = await getFromSpace({ space: sourceSpace, addr: beta_tjpAddr });
406
+ if (!resGet_r3_beta_source.success || !resGet_r3_beta_source.ibGibs || resGet_r3_beta_source.ibGibs.length === 0) {
407
+ throw new Error(`Failed to retrieve beta from sourceSpace for R3. (E: 3f9e2c8a7d5b1c4e9f6a2d8c5b7e1a3f)`);
408
+ }
409
+ const r3_beta_v0_graftPoint = resGet_r3_beta_source.ibGibs[0];
410
+ // Mutate alpha fieldC on source
411
+ const r3_alpha_v4_source_mut8fieldC = await testTransformer.mut8({
412
+ ibGib: r3_alpha_v3_graftPoint,
413
+ in: 'source',
414
+ strField: 'fieldC',
415
+ name: 'r3_alpha_v4_source_mut8fieldC',
416
+ comments_snake_plus_CamelCase: 'postGraft',
417
+ });
418
+ // Mutate beta betaFieldA on source
419
+ const r3_beta_v1_source = await testTransformer.mut8({
420
+ ibGib: r3_beta_v0_graftPoint,
421
+ in: 'source',
422
+ strField: 'betaFieldA',
423
+ name: 'r3_beta_v1_source',
424
+ });
398
425
  // #endregion r3 source edits
399
426
  // #region r3 dest edits
400
427
  if (logalot) {
401
428
  console.log(`${lc} Creating r3 divergent edits on dest...`);
402
429
  }
403
- // Dest: Mutate alpha fieldD, mutate beta betaFieldB
404
- // TODO: Add dest edits here
430
+ // Dest: Get latest alpha and beta from dest, then mutate both
431
+ const resGet_r3_alpha_dest = await getFromSpace({ space: destSpace, addr: alpha_tjpAddr });
432
+ if (!resGet_r3_alpha_dest.success || !resGet_r3_alpha_dest.ibGibs || resGet_r3_alpha_dest.ibGibs.length === 0) {
433
+ throw new Error(`Failed to retrieve alpha from destSpace for R3. (E: 2c7e9f1a4b8d3e5c7a9f2d4e6b8c1a5e)`);
434
+ }
435
+ const r3_alpha_v3_graftPoint_fromDest = resGet_r3_alpha_dest.ibGibs[0];
436
+ const resGet_r3_beta_dest = await getFromSpace({ space: destSpace, addr: beta_tjpAddr });
437
+ if (!resGet_r3_beta_dest.success || !resGet_r3_beta_dest.ibGibs || resGet_r3_beta_dest.ibGibs.length === 0) {
438
+ throw new Error(`Failed to retrieve beta from destSpace for R3. (E: 5d8c2e9a1f4b7e3c6d9a2f5e8b1c4a7e)`);
439
+ }
440
+ const r3_beta_v0_graftPoint_fromDest = resGet_r3_beta_dest.ibGibs[0];
441
+ // Mutate alpha fieldD on dest
442
+ const r3_alpha_v4_dest_mut8fieldD = await testTransformer.mut8({
443
+ ibGib: r3_alpha_v3_graftPoint_fromDest,
444
+ in: 'dest',
445
+ strField: 'fieldD',
446
+ name: 'r3_alpha_v4_dest_mut8fieldD',
447
+ comments_snake_plus_CamelCase: 'postGraft',
448
+ });
449
+ // Mutate beta betaFieldB on dest
450
+ const r3_beta_v1_dest = await testTransformer.mut8({
451
+ ibGib: r3_beta_v0_graftPoint_fromDest,
452
+ in: 'dest',
453
+ strField: 'betaFieldB',
454
+ name: 'r3_beta_v1_dest',
455
+ });
456
+ if (logalot) {
457
+ console.log(`${lc} R3 Divergence created (both alpha and beta conflicted).`);
458
+ }
405
459
  // #endregion r3 dest edits
406
460
  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
461
+ await ifWe(sir, 'dest has both alpha and beta tips (pre-conflict)', async () => {
462
+ try {
463
+ const destKV = await receiverCoordinator.getKnowledgeMap({
464
+ space: destSpace,
465
+ metaspace,
466
+ domainIbGibs: [r3_alpha_v3_graftPoint_fromDest, r3_beta_v0_graftPoint_fromDest]
467
+ });
468
+ // Verify alpha tip exists on dest
469
+ const destAlphaTip = destKV[alpha_tjpAddr];
470
+ iReckon(sir, !!destAlphaTip).asTo(`Dest KV has alpha timeline tip`).isGonnaBeTruthy();
471
+ // Verify beta tip exists on dest
472
+ const destBetaTip = destKV[beta_tjpAddr];
473
+ iReckon(sir, !!destBetaTip).asTo(`Dest KV has beta timeline tip`).isGonnaBeTruthy();
474
+ }
475
+ catch (error) {
476
+ console.error(`${lc} ${extractErrorMsg(error)}`);
477
+ iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
478
+ }
413
479
  });
414
480
  });
415
481
  if (logalot) {
416
- console.log(`${lc} Running r3 Sync...`);
482
+ console.log(`${lc} Running r3 Sync (both alpha and beta conflict)...`);
417
483
  }
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)
484
+ let r3_syncSaga;
485
+ try {
486
+ r3_syncSaga = await senderCoordinator.sync({
487
+ peer: await newTestPeer(),
488
+ localSpace: sourceSpace,
489
+ metaspace,
490
+ domainIbGibs: [r3_alpha_v4_source_mut8fieldC.ibGib, r3_beta_v1_source.ibGib],
491
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
492
+ useSessionIdentity: false,
427
493
  });
428
- await ifWeMight(sir, 'r3 alpha and deps synced', async () => {
429
- // Verify alpha's full dep graph on both spaces
494
+ if (logalot) {
495
+ console.log(`${lc} awaiting r3_syncSaga.done`);
496
+ }
497
+ await r3_syncSaga.done;
498
+ if (logalot) {
499
+ console.log(`${lc} r3_syncSaga Complete.`);
500
+ }
501
+ }
502
+ catch (e) {
503
+ console.error(`${lc} R3 Sync Failed with Error:`, e);
504
+ iReckon(sir, false).asTo(`R3 Sync failed with error: ${e}`).isGonnaBeTruthy();
505
+ return; // Exit test early
506
+ }
507
+ await respecfully(sir, `r3 verify post`, async () => {
508
+ try {
509
+ // Get KVs for both timelines after sync
510
+ const alpha_sourceKV_afterR3 = await senderCoordinator.getKnowledgeMap({
511
+ space: sourceSpace,
512
+ metaspace,
513
+ domainIbGibs: [r1_alpha_v0_source.ibGib]
514
+ });
515
+ const r3_alpha_source_tipAddr = alpha_sourceKV_afterR3[alpha_tjpAddr];
516
+ const alpha_destKV_afterR3 = await senderCoordinator.getKnowledgeMap({
517
+ space: destSpace,
518
+ metaspace,
519
+ domainIbGibs: [r1_alpha_v0_source.ibGib]
520
+ });
521
+ const r3_alpha_dest_tipAddr = alpha_destKV_afterR3[alpha_tjpAddr];
522
+ const beta_sourceKV_afterR3 = await senderCoordinator.getKnowledgeMap({
523
+ space: sourceSpace,
524
+ metaspace,
525
+ domainIbGibs: [r2_beta_v0_source.ibGib]
526
+ });
527
+ const r3_beta_source_tipAddr = beta_sourceKV_afterR3[beta_tjpAddr];
528
+ const beta_destKV_afterR3 = await senderCoordinator.getKnowledgeMap({
529
+ space: destSpace,
530
+ metaspace,
531
+ domainIbGibs: [r2_beta_v0_source.ibGib]
532
+ });
533
+ const r3_beta_dest_tipAddr = beta_destKV_afterR3[beta_tjpAddr];
534
+ if (!r3_beta_dest_tipAddr) {
535
+ ifWe(sir, 'r3_beta_dest_tipAddr is falsy?', async () => {
536
+ iReckon(sir, true).asTo('fail').isGonnaBeFalse();
537
+ });
538
+ return; /* <<<< returns early */
539
+ }
540
+ await ifWe(sir, 'r3 tip addrs match (both timelines)', async () => {
541
+ iReckon(sir, r3_alpha_source_tipAddr).asTo('alpha source/dest have same tip addrs').isGonnaBe(r3_alpha_dest_tipAddr);
542
+ iReckon(sir, r3_beta_source_tipAddr).asTo('beta source/dest have same tip addrs').isGonnaBe(r3_beta_dest_tipAddr);
543
+ });
544
+ // Fetch alpha tip ibgib
545
+ if (!r3_alpha_source_tipAddr) {
546
+ throw new Error(`r3_alpha_source_tipAddr is null/undefined (E: 746f32e97387ce2e1401ea6e1c1b4826)`);
547
+ }
548
+ const resGet_r3_alpha_tip = await getFromSpace({ space: sourceSpace, addr: r3_alpha_source_tipAddr });
549
+ const r3_alpha_tipIbGib = resGet_r3_alpha_tip.ibGibs[0];
550
+ // Fetch beta tip ibgib
551
+ if (!r3_beta_source_tipAddr) {
552
+ throw new Error(`r3_beta_source_tipAddr is null/undefined (E: 9212c9e87248017d08a243682462ba26)`);
553
+ }
554
+ const resGet_r3_beta_tip = await getFromSpace({ space: sourceSpace, addr: r3_beta_source_tipAddr });
555
+ const r3_beta_tipIbGib = resGet_r3_beta_tip.ibGibs[0];
556
+ await ifWe(sir, 'r3 alpha merge has all four fields (commonField, fieldA, fieldB, fieldC, fieldD)', async () => {
557
+ // Should have new graft point with different addr than before
558
+ iReckon(sir, r3_alpha_source_tipAddr)
559
+ .asTo(`Alpha R3 tip should NOT be r3_alpha_v4_source_mut8fieldC`)
560
+ .not.isGonnaBe(r3_alpha_v4_source_mut8fieldC.addr);
561
+ iReckon(sir, r3_alpha_source_tipAddr)
562
+ .asTo(`Alpha R3 tip should NOT be r3_alpha_v4_dest_mut8fieldD`)
563
+ .not.isGonnaBe(r3_alpha_v4_dest_mut8fieldD.addr);
564
+ // Check alpha has ALL fields: commonField (R1), fieldA (R2 source), fieldB (R2 dest), fieldC (R3 source), fieldD (R3 dest)
565
+ const r3_alpha_v4_source_mut8Info = r3_alpha_v4_source_mut8fieldC.infos[0];
566
+ const r3_alpha_v4_dest_mut8Info = r3_alpha_v4_dest_mut8fieldD.infos[0];
567
+ iReckon(sir, r3_alpha_tipIbGib.data[r3_alpha_v4_source_mut8Info.key])
568
+ .asTo(`Alpha R3 tip has source field ${r3_alpha_v4_source_mut8Info.key}`)
569
+ .isGonnaBe(r3_alpha_v4_source_mut8Info.value);
570
+ iReckon(sir, r3_alpha_tipIbGib.data[r3_alpha_v4_dest_mut8Info.key])
571
+ .asTo(`Alpha R3 tip has dest field ${r3_alpha_v4_dest_mut8Info.key}`)
572
+ .isGonnaBe(r3_alpha_v4_dest_mut8Info.value);
573
+ });
574
+ await ifWe(sir, 'r3 beta merge has both fields (betaFieldA, betaFieldB)', async () => {
575
+ // Should have new graft point with different addr than before
576
+ iReckon(sir, r3_beta_source_tipAddr)
577
+ .asTo(`Beta R3 tip should NOT be r3_beta_v1_source`)
578
+ .not.isGonnaBe(r3_beta_v1_source.addr);
579
+ iReckon(sir, r3_beta_source_tipAddr)
580
+ .asTo(`Beta R3 tip should NOT be r3_beta_v1_dest`)
581
+ .not.isGonnaBe(r3_beta_v1_dest.addr);
582
+ // Check beta has BOTH fields: betaFieldA (R3 source), betaFieldB (R3 dest)
583
+ const r3_source_beta_mut8Info = r3_beta_v1_source.infos[0];
584
+ const r3_dest_beta_mut8Info = r3_beta_v1_dest.infos[0];
585
+ iReckon(sir, r3_beta_tipIbGib.data[r3_source_beta_mut8Info.key])
586
+ .asTo(`Beta R3 tip has source field ${r3_source_beta_mut8Info.key}`)
587
+ .isGonnaBe(r3_source_beta_mut8Info.value);
588
+ iReckon(sir, r3_beta_tipIbGib.data[r3_dest_beta_mut8Info.key])
589
+ .asTo(`Beta R3 tip has dest field ${r3_dest_beta_mut8Info.key}`)
590
+ .isGonnaBe(r3_dest_beta_mut8Info.value);
591
+ });
592
+ await ifWe(sir, 'r3 alpha and deps synced', async () => {
593
+ const [alpha_dest] = await getIbGibsFromCache_fallbackToSpaces({
594
+ // addrs: [r1_alpha_v0_source.addr],
595
+ addrs: [r3_alpha_source_tipAddr],
596
+ space: destSpace,
597
+ });
598
+ iReckon(sir, alpha_dest).asTo('alpha exists in dest').isGonnaBeTruthy();
599
+ if (alpha_dest) {
600
+ try {
601
+ const depGraph_alpha_source = await getDependencyGraph({
602
+ ibGib: r1_alpha_v0_source.ibGib,
603
+ live: true,
604
+ space: sourceSpace
605
+ });
606
+ const depGraph_alpha_dest = await getDependencyGraph({
607
+ ibGib: alpha_dest,
608
+ live: true,
609
+ space: destSpace
610
+ });
611
+ const alphaDepsAreEqual = graphsAreEquivalent({
612
+ graphA: depGraph_alpha_source,
613
+ graphB: depGraph_alpha_dest,
614
+ slowButThorough: true,
615
+ });
616
+ iReckon(sir, alphaDepsAreEqual).asTo('alpha dep graph equal after R3 graft').isGonnaBeTrue();
617
+ }
618
+ catch (error) {
619
+ console.error(`${lc} ${extractErrorMsg(error)}`);
620
+ iReckon(sir, true).asTo('ERROR: alpha dep graph check failed').isGonnaBeFalse();
621
+ }
622
+ }
623
+ });
624
+ await ifWe(sir, 'r3 beta and deps synced', async () => {
625
+ const [beta_dest] = await getIbGibsFromCache_fallbackToSpaces({
626
+ // addrs: [r2_beta_v0_source.addr],
627
+ addrs: [r3_beta_dest_tipAddr],
628
+ space: destSpace,
629
+ });
630
+ iReckon(sir, beta_dest).asTo('beta exists in dest').isGonnaBeTruthy();
631
+ if (beta_dest) {
632
+ try {
633
+ const depGraph_beta_source = await getDependencyGraph({
634
+ ibGib: r2_beta_v0_source.ibGib,
635
+ live: true,
636
+ space: sourceSpace
637
+ });
638
+ const depGraph_beta_dest = await getDependencyGraph({
639
+ ibGib: beta_dest,
640
+ live: true,
641
+ space: destSpace
642
+ });
643
+ const betaDepsAreEqual = graphsAreEquivalent({
644
+ graphA: depGraph_beta_source,
645
+ graphB: depGraph_beta_dest,
646
+ slowButThorough: true,
647
+ });
648
+ iReckon(sir, betaDepsAreEqual).asTo('beta dep graph equal after R3 graft').isGonnaBeTrue();
649
+ }
650
+ catch (error) {
651
+ console.error(`${lc} ${extractErrorMsg(error)}`);
652
+ iReckon(sir, true).asTo('ERROR: beta dep graph check failed').isGonnaBeFalse();
653
+ }
654
+ }
655
+ });
656
+ }
657
+ catch (error) {
658
+ console.error(`${lc} ${extractErrorMsg(error)}`);
659
+ iReckon(sir, true).asTo('R3 verify post errored out').isGonnaBeFalse();
660
+ }
661
+ });
662
+ // #endregion Round 3: Multiple Timeline Conflicts
663
+ // #region Round 4: Chain Edits After Graft
664
+ const _r4 = testTransformer.newRound({
665
+ name: 'r4_chain_edits_after_graft',
666
+ description: 'Post-graft chain edits: mut8 → create gamma → rel8 gamma → mut8 again, tests continued editing on grafted timelines'
667
+ });
668
+ // #region r4 source edits
669
+ if (logalot) {
670
+ console.log(`${lc} Creating r4 chain edits on source (post-graft)...`);
671
+ }
672
+ // Source: Build chain on alpha post-graft
673
+ // 1. Retrieve alpha R3 graft point from source
674
+ const resGet_r4_alpha_source = await getFromSpace({ space: sourceSpace, addr: alpha_tjpAddr });
675
+ if (!resGet_r4_alpha_source.success || !resGet_r4_alpha_source.ibGibs || resGet_r4_alpha_source.ibGibs.length === 0) {
676
+ throw new Error(`Failed to retrieve alpha from sourceSpace for R4. (E: 1f2e3d4c5b6a7e8f9d0c1b2a3e4f5d6c)`);
677
+ }
678
+ const r4_alpha_v4_graftPoint = resGet_r4_alpha_source.ibGibs[0];
679
+ // 2. Mutate alpha v5 (fieldE) from R3 graft point
680
+ const r4_alpha_v5_source = await testTransformer.mut8({
681
+ ibGib: r4_alpha_v4_graftPoint,
682
+ in: 'source',
683
+ strField: 'fieldE',
684
+ name: 'r4_alpha_v5_source',
685
+ comments_snake_plus_CamelCase: 'postGraft2',
686
+ });
687
+ // 3. Create new gamma timeline
688
+ const r4_gamma_v0_source = await testTransformer.create({
689
+ atom: 'gamma',
690
+ in: 'source',
691
+ data: { gammaField: 'gamma created on source' },
692
+ name: 'r4_gamma_v0_source'
693
+ });
694
+ // 4. Relate alpha to gamma (creates v6)
695
+ const r4_alpha_v6_source_rel8dGamma = await testTransformer.rel8({
696
+ stepInfo: r4_alpha_v5_source,
697
+ in: 'source',
698
+ targetIbGibs: [r4_gamma_v0_source.ibGib],
699
+ rel8nName: TEST_REL8N_NAME,
700
+ name: 'r4_alpha_v6_source_rel8dGamma'
701
+ });
702
+ // 5. Mutate alpha again v7 (fieldF)
703
+ const r4_alpha_v7_source = await testTransformer.mut8({
704
+ ibGib: r4_alpha_v6_source_rel8dGamma.ibGib,
705
+ in: 'source',
706
+ strField: 'fieldF',
707
+ name: 'r4_alpha_v7_source',
708
+ comments_snake_plus_CamelCase: 'chainEnd',
709
+ });
710
+ // 6. Retrieve beta R3 graft point from source
711
+ const resGet_r4_beta_source = await getFromSpace({ space: sourceSpace, addr: beta_tjpAddr });
712
+ if (!resGet_r4_beta_source.success || !resGet_r4_beta_source.ibGibs || resGet_r4_beta_source.ibGibs.length === 0) {
713
+ throw new Error(`Failed to retrieve beta from sourceSpace for R4. (E: 4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b)`);
714
+ }
715
+ const r4_beta_v1_graftPoint = resGet_r4_beta_source.ibGibs[0];
716
+ // 7. Mutate beta v2 (betaFieldD) on source - creates divergence with dest
717
+ const r4_beta_v2_source = await testTransformer.mut8({
718
+ ibGib: r4_beta_v1_graftPoint,
719
+ in: 'source',
720
+ strField: 'betaFieldD',
721
+ name: 'r4_beta_v2_source',
722
+ });
723
+ // #endregion r4 source edits
724
+ // #region r4 dest edits
725
+ if (logalot) {
726
+ console.log(`${lc} Creating r4 edits on dest (post-graft)...`);
727
+ }
728
+ // Dest: Mutate both alpha and beta from their R3 graft points
729
+ // 1. Retrieve alpha R3 graft point from dest
730
+ const resGet_r4_alpha_dest = await getFromSpace({ space: destSpace, addr: alpha_tjpAddr });
731
+ if (!resGet_r4_alpha_dest.success || !resGet_r4_alpha_dest.ibGibs || resGet_r4_alpha_dest.ibGibs.length === 0) {
732
+ throw new Error(`Failed to retrieve alpha from destSpace for R4. (E: 9e8d7c6b5a4f3e2d1c0b9a8e7f6d5c4b)`);
733
+ }
734
+ const r4_alpha_v4_graftPoint_fromDest = resGet_r4_alpha_dest.ibGibs[0];
735
+ // 2. Mutate alpha v5 (fieldG) from R3 graft point
736
+ const r4_alpha_v5_dest = await testTransformer.mut8({
737
+ ibGib: r4_alpha_v4_graftPoint_fromDest,
738
+ in: 'dest',
739
+ strField: 'fieldG',
740
+ name: 'r4_alpha_v5_dest',
741
+ comments_snake_plus_CamelCase: 'postGraft2',
742
+ });
743
+ // 3. Retrieve beta R3 graft point from dest (reuse beta_tjpAddr from R3)
744
+ const resGet_r4_beta_dest = await getFromSpace({ space: destSpace, addr: beta_tjpAddr });
745
+ if (!resGet_r4_beta_dest.success || !resGet_r4_beta_dest.ibGibs || resGet_r4_beta_dest.ibGibs.length === 0) {
746
+ throw new Error(`Failed to retrieve beta from destSpace for R4. (E: 3b2a1f0e9d8c7b6a5e4f3d2c1b0a9e8f)`);
747
+ }
748
+ const r4_beta_v1_graftPoint_fromDest = resGet_r4_beta_dest.ibGibs[0];
749
+ // 4. Mutate beta v2 (betaFieldC)
750
+ const r4_beta_v2_dest = await testTransformer.mut8({
751
+ ibGib: r4_beta_v1_graftPoint_fromDest,
752
+ in: 'dest',
753
+ strField: 'betaFieldC',
754
+ name: 'r4_beta_v2_dest',
755
+ });
756
+ if (logalot) {
757
+ console.log(`${lc} R4 Divergence created (post-graft chain edits).`);
758
+ }
759
+ // #endregion r4 dest edits
760
+ await respecfully(sir, `r4 verify pre`, async () => {
761
+ await ifWe(sir, 'dest has alpha and beta post-R3 tips', async () => {
762
+ try {
763
+ const destKV = await receiverCoordinator.getKnowledgeMap({
764
+ space: destSpace,
765
+ metaspace,
766
+ domainIbGibs: [r4_alpha_v4_graftPoint_fromDest, r4_beta_v1_graftPoint_fromDest]
767
+ });
768
+ // Verify alpha tip exists on dest (should be R3 graft point)
769
+ const destAlphaTip = destKV[alpha_tjpAddr];
770
+ iReckon(sir, !!destAlphaTip).asTo(`Dest KV has alpha timeline tip`).isGonnaBeTruthy();
771
+ // Verify beta tip exists on dest (should be R3 graft point)
772
+ const destBetaTip = destKV[beta_tjpAddr];
773
+ iReckon(sir, !!destBetaTip).asTo(`Dest KV has beta timeline tip`).isGonnaBeTruthy();
774
+ }
775
+ catch (error) {
776
+ console.error(`${lc} ${extractErrorMsg(error)}`);
777
+ iReckon(sir, true).asTo('getKnowledgeMap errored out').isGonnaBeFalse();
778
+ }
430
779
  });
431
- await ifWeMight(sir, 'r3 beta and deps synced', async () => {
432
- // Verify beta's full dep graph on both spaces
780
+ });
781
+ if (logalot) {
782
+ console.log(`${lc} Running r4 Sync (chain edits after graft)...`);
783
+ }
784
+ let r4_syncSaga;
785
+ try {
786
+ r4_syncSaga = await senderCoordinator.sync({
787
+ peer: await newTestPeer(),
788
+ localSpace: sourceSpace,
789
+ metaspace,
790
+ domainIbGibs: [r4_alpha_v7_source.ibGib, r4_gamma_v0_source.ibGib, r4_beta_v2_source.ibGib],
791
+ conflictStrategy: SyncConflictStrategy.optimisticWithLCS,
792
+ useSessionIdentity: false,
433
793
  });
794
+ if (logalot) {
795
+ console.log(`${lc} awaiting r4_syncSaga.done`);
796
+ }
797
+ await r4_syncSaga.done;
798
+ if (logalot) {
799
+ console.log(`${lc} r4_syncSaga Complete.`);
800
+ }
801
+ }
802
+ catch (e) {
803
+ console.error(`${lc} R4 Sync Failed with Error:`, e);
804
+ iReckon(sir, false).asTo(`R4 Sync failed with error: ${e}`).isGonnaBeTruthy();
805
+ return; // Exit test early
806
+ }
807
+ await respecfully(sir, `r4 verify post`, async () => {
808
+ try {
809
+ // Get KVs for all three timelines after sync
810
+ const gamma_tjpAddr = getTjpAddr({ ibGib: r4_gamma_v0_source.ibGib, defaultIfNone: 'incomingAddr' });
811
+ const r4_alpha_sourceKV = await senderCoordinator.getKnowledgeMap({
812
+ space: sourceSpace,
813
+ metaspace,
814
+ domainIbGibs: [r1_alpha_v0_source.ibGib]
815
+ });
816
+ const r4_alpha_source_tipAddr = r4_alpha_sourceKV[alpha_tjpAddr];
817
+ const r4_alpha_destKV = await senderCoordinator.getKnowledgeMap({
818
+ space: destSpace,
819
+ metaspace,
820
+ domainIbGibs: [r1_alpha_v0_source.ibGib]
821
+ });
822
+ const r4_alpha_dest_tipAddr = r4_alpha_destKV[alpha_tjpAddr];
823
+ const r4_beta_sourceKV = await senderCoordinator.getKnowledgeMap({
824
+ space: sourceSpace,
825
+ metaspace,
826
+ domainIbGibs: [r2_beta_v0_source.ibGib]
827
+ });
828
+ const r4_beta_source_tipAddr = r4_beta_sourceKV[beta_tjpAddr];
829
+ const r4_beta_destKV = await senderCoordinator.getKnowledgeMap({
830
+ space: destSpace,
831
+ metaspace,
832
+ domainIbGibs: [r2_beta_v0_source.ibGib]
833
+ });
834
+ const r4_beta_dest_tipAddr = r4_beta_destKV[beta_tjpAddr];
835
+ if (!r4_beta_dest_tipAddr) {
836
+ ifWe(sir, 'r4_beta_dest_tipAddr is falsy?', async () => {
837
+ iReckon(sir, true).asTo('fail').isGonnaBeFalse();
838
+ });
839
+ return; /* <<<< returns early */
840
+ }
841
+ const r4_gamma_sourceKV = await senderCoordinator.getKnowledgeMap({
842
+ space: sourceSpace,
843
+ metaspace,
844
+ domainIbGibs: [r4_gamma_v0_source.ibGib]
845
+ });
846
+ const r4_gamma_source_tipAddr = r4_gamma_sourceKV[gamma_tjpAddr];
847
+ const r4_gamma_destKV = await senderCoordinator.getKnowledgeMap({
848
+ space: destSpace,
849
+ metaspace,
850
+ domainIbGibs: [r4_gamma_v0_source.ibGib]
851
+ });
852
+ const r4_gamma_dest_tipAddr = r4_gamma_destKV[gamma_tjpAddr];
853
+ await ifWe(sir, 'r4 tip addrs match (alpha, beta, gamma)', async () => {
854
+ iReckon(sir, r4_alpha_source_tipAddr).asTo('alpha source/dest have same tip addrs').isGonnaBe(r4_alpha_dest_tipAddr);
855
+ iReckon(sir, r4_beta_source_tipAddr).asTo('beta source/dest have same tip addrs').isGonnaBe(r4_beta_dest_tipAddr);
856
+ iReckon(sir, r4_gamma_source_tipAddr).asTo('gamma source/dest have same tip addrs').isGonnaBe(r4_gamma_dest_tipAddr);
857
+ });
858
+ // Fetch tips
859
+ if (!r4_alpha_source_tipAddr) {
860
+ throw new Error(`r4_alpha_source_tipAddr is null/undefined (E: 5e4d3c2b1a0f9e8d7c6b5a4e3f2d1c0b)`);
861
+ }
862
+ const resGet_r4_alpha_tip = await getFromSpace({ space: sourceSpace, addr: r4_alpha_source_tipAddr });
863
+ const r4_alpha_tipIbGib = resGet_r4_alpha_tip.ibGibs[0];
864
+ if (!r4_beta_source_tipAddr) {
865
+ throw new Error(`r4_beta_source_tipAddr is null/undefined (E: 2a1b0c9d8e7f6a5b4c3d2e1f0a9b8c7d)`);
866
+ }
867
+ const resGet_r4_beta_tip = await getFromSpace({ space: sourceSpace, addr: r4_beta_source_tipAddr });
868
+ const r4_beta_tipIbGib = resGet_r4_beta_tip.ibGibs[0];
869
+ if (!r4_gamma_source_tipAddr) {
870
+ throw new Error(`r4_gamma_source_tipAddr is null/undefined (E: 7f6e5d4c3b2a1e0f9d8c7b6a5e4f3d2c)`);
871
+ }
872
+ const resGet_r4_gamma_tip = await getFromSpace({ space: sourceSpace, addr: r4_gamma_source_tipAddr });
873
+ const r4_gamma_tipIbGib = resGet_r4_gamma_tip.ibGibs[0];
874
+ await ifWe(sir, 'r4 alpha has complete chain of edits and gamma relation', async () => {
875
+ // Should have new graft point with different addr than either pre-graft version
876
+ iReckon(sir, r4_alpha_source_tipAddr)
877
+ .asTo(`Alpha R4 tip should NOT be r4_alpha_v7_source`)
878
+ .not.isGonnaBe(r4_alpha_v7_source.addr);
879
+ iReckon(sir, r4_alpha_source_tipAddr)
880
+ .asTo(`Alpha R4 tip should NOT be r4_alpha_v5_dest`)
881
+ .not.isGonnaBe(r4_alpha_v5_dest.addr);
882
+ // Check alpha has ALL fields from R1-R4
883
+ // R1: commonField
884
+ // R2 source: fieldA, R2 dest: fieldB
885
+ // R3 source: fieldC (in graft), R3 dest: fieldD (in graft)
886
+ // R4 source: fieldE, fieldF (in chain), R4 dest: fieldG (in graft)
887
+ const r4_source_alpha_v5_mut8Info = r4_alpha_v5_source.infos[0];
888
+ const r4_source_alpha_v7_mut8Info = r4_alpha_v7_source.infos[0];
889
+ const r4_dest_alpha_mut8Info = r4_alpha_v5_dest.infos[0];
890
+ iReckon(sir, r4_alpha_tipIbGib.data[r4_source_alpha_v5_mut8Info.key])
891
+ .asTo(`Alpha R4 tip has source fieldE`)
892
+ .isGonnaBe(r4_source_alpha_v5_mut8Info.value);
893
+ iReckon(sir, r4_alpha_tipIbGib.data[r4_source_alpha_v7_mut8Info.key])
894
+ .asTo(`Alpha R4 tip has source fieldF`)
895
+ .isGonnaBe(r4_source_alpha_v7_mut8Info.value);
896
+ iReckon(sir, r4_alpha_tipIbGib.data[r4_dest_alpha_mut8Info.key])
897
+ .asTo(`Alpha R4 tip has dest fieldG`)
898
+ .isGonnaBe(r4_dest_alpha_mut8Info.value);
899
+ // Verify alpha has gamma in relations
900
+ const gammaAddr = getIbGibAddr({ ibGib: r4_gamma_v0_source.ibGib });
901
+ const alphaRel8ns = r4_alpha_tipIbGib.rel8ns?.[TEST_REL8N_NAME] || [];
902
+ iReckon(sir, alphaRel8ns.includes(gammaAddr))
903
+ .asTo('Alpha has gamma in testrel8n')
904
+ .isGonnaBeTrue();
905
+ });
906
+ await ifWe(sir, 'r4 beta has all edits', async () => {
907
+ // Beta should have new graft from R4
908
+ iReckon(sir, r4_beta_source_tipAddr)
909
+ .asTo(`Beta R4 tip should NOT be r4_beta_v2_source (source version pre-graft)`)
910
+ .not.isGonnaBe(r4_beta_v2_source.addr);
911
+ iReckon(sir, r4_beta_source_tipAddr)
912
+ .asTo(`Beta R4 tip should NOT be r4_beta_v2_dest (dest version pre-graft)`)
913
+ .not.isGonnaBe(r4_beta_v2_dest.addr);
914
+ // Check beta has ALL fields: betaFieldA (R3 source), betaFieldB (R3 dest), betaFieldC (R4 dest), betaFieldD (R4 source)
915
+ const r4_source_beta_mut8Info = r4_beta_v2_source.infos[0];
916
+ const r4_dest_beta_mut8Info = r4_beta_v2_dest.infos[0];
917
+ iReckon(sir, r4_beta_tipIbGib.data[r4_source_beta_mut8Info.key])
918
+ .asTo(`Beta R4 tip has source betaFieldD`)
919
+ .isGonnaBe(r4_source_beta_mut8Info.value);
920
+ iReckon(sir, r4_beta_tipIbGib.data[r4_dest_beta_mut8Info.key])
921
+ .asTo(`Beta R4 tip has dest betaFieldC`)
922
+ .isGonnaBe(r4_dest_beta_mut8Info.value);
923
+ });
924
+ await ifWe(sir, 'r4 gamma synced with deps', async () => {
925
+ const [gamma_dest] = await getIbGibsFromCache_fallbackToSpaces({
926
+ addrs: [r4_gamma_v0_source.addr],
927
+ space: destSpace,
928
+ });
929
+ iReckon(sir, gamma_dest).asTo('gamma exists in dest').isGonnaBeTruthy();
930
+ if (gamma_dest) {
931
+ try {
932
+ const depGraph_gamma_source = await getDependencyGraph({
933
+ ibGib: r4_gamma_v0_source.ibGib,
934
+ live: true,
935
+ space: sourceSpace
936
+ });
937
+ const depGraph_gamma_dest = await getDependencyGraph({
938
+ ibGib: gamma_dest,
939
+ live: true,
940
+ space: destSpace
941
+ });
942
+ const gammaDepsAreEqual = graphsAreEquivalent({
943
+ graphA: depGraph_gamma_source,
944
+ graphB: depGraph_gamma_dest,
945
+ slowButThorough: true,
946
+ });
947
+ iReckon(sir, gammaDepsAreEqual).asTo('gamma dep graph equal').isGonnaBeTrue();
948
+ }
949
+ catch (error) {
950
+ console.error(`${lc} ${extractErrorMsg(error)}`);
951
+ iReckon(sir, true).asTo('ERROR: gamma dep graph check failed').isGonnaBeFalse();
952
+ }
953
+ }
954
+ });
955
+ await ifWe(sir, 'r4 all timelines dep graphs synced', async () => {
956
+ // Verify alpha dep graphs
957
+ const [alpha_dest] = await getIbGibsFromCache_fallbackToSpaces({
958
+ addrs: [r4_alpha_source_tipAddr],
959
+ space: destSpace,
960
+ });
961
+ iReckon(sir, alpha_dest).asTo('alpha exists in dest').isGonnaBeTruthy();
962
+ if (alpha_dest) {
963
+ try {
964
+ const depGraph_alpha_source = await getDependencyGraph({
965
+ ibGib: r1_alpha_v0_source.ibGib,
966
+ live: true,
967
+ space: sourceSpace
968
+ });
969
+ const depGraph_alpha_dest = await getDependencyGraph({
970
+ ibGib: alpha_dest,
971
+ live: true,
972
+ space: destSpace
973
+ });
974
+ const alphaDepsAreEqual = graphsAreEquivalent({
975
+ graphA: depGraph_alpha_source,
976
+ graphB: depGraph_alpha_dest,
977
+ slowButThorough: true,
978
+ });
979
+ iReckon(sir, alphaDepsAreEqual).asTo('alpha dep graph equal after R4').isGonnaBeTrue();
980
+ }
981
+ catch (error) {
982
+ console.error(`${lc} ${extractErrorMsg(error)}`);
983
+ iReckon(sir, true).asTo('ERROR: alpha dep graph check failed').isGonnaBeFalse();
984
+ }
985
+ }
986
+ // Verify beta dep graphs
987
+ const [beta_dest] = await getIbGibsFromCache_fallbackToSpaces({
988
+ addrs: [r4_beta_dest_tipAddr],
989
+ space: destSpace,
990
+ });
991
+ iReckon(sir, beta_dest).asTo('beta exists in dest').isGonnaBeTruthy();
992
+ if (beta_dest) {
993
+ try {
994
+ const depGraph_beta_source = await getDependencyGraph({
995
+ ibGib: r2_beta_v0_source.ibGib,
996
+ live: true,
997
+ space: sourceSpace
998
+ });
999
+ const depGraph_beta_dest = await getDependencyGraph({
1000
+ ibGib: beta_dest,
1001
+ live: true,
1002
+ space: destSpace
1003
+ });
1004
+ const betaDepsAreEqual = graphsAreEquivalent({
1005
+ graphA: depGraph_beta_source,
1006
+ graphB: depGraph_beta_dest,
1007
+ slowButThorough: true,
1008
+ });
1009
+ iReckon(sir, betaDepsAreEqual).asTo('beta dep graph equal after R4').isGonnaBeTrue();
1010
+ }
1011
+ catch (error) {
1012
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1013
+ iReckon(sir, true).asTo('ERROR: beta dep graph check failed').isGonnaBeFalse();
1014
+ }
1015
+ }
1016
+ });
1017
+ }
1018
+ catch (error) {
1019
+ console.error(`${lc} ${extractErrorMsg(error)}`);
1020
+ iReckon(sir, true).asTo('R4 verify post errored out').isGonnaBeFalse();
1021
+ }
434
1022
  });
435
- // #endregion Round 3: Multiple Timeline Conflicts
1023
+ // #endregion Round 4: Chain Edits After Graft
436
1024
  });
437
1025
  //# sourceMappingURL=sync-conflict-adv-multitimelines.respec.mjs.map