krz/domain-dig
an ios app for DNS & SSL analysis
clone: git clone https://gitbay.org/krz/domain-dig.git
v5.0.0: DomainDigTests/DiffServiceTests.swift · raw
1import XCTest
2@testable import DomainDig
3
4/// Characterization tests for the field-level diff engine. `DiffService` is a
5/// pure function over two `DomainReport`s, so every case here pins observable
6/// behavior (change classification, normalization, summary phrasing) against a
7/// deterministic fixture.
8final class DiffServiceTests: XCTestCase {
9
10 func testIdenticalReportsProduceNoChanges() {
11 let report = SnapshotFixture.report(
12 availability: .registered,
13 ownership: DomainOwnership(registrar: "Example Registrar")
14 )
15
16 let diff = DiffService.compare(from: report, to: report)
17
18 XCTAssertEqual(diff.changeCount, 0)
19 XCTAssertTrue(diff.changedSectionTitles.isEmpty)
20 XCTAssertFalse(diff.sections.contains { $0.hasChanges })
21 }
22
23 func testAvailabilityTransitionIsReportedAsChanged() {
24 let old = SnapshotFixture.report(availability: .available)
25 let new = SnapshotFixture.report(availability: .registered)
26
27 let diff = DiffService.compare(from: old, to: new)
28
29 let availability = try? XCTUnwrap(diff.sections.first { $0.id == "availability" })
30 let item = availability?.items.first { $0.id == "availability" }
31 XCTAssertEqual(item?.changeType, .changed)
32 XCTAssertEqual(item?.oldValue, "Available")
33 XCTAssertEqual(item?.newValue, "Registered")
34 XCTAssertTrue(diff.changedSectionTitles.contains("Domain / Availability"))
35 }
36
37 func testPrimaryIPChangeSurfacesInAvailabilitySection() {
38 let old = SnapshotFixture.report(
39 availability: .registered,
40 dnsSections: [SnapshotFixture.dnsSection(type: .A, values: ["203.0.113.10"])]
41 )
42 let new = SnapshotFixture.report(
43 availability: .registered,
44 dnsSections: [SnapshotFixture.dnsSection(type: .A, values: ["203.0.113.20"])]
45 )
46
47 let diff = DiffService.compare(from: old, to: new)
48 let item = diff.sections
49 .first { $0.id == "availability" }?
50 .items.first { $0.id == "primary-ip" }
51
52 XCTAssertEqual(item?.changeType, .changed)
53 XCTAssertEqual(item?.oldValue, "203.0.113.10")
54 XCTAssertEqual(item?.newValue, "203.0.113.20")
55 }
56
57 func testCaseAndWhitespaceDifferencesAreNotChanges() {
58 let old = SnapshotFixture.report(ownership: DomainOwnership(registrar: "GoDaddy"))
59 let new = SnapshotFixture.report(ownership: DomainOwnership(registrar: " godaddy "))
60
61 let diff = DiffService.compare(from: old, to: new)
62 let item = diff.sections
63 .first { $0.id == "ownership" }?
64 .items.first { $0.id == "registrar" }
65
66 XCTAssertEqual(item?.changeType, .unchanged, "case- and whitespace-only differences must not register as changes")
67 }
68
69 func testAddedAndRemovedOwnershipFields() {
70 let absent = SnapshotFixture.report(ownership: nil)
71 let present = SnapshotFixture.report(ownership: DomainOwnership(registrar: "Example Registrar"))
72
73 let added = DiffService.compare(from: absent, to: present)
74 .sections.first { $0.id == "ownership" }?
75 .items.first { $0.id == "registrar" }
76 XCTAssertEqual(added?.changeType, .added)
77 XCTAssertNil(added?.oldValue)
78 XCTAssertEqual(added?.newValue, "Example Registrar")
79
80 let removed = DiffService.compare(from: present, to: absent)
81 .sections.first { $0.id == "ownership" }?
82 .items.first { $0.id == "registrar" }
83 XCTAssertEqual(removed?.changeType, .removed)
84 XCTAssertEqual(removed?.oldValue, "Example Registrar")
85 XCTAssertNil(removed?.newValue)
86 }
87
88 func testDNSRecordValueChangeIsDetected() {
89 let old = SnapshotFixture.report(
90 dnsSections: [SnapshotFixture.dnsSection(type: .NS, values: ["ns1.example.com", "ns2.example.com"])]
91 )
92 let new = SnapshotFixture.report(
93 dnsSections: [SnapshotFixture.dnsSection(type: .NS, values: ["ns1.example.com", "ns3.example.com"])]
94 )
95
96 let dns = DiffService.compare(from: old, to: new).sections.first { $0.id == "dns" }
97 let records = dns?.items.first { $0.id == "dns-ns-records" }
98 XCTAssertEqual(records?.changeType, .changed)
99 }
100
101 func testDNSRecordReorderIsNotAChange() {
102 // Values are normalized (sorted, lowercased) before comparison, so a pure
103 // reorder must diff as unchanged.
104 let old = SnapshotFixture.report(
105 dnsSections: [SnapshotFixture.dnsSection(type: .NS, values: ["ns1.example.com", "ns2.example.com"])]
106 )
107 let new = SnapshotFixture.report(
108 dnsSections: [SnapshotFixture.dnsSection(type: .NS, values: ["NS2.example.com", "NS1.example.com"])]
109 )
110
111 let records = DiffService.compare(from: old, to: new)
112 .sections.first { $0.id == "dns" }?
113 .items.first { $0.id == "dns-ns-records" }
114 XCTAssertEqual(records?.changeType, .unchanged)
115 }
116
117 func testSummaryMessagePhrasing() {
118 XCTAssertEqual(DiffService.summaryMessage(from: [], changeCount: 0), "No meaningful changes")
119 XCTAssertEqual(DiffService.summaryMessage(from: ["DNS"], changeCount: 1), "DNS changed")
120 XCTAssertEqual(
121 DiffService.summaryMessage(from: ["DNS", "Ownership"], changeCount: 3),
122 "DNS and ownership changed (3 items)"
123 )
124 }
125
126 func testContextNoteFlagsDifferentResolvers() {
127 let old = SnapshotFixture.report(resolverURLString: "https://one.example/dns-query")
128 let new = SnapshotFixture.report(resolverURLString: "https://two.example/dns-query")
129
130 let note = DiffService.comparisonContextNote(from: old, to: new)
131 XCTAssertEqual(note, "Compared snapshots used different DNS resolvers.")
132 }
133
134 func testContextNoteNilWhenResolversMatch() {
135 let report = SnapshotFixture.report()
136 XCTAssertNil(DiffService.comparisonContextNote(from: report, to: report))
137 }
138
139 func testCertificateWarningLevelThresholds() {
140 func level(daysUntilExpiry days: Int?) -> CertificateWarningLevel {
141 let ssl = days.map { SnapshotFixture.certificate(daysUntilExpiry: $0) }
142 return DiffService.certificateWarningLevel(for: SnapshotFixture.snapshot(sslInfo: ssl))
143 }
144
145 XCTAssertEqual(level(daysUntilExpiry: nil), .none)
146 XCTAssertEqual(level(daysUntilExpiry: 45), .none)
147 XCTAssertEqual(level(daysUntilExpiry: 29), .warning)
148 XCTAssertEqual(level(daysUntilExpiry: 14), .warning)
149 XCTAssertEqual(level(daysUntilExpiry: 13), .critical)
150 XCTAssertEqual(level(daysUntilExpiry: 0), .critical)
151 }
152
153 func testCrossDomainComparisonPairsBothDomains() {
154 let a = SnapshotFixture.report(domain: "alpha.example", availability: .registered)
155 let b = SnapshotFixture.report(domain: "beta.example", availability: .available)
156
157 let result = DiffService.compare(domainA: a, domainB: b)
158
159 XCTAssertEqual(result.domainA, "alpha.example")
160 XCTAssertEqual(result.domainB, "beta.example")
161 XCTAssertTrue(result.changeCount > 0)
162 }
163}