Files
steipete--codexbar/Tests/CodexBarTests/CodexSystemPromotionUITests.swift
T
2026-07-13 12:22:33 +08:00

154 lines
6.6 KiB
Swift

import CodexBarCore
import Foundation
import Testing
@testable import CodexBar
@Suite(.serialized)
@MainActor
struct CodexSystemPromotionUITests {
@Test
func `promotion coordinator promotes immediately`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexSystemPromotionUITests-coordinator-immediate")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "managed@example.com",
authAccountID: "acct-managed")
try container.persistAccounts([target])
_ = try container.writeLiveOAuthAuthFile(email: "live@example.com", accountID: "acct-live")
let managedVisibleAccountID = try #require(container.settings.codexVisibleAccountProjection.visibleAccounts
.first(where: { $0.storedAccountID == target.id })?
.id)
let coordinator = CodexAccountPromotionCoordinator(service: container.makeService())
let result = await coordinator.promote(managedAccountID: target.id)
let promotionResult: CodexAccountPromotionResult
switch result {
case let .success(value):
promotionResult = value
case let .failure(error):
Issue.record("Expected successful promotion, got \(error)")
throw PromotionTestError.unexpectedDisposition
}
#expect(promotionResult.outcome == .promoted)
#expect(container.settings.codexActiveSource == .liveSystem)
#expect(container.settings.codexVisibleAccountProjection.liveVisibleAccountID == managedVisibleAccountID)
#expect(coordinator.userFacingError == nil)
}
@Test
func `promotion coordinator blocks while live reauthentication is running`() async throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexSystemPromotionUITests-coordinator-live-reauth")
defer { container.tearDown() }
let target = try container.createManagedAccount(
persistedEmail: "managed@example.com",
authAccountID: "acct-managed")
try container.persistAccounts([target])
_ = try container.writeLiveOAuthAuthFile(email: "live@example.com", accountID: "acct-live")
container.settings.codexActiveSource = .managedAccount(id: target.id)
let coordinator = CodexAccountPromotionCoordinator(service: container.makeService())
coordinator.setLiveReauthenticationInProgress(true)
let result = await coordinator.promote(managedAccountID: target.id)
let error: CodexSystemAccountPromotionUserFacingError
switch result {
case .success:
Issue.record("Expected blocked promotion while live reauthentication is running")
throw PromotionTestError.unexpectedDisposition
case let .failure(value):
error = value
}
#expect(error.title == "Could not switch system account")
#expect(error.message == "Finish the current managed account change before switching the system account.")
#expect(coordinator.userFacingError == error)
#expect(coordinator.isInteractionBlocked())
#expect(container.settings.codexActiveSource == .managedAccount(id: target.id))
}
@Test
func `codex menu descriptor includes system account submenu`() throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexSystemPromotionUITests-menu-descriptor")
defer { container.tearDown() }
let managedAccountID = UUID()
let managedAccount = try container.createManagedAccount(
id: managedAccountID,
persistedEmail: "managed@example.com",
authAccountID: "acct-managed")
try container.persistAccounts([managedAccount])
container.settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
email: "live@example.com",
codexHomePath: container.liveHomeURL.path,
observedAt: Date())
let descriptor = MenuDescriptor.build(
provider: .codex,
store: container.usageStore,
settings: container.settings,
account: UsageFetcher().loadAccountInfo(),
managedCodexAccountCoordinator: ManagedCodexAccountCoordinator(),
codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator(
service: container.makeService()),
updateReady: false)
let submenu = try #require(descriptor.sections
.flatMap(\.entries)
.compactMap { entry -> (String, String?, [MenuDescriptor.SubmenuItem])? in
guard case let .submenu(title, systemImageName, items) = entry else { return nil }
return (title, systemImageName, items)
}
.first(where: { $0.0 == "System Account" }))
#expect(submenu.1 == MenuDescriptor.MenuActionSystemImage.systemAccount.rawValue)
#expect(submenu.2.map(\.title) == ["live@example.com", "managed@example.com"])
#expect(submenu.2.count == 2)
#expect(submenu.2[0].isChecked)
#expect(submenu.2[0].isEnabled == false)
#expect(submenu.2[0].action == nil)
#expect(submenu.2[1].isChecked == false)
#expect(submenu.2[1].isEnabled)
#expect(submenu.2[1].action == .requestCodexSystemPromotion(managedAccountID))
}
@Test
func `codex menu descriptor hides single live system account submenu`() throws {
let container = try CodexAccountPromotionTestContainer(
suiteName: "CodexSystemPromotionUITests-menu-single-live")
defer { container.tearDown() }
container.settings._test_liveSystemCodexAccount = ObservedSystemCodexAccount(
email: "live@example.com",
codexHomePath: container.liveHomeURL.path,
observedAt: Date())
let descriptor = MenuDescriptor.build(
provider: .codex,
store: container.usageStore,
settings: container.settings,
account: UsageFetcher().loadAccountInfo(),
managedCodexAccountCoordinator: ManagedCodexAccountCoordinator(),
codexAccountPromotionCoordinator: CodexAccountPromotionCoordinator(
service: container.makeService()),
updateReady: false)
let hasSystemAccountSubmenu = descriptor.sections
.flatMap(\.entries)
.contains { entry in
guard case let .submenu(title, _, _) = entry else { return false }
return title == "System Account"
}
#expect(!hasSystemAccountSubmenu)
}
}