import { test } from "node:test"; import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import sharp from "sharp"; import Fastify from "fastify"; import cookie from "@fastify/cookie"; import { openDb } from "../src/db.js"; import { loadConfig, type AppConfig } from "../src/config.js"; import { registerRoutes } from "../src/routes.js"; import { createUser, login } from "../src/auth.js"; import { claimNextJob, getJob } from "../src/domain/jobs.js"; import { enqueueFabricImport, runFabricImport } from "../src/domain/fabricImport.js"; import { parseFabricCollectionPage, detectFabricVendor, FabricParseError } from "../src/import/fabricParser.js"; import { parseDavisCollectionPage } from "../src/import/fabricDavis.js"; import type { FetchedSource } from "../src/media/sourceFetch.js"; const FIXTURE = fs.readFileSync(path.join(import.meta.dirname, "fixtures", "davis-collection.html"), "utf8"); const DAVIS_URL = "https://www.davis.pl/kolekcja/tilia/"; const SIC_URL = "https://sic.com.pl/oferta/tkaniny/nova/"; const SIC_HTML = `
karta
`;
// --- parser Davis: czyste HTML, zero sieci -----------------------------------
test("davis parser: nazwa, parametry, odcienie, karta PDF z PIM", () => {
const parsed = parseDavisCollectionPage(FIXTURE, DAVIS_URL);
assert.equal(parsed.code, "tilia");
assert.equal(parsed.name, "TILIA");
assert.equal(parsed.producer, "Davis");
// parametry techniczne → kanoniczne klucze params_json
assert.deepEqual(parsed.params.grammage_g_m2, { value: 420, tolerance: "±5%" });
assert.equal(parsed.params.composition, "100% poliester");
assert.deepEqual(parsed.params.usable_width_cm, { min: 140 });
assert.deepEqual(parsed.params.martindale_cycles, { min: 90000, max: 100000 });
assert.deepEqual(parsed.params.seam_slippage_mm, { warp: { min: 1.9, max: 2.1 }, weft: { min: 1.5, max: 1.7 } });
assert.equal(parsed.params.pilling_grade, 4);
assert.deepEqual(parsed.params.rubbing_fastness_dry, { light: "4-5", medium: "5", dark: "4-5" });
assert.deepEqual(parsed.params.rubbing_fastness_wet, { light: "5", medium: "5", dark: "5" });
assert.equal(parsed.params.pet_friendly, true);
assert.equal(parsed.params.water_repellent, true);
// odcienie: pełnowymiarowe próbki, czyste kody
assert.deepEqual(parsed.shades.map((s) => s.code), ["01", "03", "83", "100"]);
assert.equal(parsed.shades[0]!.imageUrl, "https://www.davis.pl/wp-content/uploads/2023/06/tilia-01_TILIA-01.jpg");
assert.ok(parsed.shades.every((s) => !s.imageUrl.includes("150x150")), "tylko pełnowymiarowe próbki, nie miniatury");
// karta PDF z PIM (protokół-względny URL → https)
assert.equal(parsed.cardUrl, "https://pim.davis.pl/fabrics/generate-card/cat/207.pdf?lang=pl");
// błędy
assert.throws(() => parseDavisCollectionPage("nic", DAVIS_URL), FabricParseError);
assert.throws(() => parseDavisCollectionPage(FIXTURE, "https://example.com/x/"), /davis\.pl/);
});
// --- router parserów: wybór strategii wg hosta --------------------------------
test("router: domena URL wybiera parser SIC lub Davis; inny host = błąd", () => {
assert.equal(detectFabricVendor(SIC_URL), "sic");
assert.equal(detectFabricVendor(DAVIS_URL), "davis");
assert.throws(() => detectFabricVendor("https://bobochicparis.com/fr/x"), /Brak adaptera/);
const sic = parseFabricCollectionPage(SIC_HTML, SIC_URL);
assert.equal(sic.producer, "SIC");
assert.equal(sic.code, "nova");
assert.equal(sic.shades[0]!.imageUrl, "https://sic.com.pl/wp-content/uploads/2024/05/nova-1-Ivory.jpg");
const davis = parseFabricCollectionPage(FIXTURE, DAVIS_URL);
assert.equal(davis.producer, "Davis");
assert.equal(davis.shades.length, 4);
// błąd adaptera SIC przepakowywany do wspólnego FabricParseError
assert.throws(() => parseFabricCollectionPage("nic", SIC_URL), FabricParseError);
assert.throws(() => parseFabricCollectionPage(FIXTURE, "https://unknown.example/x"), FabricParseError);
});
// --- job import_fabric z fixture Davis -----------------------------------------
const JPEG = (seed: number) => sharp({ create: { width: 32, height: 32, channels: 3, background: { r: 60 + (seed % 180), g: 90, b: 80 } } }).jpeg().toBuffer();
const PDF = Buffer.from("%PDF-1.4\n1 0 obj<>endobj\ntrailer<>\n%%EOF\n");
async function fixture(t: { after: (fn: () => Promise