99 lines
5.0 KiB
TypeScript
99 lines
5.0 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { MangaBakaClient, MangaBakaError, coverFormUrl } from "../src/core/mangabaka/client";
|
|
import { toExisting, type SubmissionImage } from "../src/core/mangabaka/types";
|
|
import { FakeTransport, json } from "./helpers/fake-transport";
|
|
import { fixtureJson } from "./helpers/fixtures";
|
|
|
|
const API = "https://api.mangabaka.org";
|
|
|
|
function publicFake() {
|
|
return new FakeTransport()
|
|
.on(`${API}/v1/series/9999`, async () => json(await fixtureJson("mangabaka/series-9999.json")))
|
|
.on(`${API}/v1/series/9999/links?limit=100`, async () => json(await fixtureJson("mangabaka/links-9999.json")))
|
|
.on(/\/v1\/series\/9999\/images\?.*page=2/, async () => json(await fixtureJson("mangabaka/images-9999-page2.json")))
|
|
.on(`${API}/v1/series/9999/images?limit=50`, async () => json(await fixtureJson("mangabaka/images-9999-page1.json")));
|
|
}
|
|
|
|
describe("public reads (captured fixtures)", () => {
|
|
test("series, links, and every page of images", async () => {
|
|
const fake = publicFake();
|
|
const mb = new MangaBakaClient(fake);
|
|
expect((await mb.getSeries(9999)).title).toBe("Tower of God");
|
|
expect((await mb.getLinks(9999)).map((l) => l.url)).toContain("https://www.webtoons.com/-/-/-/list?title_no=95");
|
|
const images = await mb.getImages(9999);
|
|
expect(images).toHaveLength(31);
|
|
expect(fake.calls.some((c) => c.init.headers?.["x-api-key"])).toBe(false);
|
|
expect(toExisting(images[0]!)).toEqual({
|
|
id: images[0]!.id, sha256: images[0]!.hashes!.sha256 ?? null, language: images[0]!.language, type: images[0]!.type,
|
|
});
|
|
});
|
|
|
|
test("a next link to another origin is not followed", async () => {
|
|
const fake = new FakeTransport().on(`${API}/v1/series/1/images?limit=50`, () =>
|
|
json({ status: 200, data: [], pagination: { next: "https://evil.example/page2" } }));
|
|
expect(await new MangaBakaClient(fake).getImages(1)).toEqual([]);
|
|
expect(fake.calls).toHaveLength(1);
|
|
});
|
|
|
|
test("404 is a MangaBakaError", async () => {
|
|
const fake = new FakeTransport().on(() => true, () => json({ status: 404, message: "Not found" }, 404));
|
|
const err = await new MangaBakaClient(fake).getSeries(1).catch((e) => e);
|
|
expect(err).toBeInstanceOf(MangaBakaError);
|
|
expect(err.status).toBe(404);
|
|
});
|
|
});
|
|
|
|
// Not capturable without a token: shapes built from the OpenAPI spec (spec §2.3). See docs/TODO.md.
|
|
const existing: SubmissionImage = {
|
|
id: 7193, series_id: 9999, work_id: null, url: "https://example.org/old.jpg", index: "0.1", index_numeric: 0.1,
|
|
language: "ko", type: "volume", note: "Season Cover 3", content_rating: "safe", is_default: true,
|
|
};
|
|
const snapshotBody = { status: 200, data: { data: { images: [existing] }, version: 1760000000 } };
|
|
const previewBody = { status: 200, data: { has_changes: true, changes: [{ field: "images", type: "added", identity: "https://x/new.jpg" }] } };
|
|
|
|
describe("submissions (token)", () => {
|
|
const SUB = `${API}/v0/my/submissions/series-images/9999`;
|
|
|
|
test("snapshot, preview and save send the token and the right bodies", async () => {
|
|
const fake = new FakeTransport()
|
|
.on((u, i) => u === SUB && (i.method ?? "GET") === "GET", () => json(snapshotBody))
|
|
.on(`${SUB}/preview`, () => json(previewBody))
|
|
.on((u, i) => u === SUB && i.method === "POST", () =>
|
|
json({ status: 200, data: { submission_id: 5, status: "pending", changes: [] } }));
|
|
const mb = new MangaBakaClient(fake, "mb-test-token");
|
|
const snapshot = await mb.getImageSnapshot(9999);
|
|
expect(snapshot).toEqual({ version: 1760000000, images: [existing] });
|
|
expect((await mb.previewImages(9999, snapshot.version, snapshot.images)).has_changes).toBe(true);
|
|
const saved = await mb.saveImagesForReview(9999, snapshot.version, snapshot.images, "Add WEBTOON covers");
|
|
expect(saved.submission_id).toBe(5);
|
|
|
|
expect(fake.calls.every((c) => c.init.headers?.["x-api-key"] === "mb-test-token")).toBe(true);
|
|
expect(JSON.parse(fake.calls[1]!.init.body as string)).toEqual({ version: 1760000000, data: { images: [existing] } });
|
|
expect(JSON.parse(fake.calls[2]!.init.body as string)).toEqual({
|
|
version: 1760000000, user_note: "Add WEBTOON covers", save_mode: "review", data: { images: [existing] },
|
|
});
|
|
});
|
|
|
|
test("no token: refuses before sending anything", async () => {
|
|
const fake = new FakeTransport();
|
|
await expect(new MangaBakaClient(fake).getImageSnapshot(9999)).rejects.toMatchObject({ status: 401 });
|
|
expect(fake.calls).toHaveLength(0);
|
|
});
|
|
|
|
test.each([
|
|
[403, /25 approved submissions/],
|
|
[409, /changed since/],
|
|
[401, /token/],
|
|
])("HTTP %i explains itself", async (status, message) => {
|
|
const fake = new FakeTransport().on(() => true, () => json({ status, message: "x" }, status));
|
|
const err = await new MangaBakaClient(fake, "mb-t").previewImages(9999, 1, []).catch((e) => e);
|
|
expect(err).toBeInstanceOf(MangaBakaError);
|
|
expect(err.status).toBe(status);
|
|
expect(err.message).toMatch(message);
|
|
});
|
|
});
|
|
|
|
test("coverFormUrl", () => {
|
|
expect(coverFormUrl(9999)).toBe("https://mangabaka.org/data/submissions/series/9999/images");
|
|
});
|