import { describe, expect, it } from "vitest"; import type { ServiceBindingView } from "@cfdm/shared"; import { resolveBindingIpsForSync } from "../src/services/vps-tracker-sync.js"; function binding( partial: Partial & Pick, ): ServiceBindingView { return { domain_id: 1, service_id: 1, dns_record_id: null, group_id: null, group_name: null, service_name: "svc", service_slug: "svc", target_ip: null, target_ips: [], target_ip_weights: {}, target_ip_priorities: {}, cname_target: null, lb_mode: "off", health_check_enabled: false, health_check_type: "tcp", health_check_port: null, health_check_path: null, health_check_expected_status: null, health_check_interval_sec: 60, health_check_timeout_ms: 3000, health_check_verify_tls: true, sync_status: null, created_at: "", updated_at: "", ...partial, }; } describe("resolveBindingIpsForSync", () => { it("uses A-record IPs when present", async () => { const a = binding({ id: 1, hostname: "ihome", zone_name: "rkns.top", target_ips: ["10.0.0.5"], }); const index = { byFqdn: new Map([["ihome.rkns.top", a]]) }; const ips = await resolveBindingIpsForSync(a, ["9.9.9.9"], index); expect(ips).toEqual(["10.0.0.5"]); }); it("resolves CNAME via local binding chain to VPS IP", async () => { const target = binding({ id: 1, hostname: "ihome", zone_name: "rkns.top", target_ips: ["203.0.113.10"], }); const cname = binding({ id: 2, hostname: "imsk", zone_name: "rkns.top", cname_target: "ihome.rkns.top", target_ips: [], }); const index = { byFqdn: new Map([ ["ihome.rkns.top", target], ["imsk.rkns.top", cname], ]), }; const ips = await resolveBindingIpsForSync(cname, [], index); expect(ips).toEqual(["203.0.113.10"]); }); it("resolves short CNAME target relative to zone", async () => { const target = binding({ id: 1, hostname: "ihome", zone_name: "rkns.top", target_ips: ["203.0.113.11"], }); const cname = binding({ id: 2, hostname: "imsk", zone_name: "rkns.top", cname_target: "ihome", target_ips: [], }); const index = { byFqdn: new Map([ ["ihome.rkns.top", target], ["imsk.rkns.top", cname], ]), }; const ips = await resolveBindingIpsForSync(cname, [], index); expect(ips).toEqual(["203.0.113.11"]); }); it("falls back to service IPs when CNAME target unknown locally and DNS fails", async () => { const cname = binding({ id: 2, hostname: "imsk", zone_name: "rkns.top", cname_target: "definitely-not-resolvable-xyz.invalid", target_ips: [], }); const index = { byFqdn: new Map([["imsk.rkns.top", cname]]) }; const ips = await resolveBindingIpsForSync(cname, ["198.51.100.7"], index); expect(ips).toEqual(["198.51.100.7"]); }); it("does not treat CNAME hostname in target_ips as an IP", async () => { const target = binding({ id: 1, hostname: "ihome", zone_name: "rkns.top", target_ips: ["203.0.113.10"], }); const cname = binding({ id: 2, hostname: "imsk", zone_name: "rkns.top", cname_target: "ihome.rkns.top", // как в прод: JOIN dns_record кладёт CNAME content в target_ip → target_ips target_ips: ["ihome.rkns.top"], }); const index = { byFqdn: new Map([ ["ihome.rkns.top", target], ["imsk.rkns.top", cname], ]), }; const ips = await resolveBindingIpsForSync(cname, [], index); expect(ips).toEqual(["203.0.113.10"]); }); it("treats missing target_ips as empty instead of throwing", async () => { const cname = binding({ id: 2, hostname: "imsk", zone_name: "rkns.top", cname_target: "ihome.rkns.top", }); delete (cname as { target_ips?: string[] }).target_ips; const index = { byFqdn: new Map([["imsk.rkns.top", cname]]) }; const ips = await resolveBindingIpsForSync(cname, ["198.51.100.9"], index); expect(ips).toEqual(["198.51.100.9"]); }); it("prefers service IPs over empty CNAME resolution chain", async () => { const cname = binding({ id: 2, hostname: "mhome", zone_name: "rkns.top", cname_target: "macloud.rkns.top", target_ips: ["macloud.rkns.top"], }); const index = { byFqdn: new Map([["mhome.rkns.top", cname]]) }; const ips = await resolveBindingIpsForSync(cname, ["203.0.113.55"], index); expect(ips).toEqual(["203.0.113.55"]); }); });