mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 16:45:22 +00:00
201 lines
7.1 KiB
TypeScript
201 lines
7.1 KiB
TypeScript
import { existsSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { DEBUG_LOG_FILENAME, REVIEW_LOG_FILENAME } from "#src/config-paths";
|
|
import {
|
|
DEFAULT_EXTENSION_CONFIG,
|
|
type PermissionSystemExtensionConfig,
|
|
} from "#src/extension-config";
|
|
import type { SessionLoggerDeps } from "#src/session-logger";
|
|
import { PermissionSessionLogger } from "#src/session-logger";
|
|
|
|
// ── helpers ────────────────────────────────────────────────────────────────
|
|
|
|
let tempDir: string;
|
|
|
|
beforeEach(() => {
|
|
tempDir = mkdtempSync(join(tmpdir(), "ps-session-logger-"));
|
|
});
|
|
|
|
function makeDeps(
|
|
overrides: {
|
|
globalLogsDir?: string;
|
|
getConfig?: () => PermissionSystemExtensionConfig;
|
|
} = {},
|
|
) {
|
|
return {
|
|
globalLogsDir: overrides.globalLogsDir ?? tempDir,
|
|
getConfig:
|
|
overrides.getConfig ??
|
|
((): PermissionSystemExtensionConfig => ({
|
|
...DEFAULT_EXTENSION_CONFIG,
|
|
})),
|
|
notify: vi.fn<(message: string) => void>(),
|
|
};
|
|
}
|
|
|
|
/** A `globalLogsDir` that cannot be created: a file at the parent path blocks it. */
|
|
function makeBlockedLogsDir(): string {
|
|
const barrier = join(tempDir, "barrier");
|
|
writeFileSync(barrier, "");
|
|
return join(barrier, "logs");
|
|
}
|
|
|
|
// ── PermissionSessionLogger ────────────────────────────────────────────────────
|
|
|
|
describe("PermissionSessionLogger", () => {
|
|
// ── debug ────────────────────────────────────────────────────────────────
|
|
|
|
describe("debug", () => {
|
|
it("writes a JSONL line to the debug log file when debugLog is true", () => {
|
|
const deps = makeDeps({
|
|
getConfig: () => ({ ...DEFAULT_EXTENSION_CONFIG, debugLog: true }),
|
|
});
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.debug("test.event", { key: "value" });
|
|
|
|
expect(existsSync(join(tempDir, DEBUG_LOG_FILENAME))).toBe(true);
|
|
expect(deps.notify).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not write to the debug log when debugLog is false", () => {
|
|
// DEFAULT_EXTENSION_CONFIG.debugLog === false
|
|
const deps = makeDeps();
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.debug("test.event");
|
|
|
|
expect(existsSync(join(tempDir, DEBUG_LOG_FILENAME))).toBe(false);
|
|
expect(deps.notify).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("reads getConfig at write time — a mid-session toggle change takes effect", () => {
|
|
let debugLog = true;
|
|
const deps = makeDeps({
|
|
getConfig: () => ({ ...DEFAULT_EXTENSION_CONFIG, debugLog }),
|
|
});
|
|
const logger = new PermissionSessionLogger(deps);
|
|
debugLog = false;
|
|
|
|
logger.debug("test.event");
|
|
|
|
expect(existsSync(join(tempDir, DEBUG_LOG_FILENAME))).toBe(false);
|
|
});
|
|
});
|
|
|
|
// ── review ───────────────────────────────────────────────────────────────
|
|
|
|
describe("review", () => {
|
|
it("writes a JSONL line to the review log file when permissionReviewLog is true", () => {
|
|
// DEFAULT_EXTENSION_CONFIG.permissionReviewLog === true
|
|
const deps = makeDeps();
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.review("permission.granted", { agentName: "coder" });
|
|
|
|
expect(existsSync(join(tempDir, REVIEW_LOG_FILENAME))).toBe(true);
|
|
expect(deps.notify).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it("does not write to the review log when permissionReviewLog is false", () => {
|
|
const deps = makeDeps({
|
|
getConfig: () => ({
|
|
...DEFAULT_EXTENSION_CONFIG,
|
|
permissionReviewLog: false,
|
|
}),
|
|
});
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.review("permission.granted");
|
|
|
|
expect(existsSync(join(tempDir, REVIEW_LOG_FILENAME))).toBe(false);
|
|
expect(deps.notify).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
// ── IO-failure warnings ───────────────────────────────────────────────────
|
|
|
|
describe("IO-failure warnings", () => {
|
|
it("calls notify with the error message when the logs directory cannot be created", () => {
|
|
const deps = makeDeps({
|
|
globalLogsDir: makeBlockedLogsDir(),
|
|
getConfig: () => ({ ...DEFAULT_EXTENSION_CONFIG, debugLog: true }),
|
|
});
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.debug("test.event");
|
|
|
|
expect(deps.notify).toHaveBeenCalledOnce();
|
|
expect(deps.notify).toHaveBeenCalledWith(
|
|
expect.stringContaining("Failed to"),
|
|
);
|
|
});
|
|
|
|
it("deduplicates the same IO-failure warning across multiple writes", () => {
|
|
const deps = makeDeps({
|
|
globalLogsDir: makeBlockedLogsDir(),
|
|
getConfig: () => ({ ...DEFAULT_EXTENSION_CONFIG, debugLog: true }),
|
|
});
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.debug("event.one");
|
|
logger.debug("event.two");
|
|
|
|
expect(deps.notify).toHaveBeenCalledOnce();
|
|
});
|
|
|
|
it("shares the dedup set across debug and review — same message notified only once", () => {
|
|
const deps = makeDeps({
|
|
globalLogsDir: makeBlockedLogsDir(),
|
|
getConfig: () => ({
|
|
...DEFAULT_EXTENSION_CONFIG,
|
|
debugLog: true,
|
|
permissionReviewLog: true,
|
|
}),
|
|
});
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.debug("event.one"); // emits warning
|
|
logger.review("event.two"); // same error message → suppressed
|
|
|
|
expect(deps.notify).toHaveBeenCalledOnce();
|
|
});
|
|
});
|
|
|
|
// ── warn ──────────────────────────────────────────────────────────────────
|
|
|
|
describe("warn", () => {
|
|
it("calls notify with the message directly", () => {
|
|
const deps = makeDeps();
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.warn("Something went wrong");
|
|
|
|
expect(deps.notify).toHaveBeenCalledWith("Something went wrong");
|
|
});
|
|
|
|
it("calls notify for every warn — not deduplicated", () => {
|
|
const deps = makeDeps();
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
logger.warn("same message");
|
|
logger.warn("same message");
|
|
|
|
expect(deps.notify).toHaveBeenCalledTimes(2);
|
|
});
|
|
|
|
it("does not throw when notify is a no-op", () => {
|
|
const deps: SessionLoggerDeps = {
|
|
globalLogsDir: tempDir,
|
|
getConfig: () => ({ ...DEFAULT_EXTENSION_CONFIG }),
|
|
notify: () => {},
|
|
};
|
|
const logger = new PermissionSessionLogger(deps);
|
|
|
|
expect(() => logger.warn("test")).not.toThrow();
|
|
});
|
|
});
|
|
});
|