Work through test-strategy-report.md, high-ROI no-refactor subset (no regen). - R-Infra: vitest resolve.alias docmost-client -> packages/docmost-client/src (fixes the dist-vs-src coverage artifact: canonicalize 0% -> real) - R-Cfg-1: export parseArgs + tests - canonicalize: align family / comment.resolved kept / link non-default + fixpoint & docsCanonicallyEqual reflexive/symmetric properties (0 -> 100%) - markdown-converter golden matrix: columns/embed/audio/pdf, drawio data-align rule, inline-mark matrix, textAlign, escaping idempotence, table sanitization (61 -> 79%) - schema parse-closures via generateJSON (TextStyle/comment/mention/Highlight/Column) - node-ops (immutability, table edge cases, makeFreshId property), transforms (setCalloutRange/insertMarkerAfter/commentsToFootnotes + renumber property) - stabilize normalize-on-write fixpoint (0 -> 100%); diff coarse-fallback; client-utils; firstDivergence; corpus fixtures details/columns/mention - 593 -> 695 green; build clean; corpus STABLE Deferred (Phase 3-4, refactor-gated): pull/collab/client-REST/git-merge integration.
79 lines
2.7 KiB
TypeScript
79 lines
2.7 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import { firstDivergence } from '../src/roundtrip.js';
|
|
|
|
describe('firstDivergence', () => {
|
|
it('returns null for equal nested objects', () => {
|
|
const a = { k1: { k2: 1, k3: [1, 2, 3] }, n: 'x' };
|
|
const b = { k1: { k2: 1, k3: [1, 2, 3] }, n: 'x' };
|
|
expect(firstDivergence(a, b)).toBeNull();
|
|
});
|
|
|
|
it('reports the correct path for a differing leaf', () => {
|
|
const a = { k1: { k2: 1 } };
|
|
const b = { k1: { k2: 2 } };
|
|
const d = firstDivergence(a, b);
|
|
expect(d).not.toBeNull();
|
|
expect(d!.path).toBe('$.k1.k2');
|
|
expect(d!.a).toBe(1);
|
|
expect(d!.b).toBe(2);
|
|
});
|
|
|
|
it('reports an array length mismatch at $.arr.length', () => {
|
|
const a = { arr: [1, 2, 3] };
|
|
const b = { arr: [1, 2] };
|
|
const d = firstDivergence(a, b);
|
|
expect(d).not.toBeNull();
|
|
expect(d!.path).toBe('$.arr.length');
|
|
expect(d!.a).toBe(3);
|
|
expect(d!.b).toBe(2);
|
|
});
|
|
|
|
it('reports a key present only in a', () => {
|
|
const a = { only: 'here', shared: 1 };
|
|
const b = { shared: 1 };
|
|
const d = firstDivergence(a, b);
|
|
expect(d).not.toBeNull();
|
|
expect(d!.path).toBe('$.only');
|
|
expect(d!.a).toBe('here');
|
|
expect(d!.b).toBeUndefined();
|
|
});
|
|
|
|
// --- branches NOT covered above (report §2) ---
|
|
|
|
it('reports a type mismatch (string vs number) at the current path', () => {
|
|
const d = firstDivergence({ k: '1' }, { k: 1 });
|
|
expect(d).toEqual({ path: '$.k', a: '1', b: 1 });
|
|
});
|
|
|
|
it('reports null-vs-object as a divergence (typeof object both, but one is null)', () => {
|
|
// typeof null === 'object', so this exercises the explicit `a===null` guard.
|
|
const d = firstDivergence({ k: null }, { k: { nested: true } });
|
|
expect(d).toEqual({ path: '$.k', a: null, b: { nested: true } });
|
|
});
|
|
|
|
it('reports array-vs-object as a divergence (aIsArr !== bIsArr)', () => {
|
|
const d = firstDivergence({ k: [1, 2] }, { k: { 0: 1, 1: 2 } });
|
|
expect(d).not.toBeNull();
|
|
expect(d!.path).toBe('$.k');
|
|
expect(Array.isArray(d!.a)).toBe(true);
|
|
expect(Array.isArray(d!.b)).toBe(false);
|
|
});
|
|
|
|
it('returns null for two equal primitives (the a === b fast path)', () => {
|
|
expect(firstDivergence(7, 7)).toBeNull();
|
|
expect(firstDivergence('same', 'same')).toBeNull();
|
|
expect(firstDivergence(null, null)).toBeNull();
|
|
expect(firstDivergence(true, true)).toBeNull();
|
|
});
|
|
|
|
it('reports a key present only in b (a-side undefined)', () => {
|
|
// The key union scans both objects, so a key only in `b` is detected with
|
|
// the a-side value undefined.
|
|
const d = firstDivergence({ shared: 1 }, { shared: 1, extra: 'b-only' });
|
|
expect(d).not.toBeNull();
|
|
expect(d!.path).toBe('$.extra');
|
|
expect(d!.a).toBeUndefined();
|
|
expect(d!.b).toBe('b-only');
|
|
});
|
|
});
|