feat: auto-delete old conversations, and stop a tool call that repeats (#15)
This commit was merged in pull request #15.
This commit is contained in:
@@ -1,6 +1,14 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { DEFAULT_HOST, DEFAULT_MODEL } from "../lib/constants";
|
||||
import { describeResumeTimeout, resolvePreferences, resolveResumeTimeout } from "../lib/preferences";
|
||||
import { DEFAULT_HOST, DEFAULT_MAX_TOOL_ROUNDS, DEFAULT_MODEL } from "../lib/constants";
|
||||
import {
|
||||
describeAutoDelete,
|
||||
describeMaxToolRounds,
|
||||
describeResumeTimeout,
|
||||
resolveAutoDelete,
|
||||
resolveMaxToolRounds,
|
||||
resolvePreferences,
|
||||
resolveResumeTimeout,
|
||||
} from "../lib/preferences";
|
||||
|
||||
const MINUTE = 60_000;
|
||||
const HOUR = 60 * MINUTE;
|
||||
@@ -53,6 +61,10 @@ describe("resolvePreferences", () => {
|
||||
expect(resolved.showReasoning).toBe(true);
|
||||
expect(resolved.think).toBeUndefined();
|
||||
expect(resolved.resumeTimeoutMs).toBe(HOUR);
|
||||
// Auto-deletion is the one setting here that destroys data, so its default
|
||||
// is off and every unresolvable value lands on the same answer.
|
||||
expect(resolved.autoDeleteMs).toBeUndefined();
|
||||
expect(resolved.maxToolRounds).toBe(DEFAULT_MAX_TOOL_ROUNDS);
|
||||
// An empty string rather than undefined, so callers never branch on it.
|
||||
expect(resolved.place).toBe("");
|
||||
});
|
||||
@@ -104,4 +116,90 @@ describe("resolvePreferences", () => {
|
||||
expect(resolvePreferences({ resumeTimeout: "15m" }).resumeTimeoutMs).toBe(15 * MINUTE);
|
||||
expect(resolvePreferences({ resumeTimeout: "never" }).resumeTimeoutMs).toBeUndefined();
|
||||
});
|
||||
|
||||
it("carries the auto-delete window through, including disabled", () => {
|
||||
expect(resolvePreferences({ autoDeleteAfter: "30d" }).autoDeleteMs).toBe(30 * DAY);
|
||||
expect(resolvePreferences({ autoDeleteAfter: "disabled" }).autoDeleteMs).toBeUndefined();
|
||||
expect(resolvePreferences({ autoDeleteAfter: "garbage" }).autoDeleteMs).toBeUndefined();
|
||||
});
|
||||
|
||||
it("carries the tool-call cap through, including unlimited", () => {
|
||||
expect(resolvePreferences({ maxToolRounds: "16" }).maxToolRounds).toBe(16);
|
||||
expect(resolvePreferences({ maxToolRounds: "unlimited" }).maxToolRounds).toBeUndefined();
|
||||
expect(resolvePreferences({ maxToolRounds: "garbage" }).maxToolRounds).toBe(DEFAULT_MAX_TOOL_ROUNDS);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveAutoDelete", () => {
|
||||
it("maps each configured window", () => {
|
||||
expect(resolveAutoDelete("1d")).toBe(DAY);
|
||||
expect(resolveAutoDelete("7d")).toBe(7 * DAY);
|
||||
expect(resolveAutoDelete("30d")).toBe(30 * DAY);
|
||||
expect(resolveAutoDelete("90d")).toBe(90 * DAY);
|
||||
});
|
||||
|
||||
it("returns undefined for 'disabled', which is the default", () => {
|
||||
expect(resolveAutoDelete("disabled")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined for missing or unrecognised values rather than guessing", () => {
|
||||
// Both readings of an unknown value are safe here only because the fallback
|
||||
// is "delete nothing". See `resolveMaxToolRounds` for the other convention.
|
||||
expect(resolveAutoDelete(undefined)).toBeUndefined();
|
||||
expect(resolveAutoDelete("")).toBeUndefined();
|
||||
expect(resolveAutoDelete("banana")).toBeUndefined();
|
||||
expect(resolveAutoDelete("1y")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("tolerates surrounding whitespace and casing", () => {
|
||||
expect(resolveAutoDelete(" 7D ")).toBe(7 * DAY);
|
||||
});
|
||||
});
|
||||
|
||||
describe("describeAutoDelete", () => {
|
||||
it("names the off case rather than showing a number", () => {
|
||||
expect(describeAutoDelete(undefined)).toContain("Disabled");
|
||||
});
|
||||
|
||||
it("describes days", () => {
|
||||
expect(describeAutoDelete(DAY)).toBe("After 1 day");
|
||||
expect(describeAutoDelete(30 * DAY)).toBe("After 30 days");
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveMaxToolRounds", () => {
|
||||
it("maps each configured cap", () => {
|
||||
expect(resolveMaxToolRounds("4")).toBe(4);
|
||||
expect(resolveMaxToolRounds("8")).toBe(8);
|
||||
expect(resolveMaxToolRounds("16")).toBe(16);
|
||||
});
|
||||
|
||||
it("returns undefined for unlimited, which is what removes the cap", () => {
|
||||
expect(resolveMaxToolRounds("unlimited")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("falls back to the default cap for anything unrecognised", () => {
|
||||
// Deliberately the opposite of `resolveAutoDelete`: those two settings are
|
||||
// safe when they do nothing, and this one guards a loop, so the safe reading
|
||||
// of a value this codebase does not understand is the cap and not its absence.
|
||||
expect(resolveMaxToolRounds(undefined)).toBe(DEFAULT_MAX_TOOL_ROUNDS);
|
||||
expect(resolveMaxToolRounds("")).toBe(DEFAULT_MAX_TOOL_ROUNDS);
|
||||
expect(resolveMaxToolRounds("banana")).toBe(DEFAULT_MAX_TOOL_ROUNDS);
|
||||
expect(resolveMaxToolRounds("0")).toBe(DEFAULT_MAX_TOOL_ROUNDS);
|
||||
});
|
||||
|
||||
it("tolerates surrounding whitespace and casing", () => {
|
||||
expect(resolveMaxToolRounds(" UNLIMITED ")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("describeMaxToolRounds", () => {
|
||||
it("names the uncapped case", () => {
|
||||
expect(describeMaxToolRounds(undefined)).toContain("Unlimited");
|
||||
});
|
||||
|
||||
it("describes a cap in rounds", () => {
|
||||
expect(describeMaxToolRounds(8)).toBe("8 rounds per answer");
|
||||
expect(describeMaxToolRounds(1)).toBe("1 round per answer");
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user