From 0df00f7d954a465467bd1e67471ad6394e260ce2 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Fri, 24 Apr 2026 22:16:34 +0000 Subject: [PATCH] test(execute): large-prompt Secret path coverage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add describe block "execute — large-prompt Secret path" with 5 cases: buildJobManifest called twice (promptSecretName on second call), Secret created before Job, ownerReference patched after Job creation, Secret deleted in finally block, Secret cleaned up on Job create failure - Update vi.mock for job-manifest to export LARGE_PROMPT_THRESHOLD_BYTES - Add createNamespacedSecret/deleteNamespacedSecret/patchNamespacedSecret to makeCoreApi for completeness - Update makeBatchApi to return { metadata: { uid } } so ownerRef tests work Co-Authored-By: Paperclip --- src/server/execute.test.ts | 130 ++++++++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 1 deletion(-) diff --git a/src/server/execute.test.ts b/src/server/execute.test.ts index b012197..3842c13 100644 --- a/src/server/execute.test.ts +++ b/src/server/execute.test.ts @@ -13,6 +13,7 @@ vi.mock("./k8s-client.js", () => ({ vi.mock("./job-manifest.js", () => ({ buildJobManifest: vi.fn(), + LARGE_PROMPT_THRESHOLD_BYTES: 256 * 1024, })); const MOCK_SELF_POD = { @@ -68,7 +69,7 @@ function makeCtx(configOverrides: Record = {}): AdapterExecutio function makeBatchApi(runningJobItems: unknown[] = []) { return { listNamespacedJob: vi.fn().mockResolvedValue({ items: runningJobItems }), - createNamespacedJob: vi.fn().mockResolvedValue({}), + createNamespacedJob: vi.fn().mockResolvedValue({ metadata: { uid: "test-job-uid" } }), readNamespacedJob: vi.fn().mockResolvedValue({ status: { conditions: [{ type: "Complete", status: "True" }] }, }), @@ -111,6 +112,9 @@ function makeCoreApi( }) .mockResolvedValueOnce(exitCodePod), readNamespacedPodLog: vi.fn().mockResolvedValue(jsonl), + createNamespacedSecret: vi.fn().mockResolvedValue({}), + deleteNamespacedSecret: vi.fn().mockResolvedValue({}), + patchNamespacedSecret: vi.fn().mockResolvedValue({}), }; } @@ -665,3 +669,127 @@ describe("execute — log dedup (waitForPod status dedup)", () => { expect(pendingMsgs.length).toBe(1); }); }); + +describe("execute — large-prompt Secret path", () => { + const LARGE_PROMPT = "x".repeat(300 * 1024); // 300 KiB > 256 KiB threshold + + function mockLargePrompt() { + vi.mocked(buildJobManifest).mockReturnValue({ + job: MOCK_JOB as ReturnType["job"], + jobName: JOB_NAME, + namespace: NAMESPACE, + prompt: LARGE_PROMPT, + opencodeArgs: [], + promptMetrics: null, + } as unknown as ReturnType); + } + + it("calls buildJobManifest twice and passes promptSecretName on second call", async () => { + mockLargePrompt(); + + const ctx = makeCtx(); + await execute(ctx); + + expect(vi.mocked(buildJobManifest)).toHaveBeenCalledTimes(2); + const secondCall = vi.mocked(buildJobManifest).mock.calls[1][0]; + expect(secondCall.promptSecretName).toBe(`${JOB_NAME}-prompt`); + }); + + it("creates a Secret with the prompt content before creating the Job", async () => { + mockLargePrompt(); + const coreApi = makeCoreApi(); + vi.mocked(getCoreApi).mockReturnValue(coreApi as unknown as ReturnType); + const batchApi = makeBatchApi(); + vi.mocked(getBatchApi).mockReturnValue(batchApi as unknown as ReturnType); + + const ctx = makeCtx(); + await execute(ctx); + + expect(coreApi.createNamespacedSecret).toHaveBeenCalledWith( + expect.objectContaining({ + namespace: NAMESPACE, + body: expect.objectContaining({ + metadata: expect.objectContaining({ name: `${JOB_NAME}-prompt` }), + stringData: expect.objectContaining({ prompt: LARGE_PROMPT }), + }), + }), + ); + // Secret must be created before Job + const secretOrder = coreApi.createNamespacedSecret.mock.invocationCallOrder[0]; + const jobOrder = batchApi.createNamespacedJob.mock.invocationCallOrder[0]; + expect(secretOrder).toBeLessThan(jobOrder); + }); + + it("patches the Secret with a Job ownerReference after Job creation", async () => { + mockLargePrompt(); + const batchApi = makeBatchApi(); + batchApi.createNamespacedJob.mockResolvedValue({ metadata: { uid: "uid-abc-123" } }); + vi.mocked(getBatchApi).mockReturnValue(batchApi as unknown as ReturnType); + const coreApi = makeCoreApi(); + vi.mocked(getCoreApi).mockReturnValue(coreApi as unknown as ReturnType); + + const ctx = makeCtx(); + await execute(ctx); + + expect(coreApi.patchNamespacedSecret).toHaveBeenCalledWith( + expect.objectContaining({ + name: `${JOB_NAME}-prompt`, + namespace: NAMESPACE, + body: expect.objectContaining({ + metadata: expect.objectContaining({ + ownerReferences: [ + expect.objectContaining({ + kind: "Job", + name: JOB_NAME, + uid: "uid-abc-123", + controller: true, + }), + ], + }), + }), + }), + ); + }); + + it("cleans up the Secret in the finally block", async () => { + mockLargePrompt(); + const coreApi = makeCoreApi(); + vi.mocked(getCoreApi).mockReturnValue(coreApi as unknown as ReturnType); + + const ctx = makeCtx(); + await execute(ctx); + + expect(coreApi.deleteNamespacedSecret).toHaveBeenCalledWith( + expect.objectContaining({ name: `${JOB_NAME}-prompt`, namespace: NAMESPACE }), + ); + }); + + it("cleans up the Secret when Job creation fails", async () => { + mockLargePrompt(); + const batchApi = makeBatchApi(); + batchApi.createNamespacedJob.mockRejectedValue(new Error("quota exceeded")); + vi.mocked(getBatchApi).mockReturnValue(batchApi as unknown as ReturnType); + const coreApi = makeCoreApi(); + vi.mocked(getCoreApi).mockReturnValue(coreApi as unknown as ReturnType); + + const ctx = makeCtx(); + const result = await execute(ctx); + + expect(result.errorCode).toBe("k8s_job_create_failed"); + expect(coreApi.deleteNamespacedSecret).toHaveBeenCalledWith( + expect.objectContaining({ name: `${JOB_NAME}-prompt` }), + ); + }); + + it("does not create a Secret for prompts within threshold", async () => { + // Default beforeEach mock returns "Test prompt" (11 bytes < 256 KiB) + const coreApi = makeCoreApi(); + vi.mocked(getCoreApi).mockReturnValue(coreApi as unknown as ReturnType); + + const ctx = makeCtx(); + await execute(ctx); + + expect(vi.mocked(buildJobManifest)).toHaveBeenCalledTimes(1); + expect(coreApi.createNamespacedSecret).not.toHaveBeenCalled(); + }); +});