krz/domain-dig

an ios app for DNS & SSL analysis

clone: git clone https://gitbay.org/krz/domain-dig.git

v5.0.3: DomainDigTests/Fixtures/SnapshotFixture.swift · raw

  1import Foundation
  2@testable import DomainDig
  3
  4/// Deterministic builders for the deep `LookupSnapshot` / `DomainReport` models
  5/// so the core unit tests can construct inputs without wiring up every one of the
  6/// ~75 snapshot fields at each call site.
  7///
  8/// `snapshot(...)` exposes only the fields the tests actually vary; everything
  9/// else defaults to an empty/absent value. `report(...)` runs a snapshot through
 10/// the real `DomainReportBuilder`, which is both the natural constructor for the
 11/// otherwise-unconstructable `DomainReport` and, for the builder tests, the unit
 12/// under test.
 13enum SnapshotFixture {
 14    /// Fixed instant so timestamp-derived output (diff ranges, export headers) is
 15    /// stable across runs.
 16    static let referenceDate = Date(timeIntervalSince1970: 1_700_000_000)
 17
 18    static let defaultResolverURL = "https://resolver.example/dns-query"
 19
 20    static func snapshot(
 21        domain: String = "example.com",
 22        timestamp: Date = referenceDate,
 23        resolverURLString: String = defaultResolverURL,
 24        resultSource: LookupResultSource = .live,
 25        availability: DomainAvailabilityStatus? = nil,
 26        ownership: DomainOwnership? = nil,
 27        dnsSections: [DNSSection] = [],
 28        ptrRecord: String? = nil,
 29        sslInfo: SSLCertificateInfo? = nil,
 30        httpHeaders: [HTTPHeader] = [],
 31        httpStatusCode: Int? = nil,
 32        httpSecurityGrade: String? = nil,
 33        subdomains: [DiscoveredSubdomain] = [],
 34        extendedSubdomains: [DiscoveredSubdomain] = [],
 35        isPartialSnapshot: Bool = false,
 36        validationIssues: [String] = [],
 37        changeSummary: DomainChangeSummary? = nil
 38    ) -> LookupSnapshot {
 39        LookupSnapshot(
 40            historyEntryID: nil,
 41            domain: domain,
 42            timestamp: timestamp,
 43            trackedDomainID: nil,
 44            note: nil,
 45            appVersion: "test",
 46            resolverDisplayName: "Test Resolver",
 47            resolverURLString: resolverURLString,
 48            dataSources: [],
 49            provenanceBySection: [:],
 50            availabilityConfidence: nil,
 51            ownershipConfidence: nil,
 52            subdomainConfidence: nil,
 53            emailSecurityConfidence: nil,
 54            geolocationConfidence: nil,
 55            errorDetails: [:],
 56            isPartialSnapshot: isPartialSnapshot,
 57            validationIssues: validationIssues,
 58            totalLookupDurationMs: nil,
 59            snapshotIndex: nil,
 60            previousSnapshotID: nil,
 61            changeCount: 0,
 62            severitySummary: nil,
 63            dnsSections: dnsSections,
 64            dnsError: nil,
 65            availabilityResult: availability.map { DomainAvailabilityResult(domain: domain, status: $0) },
 66            suggestions: [],
 67            sslInfo: sslInfo,
 68            sslError: nil,
 69            hstsPreloaded: nil,
 70            httpHeaders: httpHeaders,
 71            httpSecurityGrade: httpSecurityGrade,
 72            httpStatusCode: httpStatusCode,
 73            httpResponseTimeMs: nil,
 74            httpProtocol: nil,
 75            http3Advertised: false,
 76            httpHeadersError: nil,
 77            reachabilityResults: [],
 78            reachabilityError: nil,
 79            ipGeolocation: nil,
 80            ipGeolocationError: nil,
 81            emailSecurity: nil,
 82            emailSecurityError: nil,
 83            ownership: ownership,
 84            ownershipError: nil,
 85            ownershipHistory: [],
 86            ownershipHistoryError: nil,
 87            inferredProvider: nil,
 88            priorProviders: [],
 89            domainClassification: nil,
 90            ownershipTransitions: [],
 91            hostingTransitions: [],
 92            subdomainHistory: [],
 93            riskSignals: [],
 94            intelligenceTimeline: [],
 95            ptrRecord: ptrRecord,
 96            ptrError: nil,
 97            redirectChain: [],
 98            redirectChainError: nil,
 99            subdomains: subdomains,
100            subdomainsError: nil,
101            extendedSubdomains: extendedSubdomains,
102            extendedSubdomainsError: nil,
103            dnsHistory: [],
104            dnsHistoryError: nil,
105            domainPricing: nil,
106            domainPricingError: nil,
107            reputation: nil,
108            reputationError: nil,
109            portScanResults: [],
110            portScanError: nil,
111            changeSummary: changeSummary,
112            resultSource: resultSource,
113            cachedSections: [],
114            statusMessage: nil
115        )
116    }
117
118    static func report(
119        domain: String = "example.com",
120        timestamp: Date = referenceDate,
121        resolverURLString: String = defaultResolverURL,
122        resultSource: LookupResultSource = .live,
123        availability: DomainAvailabilityStatus? = nil,
124        ownership: DomainOwnership? = nil,
125        dnsSections: [DNSSection] = [],
126        ptrRecord: String? = nil,
127        sslInfo: SSLCertificateInfo? = nil,
128        httpHeaders: [HTTPHeader] = [],
129        httpStatusCode: Int? = nil,
130        httpSecurityGrade: String? = nil,
131        subdomains: [DiscoveredSubdomain] = [],
132        extendedSubdomains: [DiscoveredSubdomain] = [],
133        isPartialSnapshot: Bool = false,
134        validationIssues: [String] = []
135    ) -> DomainReport {
136        DomainReportBuilder().build(
137            from: snapshot(
138                domain: domain,
139                timestamp: timestamp,
140                resolverURLString: resolverURLString,
141                resultSource: resultSource,
142                availability: availability,
143                ownership: ownership,
144                dnsSections: dnsSections,
145                ptrRecord: ptrRecord,
146                sslInfo: sslInfo,
147                httpHeaders: httpHeaders,
148                httpStatusCode: httpStatusCode,
149                httpSecurityGrade: httpSecurityGrade,
150                subdomains: subdomains,
151                extendedSubdomains: extendedSubdomains,
152                isPartialSnapshot: isPartialSnapshot,
153                validationIssues: validationIssues
154            ),
155            deriveChangeSummary: false
156        )
157    }
158
159    // MARK: - Nested model conveniences
160
161    static func dnsSection(
162        type: DNSRecordType,
163        values: [String],
164        ttl: Int = 300,
165        dnssecSigned: Bool? = nil,
166        wildcards: [String] = []
167    ) -> DNSSection {
168        DNSSection(
169            recordType: type,
170            records: values.map { DNSRecord(value: $0, ttl: ttl) },
171            wildcardRecords: wildcards.map { DNSRecord(value: $0, ttl: ttl) },
172            dnssecSigned: dnssecSigned
173        )
174    }
175
176    static func certificate(
177        commonName: String = "example.com",
178        issuer: String = "Test CA",
179        daysUntilExpiry: Int = 90
180    ) -> SSLCertificateInfo {
181        SSLCertificateInfo(
182            commonName: commonName,
183            subjectAltNames: [commonName],
184            issuer: issuer,
185            validFrom: referenceDate,
186            validUntil: referenceDate.addingTimeInterval(Double(daysUntilExpiry) * 86_400),
187            daysUntilExpiry: daysUntilExpiry,
188            chainDepth: 1
189        )
190    }
191}