Files
Profa 2253d05618 feat: flatten project structure — archive v2, promote v3 to root
Remove the nested v3/ subdirectory by moving all active source code,
tests, configs, and assets directly to the project root. Archive v2/
(dead code, 847MB) to v2-archive.tar.gz.

Key changes:
- Move v3/src/ → src/, v3/tests/ → tests/, v3/packages/ → packages/
- Move v3/tsconfig.json, v3/vitest.config.ts to root
- Merge v3/package.json into root package.json (ESM, scripts, deps, exports)
- Merge v3/scripts/ into scripts/, v3/implementation/ into docs/implementation/
- Update all CI workflows to remove "cd v3 &&" and v3/ path prefixes
- Fix all init installer path resolution (3-level-up → 2-level-up)
- Fix hooks to use "node ./dist/cli/bundle.js" instead of bare "aqe"
- Update settings.json ADR/DDD directories to docs/implementation/
- Fix helper scripts (statusline, adr-compliance, ddd-tracker, guidance-hooks)
- Update 58 agent definitions: aqe/v3/ → aqe/ memory namespaces
- Update 15 guidance shards: v3/src/ → src/ path references
- Fix sync interfaces: remove v3/ and v2/ path references
- Fix test path calculations (process.cwd() + '..' no longer needed)
- Fix flaky perf test threshold (500ms → 1000ms for CI environments)
- Fix complexity-analyzer test to match ADR-051 keyword-based eligibility

Build passes, 17,901 tests pass, CLI and MCP bundles verified.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 11:35:41 +00:00

253 lines
6.7 KiB
TypeScript

/**
* OR-Set Unit Tests
*
* Tests for the Observed-Remove Set CRDT implementation.
* Verifies add-wins semantics, convergence, commutativity, idempotency.
*/
import { describe, it, expect } from 'vitest';
import {
createORSet,
createORSetFromState,
isORSetState,
getORSetStats,
compactORSetState,
} from '../../../src/memory/crdt/or-set.js';
describe('OR-Set', () => {
describe('add and remove', () => {
it('should start empty', () => {
// Arrange & Act
const set = createORSet<string>('node-a');
// Assert
expect(set.size()).toBe(0);
expect(set.values()).toEqual([]);
});
it('should add and check membership', () => {
// Arrange
const set = createORSet<string>('node-a');
// Act
set.add('apple');
// Assert
expect(set.has('apple')).toBe(true);
expect(set.has('banana')).toBe(false);
expect(set.size()).toBe(1);
});
it('should remove elements', () => {
// Arrange
const set = createORSet<string>('node-a');
set.add('apple');
set.add('banana');
// Act
set.remove('apple');
// Assert
expect(set.has('apple')).toBe(false);
expect(set.has('banana')).toBe(true);
expect(set.size()).toBe(1);
});
it('should allow re-adding a removed element', () => {
// Arrange
const set = createORSet<string>('node-a');
set.add('apple');
set.remove('apple');
// Act
set.add('apple');
// Assert
expect(set.has('apple')).toBe(true);
});
it('should handle removing non-existent element gracefully', () => {
// Arrange
const set = createORSet<string>('node-a');
// Act & Assert - should not throw
set.remove('ghost');
expect(set.size()).toBe(0);
});
it('should clear all elements', () => {
// Arrange
const set = createORSet<string>('node-a');
set.add('a');
set.add('b');
set.add('c');
// Act
set.clear();
// Assert
expect(set.size()).toBe(0);
expect(set.has('a')).toBe(false);
});
});
describe('convergence after merge', () => {
it('should converge two replicas with different adds to union', () => {
// Arrange
const a = createORSet<string>('node-a');
const b = createORSet<string>('node-b');
a.add('apple');
b.add('banana');
// Act
const mergedA = createORSetFromState<string>('node-a', a.getState());
mergedA.applyState(b.getState());
const mergedB = createORSetFromState<string>('node-b', b.getState());
mergedB.applyState(a.getState());
// Assert - both converge to {apple, banana}
expect(mergedA.values().sort()).toEqual(['apple', 'banana']);
expect(mergedB.values().sort()).toEqual(['apple', 'banana']);
});
it('should resolve concurrent add-remove with add-wins semantics', () => {
// Arrange
const a = createORSet<string>('node-a');
const b = createORSet<string>('node-b');
// Both start with 'apple'
a.add('apple');
b.applyState(a.getState());
// Concurrently: A removes, B re-adds
a.remove('apple');
b.add('apple'); // creates a new tag that A's tombstone doesn't cover
// Act - merge
a.merge(b);
// Assert - add wins over concurrent remove
expect(a.has('apple')).toBe(true);
});
});
describe('commutativity', () => {
it('should produce same result regardless of merge order', () => {
// Arrange
const a = createORSet<string>('node-a');
const b = createORSet<string>('node-b');
a.add('x');
a.add('y');
b.add('y');
b.add('z');
// Act
const ab = createORSetFromState<string>('node-x', a.getState());
ab.applyState(b.getState());
const ba = createORSetFromState<string>('node-y', b.getState());
ba.applyState(a.getState());
// Assert
expect(ab.values().sort()).toEqual(ba.values().sort());
expect(ab.values().sort()).toEqual(['x', 'y', 'z']);
});
});
describe('idempotency', () => {
it('should produce same set when merged with self', () => {
// Arrange
const set = createORSet<string>('node-a');
set.add('hello');
set.add('world');
// Act
set.applyState(set.getState());
// Assert
expect(set.size()).toBe(2);
expect(set.values().sort()).toEqual(['hello', 'world']);
});
it('should produce same set when applying same remote state multiple times', () => {
// Arrange
const a = createORSet<string>('node-a');
const b = createORSet<string>('node-b');
a.add('local');
b.add('remote');
const stateB = b.getState();
// Act
a.applyState(stateB);
a.applyState(stateB);
a.applyState(stateB);
// Assert
expect(a.size()).toBe(2);
});
});
describe('serialization types', () => {
it('should handle number elements', () => {
const set = createORSet<number>('node-a');
set.add(42);
set.add(0);
expect(set.has(42)).toBe(true);
expect(set.has(0)).toBe(true);
expect(set.size()).toBe(2);
});
it('should handle boolean elements', () => {
const set = createORSet<boolean>('node-a');
set.add(true);
set.add(false);
expect(set.has(true)).toBe(true);
expect(set.has(false)).toBe(true);
});
});
describe('utility functions', () => {
it('should compute OR-Set stats', () => {
// Arrange
const set = createORSet<string>('node-a');
set.add('a');
set.add('b');
set.remove('a');
// Act
const stats = getORSetStats(set.getState());
// Assert
expect(stats.activeElements).toBe(1);
expect(stats.totalTags).toBe(2);
expect(stats.totalTombstones).toBe(1);
expect(stats.elementsWithTombstones).toBe(1);
});
it('should compact state by removing fully tombstoned elements', () => {
// Arrange
const set = createORSet<string>('node-a');
set.add('keep');
set.add('remove-me');
set.remove('remove-me');
// Act
const compacted = compactORSetState(set.getState());
// Assert
expect(Object.keys(compacted.elements)).toHaveLength(1);
expect(compacted.tombstones).toEqual({});
});
it('should validate OR-Set state', () => {
const set = createORSet<string>('node-a');
set.add('test');
expect(isORSetState(set.getState())).toBe(true);
expect(isORSetState(null)).toBe(false);
expect(isORSetState({})).toBe(false);
});
});
});