feat: complete remaining Go backend modules
This commit is contained in:
1 parent
cea2751dc5
commit
aef5a97165
145 files changed
+18376
-199
No files matched your search
@@ -0,0 +1,12 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("Go legacy auth compatibility HTTP v1 contract", () => {
|
||||
it("pins login, callback, and disabled captcha endpoints", async () => {
|
||||
const fixture = JSON.parse(await readFile(new URL("../contracts/admin/http-auth-compat-v1.json", import.meta.url), "utf8"));
|
||||
const source = await readFile(new URL("../backend/internal/httpapi/auth_compat.go", import.meta.url), "utf8");
|
||||
expect(fixture.routes.map((route: { path: string }) => route.path)).toEqual(["/api/auth/login", "/api/auth/callback", "/api/auth/captcha"]);
|
||||
expect(source).toContain("callback_failed");
|
||||
expect(source).toContain("平台账号登录不使用外部验证码。");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("Go administration HTTP v1 contract", () => {
|
||||
it("pins methods, authorization, and security projections", async () => {
|
||||
const fixture = JSON.parse(await readFile(new URL("../contracts/admin/http-v1.json", import.meta.url), "utf8"));
|
||||
const source = await readFile(new URL("../backend/internal/httpapi/admin.go", import.meta.url), "utf8");
|
||||
expect(fixture.routes).toHaveLength(4);
|
||||
expect(fixture.routes.filter((route: { path: string }) => route.path !== "/api/admin/accounts/groups").every((route: { requirement: string }) => route.requirement === "admin")).toBe(true);
|
||||
expect(fixture.routes.find((route: { path: string }) => route.path === "/api/admin/accounts/groups")?.requirement).toBe("public-compatibility");
|
||||
expect(source).toContain("PlatformAdmin");
|
||||
expect(source).toContain("http.StatusGone");
|
||||
const projection = source.slice(source.indexOf("func adminAccountProjection("));
|
||||
for (const field of fixture.secretFields) expect(projection).not.toContain(`\"${field}\"`);
|
||||
for (const field of fixture.accountProjection) expect(source).toContain(`\"${field}\"`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("assets HTTP v1 compatibility contract", () => {
|
||||
it("freezes route, limit, safe-error, and binary-cache semantics", async () => {
|
||||
const contract = JSON.parse(await readFile(new URL("../contracts/assets/http-v1.json", import.meta.url), "utf8"));
|
||||
expect(contract.version).toBe(1);
|
||||
expect(contract.limits).toEqual({ jsonBytes: 1_048_576, uploadBytes: 20_971_520, remoteBytes: 20_971_520 });
|
||||
expect(contract.routes.map((route: { path: string }) => route.path)).toEqual([
|
||||
"/api/assets", "/api/assets/upload", "/api/assets/{id}", "/api/assets/{id}/download",
|
||||
"/api/v1/assets", "/api/v1/assets/{id}", "/api/v1/assets/{id}/download",
|
||||
"/uploads/{path...}", "/generated-results/{path...}"
|
||||
]);
|
||||
expect(contract.errors.internal.body).toEqual({ error: "Internal server error." });
|
||||
expect(contract.methods).toEqual({ headExecutesGet: true, optionsStatus: 204, unsupportedStatus: 405, unsupportedBody: "empty" });
|
||||
expect(contract.binary).toEqual({
|
||||
downloadCacheControl: "private, no-store",
|
||||
servedCacheControl: "public, max-age=31536000, immutable"
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("Go password change v1 contract", () => {
|
||||
it("pins the transactional password and replacement-cookie implementation", async () => {
|
||||
const fixture = JSON.parse(await readFile(new URL("../contracts/auth/password-change-v1.json", import.meta.url), "utf8"));
|
||||
const http = await readFile(new URL("../backend/internal/httpapi/auth_password_change.go", import.meta.url), "utf8");
|
||||
const identity = await readFile(new URL("../backend/internal/identity/password_change.go", import.meta.url), "utf8");
|
||||
const postgres = await readFile(new URL("../backend/internal/postgres/password_change.go", import.meta.url), "utf8");
|
||||
expect(fixture).toMatchObject({ version: 1, path: "/api/auth/password/change", minimumPasswordLength: 8, incrementsSessionVersion: true });
|
||||
expect(http).toContain("identity.SetSessionCookies");
|
||||
expect(http).toContain("PlatformApp");
|
||||
expect(identity).toContain("len(command.NewPassword) < 8");
|
||||
expect(postgres).toContain("FOR UPDATE");
|
||||
expect(postgres).toContain("session_version = session_version + 1");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("billing HTTP compatibility fixture", () => {
|
||||
it("freezes routes, refreshed-session scope, money, and admin mutation rules", async () => {
|
||||
const fixture = JSON.parse(await readFile("contracts/billing/http-v1.json", "utf8"));
|
||||
expect(fixture).toMatchObject({
|
||||
version: 1, currency: "CNY", moneyUnit: "fen",
|
||||
statuses: { insufficientBalance: 402, idempotencyConflict: 409, unboundOrganization: 422, infrastructure: 500 },
|
||||
pricePatch: { minimum: 1, maximum: 1000, precision: 4, tierPairRequired: true }
|
||||
});
|
||||
expect(fixture.routes).toEqual(expect.arrayContaining([
|
||||
expect.objectContaining({ method: "GET", path: "/api/billing", organizationSource: "refreshed_session" }),
|
||||
expect.objectContaining({ method: "PATCH", path: "/api/admin/billing/prices/{id}", requirement: "super_admin" })
|
||||
]));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
describe("Go jobs HTTP v1 contract", () => { it("freezes paths, methods, scope and statuses", async () => { const c=JSON.parse(await readFile(new URL("../contracts/jobs/http-v1.json",import.meta.url),"utf8")); expect(c.public.scope).toEqual(["ownerId","externalClientId"]); expect(c.public.statuses).toEqual({created:202,reused:200,conflict:409,notFound:404}); expect(c.platform.image.methods).toEqual(["GET","POST"]); expect(c.worker.auth).toBe("publicapi.AssertInternalWorker"); }); });
|
||||
@@ -0,0 +1,26 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const fixtureUrl = new URL("../contracts/logs/runtime-v1.json", import.meta.url);
|
||||
|
||||
describe("Go runtime logs compatibility fixture", () => {
|
||||
it("freezes storage, bounds, ordering, and redaction behavior", async () => {
|
||||
const fixture = JSON.parse(await readFile(fixtureUrl, "utf8"));
|
||||
expect(fixture).toEqual({
|
||||
version: 1,
|
||||
fileName: "server-events.jsonl",
|
||||
rotatedSuffix: ".1",
|
||||
defaultLimit: 100,
|
||||
minimumLimit: 1,
|
||||
maximumLimit: 500,
|
||||
maximumTextLength: 20000,
|
||||
maximumDepth: 5,
|
||||
maximumArrayItems: 50,
|
||||
maximumObjectKeys: 80,
|
||||
corruptLines: "skip",
|
||||
order: "newest-first",
|
||||
redactedValue: "[redacted]",
|
||||
redactedJwt: "[jwt-redacted]"
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,3 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
describe("Go provider adapter contract",()=>{it("freezes bounded adapters and safe errors",async()=>{const c=JSON.parse(await readFile(new URL("../contracts/providers/http-v1.json",import.meta.url),"utf8"));expect(Object.keys(c.providers)).toEqual(["volcengine-visual","evolink","bailian","seedance","mock"]);expect(c.errors).toEqual({generic:true,secretSafe:true});expect(c.liveCallsInTests).toBe(false);});});
|
||||
@@ -0,0 +1,21 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const fixtureUrl = new URL("../contracts/settings/runtime-v1.json", import.meta.url);
|
||||
|
||||
describe("Go runtime settings compatibility fixture", () => {
|
||||
it("freezes the editable whitelist and secret projection rules", async () => {
|
||||
const fixture = JSON.parse(await readFile(fixtureUrl, "utf8"));
|
||||
expect(fixture).toMatchObject({
|
||||
version: 1,
|
||||
fileName: ".env.local",
|
||||
emptySecretInput: "preserve",
|
||||
emptyNonSecretInput: "write-empty",
|
||||
publicSecretValue: "blank",
|
||||
runtimeApplication: "restart-required"
|
||||
});
|
||||
expect(fixture.allowedKeys).toContain("IMAGE_GENERATE_ENGINE");
|
||||
expect(fixture.secretKeys).toContain("ALI_OSS_ACCESS_KEY_SECRET");
|
||||
expect(fixture.allowedKeys).not.toContain("DATABASE_URL");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("usage HTTP compatibility fixture", () => {
|
||||
it("freezes personal/admin scope and filters", async () => {
|
||||
const fixture = JSON.parse(await readFile("contracts/usage/http-v1.json", "utf8"));
|
||||
expect(fixture).toMatchObject({
|
||||
version: 1,
|
||||
presets: ["today", "7d", "30d", "month"],
|
||||
capabilities: ["image.generate", "video.generate"],
|
||||
infrastructureStatus: 500
|
||||
});
|
||||
expect(fixture.routes).toContainEqual(expect.objectContaining({ path: "/api/admin/usage", requirement: "admin", organizationAdminScope: "refreshed_session" }));
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,8 @@ const schemaPaths = [
|
||||
"../supabase/schema.sql"
|
||||
];
|
||||
|
||||
const lifecycleMigrationPath = "../database/migrations/0002_generation_lifecycle_fencing.sql";
|
||||
|
||||
function readSchema(relativePath: string): string {
|
||||
return readFileSync(fileURLToPath(new URL(relativePath, import.meta.url)), "utf8").replace(/\r\n/g, "\n");
|
||||
}
|
||||
@@ -47,12 +49,29 @@ describe.each(schemaPaths)("wallet SQL contract: %s", (schemaPath) => {
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps the migration and Supabase compatibility snapshot synchronized", () => {
|
||||
const migration = readSchema(schemaPaths[0]);
|
||||
const snapshot = readSchema(schemaPaths[1]).replace(
|
||||
/^-- Compatibility snapshot for existing Supabase deployments\.\r?\n-- New PostgreSQL\/RDS deployments must use `npm run db:migrate`; do not use this\r?\n-- file as an unversioned migration source\.\r?\n\r?\n/,
|
||||
""
|
||||
);
|
||||
it("keeps the Supabase compatibility snapshot at the latest migrated schema", () => {
|
||||
const baseline = readSchema(schemaPaths[0]);
|
||||
const lifecycle = readSchema(lifecycleMigrationPath);
|
||||
const snapshot = readSchema(schemaPaths[1]);
|
||||
|
||||
expect(snapshot).toBe(migration);
|
||||
for (const fragment of [
|
||||
"provider_dispatch_started_at timestamptz",
|
||||
"dispatch_ready_at timestamptz",
|
||||
"finalized_at timestamptz",
|
||||
"generation_jobs_finalize_claim_idx",
|
||||
"where dispatch_ready_at is not null",
|
||||
"and finalized_at is null"
|
||||
]) {
|
||||
expect(lifecycle.toLowerCase()).toContain(fragment);
|
||||
expect(snapshot.toLowerCase()).toContain(fragment);
|
||||
}
|
||||
for (const fragment of [
|
||||
"create table if not exists assets",
|
||||
"create table if not exists billing_ledger",
|
||||
"billing_post_wallet_entry"
|
||||
]) {
|
||||
expect(baseline.toLowerCase()).toContain(fragment);
|
||||
expect(snapshot.toLowerCase()).toContain(fragment);
|
||||
}
|
||||
expect(snapshot).toContain("Compatibility snapshot for existing Supabase deployments");
|
||||
});
|
||||
Reference in new issue
Block a user