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/Fixtures/SnapshotFixture.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/Fixtures/SnapshotFixture.swift')
| -rw-r--r-- | DomainDigTests/Fixtures/SnapshotFixture.swift | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/DomainDigTests/Fixtures/SnapshotFixture.swift b/DomainDigTests/Fixtures/SnapshotFixture.swift new file mode 100644 index 0000000..4129a30 --- /dev/null +++ b/DomainDigTests/Fixtures/SnapshotFixture.swift @@ -0,0 +1,191 @@ +import Foundation +@testable import DomainDig + +/// Deterministic builders for the deep `LookupSnapshot` / `DomainReport` models +/// so the core unit tests can construct inputs without wiring up every one of the +/// ~75 snapshot fields at each call site. +/// +/// `snapshot(...)` exposes only the fields the tests actually vary; everything +/// else defaults to an empty/absent value. `report(...)` runs a snapshot through +/// the real `DomainReportBuilder`, which is both the natural constructor for the +/// otherwise-unconstructable `DomainReport` and, for the builder tests, the unit +/// under test. +enum SnapshotFixture { + /// Fixed instant so timestamp-derived output (diff ranges, export headers) is + /// stable across runs. + static let referenceDate = Date(timeIntervalSince1970: 1_700_000_000) + + static let defaultResolverURL = "https://resolver.example/dns-query" + + static func snapshot( + domain: String = "example.com", + timestamp: Date = referenceDate, + resolverURLString: String = defaultResolverURL, + resultSource: LookupResultSource = .live, + availability: DomainAvailabilityStatus? = nil, + ownership: DomainOwnership? = nil, + dnsSections: [DNSSection] = [], + ptrRecord: String? = nil, + sslInfo: SSLCertificateInfo? = nil, + httpHeaders: [HTTPHeader] = [], + httpStatusCode: Int? = nil, + httpSecurityGrade: String? = nil, + subdomains: [DiscoveredSubdomain] = [], + extendedSubdomains: [DiscoveredSubdomain] = [], + isPartialSnapshot: Bool = false, + validationIssues: [String] = [], + changeSummary: DomainChangeSummary? = nil + ) -> LookupSnapshot { + LookupSnapshot( + historyEntryID: nil, + domain: domain, + timestamp: timestamp, + trackedDomainID: nil, + note: nil, + appVersion: "test", + resolverDisplayName: "Test Resolver", + resolverURLString: resolverURLString, + dataSources: [], + provenanceBySection: [:], + availabilityConfidence: nil, + ownershipConfidence: nil, + subdomainConfidence: nil, + emailSecurityConfidence: nil, + geolocationConfidence: nil, + errorDetails: [:], + isPartialSnapshot: isPartialSnapshot, + validationIssues: validationIssues, + totalLookupDurationMs: nil, + snapshotIndex: nil, + previousSnapshotID: nil, + changeCount: 0, + severitySummary: nil, + dnsSections: dnsSections, + dnsError: nil, + availabilityResult: availability.map { DomainAvailabilityResult(domain: domain, status: $0) }, + suggestions: [], + sslInfo: sslInfo, + sslError: nil, + hstsPreloaded: nil, + httpHeaders: httpHeaders, + httpSecurityGrade: httpSecurityGrade, + httpStatusCode: httpStatusCode, + httpResponseTimeMs: nil, + httpProtocol: nil, + http3Advertised: false, + httpHeadersError: nil, + reachabilityResults: [], + reachabilityError: nil, + ipGeolocation: nil, + ipGeolocationError: nil, + emailSecurity: nil, + emailSecurityError: nil, + ownership: ownership, + ownershipError: nil, + ownershipHistory: [], + ownershipHistoryError: nil, + inferredProvider: nil, + priorProviders: [], + domainClassification: nil, + ownershipTransitions: [], + hostingTransitions: [], + subdomainHistory: [], + riskSignals: [], + intelligenceTimeline: [], + ptrRecord: ptrRecord, + ptrError: nil, + redirectChain: [], + redirectChainError: nil, + subdomains: subdomains, + subdomainsError: nil, + extendedSubdomains: extendedSubdomains, + extendedSubdomainsError: nil, + dnsHistory: [], + dnsHistoryError: nil, + domainPricing: nil, + domainPricingError: nil, + reputation: nil, + reputationError: nil, + portScanResults: [], + portScanError: nil, + changeSummary: changeSummary, + resultSource: resultSource, + cachedSections: [], + statusMessage: nil + ) + } + + static func report( + domain: String = "example.com", + timestamp: Date = referenceDate, + resolverURLString: String = defaultResolverURL, + resultSource: LookupResultSource = .live, + availability: DomainAvailabilityStatus? = nil, + ownership: DomainOwnership? = nil, + dnsSections: [DNSSection] = [], + ptrRecord: String? = nil, + sslInfo: SSLCertificateInfo? = nil, + httpHeaders: [HTTPHeader] = [], + httpStatusCode: Int? = nil, + httpSecurityGrade: String? = nil, + subdomains: [DiscoveredSubdomain] = [], + extendedSubdomains: [DiscoveredSubdomain] = [], + isPartialSnapshot: Bool = false, + validationIssues: [String] = [] + ) -> DomainReport { + DomainReportBuilder().build( + from: snapshot( + domain: domain, + timestamp: timestamp, + resolverURLString: resolverURLString, + resultSource: resultSource, + availability: availability, + ownership: ownership, + dnsSections: dnsSections, + ptrRecord: ptrRecord, + sslInfo: sslInfo, + httpHeaders: httpHeaders, + httpStatusCode: httpStatusCode, + httpSecurityGrade: httpSecurityGrade, + subdomains: subdomains, + extendedSubdomains: extendedSubdomains, + isPartialSnapshot: isPartialSnapshot, + validationIssues: validationIssues + ), + deriveChangeSummary: false + ) + } + + // MARK: - Nested model conveniences + + static func dnsSection( + type: DNSRecordType, + values: [String], + ttl: Int = 300, + dnssecSigned: Bool? = nil, + wildcards: [String] = [] + ) -> DNSSection { + DNSSection( + recordType: type, + records: values.map { DNSRecord(value: $0, ttl: ttl) }, + wildcardRecords: wildcards.map { DNSRecord(value: $0, ttl: ttl) }, + dnssecSigned: dnssecSigned + ) + } + + static func certificate( + commonName: String = "example.com", + issuer: String = "Test CA", + daysUntilExpiry: Int = 90 + ) -> SSLCertificateInfo { + SSLCertificateInfo( + commonName: commonName, + subjectAltNames: [commonName], + issuer: issuer, + validFrom: referenceDate, + validUntil: referenceDate.addingTimeInterval(Double(daysUntilExpiry) * 86_400), + daysUntilExpiry: daysUntilExpiry, + chainDepth: 1 + ) + } +} |
