Ctrl K
ComunidadGuías…

test/upload.test.js

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import { test } from "node:test";
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm, utimes } from "node:fs/promises";
import os from "node:os";
import path from "node:path";

/**
 * Upload-only research mode. The whole file runs against a scratch CONFIG dir
 * so staging never touches the real config/. paths.js resolves CONFIG at
 * import time, so the env must be set BEFORE the modules load — hence dynamic
 * imports here, never static ones.
 */
const scratch = await mkdtemp(path.join(os.tmpdir(), "forge-upload-"));
process.env.FORGE_CONFIG_DIR = scratch;

const upload = await import("../src/ai/upload.js");
const { resolveResearchSource } = await import("../src/ai/pipeline.js");
const { groundDeck } = await import("../src/ai/grounding.js");
const { researchSummary } = await import("../src/ai/research.js");

const { ingestUpload, stageUpload, readStagedUpload, sweepStagedUploads, uploadStageDir, UPLOAD_MAX_BYTES, UPLOAD_MAX_WORDS } = upload;

test("ingestUpload accepts markdown and trims it", async () => {
  const r = await ingestUpload(Buffer.from("\uFEFF# Solar pumping\n\nRooftop solar reaches 2.1 GW by 2025.\n\n"), { name: "notes.md" });
  assert.equal(r.text, "# Solar pumping\n\nRooftop solar reaches 2.1 GW by 2025.");
  assert.equal(r.ext, "md");
  assert.equal(r.words, 10);
});

test("ingestUpload rejects an empty buffer", async () => {
  await assert.rejects(ingestUpload(Buffer.alloc(0), { name: "empty.txt" }), /empty/);
});

test("ingestUpload rejects a binary file disguised as .txt", async () => {
  const junk = Buffer.concat([Buffer.from("MZ"), Buffer.alloc(64, 0x00), Buffer.from("text")]);
  await assert.rejects(ingestUpload(junk, { name: "evil.txt" }), /binary/);
});

test("ingestUpload rejects a file over the size cap", async () => {
  const big = Buffer.alloc(UPLOAD_MAX_BYTES + 1, 0x20);
  await assert.rejects(ingestUpload(big, { name: "big.md" }), /too large/);
});

test("ingestUpload rejects an unsupported extension", async () => {
  await assert.rejects(ingestUpload(Buffer.from("hello"), { name: "page.html" }), /unsupported/);
  await assert.rejects(ingestUpload(Buffer.from("x"), { ext: "exe" }), /unsupported/);
});

test("ingestUpload rejects a document beyond the word cap", async () => {
  const text = Array.from({ length: UPLOAD_MAX_WORDS + 5 }, (_, i) => `w${i}`).join(" ");
  await assert.rejects(ingestUpload(Buffer.from(text), { name: "long.md" }), /too long/);
});

test("stage + resolve a briefing document round-trips", async () => {
  const staged = await stageUpload({ text: "# Only this file\n\n42 MW of pumping.", name: "brief.docx", ext: "docx", words: 7 });
  assert.ok(staged.token.startsWith("up-"));
  const resolved = await readStagedUpload(staged.token);
  assert.equal(resolved.name, "brief.docx");
  assert.ok(resolved.text.includes("42 MW"));
  const dir = uploadStageDir();
  assert.ok((await readFile(path.join(dir, `${staged.token}.json`), "utf8")).includes("42 MW"));
  await rm(dir, { recursive: true, force: true });
});

test("resolve rejects a missing or malformed token", async () => {
  assert.equal(await readStagedUpload("up-nope-1234"), null);
  assert.equal(await readStagedUpload("../../config/local.yaml"), null);
  await assert.rejects(resolveResearchSource({ researchSource: "upload", upload: { token: "up-missing-1234" } }), /re-upload/);
});

test("sweep drops only expired staged documents", async () => {
  const dir = uploadStageDir();
  const a = await stageUpload({ text: "fresh", name: "a.md", ext: "md", words: 1 });
  const old = await stageUpload({ text: "old", name: "b.md", ext: "md", words: 1 });
  const past = new Date(Date.now() - 48 * 60 * 60 * 1000 - 1000);
  await utimes(path.join(dir, `${old.token}.json`), past, past);
  await sweepStagedUploads();
  assert.ok((await readStagedUpload(a.token)) != null, "fresh stage survives");
  assert.equal(await readStagedUpload(old.token), null, "expired stage is swept");
  await rm(dir, { recursive: true, force: true });
});

test("resolveResearchSource: explicit upload beats legacy booleans", async () => {
  const r = await resolveResearchSource({ researchSource: "upload", research: false, upload: { name: "a.md", text: "Only this file." } });
  assert.equal(r.mode, "upload");
  assert.equal(r.text, "Only this file.");
  assert.equal(r.name, "a.md");
});

test("resolveResearchSource: upload mode without content errors", async () => {
  await assert.rejects(resolveResearchSource({ researchSource: "upload", upload: {} }), /no uploaded file/);
});

test("resolveResearchSource: legacy booleans keep their meaning", async () => {
  assert.equal((await resolveResearchSource({ research: true })).mode, "web");
  assert.equal((await resolveResearchSource({ research: false, sources: ["https://x.example"] })).mode, "web");
  assert.equal((await resolveResearchSource({})).mode, "none");
  assert.equal((await resolveResearchSource({ researchSource: "none" })).mode, "none");
  assert.equal((await resolveResearchSource({ researchSource: "web" })).mode, "web");
});

test("upload-only grounding is strict fidelity against the file", () => {
  const file = "# My notes\n\nThe plant reaches 2.1 GW by 2025. Cost is 4.2 INR per unit.";
  const deck = {
    title: "T",
    slides: [
      { type: "stats", headline: "Scale", stats: [
        { value: "2.1 GW", label: "capacity" },
        { value: "900 MW", label: "invented" },
      ] },
    ],
  };
  const { findings, problems, notes } = groundDeck(deck, file, { label: "your uploaded file" });
  assert.equal(findings.length, 1);
  assert.ok(findings[0].claims.includes("900 MW"));
  assert.ok(!findings[0].claims.includes("2.1 GW"));
  assert.ok(problems.some((p) => p.includes("900 MW")));
  assert.ok(notes.slides[0].notes.includes("not found in your uploaded file"));
});

test("researchSummary counts a user-provided source without domain noise", () => {
  const s = researchSummary([
    { kind: "user-provided", name: "brief.md", title: "brief.md", words: 20 },
    { url: "https://example.com/a", title: "A" },
  ]);
  assert.equal(s.userProvided, 1);
  assert.equal(s.total, 2);
  assert.equal(s.distinctDomains, 1);
});