Files
SnapOtter/tests/integration/platform/docs-i18n.test.ts
T
SnapOtterandGitHub f858c4cea0 feat: clearer, disambiguated tool names across all surfaces (#520)
Renames 18 ambiguous or hard-to-search tool names so image tools self-qualify like the other modalities ("Compress" becomes "Compress Image"), and cleans up a few awkward names. Propagated across search (constants.ts), display (en.ts + 20 locales), the OpenAPI base spec + 20 locale specs, and the docs tool-page headings in 21 languages. Removes the duplicate "Normalize Audio" summary shared by the video and audio endpoints. Tool ids and routes are unchanged, so no API paths or bookmarks break.
2026-07-15 21:55:51 +08:00

120 lines
4.4 KiB
TypeScript

// tests/integration/platform/docs-i18n.test.ts
import rateLimit from "@fastify/rate-limit";
import Fastify from "fastify";
import { afterAll, beforeAll, describe, expect, it } from "vitest";
import { env } from "../../../apps/api/src/config.js";
import { docsRoutes } from "../../../apps/api/src/routes/docs.js";
import { buildTestApp, type TestApp } from "../test-server";
describe("API docs i18n serving", () => {
let testApp: TestApp;
beforeAll(async () => {
testApp = await buildTestApp();
});
afterAll(async () => {
await testApp.cleanup();
});
it("serves the German spec for ?lang=de", async () => {
const res = await testApp.app.inject({ method: "GET", url: "/api/v1/openapi.yaml?lang=de" });
expect(res.statusCode).toBe(200);
expect(res.headers["content-type"]).toContain("text/yaml");
expect(res.body).toContain("Bildgröße ändern");
expect(res.body).toContain("locale: de");
});
it("falls back to English for an unsupported lang", async () => {
const res = await testApp.app.inject({ method: "GET", url: "/api/v1/openapi.yaml?lang=zz" });
expect(res.statusCode).toBe(200);
// English spec has the English summary, not the German one.
expect(res.body).toContain("openapi: 3.1.0");
expect(res.body).not.toContain("Bildgröße ändern");
});
it("serves another committed locale spec without falling back to English", async () => {
const res = await testApp.app.inject({ method: "GET", url: "/api/v1/openapi.yaml?lang=fr" });
expect(res.statusCode).toBe(200);
expect(res.body).toContain("locale: fr");
expect(res.body).not.toContain("locale: de");
});
it("keeps the default (no lang) response ASCII-only", async () => {
const res = await testApp.app.inject({ method: "GET", url: "/api/v1/openapi.yaml" });
const offending = [...res.body].find((ch) => ch.charCodeAt(0) > 0x7f);
expect(offending).toBeUndefined();
});
it("serves a localized llms.de.txt using translated tool strings", async () => {
const res = await testApp.app.inject({ method: "GET", url: "/llms.de.txt" });
expect(res.statusCode).toBe(200);
expect(res.headers["content-type"]).toContain("text/plain");
expect(res.body).toContain("## Tools");
// Tag prose comes from the committed German spec.
expect(res.body).toContain("Datei-Verarbeitungstools");
// Tool lines come from shared i18n; the Resize tool id is present with a mode.
expect(res.body).toContain("Bildgröße ändern - Größe nach Pixeln");
expect(res.body).toContain("(resize, sync)");
});
it("rate-limits request-time localized docs work even when global limiting is disabled", async () => {
const app = Fastify();
await app.register(rateLimit, {
max: 50_000,
timeWindow: "1 minute",
allowList: (request) => !request.url.startsWith("/api/"),
});
await docsRoutes(app);
await app.ready();
try {
for (const url of ["/api/v1/openapi.yaml?lang=de", "/llms.de.txt"]) {
for (let request = 0; request < 60; request++) {
const response = await app.inject({ method: "GET", url });
expect(response.statusCode).toBe(200);
expect(response.headers["x-ratelimit-limit"]).toBe("60");
}
const limited = await app.inject({ method: "GET", url });
expect(limited.statusCode).toBe(429);
const head = await app.inject({ method: "HEAD", url });
expect(head.statusCode).toBe(404);
}
} finally {
await app.close();
}
});
it("does not loosen a stricter operator rate limit", async () => {
const previousLimit = env.RATE_LIMIT_PER_MIN;
env.RATE_LIMIT_PER_MIN = 2;
const app = Fastify();
try {
await app.register(rateLimit, {
max: env.RATE_LIMIT_PER_MIN,
timeWindow: "1 minute",
allowList: (request) => !request.url.startsWith("/api/"),
});
await docsRoutes(app);
await app.ready();
for (const url of ["/api/v1/openapi.yaml?lang=de", "/llms.de.txt"]) {
for (let request = 0; request < 2; request++) {
const response = await app.inject({ method: "GET", url });
expect(response.statusCode).toBe(200);
expect(response.headers["x-ratelimit-limit"]).toBe("2");
}
const limited = await app.inject({ method: "GET", url });
expect(limited.statusCode).toBe(429);
}
} finally {
env.RATE_LIMIT_PER_MIN = previousLimit;
await app.close();
}
});
});