Agent Wranglers

APP-72Recommended

Test that copies end up equal in any order

Run a property test that makes random edits on three copies and delivers the changes in every order, twice over. All copies must end equal. Run at least 1,000 cases in CI.

How to do it

The library promises that copies converge, but your own code around it can break that. Loading, saving, compacting and filtering messages are common places. Test that code with property tests. Feed the changes through your own storage and server functions, not only the library. Longer sequences of joins and drops fit Test sequences of actions against a simple model.

Examples

TypeScript example

const ORDERS = [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]];

test('copies end up equal in any delivery order', () => {
  fc.assert(fc.property(fc.array(fc.tuple(fc.nat(2), fc.nat(50), fc.string({ maxLength: 5 })), { maxLength: 30 }), edits => {
    const copies = [0, 1, 2].map(() => new Y.Doc());
    for (const [copy, at, value] of edits) {
      const text = copies[copy].getText('body');
      text.insert(Math.min(at, text.length), value);
    }
    const changes = copies.map(doc => Y.encodeStateAsUpdate(doc));
    const results = ORDERS.map(order => {
      const doc = new Y.Doc();
      for (const index of [...order, ...order]) Y.applyUpdate(doc, changes[index]);
      return doc.getText('body').toString();
    });
    expect(new Set(results).size).toBe(1);
  }), { numRuns: 1000 });
});

Python example

edits = st.lists(st.tuples(st.integers(0, 2), st.integers(0, 50), st.text(max_size=5)), max_size=30)

@settings(max_examples=1000)
@given(edits, st.randoms())
def test_copies_end_up_equal(edits, random):
    copies = [Doc() for _ in range(3)]
    for copy, at, value in edits:
        text = copies[copy].get("body", type=Text)
        text.insert(min(at, len(text)), value)
    changes = [copy.get_update() for copy in copies]
    results = set()
    for _ in range(3):
        order = changes * 2
        random.shuffle(order)
        merged = Doc()
        for change in order:
            merged.apply_update(change)
        results.add(str(merged.get("body", type=Text)))
    assert len(results) == 1

How to check

The convergence test runs 1,000 or more cases in CI and passes. Breaking the merge on purpose, such as by dropping one change, makes it fail.

For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/app-ui/live-collaboration/convergence-tests">Agent Wranglers web app interface playbook</a>. In code you write, add a comment next to it with the same link.