mirror of
https://github.com/xCyanGrizzly/DragonsStash.git
synced 2026-09-21 13:31:42 +00:00
fix(archive): close silent-drop gaps in attachment detection
Files whose names match no pattern in detect.ts are dropped with no log,
no packages row and no skipped_packages row. Five gaps of that class:
- Numbered volumes were implemented format-by-format (ZIP_NUMBERED,
SEVENZ_NUMBERED) with no RAR equivalent, so Pack.rar.001 vanished.
Replaced both with a single ARCHIVE_NUMBERED pattern over zip|7z|rar
that derives the format from the match, so adding a format can no
longer be forgotten. \d{2,} also picks up hand-renamed .rar.01 sets.
- RAR legacy sets were ordered wrong: singles were always sorted last,
which is right for .zip/.z01 (the bare .zip is the final disk) but
wrong for .rar/.r00 (the bare .rar is volume 1). parts[0] became a
headerless continuation volume, so listing failed and the package was
labelled from the wrong message. Corrected the misleading comment too.
- Trailing/leading whitespace and trailing dots survive TDLib verbatim
and every pattern is $-anchored, so "Pack.zip " was dropped. The
filename is now normalized before matching and for baseName.
- RAR_PART now accepts .partN.exe, the self-extracting first volume;
previously the set was grouped starting at part 2.
- DOCUMENT_EXTENSIONS gained the slicer-project, 3D-model and CAD
formats present in this corpus (lys, chitubox, ctb, fbx, ztl, ... plus
the blend1 autosave sibling). Image formats stay excluded on purpose.
Tests written first; 31 new cases including pattern-order safety
(ZIP_LEGACY must not swallow .7z.001 and ARCHIVE_NUMBERED must not
swallow .z01/.r00) and both legacy part-ordering directions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { groupArchiveSets, type TelegramMessage } from "./multipart.js";
|
||||
|
||||
let nextId = 1000n;
|
||||
|
||||
function msg(fileName: string): TelegramMessage {
|
||||
const id = nextId++;
|
||||
return {
|
||||
id,
|
||||
fileName,
|
||||
fileId: `file-${id}`,
|
||||
fileSize: 1024n,
|
||||
date: new Date("2026-01-01T00:00:00Z"),
|
||||
};
|
||||
}
|
||||
|
||||
function names(files: string[]): string[] {
|
||||
const sets = groupArchiveSets(files.map(msg));
|
||||
expect(sets).toHaveLength(1);
|
||||
return sets[0].parts.map((p) => p.fileName);
|
||||
}
|
||||
|
||||
describe("groupArchiveSets — legacy split part ordering", () => {
|
||||
it("puts the bare .rar FIRST in a RAR_LEGACY set (it is volume 1)", () => {
|
||||
expect(names(["Pack.r01", "Pack.rar", "Pack.r00"])).toEqual([
|
||||
"Pack.rar",
|
||||
"Pack.r00",
|
||||
"Pack.r01",
|
||||
]);
|
||||
});
|
||||
|
||||
it("puts the bare .zip LAST in a ZIP_LEGACY set (it is the final disk)", () => {
|
||||
expect(names(["Pack.z02", "Pack.zip", "Pack.z01"])).toEqual([
|
||||
"Pack.z01",
|
||||
"Pack.z02",
|
||||
"Pack.zip",
|
||||
]);
|
||||
});
|
||||
|
||||
it("marks both legacy sets as multipart with the right format", () => {
|
||||
const rar = groupArchiveSets([msg("Pack.rar"), msg("Pack.r00")])[0];
|
||||
expect(rar.isMultipart).toBe(true);
|
||||
expect(rar.type).toBe("RAR");
|
||||
|
||||
const zip = groupArchiveSets([msg("Pack.zip"), msg("Pack.z01")])[0];
|
||||
expect(zip.isMultipart).toBe(true);
|
||||
expect(zip.type).toBe("ZIP");
|
||||
});
|
||||
|
||||
it("orders numbered volume sets by part number", () => {
|
||||
expect(names(["Pack.rar.003", "Pack.rar.001", "Pack.rar.002"])).toEqual([
|
||||
"Pack.rar.001",
|
||||
"Pack.rar.002",
|
||||
"Pack.rar.003",
|
||||
]);
|
||||
});
|
||||
|
||||
it("orders .partN sets by part number with an SFX first volume", () => {
|
||||
expect(names(["Pack.part3.rar", "Pack.part1.exe", "Pack.part2.rar"])).toEqual([
|
||||
"Pack.part1.exe",
|
||||
"Pack.part2.rar",
|
||||
"Pack.part3.rar",
|
||||
]);
|
||||
});
|
||||
|
||||
it("treats unrelated singles as their own non-multipart sets", () => {
|
||||
const sets = groupArchiveSets([msg("A.zip"), msg("B.rar")]);
|
||||
expect(sets).toHaveLength(2);
|
||||
expect(sets.every((s) => !s.isMultipart)).toBe(true);
|
||||
expect(sets.every((s) => s.parts.length === 1)).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user