TM-SGNL-iOS/SignalServiceKit/tests/Storage/Database/SDSCodableModel/FactoryInitializationTests.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

160 lines
5.4 KiB
Swift

//
// Copyright 2023 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
@testable import SignalServiceKit
import XCTest
class FactoryInitializationTests: XCTestCase {
func testFactoryInitialization_SuccessFromHardcodedData() throws {
class BaseClass: NeedsFactoryInitializationFromRecordType {
enum CodingKeys: CodingKey { case recordType; case base; case foo; case bar }
static var recordTypeCodingKey: CodingKeys { .recordType }
static func classToInitialize(
forRecordType recordType: UInt
) -> (FactoryInitializableFromRecordType.Type)? {
switch recordType {
case 1:
return FooClass.self
case 2:
return BarClass.self
default:
return nil
}
}
let base: String
init(baseClassFromDecoder decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
base = try container.decode(String.self, forKey: .base)
}
}
class FooClass: BaseClass, FactoryInitializableFromRecordType {
static var recordType: UInt { 1 }
let foo: String
required init(forRecordTypeFactoryInitializationFrom decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
foo = try container.decode(String.self, forKey: .foo)
try super.init(baseClassFromDecoder: container.superDecoder())
}
}
class BarClass: BaseClass, FactoryInitializableFromRecordType {
static var recordType: UInt { 2 }
let bar: String
required init(forRecordTypeFactoryInitializationFrom decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
bar = try container.decode(String.self, forKey: .bar)
try super.init(baseClassFromDecoder: container.superDecoder())
}
}
let fooJsonData: Data = """
{
"super": {
"recordType": 1,
"base": "spaceship"
},
"foo": "millenium falcon"
}
""".data(using: .utf8)!
let barJsonData: Data = """
{
"super": {
"recordType": 2,
"base": "crystal"
},
"bar": "kyber"
}
""".data(using: .utf8)!
let fooInstance = try JSONDecoder().decode(BaseClass.self, from: fooJsonData)
let barInstance = try JSONDecoder().decode(BaseClass.self, from: barJsonData)
guard let foo = fooInstance as? FooClass else {
XCTFail("Failed to cast foo instance!")
return
}
guard let bar = barInstance as? BarClass else {
XCTFail("Failed to cast bar instance!")
return
}
XCTAssertEqual(foo.base, "spaceship")
XCTAssertEqual(foo.foo, "millenium falcon")
XCTAssertEqual(bar.base, "crystal")
XCTAssertEqual(bar.bar, "kyber")
}
func testFactoryInitialization_ThrowsForBadRecordType() throws {
let invalidJsonData: Data = #"{ "super": { "recordType": 3 } }"#.data(using: .utf8)!
let fooJsonData: Data = #"{ "super": { "recordType": 1 } }"#.data(using: .utf8)!
let barJsonData: Data = #"{ "super": { "recordType": 2 } }"#.data(using: .utf8)!
func decodeAndCatchDecodingError(fromData data: Data) throws {
do {
_ = try JSONDecoder().decode(BaseClassThatMisinterpretsRecordTypes.self, from: data)
XCTFail("Should have thrown while decoding!")
} catch let DecodingError.dataCorrupted(context) {
XCTAssertEqual(
(context.codingPath.first! as! BaseClassThatMisinterpretsRecordTypes.CodingKeys),
.recordType
)
}
}
try decodeAndCatchDecodingError(fromData: invalidJsonData)
try decodeAndCatchDecodingError(fromData: fooJsonData)
try decodeAndCatchDecodingError(fromData: barJsonData)
}
}
private class BaseClassThatMisinterpretsRecordTypes: NeedsFactoryInitializationFromRecordType {
enum CodingKeys: CodingKey { case recordType }
static var recordTypeCodingKey: CodingKeys { .recordType }
static func classToInitialize(
forRecordType recordType: UInt
) -> (FactoryInitializableFromRecordType.Type)? {
switch recordType {
case 1:
return BarClass.self
case 2:
return FooClass.self
default:
return nil
}
}
}
private class FooClass: BaseClassThatMisinterpretsRecordTypes, FactoryInitializableFromRecordType {
static var recordType: UInt { 1 }
required init(forRecordTypeFactoryInitializationFrom decoder: Decoder) throws {
XCTFail("Initializer should never have been called!")
fatalError("")
}
}
private class BarClass: BaseClassThatMisinterpretsRecordTypes, FactoryInitializableFromRecordType {
static var recordType: UInt { 2 }
required init(forRecordTypeFactoryInitializationFrom decoder: Decoder) throws {
XCTFail("Initializer should never have been called!")
fatalError("")
}
}