Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import { createAsyncCommand } from "../src/async/async-command.js";
import { AsyncUndoStack } from "../src/async/async-stack.js";
import { BranchingHistory } from "../src/branch/branch.js";
import { createCommand } from "../src/command/command.js";
import { compressHistory } from "../src/compression/history-compressor.js";
import { SelectiveUndoStack } from "../src/selective/selective-undo.js";
import { UndoStack } from "../src/stack/stack.js";
// OTel meters are no-ops by default (no exporter registered) — we just verify
// that operations complete without throwing and that the meter API is exercised.
// The default @opentelemetry/api ships a NoopMeterProvider.
interface Counter {
value: number;
}
const mkCmd = (n = 1) =>
createCommand<Counter>(
`Inc${n}`,
(s) => ({ value: s.value + n }),
(s) => ({ value: s.value - n })
);
describe("OTel instrumentation (no-op SDK)", () => {
it("UndoStack push increments counter without throwing", {
timeout: 5000,
}, () => {
const s = new UndoStack<Counter>({ value: 0 });
expect(() => {
s.push(mkCmd(1));
s.push(mkCmd(2));
}).not.toThrow();
});
it("UndoStack undo increments counter without throwing", {
timeout: 5000,
}, () => {
const s = new UndoStack<Counter>({ value: 0 });
s.push(mkCmd(1));
expect(() => s.undo()).not.toThrow();
});
it("UndoStack redo increments counter without throwing", {
timeout: 5000,
}, () => {
const s = new UndoStack<Counter>({ value: 0 });
s.push(mkCmd(1));
s.undo();
expect(() => s.redo()).not.toThrow();
});
it("BranchingHistory push, undo, redo without throwing", {
timeout: 5000,
}, () => {
const h = new BranchingHistory<Counter>({ value: 0 });
expect(() => {
h.push(mkCmd(1));
h.undo();
h.redo();
}).not.toThrow();
});
it("AsyncUndoStack push/undo/redo without throwing", {
timeout: 30000,
}, async () => {
const s = new AsyncUndoStack<Counter>({ value: 0 });
const aCmd = createAsyncCommand<Counter>(
"AI",
async (st) => ({ value: st.value + 1 }),
async (st) => ({ value: st.value - 1 })
);
await s.push(aCmd);
await s.undo();
await s.redo();
expect(s.current.value).toBe(1);
});
it("compressHistory records compress counter without throwing", {
timeout: 5000,
}, () => {
const s = new UndoStack<Counter>({ value: 0 });
for (let i = 0; i < 20; i++) s.push(mkCmd(1));
expect(() => compressHistory(s, { compressionDepth: 5 })).not.toThrow();
});
it("SelectiveUndoStack undoById records undo counter", {
timeout: 5000,
}, () => {
const s = new SelectiveUndoStack<Counter>({ value: 0 });
const a = mkCmd(1);
s.push(a);
expect(() => s.undoById(a.id)).not.toThrow();
});
it("BranchingHistory switchBranch without throwing", {
timeout: 5000,
}, () => {
const h = new BranchingHistory<Counter>({ value: 0 });
h.push(mkCmd(1));
h.undo();
h.push(mkCmd(2)); // diverge → fork
const forked = h.branches.find((b) => b.label.startsWith("fork-"));
if (forked) {
expect(() => h.switchBranch(forked.id)).not.toThrow();
}
});
});