Error statuses follow one rule: APIException takes a status and derives the code from it (BAD_REQUEST 400, UNAUTHORIZED 401, FORBIDDEN 403, NOT_FOUND 404, CONFLICT 409, RATE_LIMITED 429, SERVER_ERROR 500, BAD_GATEWAY 502), so a code can't contradict its status. That fixed real bugs: a wrong current password on change-password answered 401, on which authFetch signs the user out; editing or deleting someone else's comment or chat message answered 401 rather than 403; one invalid dump edit answered 422. A taken username is 409, like a taken email. Along the way: the database is created from schema.sql by connection.ts when the file doesn't exist, so every task works on a fresh checkout (dev and serve never ran db/init.ts, which is gone); the startup purge compared ISO dates with SQLite's datetime() as text and kept anything from the current UTC day, and now compares ISO cutoffs; the seeded admin's hash comes from lib/password.ts (split out of jwt.ts, which needs the JWT secret) rather than a copy of its format; avatar, thumbnail and playlist-cover uploads go through replaceUploadedFile, which writes beside the old file and renames over it before the record announces it (a failed avatar write lost the old avatar; a thumbnail was announced before it existed); upload.ts derives its size message from MAX_IMAGE_SIZE_BYTES and stops re-exporting the upload folders; ALLOWED_IMAGE_MIMES, which duplicated detectImageMime, is gone; the SPA fallback no longer calls next() after answering; a reply must answer a comment on the same dump (a foreign or unknown parent was a 500 or a cross-dump thread). 211 backend and 628 frontend tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
23 lines
981 B
TypeScript
23 lines
981 B
TypeScript
import { html, stubNetwork } from "../../helpers.ts";
|
|
import { assert, assertEquals, assertFalse } from "@std/assert";
|
|
import { fetchRichContent } from "../../../services/rich-content-service.ts";
|
|
import { bandcampProvider } from "../../../remote/providers/bandcamp.ts";
|
|
|
|
Deno.test("bandcamp provider", async () => {
|
|
using _ = stubNetwork(() =>
|
|
html(`<meta property="og:title" content="Album">
|
|
<meta property="og:video" content="https://bandcamp.com/EmbeddedPlayer/album=1">`)
|
|
);
|
|
const bc = await fetchRichContent("https://artist.bandcamp.com/album/a");
|
|
assertEquals(bc?.type, "bandcamp");
|
|
assertEquals(bc?.title, "Album");
|
|
assertEquals(bc?.embedUrl, "https://bandcamp.com/EmbeddedPlayer/album=1");
|
|
});
|
|
|
|
Deno.test("bandcamp provider: bandcamp.com and its subdomains only", () => {
|
|
assert(bandcampProvider.matches("https://artist.bandcamp.com/album/x"));
|
|
assertFalse(
|
|
bandcampProvider.matches("https://bandcamp.com.evil.example/album/x"),
|
|
);
|
|
});
|