/**
 * Unit tests for the server-side stripHtml utility (A03 XSS prevention).
 *
 * Covers:
 *   - Passthrough of plain text and mention tokens
 *   - Stripping of common HTML injection patterns
 *   - HTML entity decoding
 *   - Edge cases (empty string, nested tags, self-closing tags)
 */

import { describe, expect, it } from "vitest";
import { stripHtml } from "../lib/strip-html.js";

describe("stripHtml — plain text passthrough", () => {
  it("returns plain text unchanged", () => {
    expect(stripHtml("Hello, world!")).toBe("Hello, world!");
  });

  it("preserves mention tokens", () => {
    expect(stripHtml("Hey @[Alice](user_1) please check this")).toBe(
      "Hey @[Alice](user_1) please check this",
    );
  });

  it("preserves newlines and whitespace", () => {
    expect(stripHtml("Line 1\nLine 2\n  indented")).toBe("Line 1\nLine 2\n  indented");
  });

  it("returns empty string unchanged", () => {
    expect(stripHtml("")).toBe("");
  });
});

describe("stripHtml — HTML injection stripping (A03)", () => {
  it("strips a script tag and its content", () => {
    const result = stripHtml('<script>alert("xss")</script>normal text');
    expect(result).not.toContain("<script");
    expect(result).toContain("normal text");
  });

  it("strips an img onerror payload", () => {
    const result = stripHtml('<img src=x onerror="alert(1)">text');
    expect(result).not.toContain("<img");
    expect(result).toContain("text");
  });

  it("strips anchor tags with javascript: href", () => {
    const result = stripHtml('<a href="javascript:alert(1)">click me</a>');
    expect(result).not.toContain("<a");
    expect(result).toContain("click me");
  });

  it("strips iframe tags", () => {
    const result = stripHtml('<iframe src="evil.com"></iframe>safe text');
    expect(result).not.toContain("<iframe");
    expect(result).toContain("safe text");
  });

  it("strips nested tags", () => {
    const result = stripHtml("<b><i>bold italic</i></b>");
    expect(result).toBe("bold italic");
  });

  it("strips self-closing tags", () => {
    const result = stripHtml("before<br/>after");
    expect(result).toBe("beforeafter");
  });

  it("strips HTML comment injection", () => {
    const result = stripHtml("<!-- <script>alert(1)</script> -->text");
    expect(result).not.toContain("<");
    expect(result).toContain("text");
  });
});

describe("stripHtml — HTML entity decoding", () => {
  it("decodes &amp; to &", () => {
    expect(stripHtml("cats &amp; dogs")).toBe("cats & dogs");
  });

  it("decodes &lt; and &gt; to < and >", () => {
    expect(stripHtml("1 &lt; 2 &gt; 0")).toBe("1 < 2 > 0");
  });

  it("decodes &quot; to double quote", () => {
    expect(stripHtml('say &quot;hello&quot;')).toBe('say "hello"');
  });

  it("decodes &#39; and &apos; to single quote", () => {
    expect(stripHtml("it&#39;s fine and it&apos;s ok")).toBe("it's fine and it's ok");
  });

  it("decodes &nbsp; to space", () => {
    expect(stripHtml("hello&nbsp;world")).toBe("hello world");
  });
});
