perf(test): reduce module reload churn in unit suites

This commit is contained in:
Peter Steinberger
2026-02-13 15:19:09 +00:00
parent 6c4c535813
commit faec6ccb1d
4 changed files with 18 additions and 64 deletions

View File

@@ -1,5 +1,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import * as ssrf from "../../infra/net/ssrf.js";
import * as mediaStore from "../../media/store.js";
import { fetchWithSlackAuth, resolveSlackMedia, resolveSlackThreadHistory } from "./media.js";
// Store original fetch
const originalFetch = globalThis.fetch;
@@ -15,13 +17,9 @@ describe("fetchWithSlackAuth", () => {
afterEach(() => {
// Restore original fetch
globalThis.fetch = originalFetch;
vi.resetModules();
});
it("sends Authorization header on initial request with manual redirect", async () => {
// Import after mocking fetch
const { fetchWithSlackAuth } = await import("./media.js");
// Simulate direct 200 response (no redirect)
const mockResponse = new Response(Buffer.from("image data"), {
status: 200,
@@ -42,8 +40,6 @@ describe("fetchWithSlackAuth", () => {
});
it("rejects non-Slack hosts to avoid leaking tokens", async () => {
const { fetchWithSlackAuth } = await import("./media.js");
await expect(
fetchWithSlackAuth("https://example.com/test.jpg", "xoxb-test-token"),
).rejects.toThrow(/non-Slack host|non-Slack/i);
@@ -53,8 +49,6 @@ describe("fetchWithSlackAuth", () => {
});
it("follows redirects without Authorization header", async () => {
const { fetchWithSlackAuth } = await import("./media.js");
// First call: redirect response from Slack
const redirectResponse = new Response(null, {
status: 302,
@@ -89,8 +83,6 @@ describe("fetchWithSlackAuth", () => {
});
it("handles relative redirect URLs", async () => {
const { fetchWithSlackAuth } = await import("./media.js");
// Redirect with relative URL
const redirectResponse = new Response(null, {
status: 302,
@@ -113,8 +105,6 @@ describe("fetchWithSlackAuth", () => {
});
it("returns redirect response when no location header is provided", async () => {
const { fetchWithSlackAuth } = await import("./media.js");
// Redirect without location header
const redirectResponse = new Response(null, {
status: 302,
@@ -131,8 +121,6 @@ describe("fetchWithSlackAuth", () => {
});
it("returns 4xx/5xx responses directly without following", async () => {
const { fetchWithSlackAuth } = await import("./media.js");
const errorResponse = new Response("Not Found", {
status: 404,
});
@@ -146,8 +134,6 @@ describe("fetchWithSlackAuth", () => {
});
it("handles 301 permanent redirects", async () => {
const { fetchWithSlackAuth } = await import("./media.js");
const redirectResponse = new Response(null, {
status: 301,
headers: { location: "https://cdn.slack.com/new-url" },
@@ -185,20 +171,14 @@ describe("resolveSlackMedia", () => {
afterEach(() => {
globalThis.fetch = originalFetch;
vi.resetModules();
vi.restoreAllMocks();
});
it("prefers url_private_download over url_private", async () => {
// Mock the store module
vi.doMock("../../media/store.js", () => ({
saveMediaBuffer: vi.fn().mockResolvedValue({
path: "/tmp/test.jpg",
contentType: "image/jpeg",
}),
}));
const { resolveSlackMedia } = await import("./media.js");
vi.spyOn(mediaStore, "saveMediaBuffer").mockResolvedValue({
path: "/tmp/test.jpg",
contentType: "image/jpeg",
});
const mockResponse = new Response(Buffer.from("image data"), {
status: 200,
@@ -225,8 +205,6 @@ describe("resolveSlackMedia", () => {
});
it("returns null when download fails", async () => {
const { resolveSlackMedia } = await import("./media.js");
// Simulate a network error
mockFetch.mockRejectedValueOnce(new Error("Network error"));
@@ -240,8 +218,6 @@ describe("resolveSlackMedia", () => {
});
it("returns null when no files are provided", async () => {
const { resolveSlackMedia } = await import("./media.js");
const result = await resolveSlackMedia({
files: [],
token: "xoxb-test-token",
@@ -252,8 +228,6 @@ describe("resolveSlackMedia", () => {
});
it("skips files without url_private", async () => {
const { resolveSlackMedia } = await import("./media.js");
const result = await resolveSlackMedia({
files: [{ name: "test.jpg" }], // No url_private
token: "xoxb-test-token",
@@ -265,15 +239,10 @@ describe("resolveSlackMedia", () => {
});
it("falls through to next file when first file returns error", async () => {
// Mock the store module
vi.doMock("../../media/store.js", () => ({
saveMediaBuffer: vi.fn().mockResolvedValue({
path: "/tmp/test.jpg",
contentType: "image/jpeg",
}),
}));
const { resolveSlackMedia } = await import("./media.js");
vi.spyOn(mediaStore, "saveMediaBuffer").mockResolvedValue({
path: "/tmp/test.jpg",
contentType: "image/jpeg",
});
// First file: 404
const errorResponse = new Response("Not Found", { status: 404 });
@@ -301,7 +270,6 @@ describe("resolveSlackMedia", () => {
describe("resolveSlackThreadHistory", () => {
afterEach(() => {
vi.resetModules();
vi.restoreAllMocks();
});
@@ -324,7 +292,6 @@ describe("resolveSlackThreadHistory", () => {
})),
response_metadata: { next_cursor: "" },
});
const { resolveSlackThreadHistory } = await import("./media.js");
const client = {
conversations: { replies },
} as Parameters<typeof resolveSlackThreadHistory>[0]["client"];
@@ -375,7 +342,6 @@ describe("resolveSlackThreadHistory", () => {
],
response_metadata: { next_cursor: "" },
});
const { resolveSlackThreadHistory } = await import("./media.js");
const client = {
conversations: { replies },
} as Parameters<typeof resolveSlackThreadHistory>[0]["client"];
@@ -394,7 +360,6 @@ describe("resolveSlackThreadHistory", () => {
it("returns empty when limit is zero without calling Slack API", async () => {
const replies = vi.fn();
const { resolveSlackThreadHistory } = await import("./media.js");
const client = {
conversations: { replies },
} as Parameters<typeof resolveSlackThreadHistory>[0]["client"];
@@ -412,7 +377,6 @@ describe("resolveSlackThreadHistory", () => {
it("returns empty when Slack API throws", async () => {
const replies = vi.fn().mockRejectedValueOnce(new Error("slack down"));
const { resolveSlackThreadHistory } = await import("./media.js");
const client = {
conversations: { replies },
} as Parameters<typeof resolveSlackThreadHistory>[0]["client"];