import { describe, expect, it } from "vitest"; import type { DownloadItem, PackageEntry } from "../src/shared/types"; import type { DownloadPackageRow } from "../src/renderer/views/downloads/downloads-model"; import { buildPackagePresentation } from "../src/renderer/views/downloads/package-presentation"; function item(id: string, fullStatus: string, overrides: Partial = {}): DownloadItem { return { id, packageId: "pkg", url: `https://example.invalid/${id}`, provider: "realdebrid", status: "completed", retries: 0, speedBps: 0, downloadedBytes: 100, totalBytes: 100, progressPercent: 100, fileName: `${id}.rar`, targetPath: `C:\\Downloads\\${id}.rar`, resumable: true, attempts: 0, lastError: "", fullStatus, createdAt: 1, updatedAt: 1, ...overrides }; } function row(items: DownloadItem[], overrides: Partial = {}): DownloadPackageRow { const entry = { id: "pkg", name: "Paket", outputDir: "C:\\Downloads\\Paket", extractDir: "C:\\Downloads\\_entpackt\\Paket", itemIds: items.map((entry) => entry.id), enabled: true, cancelled: false, status: "completed", priority: "normal", createdAt: 1, updatedAt: 1, ...overrides } as PackageEntry; return { package: entry, items, allItems: items, collapsed: true }; } describe("download package presentation", () => { it("reserves 90 percent for completed downloads and 10 percent for extraction", () => { expect(buildPackagePresentation(row([ item("a", "Entpack-Fehler: Passwort"), item("b", "Entpack-Fehler: CRC") ])).progress.value).toBe(90); expect(buildPackagePresentation(row([ item("a", "Entpackt in 4s"), item("b", "Entpack-Fehler: CRC") ])).progress.value).toBe(95); expect(buildPackagePresentation(row([ item("a", "Entpackt in 4s"), item("b", "Entpackt in 5s") ])).progress.value).toBe(100); }); it("keeps ordinary completed downloads at 100 percent when no extraction phase exists", () => { const presentation = buildPackagePresentation(row([ item("a", "Fertig"), item("b", "Fertig") ])); expect(presentation.progress.value).toBe(100); expect(presentation.status).toBe("Fertig"); }); it("does not move backwards when an archive download enters extraction", () => { const active = item("archive", "Download läuft", { status: "downloading", downloadedBytes: 99, progressPercent: 99 }); const before = buildPackagePresentation(row([active], { status: "downloading" })); const after = buildPackagePresentation(row([{ ...active, status: "completed", downloadedBytes: 100, progressPercent: 100, fullStatus: "Entpacken - Ausstehend" }], { status: "extracting" })); expect(before.progress.value).toBe(89); expect(after.progress.value).toBe(90); }); it("includes finalization progress in the reserved extraction range", () => { const presentation = buildPackagePresentation(row([ item("archive", "Finalisieren - 99% · release.part01.rar") ], { status: "extracting", postProcessLabel: "Finalisieren - 99% (0/1) · release.part01.rar" })); expect(presentation.progress.value).toBe(99); expect(presentation.status).toBe("Finalisieren - 99% (0/1) · release.part01.rar"); }); it("summarizes mixed extraction errors and a live retry instead of showing a fraction", () => { const items = [ ...Array.from({ length: 7 }, (_, index) => item(`failed-${index}`, "Entpack-Fehler: Keine entpackten Dateien erkannt")), item("retry", "Link-Umwandlung erneut, Versuch 6/...", { status: "validating", retries: 6, downloadedBytes: 0, progressPercent: 0 }) ]; const presentation = buildPackagePresentation(row(items, { status: "queued" })); expect(presentation.status).toBe("7 Entpackfehler · Link-Umwandlung erneut"); expect(presentation.details).toContain("7 Entpackfehler"); expect(presentation.details).toContain("Link-Umwandlung erneut"); }); it("describes parallel link conversion retries without presenting the item count as attempts", () => { const retries = Array.from({ length: 20 }, (_, index) => item(`retry-${index}`, "Link-Umwandlung erneut, Versuch 2/...", { status: "validating", retries: 2, downloadedBytes: 0, progressPercent: 0 })); const presentation = buildPackagePresentation(row(retries, { status: "queued" })); expect(presentation.status).toBe("Link-Umwandlung erneut"); expect(presentation.status).not.toContain("20"); }); it("keeps a single normal active download compact", () => { const presentation = buildPackagePresentation(row([ item("active", "Download läuft", { status: "downloading", downloadedBytes: 50, progressPercent: 50 }) ], { status: "downloading" })); expect(presentation.status).toBe("Download läuft"); }); });