native macOS codings agent orchestrator prowl.onev.cat
Something went wrong. Try again.
14 kB · 372 lines
Swift
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373import ComposableArchitectureimport DependenciesTestSupportimport Foundationimport Testing
@testable import supacode
@MainActorstruct RepositorySettingsFeatureTests { @Test func githubAccountOverrideRoundTripsThroughRepositorySettings() throws { var settings = RepositorySettings.default settings.githubAccountOverride = GithubAccountOverride(host: "github.com", login: "work")
let data = try JSONEncoder().encode(settings) let decoded = try JSONDecoder().decode(RepositorySettings.self, from: data)
#expect( decoded.githubAccountOverride == GithubAccountOverride(host: "github.com", login: "work")) }
@Test func workflowsUseTheOwningRepositoryScope() async { let rootURL = URL(fileURLWithPath: "/tmp/workflow-repository") var settings = RepositorySettings.default settings.customTitle = "Workflow Repo" let store = TestStore( initialState: RepositorySettingsFeature.State( rootURL: rootURL, repositoryID: "repo-id", repositoryKind: .git, settings: settings, userSettings: .default) ) { RepositorySettingsFeature() }
await store.send(.workflowsAppeared) { $0.workflows.settingsScope = .repository( WorkflowSettingsRepositoryContext( repositoryID: "repo-id", name: "Workflow Repo", rootURL: rootURL)) } }
@Test(.dependencies) func plainFolderTaskLoadsWithoutGitRequests() async throws { let rootURL = URL(fileURLWithPath: "/tmp/folder-\(UUID().uuidString)") let settingsStorage = SettingsTestStorage() let localStorage = RepositoryLocalSettingsTestStorage() let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json") let expectedDefaultWorktreeBaseDirectoryPath = SupacodePaths.normalizedWorktreeBaseDirectoryPath("/tmp/worktrees") let storedSettings = RepositorySettings( setupScript: "echo setup", archiveScript: "echo archive", runScript: "npm run dev", openActionID: OpenWorktreeAction.automaticSettingsID, worktreeBaseRef: "origin/main", copyIgnoredOnWorktreeCreate: true, copyUntrackedOnWorktreeCreate: true, pullRequestMergeStrategy: .squash, ) let storedOnevcatSettings = UserRepositorySettings( customCommands: [.default(index: 0)] ) let repositoryID = rootURL.standardizedFileURL.path(percentEncoded: false) let bareRepositoryRequests = LockIsolated(0) let branchRefRequests = LockIsolated(0) let automaticBaseRefRequests = LockIsolated(0) var settingsFile = SettingsFile.default settingsFile.global.defaultWorktreeBaseDirectoryPath = "/tmp/worktrees" settingsFile.repositories[repositoryID] = storedSettings let settingsData = try #require(try? JSONEncoder().encode(settingsFile)) try #require(try? settingsStorage.storage.save(settingsData, settingsFileURL))
let userSettingsData = try #require(try? JSONEncoder().encode(storedOnevcatSettings)) try #require( try? localStorage.save( userSettingsData, at: SupacodePaths.userRepositorySettingsURL(for: rootURL) ) )
let store = TestStore( initialState: RepositorySettingsFeature.State( rootURL: rootURL, repositoryKind: .plain, settings: .default, userSettings: .default ) ) { RepositorySettingsFeature() } withDependencies: { $0.settingsFileStorage = settingsStorage.storage $0.settingsFileURL = settingsFileURL $0.repositoryLocalSettingsStorage = localStorage.storage $0.gitClient.isBareRepository = { _ in bareRepositoryRequests.withValue { $0 += 1 } return false } $0.gitClient.branchRefs = { _ in branchRefRequests.withValue { $0 += 1 } return [] } $0.gitClient.automaticWorktreeBaseRef = { _ in automaticBaseRefRequests.withValue { $0 += 1 } return "origin/main" } }
await store.send(.task) await store.receive(\.settingsLoaded, timeout: .seconds(5)) { $0.settings = storedSettings $0.userSettings = storedOnevcatSettings $0.globalDefaultWorktreeBaseDirectoryPath = expectedDefaultWorktreeBaseDirectoryPath } await store.finish(timeout: .seconds(5))
#expect(store.state.isBranchDataLoaded == false) #expect(store.state.branchOptions.isEmpty) #expect(bareRepositoryRequests.value == 0) #expect(branchRefRequests.value == 0) #expect(automaticBaseRefRequests.value == 0) }
@Test func plainFolderVisibilityHidesGitOnlySections() { let state = RepositorySettingsFeature.State( rootURL: URL(fileURLWithPath: "/tmp/folder"), repositoryKind: .plain, settings: .default, userSettings: .default )
#expect(state.showsWorktreeSettings == false) #expect(state.showsPullRequestSettings == false) #expect(state.showsSetupScriptSettings == false) #expect(state.showsArchiveScriptSettings == false) #expect(state.showsRunScriptSettings == true) #expect(state.showsCustomCommandsSettings == true) }
@Test(.dependencies) func conflictingCustomShortcutPersistsAsUserOverride() async throws { let rootURL = URL(fileURLWithPath: "/tmp/repo-\(UUID().uuidString)") let settingsStorage = SettingsTestStorage() let localStorage = RepositoryLocalSettingsTestStorage() let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json")
let store = TestStore( initialState: RepositorySettingsFeature.State( rootURL: rootURL, repositoryKind: .plain, settings: .default, userSettings: .default ) ) { RepositorySettingsFeature() } withDependencies: { $0.settingsFileStorage = settingsStorage.storage $0.settingsFileURL = settingsFileURL $0.repositoryLocalSettingsStorage = localStorage.storage }
let conflicted = UserRepositorySettings( customCommands: [ UserCustomCommand( title: "Run tests", systemImage: "terminal", command: "swift test", execution: .shellScript, shortcut: UserCustomShortcut( key: "b", modifiers: UserCustomShortcutModifiers(command: true) ) ) ] )
await store.send(.binding(.set(\.userSettings, conflicted))) { $0.userSettings = conflicted } await store.receive(\.delegate.settingsChanged)
let savedData = try #require( localStorage.data(at: SupacodePaths.userRepositorySettingsURL(for: rootURL))) let decoded = try JSONDecoder().decode(UserRepositorySettings.self, from: savedData) #expect(decoded.customCommands.first?.shortcut == conflicted.customCommands.first?.shortcut) }
@Test(.dependencies) func globalCommandOptOutPersistsThroughDedicatedAction() async throws { let rootURL = URL(fileURLWithPath: "/tmp/repo-\(UUID().uuidString)") let localStorage = RepositoryLocalSettingsTestStorage() let store = TestStore( initialState: RepositorySettingsFeature.State( rootURL: rootURL, repositoryKind: .plain, settings: .default, userSettings: .default ) ) { RepositorySettingsFeature() } withDependencies: { $0.repositoryLocalSettingsStorage = localStorage.storage }
await store.send(.setGlobalCommandEnabled("global-build", false)) { $0.userSettings.disabledGlobalCommandIDs = ["global-build"] } await store.receive(\.delegate.settingsChanged)
let persisted = try JSONDecoder().decode( UserRepositorySettings.self, from: #require(localStorage.data(at: SupacodePaths.userRepositorySettingsURL(for: rootURL))) ) #expect(!persisted.isGlobalCommandEnabled("global-build"))
await store.send(.setGlobalCommandEnabled("global-build", true)) { $0.userSettings.disabledGlobalCommandIDs = [] } await store.receive(\.delegate.settingsChanged) }
@Test(.dependencies) func customTitleBindingPersistsToRepositoryFile() async throws { let rootURL = URL(fileURLWithPath: "/tmp/repo-\(UUID().uuidString)") let settingsStorage = SettingsTestStorage() let localStorage = RepositoryLocalSettingsTestStorage() let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json") let repositorySettingsURL = SupacodePaths.repositorySettingsURL(for: rootURL)
// Pre-seed a per-repo settings file so save() writes through to it // instead of falling back to the global settings file. let seedData = try #require(try? JSONEncoder().encode(RepositorySettings.default)) try #require(try? localStorage.save(seedData, at: repositorySettingsURL))
let store = TestStore( initialState: RepositorySettingsFeature.State( rootURL: rootURL, repositoryKind: .plain, settings: .default, userSettings: .default ) ) { RepositorySettingsFeature() } withDependencies: { $0.settingsFileStorage = settingsStorage.storage $0.settingsFileURL = settingsFileURL $0.repositoryLocalSettingsStorage = localStorage.storage }
await store.send(.binding(.set(\.settings.customTitle, "My Custom Repo"))) { $0.settings.customTitle = "My Custom Repo" } await store.receive(\.delegate.settingsChanged)
let savedData = try #require(localStorage.data(at: repositorySettingsURL)) let decoded = try JSONDecoder().decode(RepositorySettings.self, from: savedData) #expect(decoded.customTitle == "My Custom Repo") }
@Test(.dependencies) func customTitleWhitespaceOnlyPersistsAsNil() async throws { let rootURL = URL(fileURLWithPath: "/tmp/repo-\(UUID().uuidString)") let settingsStorage = SettingsTestStorage() let localStorage = RepositoryLocalSettingsTestStorage() let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json") let repositorySettingsURL = SupacodePaths.repositorySettingsURL(for: rootURL)
let seedData = try #require(try? JSONEncoder().encode(RepositorySettings.default)) try #require(try? localStorage.save(seedData, at: repositorySettingsURL))
let store = TestStore( initialState: RepositorySettingsFeature.State( rootURL: rootURL, repositoryKind: .plain, settings: .default, userSettings: .default ) ) { RepositorySettingsFeature() } withDependencies: { $0.settingsFileStorage = settingsStorage.storage $0.settingsFileURL = settingsFileURL $0.repositoryLocalSettingsStorage = localStorage.storage }
await store.send(.binding(.set(\.settings.customTitle, " "))) { $0.settings.customTitle = " " } await store.receive(\.delegate.settingsChanged)
let savedData = try #require(localStorage.data(at: repositorySettingsURL)) let decoded = try JSONDecoder().decode(RepositorySettings.self, from: savedData) #expect(decoded.customTitle == nil) }
@Test(.dependencies) func taskLoadsLatestUserSettingsAfterAsyncGitProbe() async throws { let rootURL = URL(fileURLWithPath: "/tmp/repo-\(UUID().uuidString)") let settingsStorage = SettingsTestStorage() let localStorage = RepositoryLocalSettingsTestStorage() let settingsFileURL = URL(fileURLWithPath: "/tmp/supacode-settings-\(UUID().uuidString).json") let gitProbeGate = LockIsolated<CheckedContinuation<Void, Never>?>(nil)
let initialUserSettings = UserRepositorySettings( customCommands: [.default(index: 0)] ) let updatedUserSettings = UserRepositorySettings( customCommands: [ UserCustomCommand( title: "Updated", systemImage: "terminal", command: "echo updated", execution: .shellScript, shortcut: nil ) ] )
let initialData = try #require(try? JSONEncoder().encode(initialUserSettings)) try #require( try? localStorage.save( initialData, at: SupacodePaths.userRepositorySettingsURL(for: rootURL) ) )
let store = TestStore( initialState: RepositorySettingsFeature.State( rootURL: rootURL, repositoryKind: .git, settings: .default, userSettings: .default ) ) { RepositorySettingsFeature() } withDependencies: { $0.settingsFileStorage = settingsStorage.storage $0.settingsFileURL = settingsFileURL $0.repositoryLocalSettingsStorage = localStorage.storage $0.gitClient.isBareRepository = { _ in await withCheckedContinuation { continuation in gitProbeGate.setValue(continuation) } return false } $0.gitClient.branchRefs = { _ in [] } $0.gitClient.automaticWorktreeBaseRef = { _ in "origin/main" } }
await store.send(.task)
for _ in 0..<50 { if gitProbeGate.value != nil { break } await Task.yield() } #expect(gitProbeGate.value != nil)
await store.send(.binding(.set(\.userSettings, updatedUserSettings))) { $0.userSettings = updatedUserSettings } await store.receive(\.delegate.settingsChanged)
let continuation = try #require(gitProbeGate.value) continuation.resume()
await store.receive(\.settingsLoaded, timeout: .seconds(5)) await store.receive(\.branchDataLoaded) { $0.defaultWorktreeBaseRef = "origin/main" $0.branchOptions = ["origin/main"] $0.isBranchDataLoaded = true } #expect(store.state.userSettings == updatedUserSettings) }}