feat(plugins): mangabaka
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
06a59e1b5c
commit
ddd938935e
10 files changed
+273
No files matched your search
@@ -0,0 +1,4 @@
|
||||
{"data":[
|
||||
{"type":"upsert","series_id":1,"entry":{"series_id":1,"state":"paused","rating":70,"progress_chapter":13,"progress_volume":2,"start_date":"2024-03-05","finish_date":null,"note":null,"series":{"id":1,"title":"Alpha","type":"manga","state":"active","merged_with":null,"year":2020,"source":{}}}},
|
||||
{"type":"delete","series_id":7}
|
||||
],"next_cursor":"cur-2"}
|
||||
@@ -0,0 +1,4 @@
|
||||
{"data":[
|
||||
{"series_id":1,"state":"reading","rating":80,"progress_chapter":12,"progress_volume":2,"start_date":"2024-03-05","finish_date":null,"note":"good","series":{"id":1,"title":"Alpha","type":"manga","state":"active","merged_with":null,"year":2020,"source":{"anilist":{"id":11},"my_anime_list":{"id":22},"manga_updates":{"id":33},"kitsu":{"id":44}}}},
|
||||
{"series_id":2,"state":"plan_to_read","rating":null,"progress_chapter":null,"progress_volume":null,"start_date":null,"finish_date":null,"note":null,"series":{"id":2,"title":"Beta","type":"manga","state":"merged","merged_with":9,"year":null,"source":{}}}
|
||||
],"pagination":{"page":1,"total_pages":2}}
|
||||
@@ -0,0 +1,3 @@
|
||||
{"data":[
|
||||
{"series_id":3,"state":"completed","rating":95,"progress_chapter":50,"progress_volume":null,"start_date":"2023-01","finish_date":"2023-06-30","note":null,"series":{"id":3,"title":"Gamma","type":"manga","state":"active","merged_with":null,"year":2019,"source":{}}}
|
||||
],"pagination":{"page":2,"total_pages":2}}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"data":[{"id":5,"title":"Delta","type":"manga","state":"active","merged_with":null,"year":2018,"source":{"anilist":{"id":55}}}]}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { collect, createFixtureHttp, createTestContext } from "@sttracker/plugin-sdk/testing";
|
||||
import type { Status } from "@sttracker/plugin-sdk/types";
|
||||
import plugin from "../index.ts";
|
||||
import { mapState } from "../src/map.ts";
|
||||
import page1 from "./fixtures/library-page1.json";
|
||||
import page2 from "./fixtures/library-page2.json";
|
||||
import changes from "./fixtures/changes.json";
|
||||
import search from "./fixtures/search.json";
|
||||
|
||||
const B = "https://api.mangabaka.org";
|
||||
const ctxFor = (routes: Record<string, unknown>) => {
|
||||
const h = createFixtureHttp(routes);
|
||||
return { h, ctx: createTestContext({ fetch: h.fetch, config: { token: "tok" } }) };
|
||||
};
|
||||
|
||||
describe("mangabaka", () => {
|
||||
test("manifest", () => {
|
||||
expect(plugin.manifest.id).toBe("mangabaka");
|
||||
expect(plugin.manifest.capabilities).toEqual(["pull", "pullChanges", "lookup"]);
|
||||
});
|
||||
|
||||
test.each<[string, Status]>([
|
||||
["considering", "planned"], ["plan_to_read", "planned"], ["reading", "in_progress"],
|
||||
["rereading", "in_progress"], ["paused", "paused"], ["dropped", "dropped"], ["completed", "completed"],
|
||||
])("state %s -> %s", (a, b) => expect(mapState(a)).toBe(b));
|
||||
|
||||
test("pull pages, maps entries and engagements, sends api key", async () => {
|
||||
const { h, ctx } = ctxFor({
|
||||
[`GET ${B}/v1/my/library?page=1`]: page1,
|
||||
[`GET ${B}/v1/my/library?page=2`]: page2,
|
||||
});
|
||||
const recs = await collect(plugin.pull!(ctx));
|
||||
expect(recs.map((r) => r.key)).toEqual(["series:1", "progress:1", "series:2", "series:3", "progress:3"]);
|
||||
expect(h.calls.every((c) => c.headers.get("x-api-key") === "tok")).toBe(true);
|
||||
const a = recs[0]!;
|
||||
if (a.kind !== "entry") throw new Error();
|
||||
expect(a.entry).toEqual({ status: "in_progress", rating: 80, notes: "good" });
|
||||
expect(a.work.externalIds).toEqual({ mangabaka: "1", anilist: "11", mal: "22", mangaupdates: "33", kitsu: "44" });
|
||||
const p = recs[1]!;
|
||||
if (p.kind !== "engagement") throw new Error();
|
||||
expect(p.engagement.progress).toEqual({ chapter: 12, volume: 2 });
|
||||
expect(p.engagement.startedAt).toEqual({ value: "2024-03-05", precision: "day" });
|
||||
expect(p.engagement.finishedAt).toBeNull();
|
||||
});
|
||||
|
||||
test("merged series sets mergedIntoKey", async () => {
|
||||
const { ctx } = ctxFor({ [`GET ${B}/v1/my/library?page=1`]: page1, [`GET ${B}/v1/my/library?page=2`]: page2 });
|
||||
const recs = await collect(plugin.pull!(ctx));
|
||||
expect(recs.find((r) => r.key === "series:2")!.mergedIntoKey).toBe("series:9");
|
||||
expect(recs.find((r) => r.key === "series:1")!.mergedIntoKey).toBeUndefined();
|
||||
});
|
||||
|
||||
test("pullChanges yields upsert and deletion, saves cursor", async () => {
|
||||
const { h, ctx } = ctxFor({
|
||||
[`GET ${B}/v2/my/library/changes`]: (req: Request) =>
|
||||
new URL(req.url).searchParams.get("cursor") === "cur-2" ? { data: [], next_cursor: "cur-2" } : changes,
|
||||
});
|
||||
await ctx.cursor.set("cur-1");
|
||||
ctx.saved.cursor.length = 0;
|
||||
const recs = await collect(plugin.pullChanges!(ctx));
|
||||
expect(recs.filter((r) => r.deleted).map((r) => r.key)).toEqual(["series:7", "progress:7"]);
|
||||
expect(recs[0]!.key).toBe("series:1");
|
||||
expect(ctx.saved.cursor).toEqual(["cur-2", "cur-2"]);
|
||||
expect(await ctx.cursor.get()).toBe("cur-2");
|
||||
expect(new URL(h.calls[0]!.url).searchParams.get("cursor")).toBe("cur-1");
|
||||
});
|
||||
|
||||
test("lookup searches series", async () => {
|
||||
const { h, ctx } = ctxFor({ [`GET ${B}/v1/series/search`]: search });
|
||||
const r = await plugin.lookup!(ctx, { title: "Delta", mediaType: "manga" });
|
||||
expect(r).toEqual([{ title: "Delta", mediaType: "manga", year: 2018, externalIds: { mangabaka: "5", anilist: "55" } }]);
|
||||
expect(new URL(h.calls[0]!.url).searchParams.get("q")).toBe("Delta");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user