/**
 * Sanitization Utility Tests
 *
 * Tests for HTML sanitization and XSS prevention.
 */

import { describe, it, expect } from "vitest";
import { sanitizeString, sanitizeObject, sanitizeArray } from "./sanitize.util";

describe("Sanitization Utilities", () => {
  describe("sanitizeString", () => {
    it("should remove script tags", () => {
      const dirty = '<script>alert("XSS")</script>Hello';
      const clean = sanitizeString(dirty);

      expect(clean).not.toContain("<script>");
      expect(clean).not.toContain("alert");
      expect(clean).toContain("Hello");
    });

    it("should remove javascript: URLs", () => {
      const dirty = '<a href="javascript:alert(1)">Click</a>';
      const clean = sanitizeString(dirty);

      expect(clean).not.toContain("javascript:");
      expect(clean).toContain("Click");
    });

    it("should remove on* event handlers", () => {
      const dirty = '<div onclick="alert(1)">Click me</div>';
      const clean = sanitizeString(dirty);

      expect(clean).not.toContain("onclick");
      expect(clean).toContain("Click me");
    });

    it("should allow safe HTML tags", () => {
      const input = "<p>Hello <b>world</b>!</p>";
      const clean = sanitizeString(input);

      expect(clean).toContain("<p>");
      expect(clean).toContain("<b>");
      expect(clean).toContain("Hello");
    });

    it("should handle null and undefined", () => {
      expect(sanitizeString(null)).toBe("");
      expect(sanitizeString(undefined)).toBe("");
    });

    it("should handle non-string values", () => {
      expect(sanitizeString(123 as any)).toBe("123");
      expect(sanitizeString(true as any)).toBe("true");
    });

    it("should prevent data exfiltration via img tags", () => {
      const dirty = '<img src="x" onerror="fetch(\'/steal?data=\'+document.cookie)">';
      const clean = sanitizeString(dirty);

      expect(clean).not.toContain("onerror");
      expect(clean).not.toContain("fetch");
    });

    it("should remove style tags with malicious CSS", () => {
      const dirty = '<style>body { background: url("javascript:alert(1)") }</style>Hello';
      const clean = sanitizeString(dirty);

      expect(clean).not.toContain("<style>");
      expect(clean).not.toContain("javascript:");
    });
  });

  describe("sanitizeObject", () => {
    it("should sanitize all string values in an object", () => {
      const dirty = {
        name: '<script>alert("XSS")</script>John',
        description: "Safe text",
        nested: {
          value: '<img src=x onerror="alert(1)">',
        },
      };

      const clean = sanitizeObject(dirty);

      expect(clean.name).not.toContain("<script>");
      expect(clean.name).toContain("John");
      expect(clean.description).toBe("Safe text");
      expect(clean.nested.value).not.toContain("onerror");
    });

    it("should preserve non-string values", () => {
      const input = {
        string: "test",
        number: 123,
        boolean: true,
        nullValue: null,
        undefinedValue: undefined,
      };

      const clean = sanitizeObject(input);

      expect(clean.number).toBe(123);
      expect(clean.boolean).toBe(true);
      expect(clean.nullValue).toBeNull();
      expect(clean.undefinedValue).toBeUndefined();
    });

    it("should handle arrays within objects", () => {
      const input = {
        tags: ["<script>alert(1)</script>safe", "another<script>"],
      };

      const clean = sanitizeObject(input);

      expect(clean.tags[0]).not.toContain("<script>");
      expect(clean.tags[0]).toContain("safe");
      expect(clean.tags[1]).not.toContain("<script>");
    });

    it("should handle deeply nested objects", () => {
      const input = {
        level1: {
          level2: {
            level3: {
              xss: "<script>alert(1)</script>",
            },
          },
        },
      };

      const clean = sanitizeObject(input);

      expect(clean.level1.level2.level3.xss).not.toContain("<script>");
    });
  });

  describe("sanitizeArray", () => {
    it("should sanitize all strings in an array", () => {
      const dirty = ["<script>alert(1)</script>safe", "clean", '<img src=x onerror="alert(2)">'];

      const clean = sanitizeArray(dirty);

      expect(clean[0]).not.toContain("<script>");
      expect(clean[0]).toContain("safe");
      expect(clean[1]).toBe("clean");
      expect(clean[2]).not.toContain("onerror");
    });

    it("should handle arrays with mixed types", () => {
      const input = ["<script>test</script>", 123, true, null, { key: "<b>value</b>" }];

      const clean = sanitizeArray(input);

      expect(clean[0]).not.toContain("<script>");
      expect(clean[1]).toBe(123);
      expect(clean[2]).toBe(true);
      expect(clean[3]).toBeNull();
      expect((clean[4] as any).key).toContain("<b>");
    });

    it("should handle nested arrays", () => {
      const input = [["<script>alert(1)</script>", "safe"], ['<img src=x onerror="alert(2)">']];

      const clean = sanitizeArray(input);

      expect(clean[0][0]).not.toContain("<script>");
      expect(clean[0][1]).toBe("safe");
      expect(clean[1][0]).not.toContain("onerror");
    });
  });
});
