- contracts DownloaderPolicy(存 downloader_instance.settings.policy):硬阈值 / 恢复水位 / 清理目标 / 保种天数, 不填用全局 cache / retention - volumeThresholds:卷的生效水位 = 各实例「自己的值 ?? 全局」取最小;decideVolume、阈值清理、手动恢复、 首页卷信息、定时清理检查都改用它;cleanupCandidates 保种天数按实例 - 保存时校验合并全局后 恢复 < 硬阈值、清理目标 ≤ 恢复 - 下载器设置表单新增四项(留空用全局) Co-Authored-By: Claude Opus 5.5 <[email protected]>
98 lines
3.9 KiB
TypeScript
98 lines
3.9 KiB
TypeScript
import type { CacheSettings } from "@umt/contracts";
|
|
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
import { loadInstances, pickInstance } from "../../src/modules/acquisition-organize/services/common.ts";
|
|
import { instancePolicy, volumeThresholds } from "../../src/modules/acquisition-organize/services/volumes.ts";
|
|
import { createHarness, type Harness, pgAvailable, seedBase } from "./harness.ts";
|
|
|
|
const hasPg = await pgAvailable();
|
|
|
|
// 多下载器路由(照 MP):明确指定 > 站点指定 > 默认 > 第一个可用
|
|
describe.skipIf(!hasPg)("下载器选择", () => {
|
|
let h: Harness;
|
|
let first: string;
|
|
let anime: string;
|
|
let siteId: string;
|
|
|
|
beforeAll(async () => {
|
|
h = await createHarness("downloader_routing");
|
|
const base = await seedBase(h.db, {
|
|
libraryPath: "/tmp/lib",
|
|
cachePath: "/tmp/cache",
|
|
qbUrl: "http://127.0.0.1:9",
|
|
});
|
|
first = base.instanceId;
|
|
siteId = base.siteId;
|
|
const add = async (name: string) =>
|
|
(
|
|
await h.db
|
|
.insertInto("downloader_instance")
|
|
.values({ name, base_url: "http://127.0.0.1:9", settings: JSON.stringify({ savePath: "/tmp/cache" }) })
|
|
.returning("id")
|
|
.executeTakeFirstOrThrow()
|
|
).id;
|
|
anime = await add("zz-番剧专用");
|
|
});
|
|
afterAll(async () => {
|
|
await h?.close();
|
|
});
|
|
|
|
const pick = async (opts: Parameters<typeof pickInstance>[2]) =>
|
|
(await pickInstance(h.db, await loadInstances(h.db), opts))?.inst.id;
|
|
|
|
it("都没指定:第一个可用;设了默认后用默认", async () => {
|
|
expect(await pick({})).toBe(first);
|
|
await h.db.updateTable("downloader_instance").set({ is_default: true }).where("id", "=", anime).execute();
|
|
expect(await pick({ siteId })).toBe(anime);
|
|
});
|
|
|
|
it("站点指定优先于默认;明确指定优先于站点", async () => {
|
|
await h.db.updateTable("site").set({ downloader_instance_id: first }).where("id", "=", siteId).execute();
|
|
expect(await pick({ siteId })).toBe(first);
|
|
expect(await pick({ siteId, explicitId: anime })).toBe(anime);
|
|
});
|
|
|
|
it("站点指定的下载器停用时回落默认;明确指定的不可用时不悄悄换", async () => {
|
|
await h.db.updateTable("downloader_instance").set({ enabled: false }).where("id", "=", first).execute();
|
|
expect(await pick({ siteId })).toBe(anime);
|
|
expect(await pick({ explicitId: first })).toBeUndefined();
|
|
});
|
|
|
|
it("默认下载器至多一个", async () => {
|
|
await expect(
|
|
h.db.updateTable("downloader_instance").set({ is_default: true }).where("id", "=", first).execute(),
|
|
).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe("下载器自己的水位 / 保种", () => {
|
|
const cache = { hardThresholdPercent: 85, resumePercent: 75, cleanupTargetPercent: 70 } as CacheSettings;
|
|
const inst = (policy?: unknown) => ({ inst: { settings: policy === undefined ? {} : { policy } } }) as never;
|
|
|
|
it("没设用全局;单实例用自己的值(可以比全局宽松)", () => {
|
|
expect(volumeThresholds({ instances: [inst()] }, cache)).toEqual({
|
|
hardThresholdPercent: 85,
|
|
resumePercent: 75,
|
|
cleanupTargetPercent: 70,
|
|
});
|
|
expect(volumeThresholds({ instances: [inst({ hardThresholdPercent: 95, resumePercent: 90 })] }, cache)).toEqual({
|
|
hardThresholdPercent: 95,
|
|
resumePercent: 90,
|
|
cleanupTargetPercent: 70,
|
|
});
|
|
});
|
|
|
|
it("多实例共用一块盘:各自生效值里取最严格", () => {
|
|
const th = volumeThresholds(
|
|
{ instances: [inst({ hardThresholdPercent: 95 }), inst({ resumePercent: 60 })] },
|
|
cache,
|
|
);
|
|
expect(th).toEqual({ hardThresholdPercent: 85, resumePercent: 60, cleanupTargetPercent: 70 });
|
|
});
|
|
|
|
it("保种天数按实例取;格式不对当没设", () => {
|
|
expect(instancePolicy({ settings: { policy: { seedRetentionDays: 0 } } })).toEqual({ seedRetentionDays: 0 });
|
|
expect(instancePolicy({ settings: { policy: { seedRetentionDays: -1 } } })).toEqual({});
|
|
expect(instancePolicy({ settings: null })).toEqual({});
|
|
});
|
|
});
|