import { describe, expect, it, vi } from "vitest";
import { createFileApi } from "../fileApi";
import {
  parseContentDispositionFilename,
  validateSelectedFile,
} from "../filename";

describe("F5 file API and filename safety", () => {
  it("预览和下载统一使用documents路径并传递取消信号", async () => {
    const result = {
      blob: new Blob(["pdf"]),
      contentType: "application/pdf",
      contentDisposition: null,
      requestId: "request-id",
      httpStatus: 200,
      headers: new Headers(),
    };
    const client = { getBlob: vi.fn().mockResolvedValue(result) };
    const api = createFileApi(client);
    const signal = new AbortController().signal;
    await api.preview("7", signal);
    await api.download("7", signal);
    expect(client.getBlob).toHaveBeenNthCalledWith(1, "documents/7/preview", { signal });
    expect(client.getBlob).toHaveBeenNthCalledWith(2, "documents/7/download", { signal });
    expect(client.getBlob.mock.calls.flat().join(" ")).not.toContain("attachments/7");
  });

  it("优先解析filename*并清理路径和响应头注入字符", () => {
    expect(parseContentDispositionFilename(
      "attachment; filename=old.pdf; filename*=UTF-8''%E6%96%B0%E6%96%87%E4%BB%B6.pdf",
      "fallback.bin",
    )).toBe("新文件.pdf");
    expect(parseContentDispositionFilename('attachment; filename="../bad\\r\\nname.docx"', "fallback.bin"))
      .not.toMatch(/[\\/\r\n]/);
  });

  it("兼容filename并提供安全后备名称", () => {
    expect(parseContentDispositionFilename('attachment; filename="report.docx"', "fallback.bin")).toBe("report.docx");
    expect(parseContentDispositionFilename(null, "../fallback.pdf")).toBe("_fallback.pdf");
  });

  it("前端只做扩展名和空文件提示，不硬编码大小上限", () => {
    expect(validateSelectedFile(new File([], "empty.pdf"))).toContain("空文件");
    expect(validateSelectedFile(new File(["x"], "bad.exe"))).toContain("DOC");
    expect(validateSelectedFile(new File(["x"], "ok.xlsx"))).toBeNull();
  });
});
