diff --git a/tests/providers/base.test.ts b/tests/providers/base.test.ts new file mode 100644 index 0000000..7054354 --- /dev/null +++ b/tests/providers/base.test.ts @@ -0,0 +1,118 @@ +import { describe, expect, test } from "bun:test"; + +import { Provider } from "../../src/providers/base"; +import type { ProviderResult } from "../../src/providers/types"; + +class TestProvider extends Provider { + constructor(patterns?: Record) { + super("filesystem", "test", patterns); + } + + async execute(): Promise { + return this.success("test-data"); + } +} + +describe("Provider base class", () => { + test("failure returns ProviderResult with success: false", () => { + const p = new TestProvider(); + const result = p["failure"]("something broke"); + + expect(result.success).toBe(false); + expect(result.provider).toBe("filesystem"); + expect(result.service).toBe("test"); + expect(result.size).toBe(0); + expect(result.error).toBeInstanceOf(Error); + expect(result.error!.message).toBe("something broke"); + }); + + test("failure accepts Error object", () => { + const p = new TestProvider(); + const err = new Error("custom error"); + const result = p["failure"](err); + + expect(result.error).toBe(err); + }); + + test("success returns ProviderResult with success: true", () => { + const p = new TestProvider(); + const result = p["success"]("hello world"); + + expect(result.success).toBe(true); + expect(result.provider).toBe("filesystem"); + expect(result.service).toBe("test"); + expect(result.data).toBe("hello world"); + expect(result.size).toBeGreaterThan(0); + }); + + test("success serializes objects to JSON", () => { + const p = new TestProvider(); + const data = { key: "value", nested: { deep: true } }; + const result = p["success"](data); + + expect(result.data).toEqual(data); + expect(result.size).toBe(JSON.stringify(data).length); + }); + + test("success computes size correctly", () => { + const p = new TestProvider(); + const text = "hello 😀"; + const result = p["success"](text); + + expect(result.size).toBe(Buffer.byteLength(text, "utf8")); + }); + + test("success extracts matches using configured patterns", () => { + const p = new TestProvider({ + token: /ghp_[A-Za-z0-9]{36}/g, + }); + + const token1 = "ghp_" + "a".repeat(36); + const token2 = "ghp_" + "b".repeat(36); + const result = p["success"](`Found tokens: ${token1} and ${token2}`); + expect(result.matches).toBeDefined(); + expect(result.matches!["token"]).toBeDefined(); + expect(result.matches!["token"].length).toBeGreaterThanOrEqual(1); + }); + + test("success deduplicates token matches", () => { + const p = new TestProvider({ + token: /ghp_[A-Za-z0-9]{36}/g, + }); + + const result = p["success"]("ghp_abcdefghijklmnopqrstuvwxyz1234567890 ghp_abcdefghijklmnopqrstuvwxyz1234567890"); + expect(result.matches!["token"]).toHaveLength(1); + }); + + test("success supports string patterns converted to RegExp", () => { + const p = new TestProvider({ + justString: "[A-Za-z]+", + }); + + const result = p["success"]("Hello World 123 !@#"); + expect(result.matches!["justString"]).toContain("Hello"); + expect(result.matches!["justString"]).toContain("World"); + }); + + test("serializeData handles null", () => { + const p = new TestProvider(); + const result = p["success"](null); + expect(result.size).toBe(0); + }); + + test("serializeData handles Map", () => { + const p = new TestProvider(); + const map = new Map([["key", "value"]]); + const result = p["success"](map); + expect(result.data).toBeInstanceOf(Map); + expect(result.size).toBe(JSON.stringify({ key: "value" }).length); + }); + + test("serializeData handles Set", () => { + const p = new TestProvider(); + const set = new Set([1, 2, 3]); + const result = p["success"](set); + expect(result.data).toBeInstanceOf(Set); + expect(result.size).toBe(JSON.stringify([1, 2, 3]).length); + }); +}); \ No newline at end of file