mirror of
https://github.com/proffesor-for-testing/agentic-qe.git
synced 2026-09-19 08:45:47 +08:00
2253d05618
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>
226 lines
6.1 KiB
TypeScript
226 lines
6.1 KiB
TypeScript
/**
|
|
* PN-Counter Unit Tests
|
|
*
|
|
* Tests for the Positive-Negative Counter CRDT implementation.
|
|
* Verifies increment/decrement, convergence, commutativity, idempotency.
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
|
|
import {
|
|
createPNCounter,
|
|
createPNCounterFromState,
|
|
isPNCounterState,
|
|
getPNCounterBreakdown,
|
|
} from '../../../src/memory/crdt/pn-counter.js';
|
|
|
|
describe('PN-Counter', () => {
|
|
describe('increment and decrement', () => {
|
|
it('should start at zero', () => {
|
|
// Arrange & Act
|
|
const counter = createPNCounter('node-a');
|
|
|
|
// Assert
|
|
expect(counter.get()).toBe(0);
|
|
});
|
|
|
|
it('should increment correctly', () => {
|
|
// Arrange
|
|
const counter = createPNCounter('node-a');
|
|
|
|
// Act
|
|
counter.increment(10);
|
|
|
|
// Assert
|
|
expect(counter.get()).toBe(10);
|
|
});
|
|
|
|
it('should decrement correctly', () => {
|
|
// Arrange
|
|
const counter = createPNCounter('node-a');
|
|
counter.increment(10);
|
|
|
|
// Act
|
|
counter.decrement(3);
|
|
|
|
// Assert
|
|
expect(counter.get()).toBe(7);
|
|
});
|
|
|
|
it('should support going negative', () => {
|
|
// Arrange
|
|
const counter = createPNCounter('node-a');
|
|
|
|
// Act
|
|
counter.decrement(5);
|
|
|
|
// Assert
|
|
expect(counter.get()).toBe(-5);
|
|
});
|
|
|
|
it('should throw when increment receives negative value', () => {
|
|
const counter = createPNCounter('node-a');
|
|
expect(() => counter.increment(-1)).toThrow('Use decrement() for negative values');
|
|
});
|
|
|
|
it('should throw when decrement receives negative value', () => {
|
|
const counter = createPNCounter('node-a');
|
|
expect(() => counter.decrement(-1)).toThrow('Use increment() for negative values');
|
|
});
|
|
|
|
it('should no-op on zero increment or decrement', () => {
|
|
// Arrange
|
|
const counter = createPNCounter('node-a');
|
|
counter.increment(5);
|
|
|
|
// Act
|
|
counter.increment(0);
|
|
counter.decrement(0);
|
|
|
|
// Assert
|
|
expect(counter.get()).toBe(5);
|
|
});
|
|
});
|
|
|
|
describe('convergence after merge', () => {
|
|
it('should converge two replicas with different operations to same value', () => {
|
|
// Arrange
|
|
const a = createPNCounter('node-a');
|
|
const b = createPNCounter('node-b');
|
|
a.increment(10);
|
|
a.decrement(2);
|
|
b.increment(5);
|
|
b.decrement(3);
|
|
|
|
// Act
|
|
const mergedA = createPNCounterFromState('node-a', a.getState());
|
|
mergedA.applyState(b.getState());
|
|
|
|
const mergedB = createPNCounterFromState('node-b', b.getState());
|
|
mergedB.applyState(a.getState());
|
|
|
|
// Assert - both converge: (10-2) + (5-3) = 10
|
|
expect(mergedA.get()).toBe(10);
|
|
expect(mergedB.get()).toBe(10);
|
|
});
|
|
|
|
it('should converge three replicas after full sync', () => {
|
|
// Arrange
|
|
const a = createPNCounter('node-a');
|
|
const b = createPNCounter('node-b');
|
|
const c = createPNCounter('node-c');
|
|
a.increment(10);
|
|
b.decrement(3);
|
|
c.increment(7);
|
|
|
|
// Act - full pairwise sync
|
|
a.merge(b);
|
|
a.merge(c);
|
|
b.merge(a);
|
|
c.merge(a);
|
|
|
|
// Assert - all converge to 10 - 3 + 7 = 14
|
|
expect(a.get()).toBe(14);
|
|
expect(b.get()).toBe(14);
|
|
expect(c.get()).toBe(14);
|
|
});
|
|
});
|
|
|
|
describe('commutativity', () => {
|
|
it('should produce same result regardless of merge order', () => {
|
|
// Arrange
|
|
const a = createPNCounter('node-a');
|
|
const b = createPNCounter('node-b');
|
|
a.increment(8);
|
|
a.decrement(2);
|
|
b.increment(3);
|
|
b.decrement(1);
|
|
|
|
// Act
|
|
const ab = createPNCounterFromState('node-x', a.getState());
|
|
ab.applyState(b.getState());
|
|
|
|
const ba = createPNCounterFromState('node-y', b.getState());
|
|
ba.applyState(a.getState());
|
|
|
|
// Assert
|
|
expect(ab.get()).toBe(ba.get());
|
|
expect(ab.get()).toBe(8); // (8-2) + (3-1) = 8
|
|
});
|
|
});
|
|
|
|
describe('idempotency', () => {
|
|
it('should produce same value when merged with self', () => {
|
|
// Arrange
|
|
const counter = createPNCounter('node-a');
|
|
counter.increment(5);
|
|
counter.decrement(2);
|
|
|
|
// Act
|
|
counter.applyState(counter.getState());
|
|
|
|
// Assert
|
|
expect(counter.get()).toBe(3);
|
|
});
|
|
|
|
it('should produce same value when applying same remote state repeatedly', () => {
|
|
// Arrange
|
|
const a = createPNCounter('node-a');
|
|
const b = createPNCounter('node-b');
|
|
a.increment(10);
|
|
b.increment(5);
|
|
const stateB = b.getState();
|
|
|
|
// Act
|
|
a.applyState(stateB);
|
|
a.applyState(stateB);
|
|
a.applyState(stateB);
|
|
|
|
// Assert
|
|
expect(a.get()).toBe(15);
|
|
});
|
|
});
|
|
|
|
describe('factory from state', () => {
|
|
it('should restore counter from serialized state', () => {
|
|
// Arrange
|
|
const original = createPNCounter('node-a');
|
|
original.increment(20);
|
|
original.decrement(7);
|
|
|
|
// Act
|
|
const restored = createPNCounterFromState('node-a', original.getState());
|
|
|
|
// Assert
|
|
expect(restored.get()).toBe(13);
|
|
});
|
|
});
|
|
|
|
describe('utility functions', () => {
|
|
it('should provide breakdown of counter state', () => {
|
|
// Arrange
|
|
const counter = createPNCounter('node-a');
|
|
counter.increment(10);
|
|
counter.decrement(3);
|
|
|
|
// Act
|
|
const breakdown = getPNCounterBreakdown(counter.getState());
|
|
|
|
// Assert
|
|
expect(breakdown.total).toBe(7);
|
|
expect(breakdown.positiveTotal).toBe(10);
|
|
expect(breakdown.negativeTotal).toBe(3);
|
|
expect(breakdown.positiveByNode['node-a']).toBe(10);
|
|
expect(breakdown.negativeByNode['node-a']).toBe(3);
|
|
});
|
|
|
|
it('should validate PNCounterState', () => {
|
|
const counter = createPNCounter('node-a');
|
|
counter.increment(5);
|
|
expect(isPNCounterState(counter.getState())).toBe(true);
|
|
expect(isPNCounterState(null)).toBe(false);
|
|
expect(isPNCounterState({})).toBe(false);
|
|
});
|
|
});
|
|
});
|