mirror of
https://github.com/willchen96/mike.git
synced 2026-07-26 23:51:08 +02:00
The integration tests landed on main after this branch was cut and mocked the helper this PR deletes. Vitest factory mocks don't validate against the real module shape, so the extra key was harmless but misleading. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
107 lines
3.7 KiB
TypeScript
107 lines
3.7 KiB
TypeScript
import { describe, it, expect, vi } from "vitest";
|
|
import request from "supertest";
|
|
|
|
function mockSupabase() {
|
|
const result = { data: null, error: null };
|
|
const q: Record<string, unknown> = {};
|
|
const chain = [
|
|
"select", "insert", "update", "delete", "upsert",
|
|
"eq", "neq", "in", "is", "or", "not", "lt", "order", "limit",
|
|
];
|
|
for (const m of chain) q[m] = vi.fn(() => q);
|
|
q.single = vi.fn(() => Promise.resolve(result));
|
|
q.maybeSingle = vi.fn(() => Promise.resolve(result));
|
|
q.then = (resolve: (v: unknown) => unknown) =>
|
|
Promise.resolve(result).then(resolve);
|
|
return {
|
|
from: vi.fn(() => q),
|
|
rpc: vi.fn(() => Promise.resolve(result)),
|
|
auth: {
|
|
getUser: () =>
|
|
Promise.resolve({ data: { user: { id: "u1" } }, error: null }),
|
|
},
|
|
};
|
|
}
|
|
|
|
vi.mock("../../lib/supabase", () => ({
|
|
createServerSupabase: vi.fn(() => mockSupabase()),
|
|
}));
|
|
|
|
vi.mock("../../middleware/auth", () => ({
|
|
requireAuth: (
|
|
_req: unknown,
|
|
res: { locals: Record<string, unknown> },
|
|
next: () => void,
|
|
) => {
|
|
res.locals.userId = "u1";
|
|
res.locals.userEmail = "u1@test.local";
|
|
next();
|
|
},
|
|
requireMfaIfEnrolled: (_req: unknown, _res: unknown, next: () => void) =>
|
|
next(),
|
|
}));
|
|
|
|
// Stub the storage IO functions so a request that clears validation never
|
|
// touches R2/S3, while keeping the rest of the storage module (key builders,
|
|
// disposition helpers) real. The validation tests below reject before storage
|
|
// is reached, but this guards against accidental real IO regardless.
|
|
vi.mock("../../lib/storage", async (importOriginal) => {
|
|
const actual = await importOriginal<typeof import("../../lib/storage")>();
|
|
return {
|
|
...actual,
|
|
uploadFile: vi.fn(async () => {}),
|
|
downloadFile: vi.fn(async () => null),
|
|
deleteFile: vi.fn(async () => {}),
|
|
};
|
|
});
|
|
|
|
import { app } from "../../app";
|
|
|
|
describe("POST /single-documents — upload validation", () => {
|
|
it("rejects an unsupported file extension with 400", async () => {
|
|
const res = await request(app)
|
|
.post("/single-documents")
|
|
.set("Authorization", "Bearer test")
|
|
.attach("file", Buffer.from("hello world"), {
|
|
filename: "notes.txt",
|
|
contentType: "text/plain",
|
|
});
|
|
|
|
expect(res.status).toBe(400);
|
|
expect(res.body.detail).toMatch(/unsupported file type/i);
|
|
});
|
|
|
|
it("rejects a request with no file attached with 400", async () => {
|
|
const res = await request(app)
|
|
.post("/single-documents")
|
|
.set("Authorization", "Bearer test")
|
|
.field("note", "no file here");
|
|
|
|
expect(res.status).toBe(400);
|
|
expect(res.body.detail).toBe("file is required");
|
|
});
|
|
});
|
|
|
|
describe("POST /single-documents/download-zip — bounds", () => {
|
|
it("returns 400 when document_ids is empty", async () => {
|
|
const res = await request(app)
|
|
.post("/single-documents/download-zip")
|
|
.set("Authorization", "Bearer test")
|
|
.send({ document_ids: [] });
|
|
|
|
expect(res.status).toBe(400);
|
|
expect(res.body.detail).toMatch(/document_ids is required/i);
|
|
});
|
|
|
|
it("returns 404 when none of the requested documents are accessible", async () => {
|
|
// The documents lookup resolves to no rows (stubbed DB), so the
|
|
// access filter leaves nothing to zip.
|
|
const res = await request(app)
|
|
.post("/single-documents/download-zip")
|
|
.set("Authorization", "Bearer test")
|
|
.send({ document_ids: ["d-other-user"] });
|
|
|
|
expect(res.status).toBe(404);
|
|
expect(res.body.detail).toBe("No documents found");
|
|
});
|
|
});
|