test(ci): isolate runtime and rules dependencies
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
|
||||
import { afterEach, beforeEach, describe, expect, it } from "bun:test";
|
||||
import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
@@ -8,10 +8,6 @@ function createImportSuffix(): string {
|
||||
}
|
||||
|
||||
describe("createRuleScanCache", () => {
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
});
|
||||
|
||||
it("returns undefined before set, returns stored value, and clears entries", async () => {
|
||||
// given
|
||||
const { createRuleScanCache } = await import(`./rule-scan-cache${createImportSuffix()}`);
|
||||
@@ -55,7 +51,6 @@ describe("findRuleFiles with scan cache", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
mock.restore();
|
||||
if (existsSync(testRoot)) {
|
||||
rmSync(testRoot, { recursive: true, force: true });
|
||||
}
|
||||
@@ -63,29 +58,26 @@ describe("findRuleFiles with scan cache", () => {
|
||||
|
||||
it("reuses cached directory scan results for identical inputs", async () => {
|
||||
// given
|
||||
const findRuleFilesRecursive = mock((directoryPath: string, results: string[]) => {
|
||||
if (directoryPath === expectedRuleDir) {
|
||||
results.push(expectedRuleFile);
|
||||
}
|
||||
});
|
||||
|
||||
mock.module("./rule-file-scanner", () => ({
|
||||
findRuleFilesRecursive,
|
||||
safeRealpathSync: (filePath: string) => filePath,
|
||||
}));
|
||||
|
||||
const { createRuleScanCache } = await import(`./rule-scan-cache${createImportSuffix()}`);
|
||||
const { findRuleFiles } = await import(`./rule-file-finder${createImportSuffix()}`);
|
||||
const cache = createRuleScanCache();
|
||||
const secondRuleFile = join(expectedRuleDir, "python.instructions.md");
|
||||
|
||||
mkdirSync(expectedRuleDir, { recursive: true });
|
||||
writeFileSync(expectedRuleFile, "TypeScript rules\n");
|
||||
|
||||
// when
|
||||
const firstCandidates = findRuleFiles(projectRoot, homeDir, currentFile, undefined, cache);
|
||||
const firstInvocationCount = findRuleFilesRecursive.mock.calls.length;
|
||||
writeFileSync(secondRuleFile, "Python rules\n");
|
||||
const secondCandidates = findRuleFiles(projectRoot, homeDir, currentFile, undefined, cache);
|
||||
const uncachedCandidates = findRuleFiles(projectRoot, homeDir, currentFile);
|
||||
|
||||
// then
|
||||
expect(firstCandidates).toEqual(secondCandidates);
|
||||
expect(firstInvocationCount).toBeGreaterThan(0);
|
||||
expect(findRuleFilesRecursive).toHaveBeenCalledTimes(firstInvocationCount);
|
||||
expect(firstCandidates.map((candidate) => candidate.path)).toEqual([expectedRuleFile]);
|
||||
expect(secondCandidates.map((candidate) => candidate.path)).toEqual([expectedRuleFile]);
|
||||
expect(uncachedCandidates.map((candidate) => candidate.path).sort()).toEqual([
|
||||
expectedRuleFile,
|
||||
secondRuleFile,
|
||||
].sort());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
||||
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test"
|
||||
import type { AutoRetryHelpers } from "./auto-retry"
|
||||
import { createRuntimeFallbackHook } from "./hook"
|
||||
import type { HookDeps, RuntimeFallbackPluginInput } from "./types"
|
||||
|
||||
let capturedDeps: HookDeps | undefined
|
||||
@@ -16,31 +18,18 @@ const mockCreateAutoRetryHelpers = mock((deps: HookDeps) => {
|
||||
}
|
||||
})
|
||||
|
||||
const mockCreateEventHandler = mock(() => async () => {})
|
||||
const mockCreateMessageUpdateHandler = mock(() => async () => {})
|
||||
const mockCreateChatMessageHandler = mock(() => async () => {})
|
||||
const mockCreateEventHandler = mock((_deps: HookDeps, _helpers: AutoRetryHelpers) => async () => {})
|
||||
const mockCreateMessageUpdateHandler = mock((_deps: HookDeps, _helpers: AutoRetryHelpers) => async () => {})
|
||||
const mockCreateChatMessageHandler = mock((_deps: HookDeps) => async () => {})
|
||||
|
||||
mock.module("./auto-retry", () => ({
|
||||
createAutoRetryHelpers: mockCreateAutoRetryHelpers,
|
||||
}))
|
||||
|
||||
mock.module("./event-handler", () => ({
|
||||
createEventHandler: mockCreateEventHandler,
|
||||
}))
|
||||
|
||||
mock.module("./message-update-handler", () => ({
|
||||
createMessageUpdateHandler: mockCreateMessageUpdateHandler,
|
||||
}))
|
||||
|
||||
mock.module("./chat-message-handler", () => ({
|
||||
createChatMessageHandler: mockCreateChatMessageHandler,
|
||||
}))
|
||||
|
||||
afterAll(() => {
|
||||
mock.restore()
|
||||
})
|
||||
|
||||
const { createRuntimeFallbackHook } = await import("./hook")
|
||||
function createHookWithMocks() {
|
||||
return createRuntimeFallbackHook(createMockContext(), { pluginConfig: {} }, {
|
||||
createAutoRetryHelpers: mockCreateAutoRetryHelpers,
|
||||
createEventHandler: mockCreateEventHandler,
|
||||
createMessageUpdateHandler: mockCreateMessageUpdateHandler,
|
||||
createChatMessageHandler: mockCreateChatMessageHandler,
|
||||
})
|
||||
}
|
||||
|
||||
function createMockContext(): RuntimeFallbackPluginInput {
|
||||
return {
|
||||
@@ -109,7 +98,7 @@ describe("createRuntimeFallbackHook dispose", () => {
|
||||
|
||||
test("#given runtime-fallback hook handles its first event #when dispose() is called #then cleanup interval is cleared", async () => {
|
||||
// given
|
||||
const hook = createRuntimeFallbackHook(createMockContext(), { pluginConfig: {} })
|
||||
const hook = createHookWithMocks()
|
||||
await hook.event({ event: { type: "session.created", properties: {} } })
|
||||
|
||||
// when
|
||||
@@ -122,7 +111,7 @@ describe("createRuntimeFallbackHook dispose", () => {
|
||||
|
||||
test("#given hook with session state data #when dispose() is called #then all Maps and Sets are empty", () => {
|
||||
// given
|
||||
const hook = createRuntimeFallbackHook(createMockContext(), { pluginConfig: {} })
|
||||
const hook = createHookWithMocks()
|
||||
const fallbackTimeout = setTimeout(() => {}, 60_000)
|
||||
|
||||
capturedDeps?.sessionStates.set("session-1", {
|
||||
@@ -150,7 +139,7 @@ describe("createRuntimeFallbackHook dispose", () => {
|
||||
|
||||
test("#given hook with pending fallback timeouts #when dispose() is called #then timeouts are cleared before Map is emptied", () => {
|
||||
// given
|
||||
const hook = createRuntimeFallbackHook(createMockContext(), { pluginConfig: {} })
|
||||
const hook = createHookWithMocks()
|
||||
const fallbackTimeout = setTimeout(() => {}, 60_000)
|
||||
capturedDeps?.sessionFallbackTimeouts.set("session-1", fallbackTimeout)
|
||||
|
||||
|
||||
@@ -1,18 +1,37 @@
|
||||
import type { HookDeps, RuntimeFallbackHook, RuntimeFallbackInterval, RuntimeFallbackOptions, RuntimeFallbackPluginInput, RuntimeFallbackTimeout } from "./types"
|
||||
import { DEFAULT_CONFIG } from "./constants"
|
||||
import { createAutoRetryHelpers } from "./auto-retry"
|
||||
import { createChatMessageHandler } from "./chat-message-handler"
|
||||
import { DEFAULT_CONFIG } from "./constants"
|
||||
import { createEventHandler } from "./event-handler"
|
||||
import { createMessageUpdateHandler } from "./message-update-handler"
|
||||
import { createChatMessageHandler } from "./chat-message-handler"
|
||||
import type { HookDeps, RuntimeFallbackHook, RuntimeFallbackInterval, RuntimeFallbackOptions, RuntimeFallbackPluginInput, RuntimeFallbackTimeout } from "./types"
|
||||
|
||||
declare function setInterval(callback: () => void, delay?: number): RuntimeFallbackInterval
|
||||
declare function clearInterval(interval: RuntimeFallbackInterval): void
|
||||
declare function clearTimeout(timeout: RuntimeFallbackTimeout): void
|
||||
|
||||
type RuntimeFallbackHookFactories = {
|
||||
createAutoRetryHelpers: typeof createAutoRetryHelpers
|
||||
createEventHandler: typeof createEventHandler
|
||||
createMessageUpdateHandler: typeof createMessageUpdateHandler
|
||||
createChatMessageHandler: typeof createChatMessageHandler
|
||||
}
|
||||
|
||||
const defaultRuntimeFallbackHookFactories: RuntimeFallbackHookFactories = {
|
||||
createAutoRetryHelpers,
|
||||
createEventHandler,
|
||||
createMessageUpdateHandler,
|
||||
createChatMessageHandler,
|
||||
}
|
||||
|
||||
export function createRuntimeFallbackHook(
|
||||
ctx: RuntimeFallbackPluginInput,
|
||||
options?: RuntimeFallbackOptions
|
||||
options?: RuntimeFallbackOptions,
|
||||
factoryOverrides: Partial<RuntimeFallbackHookFactories> = {},
|
||||
): RuntimeFallbackHook {
|
||||
const factories = {
|
||||
...defaultRuntimeFallbackHookFactories,
|
||||
...factoryOverrides,
|
||||
}
|
||||
const config = {
|
||||
enabled: options?.config?.enabled ?? DEFAULT_CONFIG.enabled,
|
||||
retry_on_errors: options?.config?.retry_on_errors ?? DEFAULT_CONFIG.retry_on_errors,
|
||||
@@ -35,10 +54,10 @@ export function createRuntimeFallbackHook(
|
||||
sessionStatusRetryKeys: new Map(),
|
||||
}
|
||||
|
||||
const helpers = createAutoRetryHelpers(deps)
|
||||
const baseEventHandler = createEventHandler(deps, helpers)
|
||||
const messageUpdateHandler = createMessageUpdateHandler(deps, helpers)
|
||||
const chatMessageHandler = createChatMessageHandler(deps)
|
||||
const helpers = factories.createAutoRetryHelpers(deps)
|
||||
const baseEventHandler = factories.createEventHandler(deps, helpers)
|
||||
const messageUpdateHandler = factories.createMessageUpdateHandler(deps, helpers)
|
||||
const chatMessageHandler = factories.createChatMessageHandler(deps)
|
||||
|
||||
let cleanupInterval: RuntimeFallbackInterval | null = null
|
||||
let intervalStarted = false
|
||||
|
||||
Reference in New Issue
Block a user