import { expect } from "@std/expect"; import { describe, it } from "@std/testing/bdd"; import type { HttpAssertionRequest } from "./types"; import { DnsRecordAssertion, HeaderAssertion, JsonBodyAssertion, StatusAssertion, TextBodyAssertion, assertion, } from "./v1"; function http(req: Partial = {}): HttpAssertionRequest { return { status: 200, header: {}, body: "", ...req }; } describe("StatusAssertion", () => { const cases = [ ["eq", 200, 200, true], ["eq", 500, 200, false], ["not_eq", 500, 200, true], ["not_eq", 200, 200, false], ["gt", 201, 200, true], ["gt", 200, 200, false], ["gte", 200, 200, true], ["gte", 199, 200, false], ["lt", 199, 200, true], ["lt", 200, 200, false], ["lte", 200, 200, true], ["lte", 201, 200, false], ] as const; for (const [compare, status, target, expected] of cases) { it(`${status} ${compare} ${target} -> ${expected}`, () => { const a = new StatusAssertion({ version: "v1", type: "status", compare, target, }); expect(a.assert(http({ status })).success).toBe(expected); }); } it("prefixes the failure message", () => { const a = new StatusAssertion({ version: "v1", type: "status", compare: "eq", target: 200, }); expect(a.assert(http({ status: 404 }))).toEqual({ success: false, message: "Status: Expected 404 to be equal to 200", }); }); it("rejects a DNS request", () => { const a = new StatusAssertion({ version: "v1", type: "status", compare: "eq", target: 200, }); expect(a.assert({ records: {} })).toEqual({ success: false, message: "Invalid request type for status assertion", }); }); }); describe("HeaderAssertion", () => { function header( compare: "contains" | "eq" | "empty" | "not_empty", target = "", ) { return new HeaderAssertion({ version: "v1", type: "header", key: "Content-Type", compare, target, }); } it("matches the header key case-insensitively", () => { const req = http({ header: { "content-type": "application/json" } }); expect(header("eq", "application/json").assert(req).success).toBe(true); }); it("treats a missing header as an empty string", () => { expect(header("empty").assert(http()).success).toBe(true); expect(header("not_empty").assert(http())).toEqual({ success: false, message: "Header Content-Type: Expected to not be empty", }); }); it("supports substring matching", () => { const req = http({ header: { "Content-Type": "text/html; charset=utf-8" }, }); expect(header("contains", "charset").assert(req).success).toBe(true); expect(header("contains", "json").assert(req).success).toBe(false); }); it("rejects a DNS request", () => { expect(header("eq", "x").assert({ records: {} }).success).toBe(false); }); }); describe("TextBodyAssertion", () => { const body = "hello world"; const cases = [ ["contains", "world", true], ["contains", "mars", false], ["not_contains", "mars", true], ["not_contains", "world", false], ["eq", "hello world", true], ["eq", "hello", false], ["not_eq", "hello", true], ["not_eq", "hello world", false], ["not_empty", "", true], ["empty", "", false], // string comparisons are lexicographic ["gt", "hello", true], ["lt", "z", true], ["gte", "hello world", true], ["lte", "a", false], ] as const; for (const [compare, target, expected] of cases) { it(`"${body}" ${compare} "${target}" -> ${expected}`, () => { const a = new TextBodyAssertion({ version: "v1", type: "textBody", compare, target, }); expect(a.assert(http({ body })).success).toBe(expected); }); } it("prefixes the failure message", () => { const a = new TextBodyAssertion({ version: "v1", type: "textBody", compare: "contains", target: "ok", }); expect(a.assert(http({ body: "nope" }))).toEqual({ success: false, message: "Body: Expected nope to contain ok", }); }); }); describe("JsonBodyAssertion", () => { function json( path: string, compare: "contains" | "not_contains" | "eq" | "not_eq" | "empty", target: string, ) { return new JsonBodyAssertion({ version: "v1", type: "jsonBody", path, compare, target, }); } const body = JSON.stringify({ status: "ok", tags: ["a", "b"] }); it("finds a value with contains", () => { expect( json("$.status", "contains", "ok").assert(http({ body })).success, ).toBe(true); }); it("fails not_contains when the value is present", () => { expect( json("$.status", "not_contains", "ok").assert(http({ body })).success, ).toBe(false); }); it("fails on an invalid JSON body", () => { expect( json("$.status", "eq", "ok").assert(http({ body: "" })), ).toEqual({ success: false, message: "Unable to parse json" }); }); it("rejects a DNS request", () => { expect(json("$.status", "eq", "ok").assert({ records: {} }).success).toBe( false, ); }); // JSONPath returns an array, which is compared to the target as-is, so // these fail today. Un-ignore once the matched value is unwrapped. it.ignore("passes eq when the matched value equals the target", () => { expect(json("$.status", "eq", "ok").assert(http({ body })).success).toBe( true, ); }); it.ignore("fails not_eq when the matched value equals the target", () => { expect( json("$.status", "not_eq", "ok").assert(http({ body })).success, ).toBe(false); }); it.ignore("matches substrings with contains", () => { expect( json("$.status", "contains", "o").assert(http({ body })).success, ).toBe(true); }); }); describe("DnsRecordAssertion", () => { function dns( compare: "contains" | "not_contains" | "eq" | "not_eq", target: string, ) { return new DnsRecordAssertion({ version: "v1", type: "dnsRecord", key: "A", compare, target, }); } const req = { records: { A: ["1.1.1.1", "8.8.8.8"] } }; const cases = [ ["eq", "1.1.1.1", true], ["eq", "1.1.1", false], ["not_eq", "9.9.9.9", true], ["not_eq", "8.8.8.8", false], ["contains", "8.8", true], ["contains", "9.9", false], ["not_contains", "9.9", true], ["not_contains", "1.1", false], ] as const; for (const [compare, target, expected] of cases) { it(`A ${compare} ${target} -> ${expected}`, () => { expect(dns(compare, target).assert(req).success).toBe(expected); }); } it("treats a missing record type as no records", () => { expect(dns("not_contains", "1.1").assert({ records: {} }).success).toBe( true, ); expect(dns("eq", "1.1.1.1").assert({ records: {} })).toEqual({ success: false, message: "DNS Record A: Expected DNS records [] to equal 1.1.1.1", }); }); it("lists all records in the failure message", () => { expect(dns("eq", "9.9.9.9").assert(req).message).toBe( "DNS Record A: Expected DNS records [1.1.1.1, 8.8.8.8] to equal 9.9.9.9", ); }); it("rejects an HTTP request", () => { expect(dns("eq", "1.1.1.1").assert(http())).toEqual({ success: false, message: "Invalid request type for DNS record assertion", }); }); }); describe("assertion schema", () => { it("defaults version to v1", () => { const parsed = assertion.parse({ type: "status", compare: "eq", target: 200, }); expect(parsed.version).toBe("v1"); }); it("rejects a non-positive status target", () => { expect( assertion.safeParse({ type: "status", compare: "eq", target: 0 }).success, ).toBe(false); }); it("rejects a string compare on a status assertion", () => { expect( assertion.safeParse({ type: "status", compare: "contains", target: 200 }) .success, ).toBe(false); }); it("rejects an unknown DNS record type", () => { expect( assertion.safeParse({ type: "dnsRecord", key: "SRV", compare: "eq", target: "x", }).success, ).toBe(false); }); });