553 lines
24 KiB
Swift
553 lines
24 KiB
Swift
import AppKit
|
|
import CodexBarCore
|
|
import Foundation
|
|
import Observation
|
|
import Testing
|
|
@testable import CodexBar
|
|
|
|
struct ProviderStorageFootprintTests {
|
|
private final class ObservationFlag: @unchecked Sendable {
|
|
private let lock = NSLock()
|
|
private var value = false
|
|
|
|
func set() {
|
|
self.lock.lock()
|
|
self.value = true
|
|
self.lock.unlock()
|
|
}
|
|
|
|
func get() -> Bool {
|
|
self.lock.lock()
|
|
defer { self.lock.unlock() }
|
|
return self.value
|
|
}
|
|
}
|
|
|
|
@Test
|
|
func `scanner sums nested regular files and skips symlink targets`() throws {
|
|
let root = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: root) }
|
|
|
|
let nested = root.appendingPathComponent("nested", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: nested, withIntermediateDirectories: true)
|
|
try Data(repeating: 1, count: 5).write(to: root.appendingPathComponent("a.jsonl"))
|
|
try Data(repeating: 2, count: 7).write(to: nested.appendingPathComponent("b.jsonl"))
|
|
|
|
let external = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: external) }
|
|
let target = external.appendingPathComponent("outside.bin")
|
|
try Data(repeating: 3, count: 100).write(to: target)
|
|
let link = root.appendingPathComponent("linked.bin")
|
|
try FileManager.default.createSymbolicLink(at: link, withDestinationURL: target)
|
|
|
|
let footprint = ProviderStorageScanner().scan(provider: .codex, candidatePaths: [root.path])
|
|
|
|
#expect(footprint.totalBytes == 12)
|
|
#expect(footprint.paths == [root.path])
|
|
#expect(footprint.missingPaths.isEmpty)
|
|
#expect(footprint.components.map(\.name) == ["nested", "a.jsonl"])
|
|
#expect(footprint.components.map(\.totalBytes) == [7, 5])
|
|
}
|
|
|
|
@Test
|
|
func `scanner does not follow symlinked candidate roots`() throws {
|
|
let root = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: root) }
|
|
|
|
let target = root.appendingPathComponent("target", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: target, withIntermediateDirectories: true)
|
|
try Data(repeating: 1, count: 64).write(to: target.appendingPathComponent("session.jsonl"))
|
|
let symlinkRoot = root.appendingPathComponent("codex-link", isDirectory: true)
|
|
try FileManager.default.createSymbolicLink(at: symlinkRoot, withDestinationURL: target)
|
|
|
|
let footprint = ProviderStorageScanner().scan(provider: .codex, candidatePaths: [symlinkRoot.path])
|
|
|
|
#expect(footprint.paths == [symlinkRoot.path])
|
|
#expect(footprint.totalBytes == 0)
|
|
#expect(footprint.components.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
func `scanner records missing paths without failing`() throws {
|
|
let root = try Self.makeTemporaryDirectory()
|
|
let missing = root.appendingPathComponent("missing")
|
|
defer { try? FileManager.default.removeItem(at: root) }
|
|
|
|
let footprint = ProviderStorageScanner().scan(provider: .claude, candidatePaths: [missing.path])
|
|
|
|
#expect(footprint.totalBytes == 0)
|
|
#expect(footprint.paths.isEmpty)
|
|
#expect(footprint.missingPaths == [missing.path])
|
|
}
|
|
|
|
@Test
|
|
func `codex path catalog uses CODEX_HOME and managed homes`() {
|
|
let managed = ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "user@example.com",
|
|
managedHomePath: "/tmp/codex-managed-home",
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: nil)
|
|
|
|
let paths = ProviderStoragePathCatalog.candidatePaths(
|
|
for: .codex,
|
|
environment: ["CODEX_HOME": "/tmp/codex-home"],
|
|
managedCodexAccounts: [managed])
|
|
|
|
#expect(paths == ["/tmp/codex-home", "/tmp/codex-managed-home"])
|
|
}
|
|
|
|
@Test
|
|
func `codex path catalog falls back to default home`() {
|
|
let paths = ProviderStoragePathCatalog.candidatePaths(for: .codex, environment: [:])
|
|
let expected = FileManager.default.homeDirectoryForCurrentUser
|
|
.appendingPathComponent(".codex", isDirectory: true)
|
|
.path
|
|
|
|
#expect(paths.first == expected)
|
|
}
|
|
|
|
@Test
|
|
func `cursor path catalog includes application data and caches`() {
|
|
let home = FileManager.default.homeDirectoryForCurrentUser
|
|
let paths = ProviderStoragePathCatalog.candidatePaths(for: .cursor, environment: [:])
|
|
|
|
#expect(paths == [
|
|
home.appendingPathComponent("Library/Application Support/Cursor", isDirectory: true).path,
|
|
home.appendingPathComponent(
|
|
"Library/Application Support/Caches/cursor-updater",
|
|
isDirectory: true).path,
|
|
home.appendingPathComponent(".cursor", isDirectory: true).path,
|
|
home.appendingPathComponent("Library/Caches/Cursor", isDirectory: true).path,
|
|
home.appendingPathComponent("Library/Caches/com.todesktop.230313mzl4w4u92", isDirectory: true).path,
|
|
home.appendingPathComponent("Library/Caches/com.todesktop.230313mzl4w4u92.ShipIt", isDirectory: true).path,
|
|
home.appendingPathComponent("Library/Caches/cursor-compile-cache", isDirectory: true).path,
|
|
home.appendingPathComponent(
|
|
"Library/HTTPStorages/com.todesktop.230313mzl4w4u92",
|
|
isDirectory: true).path,
|
|
])
|
|
}
|
|
|
|
@Test
|
|
func `claude recommendations use documented cleanup categories`() {
|
|
let root = "/Users/test/.claude"
|
|
let footprint = ProviderStorageFootprint(
|
|
provider: .claude,
|
|
totalBytes: 28,
|
|
paths: [root],
|
|
missingPaths: [],
|
|
unreadablePaths: [],
|
|
components: [
|
|
.init(path: "\(root)/projects", totalBytes: 10),
|
|
.init(path: "\(root)/file-history", totalBytes: 8),
|
|
.init(path: "\(root)/paste-cache", totalBytes: 6),
|
|
.init(path: "\(root)/settings.json", totalBytes: 4),
|
|
],
|
|
updatedAt: Date(timeIntervalSince1970: 0))
|
|
|
|
let recommendations = footprint.cleanupRecommendations
|
|
|
|
#expect(recommendations.map(\.path) == [
|
|
"\(root)/projects",
|
|
"\(root)/file-history",
|
|
"\(root)/paste-cache",
|
|
])
|
|
#expect(recommendations[0].consequence.contains("resume"))
|
|
#expect(recommendations.allSatisfy { $0.riskLevel == .manualCleanup })
|
|
}
|
|
|
|
@Test
|
|
func `codex recommendations stay under known homes and exclude auth and config`() {
|
|
let root = "/Users/test/.codex"
|
|
let footprint = ProviderStorageFootprint(
|
|
provider: .codex,
|
|
totalBytes: 51,
|
|
paths: [root],
|
|
missingPaths: [],
|
|
unreadablePaths: [],
|
|
components: [
|
|
.init(path: "\(root)/sessions", totalBytes: 20),
|
|
.init(path: "\(root)/archived_sessions", totalBytes: 15),
|
|
.init(path: "\(root)/log", totalBytes: 12),
|
|
.init(path: "\(root)/logs_2.sqlite", totalBytes: 11),
|
|
.init(path: "\(root)/cache", totalBytes: 10),
|
|
.init(path: "\(root)/shell_snapshots", totalBytes: 9),
|
|
.init(path: "\(root)/auth.json", totalBytes: 4),
|
|
.init(path: "\(root)/config.toml", totalBytes: 2),
|
|
.init(path: "/tmp/outside/sessions", totalBytes: 99),
|
|
],
|
|
updatedAt: Date(timeIntervalSince1970: 0))
|
|
|
|
let recommendations = footprint.cleanupRecommendations
|
|
|
|
#expect(recommendations.map(\.path) == [
|
|
"\(root)/sessions",
|
|
"\(root)/archived_sessions",
|
|
"\(root)/cache",
|
|
"\(root)/log",
|
|
"\(root)/logs_2.sqlite",
|
|
"\(root)/shell_snapshots",
|
|
])
|
|
#expect(recommendations.map(\.bytes) == [20, 15, 10, 12, 11, 9])
|
|
}
|
|
|
|
@Test
|
|
func `unknown provider storage returns no cleanup recommendations`() {
|
|
let footprint = ProviderStorageFootprint(
|
|
provider: .gemini,
|
|
totalBytes: 10,
|
|
paths: ["/Users/test/.gemini"],
|
|
missingPaths: [],
|
|
unreadablePaths: [],
|
|
components: [
|
|
.init(path: "/Users/test/.gemini/cache", totalBytes: 10),
|
|
],
|
|
updatedAt: Date(timeIntervalSince1970: 0))
|
|
|
|
#expect(footprint.cleanupRecommendations.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `overview row carries storage text outside provider detail model`() throws {
|
|
let now = Date()
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let snapshot = UsageSnapshot(
|
|
primary: RateWindow(
|
|
usedPercent: 25,
|
|
windowMinutes: nil,
|
|
resetsAt: now.addingTimeInterval(3600),
|
|
resetDescription: nil),
|
|
secondary: nil,
|
|
updatedAt: now,
|
|
identity: nil)
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: snapshot,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: now))
|
|
|
|
let overview = OverviewMenuCardRowView(model: model, storageText: "1.5 GB", width: 310)
|
|
let detail = UsageMenuCardView(model: model, width: 310)
|
|
|
|
#expect(overview.storageText == "1.5 GB")
|
|
#expect(detail.model.provider == UsageProvider.claude)
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `storage detail view exposes cleanup recommendations while overview remains number only`() throws {
|
|
let root = "/Users/test/.claude"
|
|
let footprint = ProviderStorageFootprint(
|
|
provider: .claude,
|
|
totalBytes: 10,
|
|
paths: [root],
|
|
missingPaths: [],
|
|
unreadablePaths: [],
|
|
components: [
|
|
.init(path: "\(root)/projects", totalBytes: 10),
|
|
],
|
|
updatedAt: Date(timeIntervalSince1970: 0))
|
|
let detailView = StorageBreakdownMenuView(footprint: footprint, width: 310)
|
|
let metadata = try #require(ProviderDefaults.metadata[.claude])
|
|
let model = UsageMenuCardView.Model.make(.init(
|
|
provider: .claude,
|
|
metadata: metadata,
|
|
snapshot: nil,
|
|
credits: nil,
|
|
creditsError: nil,
|
|
dashboard: nil,
|
|
dashboardError: nil,
|
|
tokenSnapshot: nil,
|
|
tokenError: nil,
|
|
account: AccountInfo(email: nil, plan: nil),
|
|
isRefreshing: false,
|
|
lastError: nil,
|
|
usageBarsShowUsed: false,
|
|
resetTimeDisplayStyle: .countdown,
|
|
tokenCostUsageEnabled: false,
|
|
showOptionalCreditsAndExtraUsage: true,
|
|
hidePersonalInfo: false,
|
|
now: Date(timeIntervalSince1970: 0)))
|
|
let overview = OverviewMenuCardRowView(model: model, storageText: "10 B", width: 310)
|
|
|
|
#expect(detailView.cleanupRecommendations.map(\.path) == ["\(root)/projects"])
|
|
#expect(overview.storageText == "10 B")
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `storage detail view exposes copyable exact paths`() {
|
|
let root = "/Users/test/.claude"
|
|
let footprint = ProviderStorageFootprint(
|
|
provider: .claude,
|
|
totalBytes: 110,
|
|
paths: [root],
|
|
missingPaths: [],
|
|
unreadablePaths: [],
|
|
components: [
|
|
.init(path: "\(root)/projects", totalBytes: 100),
|
|
.init(path: "\(root)/file-history", totalBytes: 10),
|
|
],
|
|
updatedAt: Date(timeIntervalSince1970: 0))
|
|
let detailView = StorageBreakdownMenuView(footprint: footprint, width: 310)
|
|
|
|
#expect(detailView.copyablePaths.contains("\(root)/projects"))
|
|
#expect(detailView.copyablePaths.contains("\(root)/file-history"))
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `manual storage refresh updates deleted provider data`() async throws {
|
|
let home = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: home) }
|
|
|
|
let codexHome = home.appendingPathComponent(".codex", isDirectory: true)
|
|
let sessions = codexHome.appendingPathComponent("sessions", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: sessions, withIntermediateDirectories: true)
|
|
try Data(repeating: 1, count: 32).write(to: sessions.appendingPathComponent("session.jsonl"))
|
|
|
|
let suite = "ProviderStorageFootprintTests-storage-refresh-\(UUID().uuidString)"
|
|
let defaults = try #require(UserDefaults(suiteName: suite))
|
|
defaults.removePersistentDomain(forName: suite)
|
|
let settings = SettingsStore(
|
|
userDefaults: defaults,
|
|
configStore: testConfigStore(suiteName: suite),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
if let codexMetadata = ProviderDefaults.metadata[.codex] {
|
|
settings.setProviderEnabled(provider: .codex, metadata: codexMetadata, enabled: true)
|
|
}
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
environmentBase: ["CODEX_HOME": codexHome.path])
|
|
settings.providerStorageFootprintsEnabled = true
|
|
store.managedCodexAccountsForStorageOverride = []
|
|
|
|
await store.refreshStorageFootprintsForOverviewNow()
|
|
#expect(store.storageFootprint(for: .codex)?.totalBytes == 32)
|
|
|
|
try FileManager.default.removeItem(at: sessions)
|
|
await store.refreshStorageFootprintsForOverviewNow()
|
|
|
|
#expect(store.storageFootprint(for: .codex)?.totalBytes == 0)
|
|
#expect(store.storageFootprintText(for: .codex) == "No local data found")
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `repeated identical storage refresh does not republish observable footprints`() async throws {
|
|
let home = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: home) }
|
|
|
|
let codexHome = home.appendingPathComponent(".codex", isDirectory: true)
|
|
let sessions = codexHome.appendingPathComponent("sessions", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: sessions, withIntermediateDirectories: true)
|
|
try Data(repeating: 1, count: 32).write(to: sessions.appendingPathComponent("session.jsonl"))
|
|
|
|
let suite = "ProviderStorageFootprintTests-identity-\(UUID().uuidString)"
|
|
let defaults = try #require(UserDefaults(suiteName: suite))
|
|
defaults.removePersistentDomain(forName: suite)
|
|
let settings = SettingsStore(
|
|
userDefaults: defaults,
|
|
configStore: testConfigStore(suiteName: suite),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
if let codexMetadata = ProviderDefaults.metadata[.codex] {
|
|
settings.setProviderEnabled(provider: .codex, metadata: codexMetadata, enabled: true)
|
|
}
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
environmentBase: ["CODEX_HOME": codexHome.path])
|
|
settings.providerStorageFootprintsEnabled = true
|
|
store.managedCodexAccountsForStorageOverride = []
|
|
|
|
await store.refreshStorageFootprintsNow(for: [.codex])
|
|
#expect(store.storageFootprint(for: .codex)?.totalBytes == 32)
|
|
|
|
// A second scan over identical on-disk data must not re-assign the observable property.
|
|
// Storage scans run on every menu open and every ~5 min; an unconditional re-publish wakes
|
|
// the controller's `menuObservationToken` -> `invalidateMenus` path for no value change.
|
|
let didRepublish = ObservationFlag()
|
|
withObservationTracking {
|
|
_ = store.providerStorageFootprints
|
|
} onChange: {
|
|
didRepublish.set()
|
|
}
|
|
await store.refreshStorageFootprintsNow(for: [.codex])
|
|
try? await Task.sleep(for: .milliseconds(50))
|
|
|
|
#expect(didRepublish.get() == false)
|
|
#expect(store.storageFootprint(for: .codex)?.totalBytes == 32)
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `storage refresh is opt in and clears stale footprints when disabled`() async throws {
|
|
let home = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: home) }
|
|
|
|
let codexHome = home.appendingPathComponent(".codex", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: codexHome, withIntermediateDirectories: true)
|
|
try Data(repeating: 1, count: 16).write(to: codexHome.appendingPathComponent("session.jsonl"))
|
|
|
|
let suite = "ProviderStorageFootprintTests-storage-opt-in-\(UUID().uuidString)"
|
|
let defaults = try #require(UserDefaults(suiteName: suite))
|
|
defaults.removePersistentDomain(forName: suite)
|
|
let settings = SettingsStore(
|
|
userDefaults: defaults,
|
|
configStore: testConfigStore(suiteName: suite),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
if let codexMetadata = ProviderDefaults.metadata[.codex] {
|
|
settings.setProviderEnabled(provider: .codex, metadata: codexMetadata, enabled: true)
|
|
}
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
environmentBase: ["CODEX_HOME": codexHome.path])
|
|
store.managedCodexAccountsForStorageOverride = []
|
|
|
|
await store.refreshStorageFootprintsForOverviewNow()
|
|
#expect(store.storageFootprint(for: .codex) == nil)
|
|
|
|
settings.providerStorageFootprintsEnabled = true
|
|
await store.refreshStorageFootprintsForOverviewNow()
|
|
#expect(store.storageFootprint(for: .codex)?.totalBytes == 16)
|
|
|
|
settings.providerStorageFootprintsEnabled = false
|
|
await store.refreshStorageFootprintsForOverviewNow()
|
|
#expect(store.storageFootprint(for: .codex) == nil)
|
|
#expect(store.providerStorageFootprints.isEmpty)
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `forced scheduled storage refresh does not restart identical in flight scan`() throws {
|
|
let home = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: home) }
|
|
|
|
let codexHome = home.appendingPathComponent(".codex", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: codexHome, withIntermediateDirectories: true)
|
|
|
|
let suite = "ProviderStorageFootprintTests-storage-in-flight-\(UUID().uuidString)"
|
|
let defaults = try #require(UserDefaults(suiteName: suite))
|
|
defaults.removePersistentDomain(forName: suite)
|
|
let settings = SettingsStore(
|
|
userDefaults: defaults,
|
|
configStore: testConfigStore(suiteName: suite),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
environmentBase: ["CODEX_HOME": codexHome.path])
|
|
settings.providerStorageFootprintsEnabled = true
|
|
store.storageRefreshGeneration = 41
|
|
store.storageRefreshInFlightSignature = "codex=\(codexHome.path)"
|
|
store.storageRefreshTask = Task.detached {
|
|
try? await Task.sleep(for: .seconds(30))
|
|
}
|
|
defer {
|
|
store.storageRefreshTask?.cancel()
|
|
store.storageRefreshTask = nil
|
|
store.storageRefreshInFlightSignature = nil
|
|
}
|
|
|
|
store.scheduleStorageFootprintRefresh(for: [.codex], force: true)
|
|
|
|
#expect(store.storageRefreshGeneration == 41)
|
|
}
|
|
|
|
@Test
|
|
@MainActor
|
|
func `scheduled storage refresh notices managed Codex home changes`() async throws {
|
|
let home = try Self.makeTemporaryDirectory()
|
|
defer { try? FileManager.default.removeItem(at: home) }
|
|
|
|
let ambientHome = home.appendingPathComponent("ambient", isDirectory: true)
|
|
let firstManagedHome = home.appendingPathComponent("managed-a", isDirectory: true)
|
|
let secondManagedHome = home.appendingPathComponent("managed-b", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: ambientHome, withIntermediateDirectories: true)
|
|
try FileManager.default.createDirectory(at: firstManagedHome, withIntermediateDirectories: true)
|
|
try FileManager.default.createDirectory(at: secondManagedHome, withIntermediateDirectories: true)
|
|
try Data(repeating: 1, count: 16).write(to: firstManagedHome.appendingPathComponent("session.jsonl"))
|
|
try Data(repeating: 2, count: 32).write(to: secondManagedHome.appendingPathComponent("session.jsonl"))
|
|
|
|
let suite = "ProviderStorageFootprintTests-managed-refresh-\(UUID().uuidString)"
|
|
let defaults = try #require(UserDefaults(suiteName: suite))
|
|
defaults.removePersistentDomain(forName: suite)
|
|
let settings = SettingsStore(
|
|
userDefaults: defaults,
|
|
configStore: testConfigStore(suiteName: suite),
|
|
zaiTokenStore: NoopZaiTokenStore(),
|
|
syntheticTokenStore: NoopSyntheticTokenStore())
|
|
if let codexMetadata = ProviderDefaults.metadata[.codex] {
|
|
settings.setProviderEnabled(provider: .codex, metadata: codexMetadata, enabled: true)
|
|
}
|
|
let store = UsageStore(
|
|
fetcher: UsageFetcher(),
|
|
browserDetection: BrowserDetection(cacheTTL: 0),
|
|
settings: settings,
|
|
environmentBase: ["CODEX_HOME": ambientHome.path])
|
|
settings.providerStorageFootprintsEnabled = true
|
|
store.managedCodexAccountsForStorageOverride = [
|
|
Self.managedCodexAccount(homePath: firstManagedHome.path),
|
|
]
|
|
|
|
store.scheduleStorageFootprintRefresh(for: [.codex])
|
|
for _ in 0..<100 where store.isStorageRefreshInFlight {
|
|
try await Task.sleep(for: .milliseconds(10))
|
|
}
|
|
#expect(store.storageFootprint(for: .codex)?.totalBytes == 16)
|
|
|
|
store.managedCodexAccountsForStorageOverride = [
|
|
Self.managedCodexAccount(homePath: secondManagedHome.path),
|
|
]
|
|
store.scheduleStorageFootprintRefresh(for: [.codex])
|
|
for _ in 0..<100 where store.isStorageRefreshInFlight {
|
|
try await Task.sleep(for: .milliseconds(10))
|
|
}
|
|
|
|
#expect(store.storageFootprint(for: .codex)?.totalBytes == 32)
|
|
}
|
|
|
|
private static func managedCodexAccount(homePath: String) -> ManagedCodexAccount {
|
|
ManagedCodexAccount(
|
|
id: UUID(),
|
|
email: "storage@example.com",
|
|
managedHomePath: homePath,
|
|
createdAt: 1,
|
|
updatedAt: 1,
|
|
lastAuthenticatedAt: nil)
|
|
}
|
|
|
|
private static func makeTemporaryDirectory() throws -> URL {
|
|
let url = FileManager.default.temporaryDirectory
|
|
.appendingPathComponent("ProviderStorageFootprintTests-\(UUID().uuidString)", isDirectory: true)
|
|
try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
|
|
return url
|
|
}
|
|
}
|