feat: add web testing foundation with infra-backed suites (#349)
* feat: add web test framework with infra-backed suites * fix: honor DATABASE_URL env in integration prepare script * fix: apply web test review feedback * fix: streamline web test infra lifecycle and workflow scope
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { UnsendApiError } from "~/server/public-api/api-error";
|
||||
|
||||
const { mockGetTeamFromToken, mockRedis } = vi.hoisted(() => ({
|
||||
mockGetTeamFromToken: vi.fn(),
|
||||
mockRedis: {
|
||||
incr: vi.fn(),
|
||||
expire: vi.fn(),
|
||||
ttl: vi.fn(),
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("~/server/public-api/auth", () => ({
|
||||
getTeamFromToken: mockGetTeamFromToken,
|
||||
}));
|
||||
|
||||
vi.mock("~/server/redis", () => ({
|
||||
getRedis: () => mockRedis,
|
||||
}));
|
||||
|
||||
vi.mock("~/utils/common", () => ({
|
||||
isSelfHosted: () => false,
|
||||
}));
|
||||
|
||||
import { getApp } from "~/server/public-api/hono";
|
||||
|
||||
describe("public API Hono middleware", () => {
|
||||
beforeEach(() => {
|
||||
mockGetTeamFromToken.mockReset();
|
||||
mockRedis.incr.mockReset();
|
||||
mockRedis.expire.mockReset();
|
||||
mockRedis.ttl.mockReset();
|
||||
});
|
||||
|
||||
it("applies auth and rate limit headers", async () => {
|
||||
mockGetTeamFromToken.mockResolvedValue({
|
||||
id: 1,
|
||||
apiRateLimit: 2,
|
||||
apiKeyId: 11,
|
||||
apiKey: { domainId: null },
|
||||
});
|
||||
mockRedis.incr.mockResolvedValue(1);
|
||||
mockRedis.expire.mockResolvedValue(1);
|
||||
mockRedis.ttl.mockResolvedValue(1);
|
||||
|
||||
const app = getApp();
|
||||
app.get("/v1/ping", (c) => c.json({ ok: true }));
|
||||
|
||||
const response = await app.request("http://localhost/api/v1/ping", {
|
||||
headers: {
|
||||
Authorization: "Bearer test-key",
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get("X-RateLimit-Limit")).toBe("2");
|
||||
expect(response.headers.get("X-RateLimit-Remaining")).toBe("1");
|
||||
});
|
||||
|
||||
it("returns 429 when limit is exceeded", async () => {
|
||||
mockGetTeamFromToken.mockResolvedValue({
|
||||
id: 1,
|
||||
apiRateLimit: 2,
|
||||
apiKeyId: 11,
|
||||
apiKey: { domainId: null },
|
||||
});
|
||||
mockRedis.incr.mockResolvedValue(3);
|
||||
mockRedis.ttl.mockResolvedValue(1);
|
||||
|
||||
const app = getApp();
|
||||
app.get("/v1/ping", (c) => c.json({ ok: true }));
|
||||
|
||||
const response = await app.request("http://localhost/api/v1/ping", {
|
||||
headers: {
|
||||
Authorization: "Bearer test-key",
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.status).toBe(429);
|
||||
const body = await response.json();
|
||||
expect(body).toMatchObject({
|
||||
error: {
|
||||
code: "RATE_LIMITED",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("returns auth error from middleware", async () => {
|
||||
mockGetTeamFromToken.mockRejectedValue(
|
||||
new UnsendApiError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "No Authorization header provided",
|
||||
}),
|
||||
);
|
||||
|
||||
const app = getApp();
|
||||
app.get("/v1/ping", (c) => c.json({ ok: true }));
|
||||
|
||||
const response = await app.request("http://localhost/api/v1/ping");
|
||||
|
||||
expect(response.status).toBe(401);
|
||||
const body = await response.json();
|
||||
expect(body).toMatchObject({
|
||||
error: {
|
||||
code: "UNAUTHORIZED",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user