Skip to content

Commit

Permalink
Add test to check migration of very invalid database data
Browse files Browse the repository at this point in the history
  • Loading branch information
aataraxiaa committed Jul 22, 2024
1 parent 0906b52 commit 3a06484
Show file tree
Hide file tree
Showing 3 changed files with 193 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -568,8 +568,8 @@ final class DataBrokerProtectionDatabaseMigrations {
}

private static func recreateTable(name: String,
database: Database,
creationActions: () throws -> Void) throws {
database: Database,
creationActions: () throws -> Void) throws {
try database.rename(table: name,
to: name + "Old")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,12 @@ final class DataBrokerProtectionDatabaseProviderTests: XCTestCase {
XCTAssertNoThrow(try DefaultDataBrokerProtectionDatabaseProvider(file: vaultURL, key: key, registerMigrationsHandler: Migrations.v3Migrations))
XCTAssertEqual(try sut.fetchAllScans().filter { $0.profileQueryId == 43 }.count, 50)
let allBrokerIds = try sut.fetchAllBrokers().map { $0.id! }
var allExtractedProfiles = try allBrokerIds.flatMap { try sut.fetchExtractedProfiles(for: $0, with:43) }
var allExtractedProfiles = try allBrokerIds.flatMap { try sut.fetchExtractedProfiles(for: $0, with: 43) }
let extractedProfileId = allExtractedProfiles.first!.id
var optOutAttempt = try sut.fetchAttemptInformation(for: extractedProfileId!)
var allOptOuts = try allBrokerIds.flatMap { try sut.fetchOptOuts(brokerId: $0, profileQueryId:43) }
var allScanHistoryEvents = try allBrokerIds.flatMap { try sut.fetchScanEvents(brokerId: $0, profileQueryId:43) }
var allOptOutHistoryEvents = try allBrokerIds.flatMap { try sut.fetchOptOutEvents(brokerId: $0, profileQueryId:43) }
var allOptOuts = try allBrokerIds.flatMap { try sut.fetchOptOuts(brokerId: $0, profileQueryId: 43) }
var allScanHistoryEvents = try allBrokerIds.flatMap { try sut.fetchScanEvents(brokerId: $0, profileQueryId: 43) }
var allOptOutHistoryEvents = try allBrokerIds.flatMap { try sut.fetchOptOutEvents(brokerId: $0, profileQueryId: 43) }
XCTAssertNotNil(optOutAttempt)
XCTAssertEqual(allExtractedProfiles.count, 1)
XCTAssertEqual(allOptOuts.count, 1)
Expand All @@ -94,35 +94,87 @@ final class DataBrokerProtectionDatabaseProviderTests: XCTestCase {

// Then
XCTAssertEqual(try sut.fetchAllScans().filter { $0.profileQueryId == 43 }.count, 0)
allExtractedProfiles = try allBrokerIds.flatMap { try sut.fetchExtractedProfiles(for: $0, with:43) }
allExtractedProfiles = try allBrokerIds.flatMap { try sut.fetchExtractedProfiles(for: $0, with: 43) }
optOutAttempt = try sut.fetchAttemptInformation(for: extractedProfileId!)
allOptOuts = try allBrokerIds.flatMap { try sut.fetchOptOuts(brokerId: $0, profileQueryId:43) }
allScanHistoryEvents = try allBrokerIds.flatMap { try sut.fetchScanEvents(brokerId: $0, profileQueryId:43) }
allOptOutHistoryEvents = try allBrokerIds.flatMap { try sut.fetchOptOutEvents(brokerId: $0, profileQueryId:43) }
allOptOuts = try allBrokerIds.flatMap { try sut.fetchOptOuts(brokerId: $0, profileQueryId: 43) }
allScanHistoryEvents = try allBrokerIds.flatMap { try sut.fetchScanEvents(brokerId: $0, profileQueryId: 43) }
allOptOutHistoryEvents = try allBrokerIds.flatMap { try sut.fetchOptOutEvents(brokerId: $0, profileQueryId: 43) }
XCTAssertNil(optOutAttempt)
XCTAssertEqual(allExtractedProfiles.count, 0)
XCTAssertEqual(allOptOuts.count, 0)
XCTAssertEqual(allScanHistoryEvents.count, 0)
XCTAssertEqual(allOptOutHistoryEvents.count, 0)
}

func testV3MigrationOfDatabaseWithLotsOfIntegrityIssues() throws {
// Given
do {
try sut.db.writeWithoutTransaction { db in
try db.execute(sql: "PRAGMA foreign_keys = OFF")
}

let profileQueries = ProfileQueryDB.random(withProfileIds: Int64.randomValues())
for query in profileQueries {
_ = try sut.save(query)
}

for broker in BrokerDB.random(count: 10) {
_ = try sut.save(broker)
}

let brokerIds = Int64.randomValues()
let profileQueryIds = Int64.randomValues()
let extractedProfileIds = Int64.randomValues()

for scanHistoryEvent in ScanHistoryEventDB.random(withBrokerIds: brokerIds, profileQueryIds: profileQueryIds) {
_ = try sut.save(scanHistoryEvent)
}

for optOutHistoryEvent in OptOutHistoryEventDB.random(withBrokerIds: brokerIds, profileQueryIds: profileQueryIds, extractedProfileIds: extractedProfileIds) {
_ = try sut.save(optOutHistoryEvent)
}

for extractedProfile in ExtractedProfileDB.random(withBrokerIds: brokerIds, profileQueryIds: profileQueryIds) {
_ = try sut.save(extractedProfile)
}

try sut.db.writeWithoutTransaction { db in
try db.execute(sql: "PRAGMA foreign_keys = ON")
}

} catch {
XCTFail("Failed to setup invalid data")
}

let failingMigration: (inout DatabaseMigrator) throws -> Void = { migrator in
migrator.registerMigration("v3") { database in
try database.checkForeignKeys()
}
}

let passingMigration: (inout DatabaseMigrator) throws -> Void = { migrator in
migrator.registerMigration("v4") { database in
try database.checkForeignKeys()
}
}

XCTAssertThrowsError(try DefaultDataBrokerProtectionDatabaseProvider(file: vaultURL, key: key, registerMigrationsHandler: failingMigration))

// When
XCTAssertNoThrow(try DefaultDataBrokerProtectionDatabaseProvider(file: vaultURL, key: key, registerMigrationsHandler: Migrations.v3Migrations))

// Then
XCTAssertNoThrow(try DefaultDataBrokerProtectionDatabaseProvider(file: vaultURL, key: key, registerMigrationsHandler: passingMigration))
}

func testDeleteAllDataSucceedsInRemovingAllData() throws {
XCTAssertFalse(try sut.db.allTablesAreEmpty())
XCTAssertNoThrow(try sut.deleteProfileData())
XCTAssertTrue(try sut.db.allTablesAreEmpty())
}
}

extension DataBrokerProtectionDatabaseProvider {
func fetchAllProfiles() throws -> [ProfileDB] {
return try db.read { db in
let profiles = try ProfileDB.fetchAll(db, sql: "SELECT * FROM profile")
return profiles
}
}
}

extension DatabaseWriter {
private extension DatabaseWriter {

func allTablesAreEmpty() throws -> Bool {
return try self.read { db in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1608,3 +1608,124 @@ final class MockActionsHandler: ActionsHandler {
return nil
}
}

private extension String {
static func random(length: Int) -> String {
let characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<length).map { _ in characters.randomElement()! })
}
}

private extension Int {
static func randomBirthdate() -> Int {
Int.random(in: 1960...2000)
}
}

extension Int64 {
static func randomValues(ofLength length: Int = 20, start: Int64 = 1001, end: Int64 = 2000) -> [Int64] {
[0..<length].map { _ in
Int64.random(in: start..<end)
}
}
}

private extension Data {
static func randomStringData(length: Int) -> Data {
String.random(length: length).data(using: .utf8)!
}

static func randomBirthdateData() -> Data {
String(Int.randomBirthdate()).data(using: .utf8)!
}

static func randomEventData(length: Int) -> Data {
return .randomStringData(length: length)
}
}

extension Date {
static func random() -> Date {
let currentTime = Date().timeIntervalSince1970
let randomTimeInterval = TimeInterval.random(in: 0..<currentTime)
return Date(timeIntervalSince1970: randomTimeInterval)
}
}

extension ProfileQueryDB {
static func random(withProfileIds profileIds: [Int64]) -> [ProfileQueryDB] {
profileIds.map {
ProfileQueryDB(id: nil, profileId: $0,
first: .randomStringData(length: 4),
last: .randomStringData(length: 4),
middle: nil,
suffix: nil,
city: .randomStringData(length: 4),
state: .randomStringData(length: 4), street: .randomStringData(length: 4),
zipCode: nil,
phone: nil,
birthYear: Data.randomBirthdateData(),
deprecated: Bool.random())
}
}
}

extension BrokerDB {
static func random(count: Int) -> [BrokerDB] {
[0..<count].map {
BrokerDB(id: nil, name: .random(length: 4),
json: try! JSONSerialization.data(withJSONObject: [:], options: []),
version: "\($0).\($0).\($0)",
url: "www.testbroker.com")
}
}
}

extension ScanHistoryEventDB {
static func random(withBrokerIds brokerIds: [Int64], profileQueryIds: [Int64]) -> [ScanHistoryEventDB] {
brokerIds.flatMap { brokerId in
profileQueryIds.map { profileQueryId in
ScanHistoryEventDB(
brokerId: brokerId,
profileQueryId: profileQueryId,
event: .randomEventData(length: 8),
timestamp: .random()
)
}
}
}
}

extension OptOutHistoryEventDB {
static func random(withBrokerIds brokerIds: [Int64], profileQueryIds: [Int64], extractedProfileIds: [Int64]) -> [OptOutHistoryEventDB] {
brokerIds.flatMap { brokerId in
profileQueryIds.flatMap { profileQueryId in
extractedProfileIds.map { extractedProfileId in
OptOutHistoryEventDB(
brokerId: brokerId,
profileQueryId: profileQueryId,
extractedProfileId: extractedProfileId,
event: .randomEventData(length: 8),
timestamp: .random()
)
}
}
}
}
}

extension ExtractedProfileDB {
static func random(withBrokerIds brokerIds: [Int64], profileQueryIds: [Int64]) -> [ExtractedProfileDB] {
brokerIds.flatMap { brokerId in
profileQueryIds.map { profileQueryId in
ExtractedProfileDB(
id: nil,
brokerId: brokerId,
profileQueryId: profileQueryId,
profile: .randomEventData(length: 50),
removedDate: Bool.random() ? .random() : nil
)
}
}
}
}

0 comments on commit 3a06484

Please sign in to comment.