# Sync Testing Guidelines

This document outlines the patterns, conventions, and organizational strategies for writing advanced sync tests in the ibGib protocol implementation.

## Core Concepts

### Rounds

Tests are organized into **rounds** rather than thinking in terms of pre/post-merge states. Each round represents a complete cycle of:
1. **State Setup**: Establish expected initial conditions
2. **Actions**: Perform mutations, relations, and sync operations
3. **Verification**: Assert final state expectations

The **first round** establishes common history between source and dest using sync itself—not manual data duplication.

### Test Scenarios

Each timeline should go through at least one round of each main scenario:
- **Fast-Forward (Source Ahead)**: Source has newer versions that dest lacks
- **Fast-Forward (Dest Ahead)**: Dest has newer versions that source lacks
- **Conflict**: Both source and dest have divergent edits from a common ancestor
- **No Change**: Timeline exists on both sides with no edits

## Variable Naming Convention

Format: `round_timeline_version_location_comments`

### Components

- **Round**: `r1`, `r2`, `r3`, etc. (round number)
- **Timeline**: Greek letter (`alpha`, `beta`, `gamma`, `delta`, `epsilon`, etc.)
- **Version**: `v0`, `v1`, `v2`, etc. (from test POV, NOT necessarily matching `data.n`)
- **Location**: `_source` or `_dest` (where the ibgib was **created**, not where it currently exists)
- **Comments**: Optional descriptive suffixes (can chain multiple)

### Examples

```typescript
r1_alpha_v0_source                           // Round 1, alpha root, created on source
r1_alpha_v1_source_common                    // Round 1, alpha v1, common to both (pre-divergence)
r1_beta_v0_dest                              // Round 1, beta root, created on dest
r1_alpha_v2_source_sansBeta                  // Round 1, alpha v2 on source, before beta relation
r1_alpha_v3_source_rel8beta                  // Round 1, alpha v3 on source, after adding beta relation
r1_delta_v1_source_createdInR1_rel8edFromAlpha  // Round 1, delta v1 with multiple comments
r2_alpha_v4_dest_postConflict               // Round 2, alpha v4 on dest, after conflict resolution
```

## Test Structure

### File Organization

```typescript
await respecfully(sir, `Test Suite Name`, async () => {
    // 1. Setup Spaces & Coordinators

    // 2. Round 1 - Common History Establishment
    // #region r1 source setup
    // #endregion r1 source setup

    // #region r1 dest setup
    // #endregion r1 dest setup

    // Sync Round 1
    await respecfully(sir, `r1 verify results`, async () => {
        await ifWeMight(sir, 'alpha synced correctly', async () => {
            // timeline-specific assertions
        });
        await ifWeMight(sir, 'beta synced correctly', async () => {
            // timeline-specific assertions
        });
    });

    // 3. Round 2 - Divergent Edits & Conflicts
    // #region r2 source edits
    // #endregion r2 source edits

    // #region r2 dest edits
    // #endregion r2 dest edits

    // Sync Round 2
    await respecfully(sir, `r2 verify results`, async () => {
        await ifWeMight(sir, 'alpha conflict resolved', async () => {
            // verify merge structure
        });
    });
});
```

### Region Usage

Use `// #region` and `// #endregion` tags to encapsulate **timeline edit groupings** within a round. This allows collapsing code sections to manage complexity.

Pattern:
- `// #region r{N} {location} setup` - For initial timeline creation
- `// #region r{N} {location} edits` - For mutations/relations within a round

### Verification Blocks

- **`respecfully`**: Outer verification block per round
- **`ifWeMight`**: Per-timeline verification within a round
- **`iReckon`**: Individual assertions

Example:
```typescript
await respecfully(sir, `r1 verify results`, async () => {
    await ifWeMight(sir, 'alpha exists on both sides', async () => {
        iReckon(sir, alphaOnSource).asTo('alpha on source').isGonnaBeTruthy();
        iReckon(sir, alphaOnDest).asTo('alpha on dest').isGonnaBeTruthy();
    });

    await ifWeMight(sir, 'dependency graphs match', async () => {
        const graphsMatch = graphsAreEquivalent({ graphA, graphB });
        iReckon(sir, graphsMatch).asTo('graphs equivalent').isGonnaBeTrue();
    });
});
```

## Multiple Syncs Per Round

A single round can have **multiple sync calls**. This is useful when:
- Creating multiple timeline pairs (some on source, some on dest)
- Testing specific sync scenarios in isolation

Key principle: Verify **final state** at end of round, not intermediate states between syncs.

```typescript
// Round 1: Establish common history
// Sync 1: alpha and beta (created on source)
await senderCoordinator.sync({ domainIbGibs: [alpha, beta], ... });

// Sync 2: gamma and delta (created on dest, reverse direction)
await receiverCoordinator.sync({ domainIbGibs: [gamma, delta], ... });

// Single verification checks all 4 timelines
await respecfully(sir, `r1 verify all timelines synced`, async () => {
    // check alpha, beta, gamma, delta all exist on both sides
});
```

## Domain-Level Focus

Advanced tests focus on **domain-level correctness**, not protocol mechanics:

❌ **Don't capture**: `baseAddr`, `orphanAddr`, temporal ordering details
❌ **Don't use**: `delay()` calls to control base/orphan selection
✅ **Do focus on**: Data field correctness, relation preservation, dependency completeness

## Complexity Management

### Principles

1. **Self-Documenting Names**: Variable names should tell the story
2. **Region Collapsibility**: Use regions to hide complexity
3. **Round Isolation**: Each round is conceptually independent
4. **Minimal Commenting**: Rely on naming and structure over heavy comments
5. **Test Helpers**: Use helper functions to reduce boilerplate (see below)

### Test Helper Functions

To reduce repetitive mutation/relation calls, use test helpers that return structured info:

```typescript
// Instead of manual mut8Timeline calls
const stepInfo = await testMut8({ ibGib: alpha, space: sourceSpace });
// Returns: { ibGib, key, value } for verification

// Instead of manual appendToTimeline calls
const rel8Info = await testRel8({ ibGib: alpha, targetIbGib: beta, space: sourceSpace });
// Returns: { ibGib, rel8nName, targetIbGibs } for verification
```

See `test-helpers.mts` and `test-types.mts` for implementations.

## Scenarios to Test

### Basic Scenarios
- Fast-forward (source ahead)
- Fast-forward (dest ahead)
- Simple conflict (different fields edited)
- No changes

### Advanced Scenarios
- **Divergent edits on related ibgibs**: Alpha conflicts while beta (rel8'd from alpha) also conflicts
- **One-sided new timelines**: Timeline exists only on source or dest initially
- **Transitive dependencies**: Delta rel8'd from alpha, alpha rel8'd from beta
- **Reverse dependencies**: Timeline created on dest, source adds rel8n to it
- **Removal conflicts**: Source removes rel8n while dest mut8s the rel8'd ibgib
- **Chain edits**: Mutation → rel8 → mutation again on same timeline in one round

## Summary

These guidelines aim to **minimize complexity** in necessarily complex integration tests through:
- Consistent naming conventions
- Round-based mental model
- Region-based code organization
- Domain-level focus
- Helper function abstraction
- Clear verification patterns

When in doubt, prioritize **readability** and **maintainability** over brevity.
