/*
RFC-0812: Unit tests for toApiRecord covering SVCB, HTTPS, A, AAAA, TXT,
CNAME record types, optional fields, and edge cases.
RFC-0817: Unit test for graceful skip when dns-records.yaml is absent.
- Do not test the full dns.record.upsert pipeline — that requires Cloudflare API credentials.
- Do not fix quoted-value parsing in SVCB/HTTPS content — documented as known issue, requires its own RFC.
- RFC-0812: initial unit tests for toApiRecord.
- RFC-0817: add graceful skip test for missing dns-records.yaml.
- RFC-1254: drop unused io-migration leftovers
*/
import { describe, it, expect, vi } from "vitest";
import { toApiRecord, runDnsRecordUpsert } from "./dns-record-upsert.ts";
import type { DnsRecordDeclaration } from "@warpgogol/werkstatt-shared/ontology/schemas";
import type { KernelCommandInput, KernelRuntimeContext } from "@warpgogol/werkstatt-engine/kernel";
import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
import { loadDnsRecordFile } from "./dns-helpers.ts";
import {
listDnsRecords,
createDnsRecord,
updateDnsRecord,
} from "../leitstand/adapters/cloudflare-api.ts";
vi.mock("./dns-helpers.ts", async (importOriginal) => {
const original = await importOriginal();
return {
...original,
loadDnsRecordFile: vi.fn().mockResolvedValue(null),
resolveDnsZoneId: vi.fn().mockReturnValue("zone-1"),
resolveDnsEnv: vi.fn().mockResolvedValue({ CLOUDFLARE_API_TOKEN: "token" }),
};
});
vi.mock("../leitstand/adapters/cloudflare-api.ts", () => ({
listDnsRecords: vi.fn(),
createDnsRecord: vi.fn(),
updateDnsRecord: vi.fn(),
}));
vi.mock("../sternsystem/registry-io.ts", async (importOriginal) => {
const original = await importOriginal();
return {
...original,
discoverSystems: vi.fn().mockResolvedValue({ systems: [] }),
};
});
function rec(
overrides: Partial &
Pick,
): DnsRecordDeclaration {
return {
proxied: false,
...overrides,
};
}
describe("toApiRecord — SVCB records", () => {
it("parses content into data object with priority, target, value", () => {
const result = toApiRecord(
rec({
type: "SVCB",
name: "_dns.resolver.example.com",
content: "1 . alpn=h2 dohpath=/dns-query{?dns}",
}),
);
expect(result.type).toBe("SVCB");
expect(result.content).toBeUndefined();
expect(result.data).toEqual({
priority: 1,
target: ".",
value: "alpn=h2 dohpath=/dns-query{?dns}",
});
expect(result.proxied).toBe(false);
});
it("handles content with only priority and target (value = empty)", () => {
const result = toApiRecord(
rec({
type: "SVCB",
name: "_dns.resolver.example.com",
content: "1 .",
}),
);
expect(result.data).toEqual({
priority: 1,
target: ".",
value: "",
});
});
it("handles content with only priority (target = dot, value = empty)", () => {
const result = toApiRecord(
rec({
type: "SVCB",
name: "_dns.resolver.example.com",
content: "1",
}),
);
expect(result.data).toEqual({
priority: 1,
target: ".",
value: "",
});
});
it("handles empty content", () => {
const result = toApiRecord(
rec({
type: "SVCB",
name: "_dns.resolver.example.com",
content: "",
}),
);
expect(result.data).toEqual({
priority: NaN,
target: ".",
value: "",
});
});
it("includes ttl and comment when present", () => {
const result = toApiRecord(
rec({
type: "SVCB",
name: "_dns.resolver.example.com",
content: "1 . alpn=h2",
ttl: 300,
comment: "test record",
}),
);
expect(result.ttl).toBe(300);
expect(result.comment).toBe("test record");
});
it("omits ttl and comment when absent", () => {
const result = toApiRecord(
rec({
type: "SVCB",
name: "_dns.resolver.example.com",
content: "1 . alpn=h2",
}),
);
expect(result.ttl).toBeUndefined();
expect(result.comment).toBeUndefined();
});
});
describe("toApiRecord — HTTPS records", () => {
it("parses content into data object", () => {
const result = toApiRecord(
rec({
type: "HTTPS",
name: "example.com",
content: "1 . alpn=h2",
}),
);
expect(result.type).toBe("HTTPS");
expect(result.content).toBeUndefined();
expect(result.data).toEqual({
priority: 1,
target: ".",
value: "alpn=h2",
});
});
it("handles content with only priority and target", () => {
const result = toApiRecord(
rec({
type: "HTTPS",
name: "example.com",
content: "1 .",
}),
);
expect(result.data).toEqual({
priority: 1,
target: ".",
value: "",
});
});
});
describe("toApiRecord — A records", () => {
it("passes content through without data object", () => {
const result = toApiRecord(
rec({
type: "A",
name: "example.com",
content: "192.0.2.1",
proxied: true,
}),
);
expect(result.type).toBe("A");
expect(result.content).toBe("192.0.2.1");
expect(result.data).toBeUndefined();
expect(result.proxied).toBe(true);
});
});
describe("toApiRecord — AAAA records", () => {
it("passes content through without data object", () => {
const result = toApiRecord(
rec({
type: "AAAA",
name: "example.com",
content: "2001:db8::1",
}),
);
expect(result.type).toBe("AAAA");
expect(result.content).toBe("2001:db8::1");
expect(result.data).toBeUndefined();
});
});
describe("toApiRecord — TXT records", () => {
it("wraps unquoted TXT content in double quotes", () => {
const result = toApiRecord(
rec({
type: "TXT",
name: "example.com",
content: "v=spf1 -all",
}),
);
expect(result.content).toBe('"v=spf1 -all"');
expect(result.data).toBeUndefined();
});
it("preserves already-quoted TXT content (re-quotes consistently)", () => {
const result = toApiRecord(
rec({
type: "TXT",
name: "example.com",
content: '"v=spf1 -all"',
}),
);
expect(result.content).toBe('"v=spf1 -all"');
});
it("escapes internal double quotes in TXT content", () => {
const result = toApiRecord(
rec({
type: "TXT",
name: "example.com",
content: 'v=spf1 "include:_spf"',
}),
);
expect(result.content).toBe('"v=spf1 \\"include:_spf\\""');
});
});
describe("toApiRecord — CNAME records", () => {
it("passes content through without data object", () => {
const result = toApiRecord(
rec({
type: "CNAME",
name: "www.example.com",
content: "example.com",
}),
);
expect(result.type).toBe("CNAME");
expect(result.content).toBe("example.com");
expect(result.data).toBeUndefined();
});
});
describe("toApiRecord — optional fields", () => {
it("includes priority (non-SVCB) when present", () => {
const result = toApiRecord(
rec({
type: "A",
name: "example.com",
content: "192.0.2.1",
priority: 10,
}),
);
expect(result.priority).toBe(10);
});
it("omits priority (non-SVCB) when absent", () => {
const result = toApiRecord(
rec({
type: "A",
name: "example.com",
content: "192.0.2.1",
}),
);
expect(result.priority).toBeUndefined();
});
it("includes ttl when present", () => {
const result = toApiRecord(
rec({
type: "A",
name: "example.com",
content: "192.0.2.1",
ttl: 3600,
}),
);
expect(result.ttl).toBe(3600);
});
it("omits ttl when absent", () => {
const result = toApiRecord(
rec({
type: "A",
name: "example.com",
content: "192.0.2.1",
}),
);
expect(result.ttl).toBeUndefined();
});
it("includes comment when present", () => {
const result = toApiRecord(
rec({
type: "A",
name: "example.com",
content: "192.0.2.1",
comment: "primary",
}),
);
expect(result.comment).toBe("primary");
});
it("omits comment when absent", () => {
const result = toApiRecord(
rec({
type: "A",
name: "example.com",
content: "192.0.2.1",
}),
);
expect(result.comment).toBeUndefined();
});
});
describe("toApiRecord — SVCB content parsing", () => {
// SVCB/HTTPS content is parsed into structured data (priority, target, value)
// for the Cloudflare API. Quoted values with spaces are a known parsing limitation.
it("parses quoted values with spaces (known limitation: split by whitespace)", () => {
const result = toApiRecord(
rec({
type: "SVCB",
name: "_dns.resolver.example.com",
content: '1 . alpn="h3 h2"',
}),
);
expect(result.data?.value).toBe('alpn="h3 h2"');
});
});
describe("RFC-0817: dns.record.upsert graceful skip", () => {
it("returns skip result when dns-records.yaml is absent", async () => {
const input = {
argv: [],
flags: { system: "test-system" },
} as unknown as KernelCommandInput;
const context = {
io: createDefaultIO().io,
workspaceRoot: "/test",
} as unknown as KernelRuntimeContext;
const result = await runDnsRecordUpsert(input, context);
expect(result.data?.summary.total).toBe(0);
expect(result.data?.results).toEqual([]);
expect(result.summary).toContain("skipped");
expect(result.summary).toContain("no dns-records.yaml");
});
});
describe("RECOVERY-2026-10-04: upsert best-fit live matching", () => {
function ctx(): KernelRuntimeContext {
return {
io: createDefaultIO().io,
workspaceRoot: "/test",
} as unknown as KernelRuntimeContext;
}
function input(): KernelCommandInput {
return { argv: [], flags: { system: "test-system" } } as unknown as KernelCommandInput;
}
const liveTxt = (id: string, content: string) => ({
id,
type: "TXT",
name: "example.com",
content,
proxied: false,
priority: null,
ttl: 1,
});
it("skips a TXT declaration that matches a non-first same-name live record", async () => {
vi.mocked(loadDnsRecordFile).mockResolvedValue({
zone: "example.com",
records: [
rec({ type: "TXT", name: "example.com", content: "v=spf1 -all" }),
rec({ type: "TXT", name: "example.com", content: "google-site-verification=abc" }),
],
} as never);
vi.mocked(listDnsRecords).mockResolvedValue([
liveTxt("r-spf", '"v=spf1 -all"'),
liveTxt("r-google", '"google-site-verification=abc"'),
] as never);
const result = await runDnsRecordUpsert(input(), ctx());
expect(result.data?.summary.errors).toBe(0);
expect(result.data?.summary.skipped).toBe(2);
expect(vi.mocked(createDnsRecord)).not.toHaveBeenCalled();
expect(vi.mocked(updateDnsRecord)).not.toHaveBeenCalled();
});
it("classifies Cloudflare 1046 (externally managed) as skipped, not error", async () => {
vi.mocked(loadDnsRecordFile).mockResolvedValue({
zone: "example.com",
records: [
rec({ type: "MX", name: "example.com", content: "route1.mx.cloudflare.net", priority: 92 }),
],
} as never);
vi.mocked(listDnsRecords).mockResolvedValue([
{
id: "r-mx",
type: "MX",
name: "example.com",
content: "route9.mx.cloudflare.net",
proxied: false,
priority: 11,
ttl: 1,
},
] as never);
vi.mocked(updateDnsRecord).mockRejectedValue(
new Error(
"[cloudflare-api] updateDnsRecord API errors: 1046: This record is managed by Email Routing.",
),
);
const result = await runDnsRecordUpsert(input(), ctx());
expect(result.data?.summary.errors).toBe(0);
expect(result.data?.summary.skipped).toBe(1);
});
});
describe("provisional workers.dev hosts", () => {
it("treats a workers.dev zone as not applicable without calling Cloudflare", async () => {
vi.mocked(loadDnsRecordFile).mockResolvedValue({
zone: "dp7.syrokomskyi.workers.dev",
records: [
rec({
type: "SVCB",
name: "_index._agents.dp7.syrokomskyi.workers.dev",
content: "1 dp7.syrokomskyi.workers.dev alpn=h2 port=443",
}),
],
} as never);
const result = await runDnsRecordUpsert(
{ argv: [], flags: { system: "dp7" } } as unknown as KernelCommandInput,
{ io: createDefaultIO().io, workspaceRoot: "/test" } as unknown as KernelRuntimeContext,
);
expect(result.exitCode).toBe(0);
expect(result.summary).toContain("not applicable");
expect(vi.mocked(createDnsRecord)).not.toHaveBeenCalled();
});
});