diff --git a/Sources/Vexil/Sources/FlagValueDictionary.swift b/Sources/Vexil/Sources/FlagValueDictionary.swift index 74ec422c..0fc0171e 100644 --- a/Sources/Vexil/Sources/FlagValueDictionary.swift +++ b/Sources/Vexil/Sources/FlagValueDictionary.swift @@ -14,12 +14,12 @@ import Foundation /// A simple dictionary-backed FlagValueSource that can be useful for testing /// and other purposes. /// -open class FlagValueDictionary: Identifiable, ExpressibleByDictionaryLiteral { +open class FlagValueDictionary: Identifiable, ExpressibleByDictionaryLiteral, Codable { // MARK: - Properties /// A Unique Identifier for this FlagValueDictionary - public let id = UUID() + public let id: UUID /// Our internal dictionary type public typealias DictionaryType = [String: BoxedFlagValue] @@ -33,18 +33,43 @@ open class FlagValueDictionary: Identifiable, ExpressibleByDictionaryLiteral { // MARK: - Initialisation + /// Private (but for @testable) memeberwise initialiser + init (id: UUID, storage: DictionaryType) { + self.id = id + self.storage = storage + } + /// Initialises a `FlagValueDictionary` with the specified dictionary /// - public init (_ dictionary: DictionaryType = [:]) { - self.storage = dictionary + public convenience init (_ dictionary: DictionaryType = [:]) { + self.init( + id: UUID(), + storage: dictionary + ) } /// Initialises a `FlagValueDictionary` using a dictionary literal /// public required init(dictionaryLiteral elements: (String, BoxedFlagValue)...) { + self.id = UUID() self.storage = elements.reduce(into: [:]) { dict, pair in dict.updateValue(pair.1, forKey: pair.0) } } + // MARK: - Codable Support + + enum CodingKeys: String, CodingKey { + case id + case storage + } + +} + +// MARK: - Equatable Support + +extension FlagValueDictionary: Equatable { + public static func == (lhs: FlagValueDictionary, rhs: FlagValueDictionary) -> Bool { + return lhs.id == rhs.id && lhs.storage == rhs.storage + } } diff --git a/Tests/VexilTests/FlagValueDictionaryTests.swift b/Tests/VexilTests/FlagValueDictionaryTests.swift index 96cf4abc..a71227ca 100644 --- a/Tests/VexilTests/FlagValueDictionaryTests.swift +++ b/Tests/VexilTests/FlagValueDictionaryTests.swift @@ -5,6 +5,7 @@ // Created by Rob Amos on 17/8/20. // +import Foundation @testable import Vexil import XCTest @@ -25,19 +26,72 @@ final class FlagValueDictionaryTests: XCTestCase { // MARK: - Writing Values - func testWritesValues () { - AssertNoThrow { - let source = FlagValueDictionary() - let flagPole = FlagPole(hoist: TestFlags.self, sources: [ source ]) + func testWritesValues () throws { + let source = FlagValueDictionary() + let flagPole = FlagPole(hoist: TestFlags.self, sources: [ source ]) + + let snapshot = flagPole.emptySnapshot() + snapshot.topLevelFlag = true + snapshot.oneFlagGroup.secondLevelFlag = false + try flagPole.save(snapshot: snapshot, to: source) + + XCTAssertEqual(source.storage["top-level-flag"], .bool(true)) + XCTAssertEqual(source.storage["one-flag-group.second-level-flag"], .bool(false)) + } + + // MARK: - Equatable Tests + + func testEquatable() { + + let identifier1 = UUID() + let original = FlagValueDictionary( + id: identifier1, + storage: [ + "top-level-flag": .bool(true) + ] + ) + + let same = FlagValueDictionary( + id: identifier1, + storage: [ + "top-level-flag": .bool(true) + ] + ) + + let differentContent = FlagValueDictionary( + id: identifier1, + storage: [ + "top-level-flag": .bool(false) + ] + ) + + let differentIdentifier = FlagValueDictionary( + id: UUID(), + storage: [ + "top-level-flag": .bool(true) + ] + ) + + XCTAssertEqual(original, same) + XCTAssertNotEqual(original, differentContent) + XCTAssertNotEqual(original, differentIdentifier) + + } + + // MARK: - Codable Tests + + func testCodable() throws { + // BoxedFlagValue's Codable support is more heavily tested in it's tests + let source: FlagValueDictionary = [ + "bool-flag": .bool(true), + "string-flag": .string("alpha"), + "integer-flag": .integer(123) + ] - let snapshot = flagPole.emptySnapshot() - snapshot.topLevelFlag = true - snapshot.oneFlagGroup.secondLevelFlag = false - try flagPole.save(snapshot: snapshot, to: source) + let encoded = try JSONEncoder().encode(source) + let decoded = try JSONDecoder().decode(FlagValueDictionary.self, from: encoded) - XCTAssertEqual(source.storage["top-level-flag"], .bool(true)) - XCTAssertEqual(source.storage["one-flag-group.second-level-flag"], .bool(false)) - } + XCTAssertEqual(source, decoded) } @@ -89,7 +143,7 @@ private struct TestFlags: FlagContainer { var oneFlagGroup: OneFlags @Flag(description: "Top level test flag") - var topLevelFlag: Bool = false + var topLevelFlag = false }