From b06ff2abf22ffbd6b11f58c44f9434f2d81c5825 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 21 Apr 2026 03:03:50 +0100 Subject: [PATCH] test: cover session maintenance defaults (#69404) (thanks @bobrenze-bot) --- src/config/sessions/sessions.test.ts | 8 +-- .../store.pruning.integration.test.ts | 51 +++++++++++++++++++ src/config/sessions/store.pruning.test.ts | 9 ++++ .../store.session-key-normalization.test.ts | 5 +- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/config/sessions/sessions.test.ts b/src/config/sessions/sessions.test.ts index 9ec166aa692..ace50ffc9f1 100644 --- a/src/config/sessions/sessions.test.ts +++ b/src/config/sessions/sessions.test.ts @@ -176,7 +176,7 @@ describe("session store lock (Promise chain mutex)", () => { it("serializes concurrent updateSessionStore calls without data loss", async () => { const key = "agent:main:test"; const { storePath } = await makeTmpStore({ - [key]: { sessionId: "s1", updatedAt: 100, counter: 0 }, + [key]: { sessionId: "s1", updatedAt: Date.now(), counter: 0 }, }); const N = 4; @@ -216,7 +216,7 @@ describe("session store lock (Promise chain mutex)", () => { it("multiple consecutive errors do not permanently poison the queue", async () => { const key = "agent:main:multi-err"; const { storePath } = await makeTmpStore({ - [key]: { sessionId: "s1", updatedAt: 100 }, + [key]: { sessionId: "s1", updatedAt: Date.now() }, }); const errors = Array.from({ length: 3 }, (_, i) => @@ -287,7 +287,7 @@ describe("session store lock (Promise chain mutex)", () => { const { storePath } = await makeTmpStore({ [key]: { sessionId: "sess-acp", - updatedAt: 100, + updatedAt: Date.now(), acp, }, }); @@ -295,7 +295,7 @@ describe("session store lock (Promise chain mutex)", () => { await updateSessionStore(storePath, (store) => { store[key] = { sessionId: "sess-acp", - updatedAt: 200, + updatedAt: Date.now(), modelProvider: "openai-codex", model: "gpt-5.4", }; diff --git a/src/config/sessions/store.pruning.integration.test.ts b/src/config/sessions/store.pruning.integration.test.ts index 2b7dd5a7411..21a12346c7f 100644 --- a/src/config/sessions/store.pruning.integration.test.ts +++ b/src/config/sessions/store.pruning.integration.test.ts @@ -246,6 +246,57 @@ describe("Integration: saveSessionStore with pruning", () => { expect(Object.keys(loaded)).toHaveLength(2); }); + it("loadSessionStore prunes stale entries from oversized stores by default", async () => { + mockLoadConfig.mockReturnValue({ + session: { + maintenance: { + maxEntries: 2, + pruneAfter: "7d", + rotateBytes: 10_485_760, + }, + }, + }); + const now = Date.now(); + const store: Record = { + stale: makeEntry(now - 31 * DAY_MS), + recent: makeEntry(now - DAY_MS), + newest: makeEntry(now), + }; + await fs.writeFile(storePath, JSON.stringify(store), "utf-8"); + + const loaded = loadSessionStore(storePath, { skipCache: true }); + + expect(loaded.stale).toBeUndefined(); + expect(loaded.recent).toBeDefined(); + expect(loaded.newest).toBeDefined(); + }); + + it("loadSessionStore caps oversized stores by default", async () => { + mockLoadConfig.mockReturnValue({ + session: { + maintenance: { + maxEntries: 2, + pruneAfter: "365d", + rotateBytes: 10_485_760, + }, + }, + }); + const now = Date.now(); + const store: Record = { + oldest: makeEntry(now - 3 * DAY_MS), + recent: makeEntry(now - DAY_MS), + newest: makeEntry(now), + }; + await fs.writeFile(storePath, JSON.stringify(store), "utf-8"); + + const loaded = loadSessionStore(storePath, { skipCache: true }); + + expect(Object.keys(loaded)).toHaveLength(2); + expect(loaded.oldest).toBeUndefined(); + expect(loaded.recent).toBeDefined(); + expect(loaded.newest).toBeDefined(); + }); + it("archives transcript files for entries evicted by maxEntries capping", async () => { applyCappedMaintenanceConfig(mockLoadConfig); diff --git a/src/config/sessions/store.pruning.test.ts b/src/config/sessions/store.pruning.test.ts index cd01ca4fd52..5ddf943e671 100644 --- a/src/config/sessions/store.pruning.test.ts +++ b/src/config/sessions/store.pruning.test.ts @@ -3,6 +3,7 @@ import fs from "node:fs/promises"; import path from "node:path"; import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { createFixtureSuite } from "../../test-utils/fixture-suite.js"; +import { resolveMaintenanceConfigFromInput } from "./store-maintenance.js"; import { capEntryCount, getActiveSessionMaintenanceWarning, @@ -75,6 +76,14 @@ describe("capEntryCount", () => { }); }); +describe("resolveMaintenanceConfigFromInput", () => { + it("defaults to enforcing session maintenance", () => { + const maintenance = resolveMaintenanceConfigFromInput(); + + expect(maintenance.mode).toBe("enforce"); + }); +}); + describe("getActiveSessionMaintenanceWarning", () => { it("warns when the active session is outside the retained recent entries", () => { const now = Date.now(); diff --git a/src/config/sessions/store.session-key-normalization.test.ts b/src/config/sessions/store.session-key-normalization.test.ts index b6a47a26604..3f30af03ea4 100644 --- a/src/config/sessions/store.session-key-normalization.test.ts +++ b/src/config/sessions/store.session-key-normalization.test.ts @@ -118,13 +118,14 @@ describe("session store key normalization", () => { }); it("preserves updatedAt when recording inbound metadata for an existing session", async () => { + const existingUpdatedAt = Date.now(); await fs.writeFile( storePath, JSON.stringify( { [CANONICAL_KEY]: { sessionId: "existing-session", - updatedAt: 1111, + updatedAt: existingUpdatedAt, chatType: "direct", channel: "webchat", origin: { @@ -150,7 +151,7 @@ describe("session store key normalization", () => { const store = loadSessionStore(storePath, { skipCache: true }); expect(store[CANONICAL_KEY]?.sessionId).toBe("existing-session"); - expect(store[CANONICAL_KEY]?.updatedAt).toBe(1111); + expect(store[CANONICAL_KEY]?.updatedAt).toBe(existingUpdatedAt); expect(store[CANONICAL_KEY]?.origin?.provider).toBe("webchat"); }); });