Initial Commit
This commit is contained in:
108
packages/foundation/flow/undo/tests/otel.test.ts
Normal file
108
packages/foundation/flow/undo/tests/otel.test.ts
Normal 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();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user