forked from bai/curriculum-project-hub
fix: enforce active organization boundary
This commit is contained in:
@@ -250,7 +250,7 @@ function mockPrisma(): PrismaClient {
|
||||
let lock: { projectId: string; runId: string } | null = null;
|
||||
const session = { id: "session-1", metadata: {} };
|
||||
|
||||
return {
|
||||
const client = {
|
||||
feishuEventReceipt: {
|
||||
findUnique: vi.fn(async () => null),
|
||||
create: vi.fn(async () => ({ id: "receipt-1" })),
|
||||
@@ -286,6 +286,11 @@ function mockPrisma(): PrismaClient {
|
||||
create: vi.fn(async () => ({ id: "audit-1" })),
|
||||
},
|
||||
} as unknown as PrismaClient;
|
||||
Object.assign(client, {
|
||||
$transaction: vi.fn(async (callback: (tx: PrismaClient) => Promise<unknown>) => callback(client)),
|
||||
$queryRaw: vi.fn(async () => [{ id: "org_test_default", status: "ACTIVE" }]),
|
||||
});
|
||||
return client;
|
||||
}
|
||||
|
||||
function silentLogger(): FeishuRuntime["logger"] {
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { mkdir, mkdtemp, readdir, rm, symlink, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { removeAbandonedMessageResourceStages } from "../../src/feishu/resourceStaging.js";
|
||||
|
||||
const roots: string[] = [];
|
||||
|
||||
describe("Feishu resource staging recovery", () => {
|
||||
afterEach(async () => {
|
||||
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
|
||||
});
|
||||
|
||||
it("removes every abandoned batch during startup recovery", async () => {
|
||||
const root = await tempRoot();
|
||||
const batch = join(root, ".cph-staging", "abandoned-batch");
|
||||
await mkdir(batch, { recursive: true });
|
||||
await writeFile(join(batch, "resource-0"), "sensitive attachment");
|
||||
|
||||
await expect(removeAbandonedMessageResourceStages(root)).resolves.toBe(1);
|
||||
await expect(readdir(join(root, ".cph-staging"))).resolves.toEqual([]);
|
||||
});
|
||||
|
||||
it("refuses to traverse a staging-base symlink", async () => {
|
||||
const root = await tempRoot();
|
||||
const outside = await tempRoot();
|
||||
await symlink(outside, join(root, ".cph-staging"));
|
||||
|
||||
await expect(removeAbandonedMessageResourceStages(root)).rejects.toThrow(
|
||||
/staging base is not a real directory/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
async function tempRoot(): Promise<string> {
|
||||
const root = await mkdtemp(join(tmpdir(), "cph-resource-staging-test-"));
|
||||
roots.push(root);
|
||||
return root;
|
||||
}
|
||||
Reference in New Issue
Block a user