mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-22 18:35:21 -06:00
3d76246792
* refactor: resolve final export name collisions * refactor: update remaining collision rename consumers * style: format rebased auth helpers * test: update remaining session entry mocks * test: update remaining runtime mock exports * test: update delivery info path mock * refactor: reconcile combined collision sweeps * chore: regenerate collision and sdk baselines
204 lines
7.2 KiB
TypeScript
204 lines
7.2 KiB
TypeScript
import path from "node:path";
|
|
import { afterEach, beforeEach, describe, expect, it } from "vitest";
|
|
import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js";
|
|
import { upsertSessionEntryCore } from "../config/sessions/session-accessor.js";
|
|
import {
|
|
runExclusiveSqliteSessionWrite,
|
|
resolveSqliteTranscriptScope,
|
|
toDatabaseOptions,
|
|
} from "../config/sessions/session-accessor.sqlite-scope.js";
|
|
import { SessionTranscriptReadFenceError } from "../config/sessions/session-transcript-read-fence.js";
|
|
import { waitForSessionTranscriptProjection } from "../config/sessions/session-transcript-reconcile.js";
|
|
import { runOpenClawAgentWriteTransaction } from "../state/openclaw-agent-db.js";
|
|
import {
|
|
readCodexSessionTranscriptEventsBeforeAdmission,
|
|
withCodexSessionTranscriptMirrorWriteLock,
|
|
} from "./codex-session-transcript-runtime.js";
|
|
import {
|
|
appendSessionTranscriptMessageByIdentity,
|
|
readSessionTranscriptVisibleMessageDelta,
|
|
} from "./session-transcript-runtime.js";
|
|
|
|
const tempDirs = useAutoCleanupTempDirTracker(afterEach);
|
|
|
|
describe("private session transcript mirror runtime", () => {
|
|
let storePath: string;
|
|
|
|
beforeEach(() => {
|
|
const tempDir = tempDirs.make("openclaw-sdk-transcript-mirror-");
|
|
storePath = path.join(tempDir, "sessions.json");
|
|
});
|
|
|
|
it("rechecks idempotency and publishes only committed visible sequences", async () => {
|
|
const scope = {
|
|
agentId: "main",
|
|
sessionId: "indexed-mirror-session",
|
|
sessionKey: "agent:main:indexed-mirror",
|
|
storePath,
|
|
};
|
|
await upsertSessionEntryCore(scope, { sessionId: scope.sessionId, updatedAt: 1 });
|
|
|
|
await withCodexSessionTranscriptMirrorWriteLock(scope, async (locked) => {
|
|
expect(await locked.readMessageFacts({ idempotencyKeys: ["mirror-user"] })).toEqual({
|
|
anchorsByIdempotencyKey: new Map(),
|
|
existingIdempotencyKeys: new Set(),
|
|
messagesByIdempotencyKey: new Map(),
|
|
});
|
|
const first = await locked.appendMessageWithMessageSequence({
|
|
idempotencyLookup: "scan",
|
|
message: {
|
|
role: "user",
|
|
content: [{ type: "text", text: "persist once" }],
|
|
idempotencyKey: "mirror-user",
|
|
timestamp: 1,
|
|
},
|
|
});
|
|
expect(first).toMatchObject({
|
|
messageSeq: 1,
|
|
result: { appended: true, message: { role: "user" } },
|
|
});
|
|
expect(await locked.readMessageFacts({ idempotencyKeys: ["mirror-user"] })).toMatchObject({
|
|
existingIdempotencyKeys: new Set(["mirror-user"]),
|
|
messagesByIdempotencyKey: new Map([
|
|
["mirror-user", expect.objectContaining({ role: "user" })],
|
|
]),
|
|
});
|
|
const replay = await locked.appendMessageWithMessageSequence({
|
|
idempotencyLookup: "scan",
|
|
message: {
|
|
role: "user",
|
|
content: [{ type: "text", text: "persist once" }],
|
|
idempotencyKey: "mirror-user",
|
|
timestamp: 2,
|
|
},
|
|
});
|
|
expect(replay).toMatchObject({
|
|
result: {
|
|
appended: false,
|
|
message: { content: [{ text: "persist once", type: "text" }], role: "user" },
|
|
},
|
|
});
|
|
expect(replay.messageSeq).toBeUndefined();
|
|
|
|
if (!first.result) {
|
|
throw new Error("expected initial mirror append");
|
|
}
|
|
await locked.appendMessage({
|
|
message: {
|
|
role: "assistant",
|
|
content: [{ type: "text", text: "abandoned branch" }],
|
|
idempotencyKey: "mirror-abandoned",
|
|
timestamp: 3,
|
|
},
|
|
parentId: first.result.messageId,
|
|
});
|
|
const activeBranch = await locked.appendMessageWithMessageSequence({
|
|
message: {
|
|
role: "assistant",
|
|
content: [{ type: "text", text: "final active branch" }],
|
|
idempotencyKey: "mirror-active",
|
|
timestamp: 4,
|
|
},
|
|
parentId: first.result.messageId,
|
|
});
|
|
expect(activeBranch).toMatchObject({
|
|
result: { appended: true, message: { role: "assistant" } },
|
|
});
|
|
expect(activeBranch.messageSeq).toBeUndefined();
|
|
});
|
|
|
|
const resolvedScope = resolveSqliteTranscriptScope(scope);
|
|
await expect(
|
|
readSessionTranscriptVisibleMessageDelta({ ...scope, maxMessages: 10 }),
|
|
).resolves.toEqual({
|
|
kind: "unavailable",
|
|
reason: "projection_rebuilding",
|
|
});
|
|
await waitForSessionTranscriptProjection(scope);
|
|
await withCodexSessionTranscriptMirrorWriteLock(scope, async (locked) => {
|
|
const afterReconcile = await locked.appendMessageWithMessageSequence({
|
|
message: {
|
|
role: "assistant",
|
|
content: [{ type: "text", text: "after active branch" }],
|
|
idempotencyKey: "mirror-after-reconcile",
|
|
timestamp: 5,
|
|
},
|
|
});
|
|
expect(afterReconcile).toMatchObject({
|
|
messageSeq: 3,
|
|
result: { appended: true, message: { role: "assistant" } },
|
|
});
|
|
});
|
|
await waitForSessionTranscriptProjection(scope);
|
|
|
|
await runExclusiveSqliteSessionWrite(resolvedScope, async () => {
|
|
runOpenClawAgentWriteTransaction((database) => {
|
|
database.db
|
|
.prepare(
|
|
"UPDATE session_transcript_index_state SET needs_rebuild = 1 WHERE session_id = ?",
|
|
)
|
|
.run(scope.sessionId);
|
|
}, toDatabaseOptions(resolvedScope));
|
|
});
|
|
|
|
await withCodexSessionTranscriptMirrorWriteLock(scope, async (locked) => {
|
|
expect(await locked.readMessageFacts({ idempotencyKeys: ["mirror-user"] })).toMatchObject({
|
|
existingIdempotencyKeys: new Set(["mirror-user"]),
|
|
messagesByIdempotencyKey: new Map([
|
|
["mirror-user", expect.objectContaining({ role: "user" })],
|
|
]),
|
|
});
|
|
const dirtyProjection = await locked.appendMessageWithMessageSequence({
|
|
idempotencyLookup: "scan",
|
|
message: {
|
|
role: "assistant",
|
|
content: [{ type: "text", text: "projection fallback" }],
|
|
idempotencyKey: "mirror-dirty",
|
|
timestamp: 6,
|
|
},
|
|
});
|
|
expect(dirtyProjection).toMatchObject({
|
|
result: { appended: true, message: { role: "assistant" } },
|
|
});
|
|
expect(dirtyProjection.messageSeq).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
it("rejects an admission receipt for a different transcript target", async () => {
|
|
const admittedScope = {
|
|
agentId: "main",
|
|
sessionId: "admitted-session",
|
|
sessionKey: "agent:main:admitted-session",
|
|
storePath,
|
|
};
|
|
const requestedScope = {
|
|
...admittedScope,
|
|
sessionId: "requested-session",
|
|
sessionKey: "agent:main:requested-session",
|
|
};
|
|
await upsertSessionEntryCore(admittedScope, {
|
|
sessionId: admittedScope.sessionId,
|
|
updatedAt: 1,
|
|
});
|
|
await upsertSessionEntryCore(requestedScope, {
|
|
sessionId: requestedScope.sessionId,
|
|
updatedAt: 1,
|
|
});
|
|
const admitted = await appendSessionTranscriptMessageByIdentity({
|
|
...admittedScope,
|
|
message: { role: "user", content: "admitted elsewhere" },
|
|
});
|
|
if (!admitted?.anchor) {
|
|
throw new Error("expected admitted transcript anchor");
|
|
}
|
|
|
|
await expect(
|
|
readCodexSessionTranscriptEventsBeforeAdmission(requestedScope, {
|
|
...admitted.anchor,
|
|
logicalTurnId: "admitted-turn",
|
|
role: "user",
|
|
}),
|
|
).rejects.toBeInstanceOf(SessionTranscriptReadFenceError);
|
|
});
|
|
});
|