184 lines
4.6 KiB
TypeScript
184 lines
4.6 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
|
|
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
import { join } from "path";
|
|
|
|
const { TEST_HOME } = vi.hoisted(() => {
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
const path = require("path");
|
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
const os = require("os");
|
|
return {
|
|
TEST_HOME: path.join(
|
|
os.tmpdir(),
|
|
`hermes-memory-limits-test-${Date.now()}`,
|
|
),
|
|
};
|
|
});
|
|
|
|
vi.mock("../src/main/installer", () => ({
|
|
HERMES_HOME: TEST_HOME,
|
|
}));
|
|
|
|
vi.mock("better-sqlite3", () => ({
|
|
default: vi.fn(),
|
|
}));
|
|
|
|
import {
|
|
DEFAULT_MEMORY_CHAR_LIMIT,
|
|
DEFAULT_USER_CHAR_LIMIT,
|
|
parseMemoryLimitsConfig,
|
|
} from "../src/main/memory-limits";
|
|
import {
|
|
addMemoryEntry,
|
|
readMemory,
|
|
updateMemoryEntry,
|
|
writeUserProfile,
|
|
} from "../src/main/memory";
|
|
|
|
function profileDir(profile?: string): string {
|
|
return profile ? join(TEST_HOME, "profiles", profile) : TEST_HOME;
|
|
}
|
|
|
|
function writeConfig(content: string, profile?: string): void {
|
|
const dir = profileDir(profile);
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(join(dir, "config.yaml"), content, "utf-8");
|
|
}
|
|
|
|
function writeMemoryFile(content: string, profile?: string): void {
|
|
const dir = join(profileDir(profile), "memories");
|
|
mkdirSync(dir, { recursive: true });
|
|
writeFileSync(join(dir, "MEMORY.md"), content, "utf-8");
|
|
}
|
|
|
|
beforeEach(() => {
|
|
rmSync(TEST_HOME, { recursive: true, force: true });
|
|
mkdirSync(TEST_HOME, { recursive: true });
|
|
});
|
|
|
|
afterEach(() => {
|
|
rmSync(TEST_HOME, { recursive: true, force: true });
|
|
});
|
|
|
|
describe("parseMemoryLimitsConfig", () => {
|
|
it("reads both memory limits from the memory block", () => {
|
|
expect(
|
|
parseMemoryLimitsConfig(
|
|
[
|
|
"memory:",
|
|
" memory_char_limit: 3200",
|
|
" user_char_limit: '2000'",
|
|
"",
|
|
].join("\n"),
|
|
),
|
|
).toEqual({ memoryCharLimit: 3200, userCharLimit: 2000 });
|
|
});
|
|
|
|
it("falls back when configured limits are missing or invalid", () => {
|
|
expect(
|
|
parseMemoryLimitsConfig(
|
|
[
|
|
"memory:",
|
|
" memory_char_limit: not-a-number",
|
|
" user_char_limit: 0",
|
|
"",
|
|
].join("\n"),
|
|
),
|
|
).toEqual({
|
|
memoryCharLimit: DEFAULT_MEMORY_CHAR_LIMIT,
|
|
userCharLimit: DEFAULT_USER_CHAR_LIMIT,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("memory limits from active profile config.yaml", () => {
|
|
it("returns configured local memory limits in readMemory", () => {
|
|
writeConfig(
|
|
[
|
|
"memory:",
|
|
" memory_char_limit: 3200",
|
|
" user_char_limit: 2000",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
writeMemoryFile("remember this");
|
|
|
|
const info = readMemory();
|
|
|
|
expect(info.memory.charLimit).toBe(3200);
|
|
expect(info.user.charLimit).toBe(2000);
|
|
});
|
|
|
|
it("uses a named profile's config.yaml instead of default constants", () => {
|
|
writeConfig(
|
|
[
|
|
"memory:",
|
|
" memory_char_limit: 4096",
|
|
" user_char_limit: 2048",
|
|
"",
|
|
].join("\n"),
|
|
"work",
|
|
);
|
|
writeMemoryFile("profile-scoped memory", "work");
|
|
|
|
const info = readMemory("work");
|
|
|
|
expect(info.memory.charLimit).toBe(4096);
|
|
expect(info.user.charLimit).toBe(2048);
|
|
});
|
|
|
|
it("allows memory and user writes up to the configured limits", () => {
|
|
writeConfig(
|
|
[
|
|
"memory:",
|
|
" memory_char_limit: 3200",
|
|
" user_char_limit: 2000",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
|
|
expect(addMemoryEntry("x".repeat(2600))).toEqual({ success: true });
|
|
expect(writeUserProfile("u".repeat(1800))).toEqual({ success: true });
|
|
|
|
expect(
|
|
readFileSync(join(TEST_HOME, "memories", "MEMORY.md"), "utf-8").length,
|
|
).toBe(2600);
|
|
expect(
|
|
readFileSync(join(TEST_HOME, "memories", "USER.md"), "utf-8").length,
|
|
).toBe(1800);
|
|
});
|
|
|
|
it("uses profile limits when validating memory entry updates", () => {
|
|
writeConfig(
|
|
[
|
|
"memory:",
|
|
" memory_char_limit: 2600",
|
|
" user_char_limit: 1375",
|
|
"",
|
|
].join("\n"),
|
|
"work",
|
|
);
|
|
writeMemoryFile("short", "work");
|
|
|
|
expect(updateMemoryEntry(0, "x".repeat(2300), "work")).toEqual({
|
|
success: true,
|
|
});
|
|
});
|
|
|
|
it("falls back to defaults for invalid config values", () => {
|
|
writeConfig(
|
|
[
|
|
"memory:",
|
|
" memory_char_limit: nope",
|
|
" user_char_limit: -1",
|
|
"",
|
|
].join("\n"),
|
|
);
|
|
|
|
const result = addMemoryEntry("x".repeat(DEFAULT_MEMORY_CHAR_LIMIT + 1));
|
|
|
|
expect(result.success).toBe(false);
|
|
expect(result.error).toContain(`/${DEFAULT_MEMORY_CHAR_LIMIT} chars`);
|
|
});
|
|
});
|