diff options
| author | Christian Cleberg <[email protected]> | 2026-07-24 19:27:24 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-07-24 22:52:53 -0500 |
| commit | a7d154e1db9fd9904b2255db889144ad15aac2f2 (patch) | |
| tree | 36e9d636821952ca1a4a0b2d072065687abc1960 /DomainDigTests/DomainReportBuilderTests.swift | |
| parent | 22ad96331a4456662f740c48882cab8aadd6d0b4 (diff) | |
| download | domain-dig-a7d154e1db9fd9904b2255db889144ad15aac2f2.tar.gz domain-dig-a7d154e1db9fd9904b2255db889144ad15aac2f2.tar.bz2 domain-dig-a7d154e1db9fd9904b2255db889144ad15aac2f2.zip | |
test: establish unit-test net for the deterministic core (v5 step 1)
Stands up the DomainDigTests unit-test target the v5.0.0 roadmap flags as the
mandatory first move before decomposing the god-files and locking external
contracts. The project had no XCTest unit target — only the DomainDigUITests
accessibility suite.
- New DomainDigTests target (unit_test_bundle), hosted by the app so
@testable import DomainDig links. Mirrors the UITests build settings
(SWIFT_VERSION 5.0 + MainActor default isolation) to avoid the XCTest
override-isolation issue recorded for the Swift 6 targets. Wired into the
shared DomainDig scheme's Test action, so it runs in CI and the pre-push
audit hook automatically (both invoke the whole scheme).
- SnapshotFixture builds the deep LookupSnapshot/DomainReport models through
their real initializer and the DomainReportBuilder, exposing only the fields
the tests vary.
- 36 characterization tests across the four deterministic units the roadmap
names:
- DiffService: change classification, case/whitespace normalization, DNS
record reorder-vs-change, summary phrasing, resolver context note,
certificate-warning thresholds.
- DomainReportBuilder: snapshot -> report field mapping, primary-IP and
DNSSEC derivation, TLS status, partial-snapshot/validation passthrough.
- DomainReportExporter: format dispatch, JSON round-trip (the machine
contract), CSV/markdown/text/PDF structural invariants, timeline.
- DomainDataPortabilityService: CSV round-trip and the merge/dedup semantics
(case-insensitive collapse, OR-merged pin state, min-created/max-updated,
recency sort) via an ephemeral UserDefaults suite.
- build.yml comment updated to reflect the second test target.
Diffstat (limited to 'DomainDigTests/DomainReportBuilderTests.swift')
| -rw-r--r-- | DomainDigTests/DomainReportBuilderTests.swift | 130 |
1 files changed, 130 insertions, 0 deletions
diff --git a/DomainDigTests/DomainReportBuilderTests.swift b/DomainDigTests/DomainReportBuilderTests.swift new file mode 100644 index 0000000..7c8db15 --- /dev/null +++ b/DomainDigTests/DomainReportBuilderTests.swift @@ -0,0 +1,130 @@ +import XCTest +@testable import DomainDig + +/// Characterization tests for the snapshot → report projection. These lock the +/// field-mapping and derivation rules the export/diff contracts depend on. +final class DomainReportBuilderTests: XCTestCase { + private let builder = DomainReportBuilder() + + func testCoreIdentityFieldsPassThrough() { + let report = builder.build( + from: SnapshotFixture.snapshot( + domain: "mapped.example", + resolverURLString: "https://r.example/dns-query" + ), + deriveChangeSummary: false + ) + + XCTAssertEqual(report.domain, "mapped.example") + XCTAssertEqual(report.timestamp, SnapshotFixture.referenceDate) + XCTAssertEqual(report.resolverURLString, "https://r.example/dns-query") + XCTAssertFalse(report.metadata.schemaVersion.isEmpty) + } + + func testAvailabilityDefaultsToUnknownWhenAbsent() { + let unknown = builder.build(from: SnapshotFixture.snapshot(availability: nil), deriveChangeSummary: false) + XCTAssertEqual(unknown.availability, .unknown) + + let registered = builder.build(from: SnapshotFixture.snapshot(availability: .registered), deriveChangeSummary: false) + XCTAssertEqual(registered.availability, .registered) + } + + func testPrimaryIPComesFromFirstARecord() { + let report = builder.build( + from: SnapshotFixture.snapshot( + dnsSections: [ + SnapshotFixture.dnsSection(type: .A, values: ["198.51.100.7", "198.51.100.8"]), + SnapshotFixture.dnsSection(type: .AAAA, values: ["2001:db8::1"]) + ] + ), + deriveChangeSummary: false + ) + + XCTAssertEqual(report.dns.primaryIP, "198.51.100.7") + XCTAssertEqual(report.network.primaryIP, "198.51.100.7") + } + + func testPrimaryIPNilWithoutARecord() { + let report = builder.build( + from: SnapshotFixture.snapshot( + dnsSections: [SnapshotFixture.dnsSection(type: .MX, values: ["mail.example.com"])] + ), + deriveChangeSummary: false + ) + XCTAssertNil(report.dns.primaryIP) + } + + func testDNSSECDerivedFromSections() { + let signed = builder.build( + from: SnapshotFixture.snapshot( + dnsSections: [SnapshotFixture.dnsSection(type: .A, values: ["203.0.113.1"], dnssecSigned: true)] + ), + deriveChangeSummary: false + ) + XCTAssertEqual(signed.dns.dnssecSigned, true) + + let unknown = builder.build( + from: SnapshotFixture.snapshot( + dnsSections: [SnapshotFixture.dnsSection(type: .A, values: ["203.0.113.1"])] + ), + deriveChangeSummary: false + ) + XCTAssertNil(unknown.dns.dnssecSigned) + } + + func testTLSStatusReflectsCertificatePresence() { + let valid = builder.build( + from: SnapshotFixture.snapshot(sslInfo: SnapshotFixture.certificate()), + deriveChangeSummary: false + ) + XCTAssertEqual(valid.web.tlsStatus, "valid") + + let missing = builder.build(from: SnapshotFixture.snapshot(sslInfo: nil), deriveChangeSummary: false) + XCTAssertEqual(missing.web.tlsStatus, "unavailable") + } + + func testWebHeaderCountAndFinalURL() { + let report = builder.build( + from: SnapshotFixture.snapshot( + httpHeaders: [ + HTTPHeader(name: "Content-Type", value: "text/html"), + HTTPHeader(name: "Strict-Transport-Security", value: "max-age=63072000") + ], + httpStatusCode: 200 + ), + deriveChangeSummary: false + ) + + XCTAssertEqual(report.web.headerCount, 2) + XCTAssertEqual(report.web.statusCode, 200) + XCTAssertNil(report.web.finalURL, "no redirect chain means no final URL") + } + + func testPartialSnapshotAndValidationIssuesPropagate() { + let report = builder.build( + from: SnapshotFixture.snapshot( + isPartialSnapshot: true, + validationIssues: ["missing DNS", "stale WHOIS"] + ), + deriveChangeSummary: false + ) + + XCTAssertTrue(report.isPartialSnapshot) + XCTAssertEqual(report.validationIssues, ["missing DNS", "stale WHOIS"]) + XCTAssertTrue(report.metadata.isPartialSnapshot) + XCTAssertEqual(report.metadata.validationIssues, ["missing DNS", "stale WHOIS"]) + } + + func testRecordSectionsArePreserved() { + let sections = [ + SnapshotFixture.dnsSection(type: .A, values: ["203.0.113.1"]), + SnapshotFixture.dnsSection(type: .MX, values: ["mail.example.com"]) + ] + let report = builder.build( + from: SnapshotFixture.snapshot(dnsSections: sections), + deriveChangeSummary: false + ) + + XCTAssertEqual(Set(report.dns.recordSections.map(\.recordType)), [.A, .MX]) + } +} |
