242 lines · 9.1 KB · Swift
RawDownload
import CodexBarCoreimport Foundationimport Testing @Suite(.serialized)struct CodexActiveSourceConfigTests {    @Test    func `legacy config without codex active source decodes to nil`() throws {        let legacyJSON = """        {            "version": 1,            "providers": [                {                    "id": "codex"                }            ]        }        """         let decoded = try JSONDecoder().decode(            CodexBarConfig.self,            from: Data(legacyJSON.utf8))         #expect(decoded.providerConfig(for: .codex)?.codexActiveSource == nil)        #expect(decoded.providerConfig(for: .codex)?.quotaWarnings == nil)    }     @Test    func `provider config round trips quota warning overrides`() throws {        let config = CodexBarConfig(            providers: [                ProviderConfig(                    id: .codex,                    quotaWarnings: QuotaWarningConfig(                        session: QuotaWarningWindowConfig(thresholds: [10]),                        weekly: QuotaWarningWindowConfig(thresholds: [50, 20]))),            ])         let data = try JSONEncoder().encode(config)        let decoded = try JSONDecoder().decode(CodexBarConfig.self, from: data)        let quotaWarnings = try #require(decoded.providerConfig(for: .codex)?.quotaWarnings)         #expect(quotaWarnings.thresholds(for: .session, global: [80]) == [10])        #expect(quotaWarnings.thresholds(for: .weekly, global: [80]) == [50, 20])    }     @Test    func `quota warning window enabled defaults stay backward compatible`() throws {        let legacyJSON = """        {            "version": 1,            "providers": [                {                    "id": "codex",                    "quotaWarnings": {                        "session": { "thresholds": [10] },                        "weekly": { "enabled": false }                    }                }            ]        }        """         let decoded = try JSONDecoder().decode(CodexBarConfig.self, from: Data(legacyJSON.utf8))        let quotaWarnings = try #require(decoded.providerConfig(for: .codex)?.quotaWarnings)         #expect(quotaWarnings.isEnabled(for: .session, global: false) == true)        #expect(quotaWarnings.isEnabled(for: .weekly, global: true) == false)        #expect(quotaWarnings.hasOverride(for: .session) == true)        #expect(quotaWarnings.hasOverride(for: .weekly) == true)    }     @Test    func `provider config encodes live system active source with expected schema`() throws {        let config = CodexBarConfig(            providers: [                ProviderConfig(                    id: .codex,                    codexActiveSource: .liveSystem),            ])         let data = try JSONEncoder().encode(config)        let object = try JSONSerialization.jsonObject(with: data) as? [String: Any]        let providers = try #require(object?["providers"] as? [[String: Any]])        let provider = try #require(providers.first(where: { $0["id"] as? String == "codex" }))        let activeSource = try #require(provider["codexActiveSource"] as? [String: Any])         #expect(activeSource.count == 1)        #expect(activeSource["kind"] as? String == "liveSystem")        #expect(activeSource["accountID"] == nil)    }     @Test    func `provider config encodes managed account active source with expected schema`() throws {        let accountID = UUID()        let config = CodexBarConfig(            providers: [                ProviderConfig(                    id: .codex,                    codexActiveSource: .managedAccount(id: accountID)),            ])         let data = try JSONEncoder().encode(config)        let object = try JSONSerialization.jsonObject(with: data) as? [String: Any]        let providers = try #require(object?["providers"] as? [[String: Any]])        let provider = try #require(providers.first(where: { $0["id"] as? String == "codex" }))        let activeSource = try #require(provider["codexActiveSource"] as? [String: Any])         #expect(activeSource.count == 2)        #expect(activeSource["kind"] as? String == "managedAccount")        #expect((activeSource["accountID"] as? String) == accountID.uuidString)    }     @Test    func `provider config encodes profile home source in downgrade readable envelope`() throws {        let config = CodexBarConfig(            providers: [                ProviderConfig(                    id: .codex,                    codexActiveSource: .profileHome(path: "/Users/test/.codex-work"),                    codexProfileHomePaths: ["/Users/test/.codex-work"]),            ])         let data = try JSONEncoder().encode(config)        let object = try JSONSerialization.jsonObject(with: data) as? [String: Any]        let providers = try #require(object?["providers"] as? [[String: Any]])        let provider = try #require(providers.first(where: { $0["id"] as? String == "codex" }))        let activeSource = try #require(provider["codexActiveSource"] as? [String: Any])         #expect(activeSource.count == 2)        #expect(activeSource["kind"] as? String == "liveSystem")        #expect(activeSource["homePath"] as? String == "/Users/test/.codex-work")        #expect(provider["codexProfileHomePaths"] as? [String] == ["/Users/test/.codex-work"])         let releasedConfig = try JSONDecoder().decode(ReleasedCodexBarConfig.self, from: data)        #expect(releasedConfig.providers.first?.codexActiveSource == .liveSystem)    }     @Test    func `provider config round trips live system active source`() throws {        let config = CodexBarConfig(            providers: [                ProviderConfig(                    id: .codex,                    codexActiveSource: .liveSystem),            ])         let data = try JSONEncoder().encode(config)        let decoded = try JSONDecoder().decode(CodexBarConfig.self, from: data)         #expect(decoded.providerConfig(for: .codex)?.codexActiveSource == .liveSystem)    }     @Test    func `provider config round trips managed account active source`() throws {        let accountID = UUID()        let config = CodexBarConfig(            providers: [                ProviderConfig(                    id: .codex,                    codexActiveSource: .managedAccount(id: accountID)),            ])         let data = try JSONEncoder().encode(config)        let decoded = try JSONDecoder().decode(CodexBarConfig.self, from: data)         #expect(decoded.providerConfig(for: .codex)?.codexActiveSource == .managedAccount(id: accountID))    }     @Test    func `provider config round trips profile home active source`() throws {        let config = CodexBarConfig(            providers: [                ProviderConfig(                    id: .codex,                    codexActiveSource: .profileHome(path: "/Users/test/.codex-work"),                    codexProfileHomePaths: ["/Users/test/.codex-work"]),            ])         let data = try JSONEncoder().encode(config)        let decoded = try JSONDecoder().decode(CodexBarConfig.self, from: data)        let providerConfig = decoded.providerConfig(for: .codex)         #expect(providerConfig?.codexActiveSource == .profileHome(path: "/Users/test/.codex-work"))        #expect(providerConfig?.codexProfileHomePaths == ["/Users/test/.codex-work"])    }     @Test    func `profile home discriminator written by development builds still decodes`() throws {        let data = Data(#"{"kind":"profileHome","homePath":"/Users/test/.codex-work"}"#.utf8)         let decoded = try JSONDecoder().decode(CodexActiveSource.self, from: data)        let canonicalData = try JSONEncoder().encode(decoded)        let canonical = try #require(JSONSerialization.jsonObject(with: canonicalData) as? [String: Any])         #expect(decoded == .profileHome(path: "/Users/test/.codex-work"))        #expect(canonical["kind"] as? String == "liveSystem")        #expect(canonical["homePath"] as? String == "/Users/test/.codex-work")    }     @Test    func `blank profile home sentinel falls back to live system`() throws {        let data = Data(#"{"kind":"liveSystem","homePath":"  "}"#.utf8)         let decoded = try JSONDecoder().decode(CodexActiveSource.self, from: data)         #expect(decoded == .liveSystem)    }} private struct ReleasedCodexBarConfig: Decodable {    let providers: [ReleasedProviderConfig]} private struct ReleasedProviderConfig: Decodable {    let codexActiveSource: ReleasedCodexActiveSource?} private enum ReleasedCodexActiveSource: Decodable, Equatable {    case liveSystem    case managedAccount(id: UUID)     private enum CodingKeys: String, CodingKey {        case kind        case accountID    }     private enum Kind: String, Decodable {        case liveSystem        case managedAccount    }     init(from decoder: any Decoder) throws {        let container = try decoder.container(keyedBy: CodingKeys.self)        switch try container.decode(Kind.self, forKey: .kind) {        case .liveSystem:            self = .liveSystem        case .managedAccount:            self = try .managedAccount(id: container.decode(UUID.self, forKey: .accountID))        }    }}