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/DomainReportExporterTests.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/DomainReportExporterTests.swift')
| -rw-r--r-- | DomainDigTests/DomainReportExporterTests.swift | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/DomainDigTests/DomainReportExporterTests.swift b/DomainDigTests/DomainReportExporterTests.swift new file mode 100644 index 0000000..ddecabf --- /dev/null +++ b/DomainDigTests/DomainReportExporterTests.swift @@ -0,0 +1,107 @@ +import XCTest +@testable import DomainDig + +/// Characterization tests for the export surface. These pin the format dispatch, +/// the JSON round-trip (the canonical machine contract), and the structural +/// invariants of the human-readable formats. +final class DomainReportExporterTests: XCTestCase { + + private let decoder: JSONDecoder = { + let decoder = JSONDecoder() + decoder.dateDecodingStrategy = .iso8601 + return decoder + }() + + func testEveryFormatProducesNonEmptyData() throws { + let report = SnapshotFixture.report(availability: .registered) + for format in DomainExportFormat.allCases { + let data = try DomainReportExporter.data(for: report, format: format) + XCTAssertFalse(data.isEmpty, "\(format.rawValue) export was empty") + } + } + + func testJSONExportRoundTripsToReport() throws { + let report = SnapshotFixture.report(domain: "roundtrip.example", availability: .registered) + + let data = try DomainReportExporter.data(for: report, format: .json) + let decoded = try decoder.decode(DomainReport.self, from: data) + + XCTAssertEqual(decoded.domain, "roundtrip.example") + XCTAssertEqual(decoded.availability, .registered) + XCTAssertEqual(decoded.timestamp, report.timestamp) + } + + func testBatchJSONExportRoundTripsToReportArray() throws { + let reports = [ + SnapshotFixture.report(domain: "one.example"), + SnapshotFixture.report(domain: "two.example") + ] + + let data = try DomainReportExporter.data(for: reports, format: .json, title: "Batch") + let decoded = try decoder.decode([DomainReport].self, from: data) + + XCTAssertEqual(decoded.map(\.domain), ["one.example", "two.example"]) + } + + func testCSVHasHeaderAndOneRowPerReport() { + let reports = [ + SnapshotFixture.report(domain: "alpha.example"), + SnapshotFixture.report(domain: "beta.example") + ] + + let csv = DomainReportExporter.csv(for: reports) + let lines = csv.split(separator: "\n", omittingEmptySubsequences: false) + + XCTAssertEqual(lines.count, 3, "one header line plus one row per report") + XCTAssertTrue(lines[0].contains("\"domain\"")) + XCTAssertTrue(csv.contains("\"alpha.example\"")) + XCTAssertTrue(csv.contains("\"beta.example\"")) + } + + func testMarkdownIsHeadedAndNamesTheDomain() { + let markdown = DomainReportExporter.markdown(for: SnapshotFixture.report(domain: "md.example")) + + XCTAssertTrue(markdown.hasPrefix("# "), "markdown export should open with an H1") + XCTAssertTrue(markdown.contains("md.example")) + } + + func testTextExportNamesTheDomain() { + let text = DomainReportExporter.text(for: SnapshotFixture.report(domain: "txt.example")) + XCTAssertTrue(text.contains("txt.example")) + } + + func testBatchExportsCarryTitleAndEveryDomain() { + let reports = [ + SnapshotFixture.report(domain: "first.example"), + SnapshotFixture.report(domain: "second.example") + ] + + let markdown = DomainReportExporter.batchMarkdown(for: reports, title: "Portfolio Sweep") + XCTAssertTrue(markdown.contains("Portfolio Sweep")) + XCTAssertTrue(markdown.contains("first.example")) + XCTAssertTrue(markdown.contains("second.example")) + + let text = DomainReportExporter.batchText(for: reports, title: "Portfolio Sweep") + XCTAssertTrue(text.contains("Portfolio Sweep")) + XCTAssertTrue(text.contains("first.example")) + XCTAssertTrue(text.contains("second.example")) + } + + func testPDFExportHasPDFSignature() throws { + let data = try DomainReportExporter.data(for: SnapshotFixture.report(), format: .pdf) + XCTAssertTrue(data.starts(with: Array("%PDF".utf8)), "PDF export should begin with the %PDF signature") + } + + func testTimelineTextNamesTheDomain() { + let reports = [ + SnapshotFixture.report(domain: "timeline.example", timestamp: SnapshotFixture.referenceDate), + SnapshotFixture.report( + domain: "timeline.example", + timestamp: SnapshotFixture.referenceDate.addingTimeInterval(86_400) + ) + ] + + let text = DomainReportExporter.timelineText(for: reports, domain: "timeline.example", includeDiffSummary: false) + XCTAssertTrue(text.contains("timeline.example")) + } +} |
