diff --git a/scripts/e2e/lib/upgrade-survivor/assertions.mjs b/scripts/e2e/lib/upgrade-survivor/assertions.mjs index d46235fa327a..4a04e9fb9133 100644 --- a/scripts/e2e/lib/upgrade-survivor/assertions.mjs +++ b/scripts/e2e/lib/upgrade-survivor/assertions.mjs @@ -96,13 +96,14 @@ function assert(condition, message) { function seedLegacySessionMetadata(stateDir) { const legacySessionsDir = path.join(stateDir, "sessions"); + const baseUpdatedAt = Date.now() - 24 * 60 * 60 * 1000; writeJson(path.join(legacySessionsDir, "sessions.json"), { main: { sessionId: LEGACY_SESSION_MAIN_ID, sessionFile: path.join(legacySessionsDir, `${LEGACY_SESSION_MAIN_ID}.jsonl`), provider: "openai", model: "gpt-5.5", - updatedAt: 1710000000000, + updatedAt: baseUpdatedAt, skillsSnapshot: { prompt: "legacy prompt survives as metadata", resolvedSkills: [ @@ -118,14 +119,14 @@ function seedLegacySessionMetadata(stateDir) { sessionFile: path.join(legacySessionsDir, `${LEGACY_SESSION_DIRECT_ID}.jsonl`), provider: "openai", model: "gpt-5.5", - updatedAt: 1710000000100, + updatedAt: baseUpdatedAt + 100, }, "slack:channel:CUPGRADE": { sessionId: LEGACY_SESSION_GROUP_ID, sessionFile: path.join(legacySessionsDir, `${LEGACY_SESSION_GROUP_ID}.jsonl`), provider: "openai", model: "gpt-5.5", - updatedAt: 1710000000200, + updatedAt: baseUpdatedAt + 200, lastChannel: "slack", lastTo: "CUPGRADE", }, diff --git a/test/scripts/upgrade-survivor-assertions.test.ts b/test/scripts/upgrade-survivor-assertions.test.ts index 50793214c9e9..1c94397944e2 100644 --- a/test/scripts/upgrade-survivor-assertions.test.ts +++ b/test/scripts/upgrade-survivor-assertions.test.ts @@ -1,6 +1,6 @@ // Upgrade Survivor Assertions tests cover upgrade survivor assertions script behavior. import { execFileSync } from "node:child_process"; -import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs"; +import { mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { DatabaseSync } from "node:sqlite"; @@ -248,6 +248,71 @@ describe("upgrade survivor assertions", () => { expect(new Set(scenarios).size).toBe(scenarios.length); }); + it("seeds recent ordered legacy session timestamps", () => { + const root = mkdtempSync(join(tmpdir(), "openclaw-upgrade-survivor-seed-")); + try { + const stateDir = join(root, "state"); + const workspace = join(root, "workspace"); + mkdirSync(stateDir, { recursive: true }); + mkdirSync(workspace, { recursive: true }); + + const beforeSeed = Date.now(); + execFileSync(process.execPath, [ASSERTIONS_PATH, "seed"], { + env: { + ...process.env, + OPENCLAW_STATE_DIR: stateDir, + OPENCLAW_TEST_WORKSPACE_DIR: workspace, + OPENCLAW_UPGRADE_SURVIVOR_SCENARIO: "base", + }, + stdio: "pipe", + }); + const afterSeed = Date.now(); + + const sessions = JSON.parse( + readFileSync(join(stateDir, "sessions", "sessions.json"), "utf8"), + ) as Record; + const seededRows = [ + sessions.main, + sessions["+15551234567"], + sessions["slack:channel:CUPGRADE"], + ]; + expect(seededRows.map((row) => row?.sessionId)).toEqual([ + "upgrade-main-session", + "upgrade-direct-session", + "upgrade-group-session", + ]); + + const timestamps = seededRows.map((row) => row?.updatedAt); + for (const timestamp of timestamps) { + expect(typeof timestamp).toBe("number"); + } + const [mainUpdatedAt, directUpdatedAt, groupUpdatedAt] = timestamps as [ + number, + number, + number, + ]; + expect(directUpdatedAt - mainUpdatedAt).toBe(100); + expect(groupUpdatedAt - mainUpdatedAt).toBe(200); + expect(mainUpdatedAt).toBeLessThan(directUpdatedAt); + expect(directUpdatedAt).toBeLessThan(groupUpdatedAt); + + const dayMs = 24 * 60 * 60 * 1000; + const thirtyDaysMs = 30 * dayMs; + for (const [timestamp, offset] of [ + [mainUpdatedAt, 0], + [directUpdatedAt, 100], + [groupUpdatedAt, 200], + ] as const) { + expect(timestamp).toBeGreaterThanOrEqual(beforeSeed - dayMs + offset); + expect(timestamp).toBeLessThanOrEqual(afterSeed - dayMs + offset); + expect(timestamp).toBeGreaterThan(afterSeed - thirtyDaysMs); + expect(timestamp).toBeLessThanOrEqual(afterSeed); + } + } finally { + rmSync(root, { force: true, recursive: true }); + } + }); + it("accepts the ACPX OpenClaw tools bridge scenario during seed", () => { const root = mkdtempSync(join(tmpdir(), "openclaw-upgrade-survivor-acpx-")); try {