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

157 lines
7.3 KiB
Swift

import Foundation
import Testing
@testable import CodexBarCore
struct ClaudeProbeWorkingDirectoryTests {
@Test
func `probe working directory disables deep link registration`() throws {
let directory = try Self.makeTemporaryDirectory()
try ClaudeStatusProbe.prepareProbeWorkingDirectory(at: directory)
let settings = try Self.readSettings(from: directory)
#expect(settings["disableDeepLinkRegistration"] as? String == "disable")
}
@Test
func `probe working directory preserves existing local settings`() throws {
let directory = try Self.makeTemporaryDirectory()
let settingsURL = directory
.appendingPathComponent(".claude", isDirectory: true)
.appendingPathComponent("settings.local.json")
try FileManager.default.createDirectory(
at: settingsURL.deletingLastPathComponent(),
withIntermediateDirectories: true)
let existing: [String: Any] = [
"permissions": [
"allow": ["Bash(*)"],
],
]
let data = try JSONSerialization.data(withJSONObject: existing)
try data.write(to: settingsURL)
try ClaudeStatusProbe.prepareProbeWorkingDirectory(at: directory)
let settings = try Self.readSettings(from: directory)
#expect(settings["disableDeepLinkRegistration"] as? String == "disable")
let permissions = try #require(settings["permissions"] as? [String: Any])
#expect(permissions["allow"] as? [String] == ["Bash(*)"])
}
@Test
func `probe working directory overwrites invalid local settings`() throws {
let directory = try Self.makeTemporaryDirectory()
let settingsURL = directory
.appendingPathComponent(".claude", isDirectory: true)
.appendingPathComponent("settings.local.json")
try FileManager.default.createDirectory(
at: settingsURL.deletingLastPathComponent(),
withIntermediateDirectories: true)
try Data("{".utf8).write(to: settingsURL)
try ClaudeStatusProbe.prepareProbeWorkingDirectory(at: directory)
let settings = try Self.readSettings(from: directory)
#expect(settings["disableDeepLinkRegistration"] as? String == "disable")
}
@Test
func `probe project directory name matches Claude Code encoding`() {
let cases = [
(
"/Users/test/Library/Application Support/CodexBar/ClaudeProbe",
"-Users-test-Library-Application-Support-CodexBar-ClaudeProbe"),
(
"/Users/test.name/t\u{00E9}st_under/Library/Application Support/CodexBar/ClaudeProbe",
"-Users-test-name-t-st-under-Library-Application-Support-CodexBar-ClaudeProbe"),
(
"/Users/test/emoji_😀/ClaudeProbe",
"-Users-test-emoji----ClaudeProbe"),
(
"/tmp/\(String(repeating: "segment_", count: 40))/ClaudeProbe",
"-tmp-segment-segment-segment-segment-segment-segment-segment-segment-segment-segment-segment-" +
"segment-segment-segment-segment-segment-segment-segment-segment-segment-segment-segment-" +
"segment-segment-seg-x9mpdi"),
]
for (path, expected) in cases {
let directory = URL(fileURLWithPath: path)
#expect(ClaudeProbeSessionArtifactCleaner.claudeProjectDirectoryName(for: directory) == expected)
}
}
@Test
func `cleanup removes only probe session jsonl artifacts`() throws {
let probeDirectory = try Self.makeTemporaryDirectory()
let claudeRoot = try Self.makeTemporaryDirectory()
let projectsRoot = claudeRoot.appendingPathComponent("projects", isDirectory: true)
let probeProject = projectsRoot
.appendingPathComponent(
ClaudeProbeSessionArtifactCleaner.claudeProjectDirectoryName(for: probeDirectory),
isDirectory: true)
let unrelatedProject = projectsRoot.appendingPathComponent("unrelated-project", isDirectory: true)
try FileManager.default.createDirectory(at: probeProject, withIntermediateDirectories: true)
try FileManager.default.createDirectory(at: unrelatedProject, withIntermediateDirectories: true)
defer {
try? FileManager.default.removeItem(at: probeDirectory)
try? FileManager.default.removeItem(at: claudeRoot)
}
let probeSession = probeProject.appendingPathComponent("probe-session.jsonl")
let probeNote = probeProject.appendingPathComponent("keep.txt")
let unrelatedSession = unrelatedProject.appendingPathComponent("user-session.jsonl")
try Data("{}\n".utf8).write(to: probeSession)
try Data("keep".utf8).write(to: probeNote)
try Data("{}\n".utf8).write(to: unrelatedSession)
let removed = ClaudeProbeSessionArtifactCleaner.cleanupProbeSessionArtifacts(
probeDirectory: probeDirectory,
environment: ["CLAUDE_CONFIG_DIR": claudeRoot.path, "HOME": claudeRoot.path])
#expect(removed.map(\.lastPathComponent) == ["probe-session.jsonl"])
#expect(!FileManager.default.fileExists(atPath: probeSession.path))
#expect(FileManager.default.fileExists(atPath: probeNote.path))
#expect(FileManager.default.fileExists(atPath: unrelatedSession.path))
}
@Test
func `cleanup removes hashed long probe project artifacts`() throws {
let probeDirectory = URL(fileURLWithPath: "/tmp/\(String(repeating: "segment_", count: 40))/ClaudeProbe")
let claudeRoot = try Self.makeTemporaryDirectory()
let projectsRoot = claudeRoot.appendingPathComponent("projects", isDirectory: true)
let probeProject = projectsRoot
.appendingPathComponent(
ClaudeProbeSessionArtifactCleaner.claudeProjectDirectoryName(for: probeDirectory),
isDirectory: true)
try FileManager.default.createDirectory(at: probeProject, withIntermediateDirectories: true)
defer {
try? FileManager.default.removeItem(at: claudeRoot)
}
let probeSession = probeProject.appendingPathComponent("probe-session.jsonl")
try Data("{}\n".utf8).write(to: probeSession)
let removed = ClaudeProbeSessionArtifactCleaner.cleanupProbeSessionArtifacts(
probeDirectory: probeDirectory,
environment: ["CLAUDE_CONFIG_DIR": claudeRoot.path, "HOME": claudeRoot.path])
#expect(removed.map(\.lastPathComponent) == ["probe-session.jsonl"])
#expect(!FileManager.default.fileExists(atPath: probeSession.path))
}
private static func makeTemporaryDirectory() throws -> URL {
let directory = FileManager.default.temporaryDirectory
.appendingPathComponent("codexbar-claude-probe-\(UUID().uuidString)", isDirectory: true)
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
return directory
}
private static func readSettings(from directory: URL) throws -> [String: Any] {
let settingsURL = directory
.appendingPathComponent(".claude", isDirectory: true)
.appendingPathComponent("settings.local.json")
let data = try Data(contentsOf: settingsURL)
return try #require(JSONSerialization.jsonObject(with: data) as? [String: Any])
}
}