native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

test(repo-appearance): clean up scratch dirs from icon-store tests

The previous test fixtures created `prowl-icon-store-<UUID>` and
`prowl-icon-source-<UUID>` directories under `$TMPDIR` per test and
never removed them, so each `make test` left ~24 orphan folders behind
that accumulated across runs.

Replace the static helper functions with a small RAII `ScratchDirectory`
class. Each test now holds its scratch directories as locals; deinit
fires at end-of-scope and removes them. Verified clean after a full
suite run: 0 leaked directories.

Production code (`RepositoryIconAssetStore.liveValue`) is unaffected —
it writes to `~/.prowl/repo/<name>/icons/`, not `$TMPDIR`, and was never
the source of these orphan folders.

onevcat 84fcd64a dc2e31b4

+69 -47
+69 -47
supacodeTests/RepositoryIconAssetStoreTests.swift
··· 3 3 4 4 @testable import supacode 5 5 6 + /// RAII helper: creates a unique scratch directory under `$TMPDIR` and 7 + /// removes it on deinit so test runs don't accumulate orphan folders 8 + /// between invocations (we ship to `make test` repeatedly during dev). 9 + private final class ScratchDirectory { 10 + let url: URL 11 + 12 + init(prefix: String) { 13 + url = URL(fileURLWithPath: NSTemporaryDirectory()) 14 + .appending(path: "\(prefix)-\(UUID().uuidString)", directoryHint: .isDirectory) 15 + try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true) 16 + } 17 + 18 + deinit { 19 + try? FileManager.default.removeItem(at: url) 20 + } 21 + } 22 + 6 23 @MainActor 7 24 struct RepositoryIconAssetStoreTests { 8 25 // MARK: - Helpers 9 26 10 - private static func makeTempRepoRoot() -> URL { 11 - let url = URL(fileURLWithPath: NSTemporaryDirectory()) 12 - .appending(path: "prowl-icon-store-\(UUID().uuidString)", directoryHint: .isDirectory) 13 - try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true) 14 - return url 27 + private func makeRepoRootScratch() -> ScratchDirectory { 28 + ScratchDirectory(prefix: "prowl-icon-store") 15 29 } 16 30 17 - private static func writeSourceFile(extension ext: String, contents: Data = Data([0xDE, 0xAD])) 18 - throws 19 - -> URL 20 - { 21 - let dir = URL(fileURLWithPath: NSTemporaryDirectory()) 22 - .appending(path: "prowl-icon-source-\(UUID().uuidString)", directoryHint: .isDirectory) 23 - try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) 24 - let url = dir.appending(path: "icon.\(ext)", directoryHint: .notDirectory) 31 + private func writeSourceFile( 32 + in scratch: ScratchDirectory, 33 + extension ext: String, 34 + contents: Data = Data([0xDE, 0xAD]) 35 + ) throws -> URL { 36 + let url = scratch.url.appending(path: "icon.\(ext)", directoryHint: .notDirectory) 25 37 try contents.write(to: url) 26 38 return url 27 39 } ··· 30 42 31 43 @Test func importImageCopiesFileWithUUIDName() throws { 32 44 let store = RepositoryIconAssetStore.liveValue 33 - let repoRoot = Self.makeTempRepoRoot() 34 - let source = try Self.writeSourceFile(extension: "png", contents: Data([0x01, 0x02, 0x03])) 45 + let repoRoot = makeRepoRootScratch() 46 + let source = ScratchDirectory(prefix: "prowl-icon-source") 47 + let sourceFile = try writeSourceFile( 48 + in: source, extension: "png", contents: Data([0x01, 0x02, 0x03]) 49 + ) 35 50 36 - let filename = try store.importImage(source, repoRoot) 51 + let filename = try store.importImage(sourceFile, repoRoot.url) 37 52 38 53 #expect(filename.hasSuffix(".png")) 39 54 #expect(UUID(uuidString: String(filename.dropLast(4))) != nil) 40 55 41 56 let resolved = SupacodePaths.repositoryIconFileURL( 42 - filename: filename, repositoryRootURL: repoRoot 57 + filename: filename, repositoryRootURL: repoRoot.url 43 58 ) 44 59 let copied = try Data(contentsOf: resolved) 45 60 #expect(copied == Data([0x01, 0x02, 0x03])) ··· 47 62 48 63 @Test func importImageNormalizesUppercaseExtension() throws { 49 64 let store = RepositoryIconAssetStore.liveValue 50 - let repoRoot = Self.makeTempRepoRoot() 51 - let source = try Self.writeSourceFile(extension: "PNG") 65 + let repoRoot = makeRepoRootScratch() 66 + let source = ScratchDirectory(prefix: "prowl-icon-source") 67 + let sourceFile = try writeSourceFile(in: source, extension: "PNG") 52 68 53 - let filename = try store.importImage(source, repoRoot) 69 + let filename = try store.importImage(sourceFile, repoRoot.url) 54 70 #expect(filename.hasSuffix(".png")) 55 71 } 56 72 57 73 @Test func importImageAcceptsSVG() throws { 58 74 let store = RepositoryIconAssetStore.liveValue 59 - let repoRoot = Self.makeTempRepoRoot() 60 - let source = try Self.writeSourceFile(extension: "svg") 75 + let repoRoot = makeRepoRootScratch() 76 + let source = ScratchDirectory(prefix: "prowl-icon-source") 77 + let sourceFile = try writeSourceFile(in: source, extension: "svg") 61 78 62 - let filename = try store.importImage(source, repoRoot) 79 + let filename = try store.importImage(sourceFile, repoRoot.url) 63 80 #expect(filename.hasSuffix(".svg")) 64 81 } 65 82 66 83 @Test func importImageRejectsUnsupportedExtension() throws { 67 84 let store = RepositoryIconAssetStore.liveValue 68 - let repoRoot = Self.makeTempRepoRoot() 69 - let source = try Self.writeSourceFile(extension: "jpeg") 85 + let repoRoot = makeRepoRootScratch() 86 + let source = ScratchDirectory(prefix: "prowl-icon-source") 87 + let sourceFile = try writeSourceFile(in: source, extension: "jpeg") 70 88 71 89 #expect(throws: RepositoryIconAssetStoreError.self) { 72 - _ = try store.importImage(source, repoRoot) 90 + _ = try store.importImage(sourceFile, repoRoot.url) 73 91 } 74 92 } 75 93 76 94 @Test func importImageCreatesIconsDirectoryWhenMissing() throws { 77 95 let store = RepositoryIconAssetStore.liveValue 78 - let repoRoot = Self.makeTempRepoRoot() 96 + let repoRoot = makeRepoRootScratch() 79 97 // Don't pre-create the icons directory — importImage should make 80 98 // it itself, otherwise first-time imports would fail. 81 - let source = try Self.writeSourceFile(extension: "png") 99 + let source = ScratchDirectory(prefix: "prowl-icon-source") 100 + let sourceFile = try writeSourceFile(in: source, extension: "png") 82 101 83 - _ = try store.importImage(source, repoRoot) 102 + _ = try store.importImage(sourceFile, repoRoot.url) 84 103 85 - let iconsDir = SupacodePaths.repositoryIconsDirectory(for: repoRoot) 104 + let iconsDir = SupacodePaths.repositoryIconsDirectory(for: repoRoot.url) 86 105 var isDirectory: ObjCBool = false 87 106 let exists = FileManager.default.fileExists( 88 107 atPath: iconsDir.path(percentEncoded: false), isDirectory: &isDirectory ··· 93 112 94 113 @Test func importImageGeneratesUniqueFilenamePerCall() throws { 95 114 let store = RepositoryIconAssetStore.liveValue 96 - let repoRoot = Self.makeTempRepoRoot() 97 - let source = try Self.writeSourceFile(extension: "png") 115 + let repoRoot = makeRepoRootScratch() 116 + let source = ScratchDirectory(prefix: "prowl-icon-source") 117 + let sourceFile = try writeSourceFile(in: source, extension: "png") 98 118 99 - let first = try store.importImage(source, repoRoot) 100 - let second = try store.importImage(source, repoRoot) 119 + let first = try store.importImage(sourceFile, repoRoot.url) 120 + let second = try store.importImage(sourceFile, repoRoot.url) 101 121 #expect(first != second) 102 122 } 103 123 ··· 105 125 106 126 @Test func existsReportsFalseWhenMissing() { 107 127 let store = RepositoryIconAssetStore.liveValue 108 - let repoRoot = Self.makeTempRepoRoot() 109 - #expect(!store.exists("nonexistent.png", repoRoot)) 128 + let repoRoot = makeRepoRootScratch() 129 + #expect(!store.exists("nonexistent.png", repoRoot.url)) 110 130 } 111 131 112 132 @Test func existsReportsTrueAfterImport() throws { 113 133 let store = RepositoryIconAssetStore.liveValue 114 - let repoRoot = Self.makeTempRepoRoot() 115 - let source = try Self.writeSourceFile(extension: "png") 116 - let filename = try store.importImage(source, repoRoot) 117 - #expect(store.exists(filename, repoRoot)) 134 + let repoRoot = makeRepoRootScratch() 135 + let source = ScratchDirectory(prefix: "prowl-icon-source") 136 + let sourceFile = try writeSourceFile(in: source, extension: "png") 137 + let filename = try store.importImage(sourceFile, repoRoot.url) 138 + #expect(store.exists(filename, repoRoot.url)) 118 139 } 119 140 120 141 // MARK: - remove 121 142 122 143 @Test func removeDeletesImportedFile() throws { 123 144 let store = RepositoryIconAssetStore.liveValue 124 - let repoRoot = Self.makeTempRepoRoot() 125 - let source = try Self.writeSourceFile(extension: "png") 126 - let filename = try store.importImage(source, repoRoot) 145 + let repoRoot = makeRepoRootScratch() 146 + let source = ScratchDirectory(prefix: "prowl-icon-source") 147 + let sourceFile = try writeSourceFile(in: source, extension: "png") 148 + let filename = try store.importImage(sourceFile, repoRoot.url) 127 149 128 - try store.remove(filename, repoRoot) 129 - #expect(!store.exists(filename, repoRoot)) 150 + try store.remove(filename, repoRoot.url) 151 + #expect(!store.exists(filename, repoRoot.url)) 130 152 } 131 153 132 154 @Test func removeIsIdempotent() throws { 133 155 // Reset / replace flows can call remove repeatedly; missing files 134 156 // shouldn't throw or the reducer would have to track existence. 135 157 let store = RepositoryIconAssetStore.liveValue 136 - let repoRoot = Self.makeTempRepoRoot() 137 - try store.remove("never-existed.png", repoRoot) 158 + let repoRoot = makeRepoRootScratch() 159 + try store.remove("never-existed.png", repoRoot.url) 138 160 } 139 161 }