Tests/KeycaskCLITests/ClipboardTests.swift
168 lines · 6507 bytes
1import Foundation
2import Testing
3
4@testable import keycask
5
6@Suite struct ClipboardTests {
7 @Test func clearsOnlyWhenClipboardStillHoldsTheSecret() {
8 #expect(Clipboard.shouldClear(secret: "s", current: "s"))
9 #expect(!Clipboard.shouldClear(secret: "s", current: "user pasted"))
10 #expect(!Clipboard.shouldClear(secret: "s", current: nil))
11 }
12
13 @Test func handoffRoundTrips() throws {
14 let h = Clipboard.Handoff(secret: "s3cret")
15 let data = try JSONEncoder().encode(h)
16 #expect(try JSONDecoder().decode(Clipboard.Handoff.self, from: data) == h)
17 }
18
19 @Test func stripsOneTrailingNewline() {
20 #expect(Clipboard.stripTrailingNewline("s\r\n") == "s")
21 #expect(Clipboard.stripTrailingNewline("s\n") == "s")
22 #expect(Clipboard.stripTrailingNewline("s") == "s")
23 #expect(Clipboard.stripTrailingNewline("s\n\n") == "s\n")
24 }
25
26 @Test func findToolScansPathInOrder() throws {
27 let dir = FileManager.default.temporaryDirectory
28 .appendingPathComponent("keycask-clip-\(UUID().uuidString)")
29 try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
30 #expect(Clipboard.findTool(path: dir.path) == nil)
31
32 #if os(macOS)
33 let names = ["pbcopy", "pbpaste"]
34 #elseif os(Windows)
35 let names = ["clip.exe", "powershell.exe"]
36 #else
37 let names = ["xclip"]
38 #endif
39 for n in names {
40 let f = dir.appendingPathComponent(n)
41 try Data("#!/bin/sh\n".utf8).write(to: f)
42 try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: f.path)
43 }
44 let tool = Clipboard.findTool(path: dir.path)
45 #expect(tool != nil)
46 #expect(tool?.copy.first?.hasPrefix(dir.path) == true)
47 }
48
49 @Test func daemonWithoutHandoffFails() throws {
50 let cli = try CLI()
51 let r = try cli.run(["clipboard-daemon", "1"], stdin: "not json", passphrase: nil)
52 #expect(r.status == 1)
53 }
54
55 @Test func daemonIsHiddenFromHelp() throws {
56 let cli = try CLI()
57 let r = try cli.run(["--help"], passphrase: nil)
58 #expect(!r.stdout.contains("clipboard-daemon"))
59 #expect(r.stdout.contains("clip"))
60 }
61
62 @Test func clipOfMissingEntryIsNotFoundBeforeTouchingClipboard() throws {
63 let cli = try CLI.initialized()
64 let r = try cli.run(["clip", "nope"])
65 #expect(r.status == 3)
66 }
67
68 #if !os(Windows)
69 /// A temp directory of clipboard tools backed by a file, so tests never
70 /// touch the real clipboard.
71 struct Stub {
72 let dir: URL
73
74 var file: URL { dir.appendingPathComponent("clip.txt") }
75 var environment: [String: String] { ["PATH": "\(dir.path):/bin:/usr/bin"] }
76
77 init() throws {
78 dir = FileManager.default.temporaryDirectory
79 .appendingPathComponent("keycask-clip-stub-\(UUID().uuidString)")
80 try FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
81 #if os(macOS)
82 try install("pbcopy", "#!/bin/sh\ncat > \"$(dirname \"$0\")/clip.txt\"\n")
83 try install(
84 "pbpaste", "#!/bin/sh\ncat \"$(dirname \"$0\")/clip.txt\" 2>/dev/null\n")
85 #else
86 try install(
87 "xclip",
88 """
89 #!/bin/sh
90 for a in "$@"; do
91 if [ "$a" = "-o" ]; then
92 cat "$(dirname "$0")/clip.txt" 2>/dev/null
93 exit 0
94 fi
95 done
96 cat > "$(dirname "$0")/clip.txt"
97
98 """)
99 #endif
100 }
101
102 func contents() -> String {
103 (try? String(contentsOf: file, encoding: .utf8)) ?? ""
104 }
105
106 func set(_ text: String) throws {
107 try Data(text.utf8).write(to: file)
108 }
109
110 private func install(_ name: String, _ script: String) throws {
111 let url = dir.appendingPathComponent(name)
112 try Data(script.utf8).write(to: url)
113 try FileManager.default.setAttributes(
114 [.posixPermissions: 0o755], ofItemAtPath: url.path)
115 }
116 }
117
118 @Test func clipCopiesTheFieldThroughTheTool() throws {
119 let cli = try CLI.initialized()
120 let stub = try Stub()
121 try cli.run(["add", "t", "--generate"])
122 let expected = try cli.run(["show", "t", "--field", "password"]).stdout
123 .trimmingCharacters(in: .whitespacesAndNewlines)
124
125 let start = Date()
126 let r = try cli.run(["clip", "t"], extraEnvironment: stub.environment)
127 let elapsed = Date().timeIntervalSince(start)
128
129 #expect(r.status == 0)
130 #expect(r.stdout.contains("copied password of t"))
131 #expect(stub.contents() == expected)
132 #expect(elapsed < 5)
133 }
134
135 @Test func generateCopyWritesTheTool() throws {
136 let cli = try CLI()
137 let stub = try Stub()
138 let r = try cli.run(
139 ["generate", "--copy"], passphrase: nil, extraEnvironment: stub.environment)
140 #expect(r.status == 0)
141 let copied = stub.contents()
142 #expect(copied.count == 24)
143 #expect(!r.stdout.contains(copied))
144 }
145
146 @Test func daemonClearsWhenClipboardStillHoldsSecret() throws {
147 let cli = try CLI()
148 let stub = try Stub()
149 try stub.set("probe")
150 let r = try cli.run(
151 ["clipboard-daemon", "1"], stdin: #"{"secret":"probe"}"#, passphrase: nil,
152 extraEnvironment: stub.environment)
153 #expect(r.status == 0)
154 #expect(stub.contents() == "")
155 }
156
157 @Test func daemonLeavesForeignContentAlone() throws {
158 let cli = try CLI()
159 let stub = try Stub()
160 try stub.set("other")
161 let r = try cli.run(
162 ["clipboard-daemon", "1"], stdin: #"{"secret":"probe"}"#, passphrase: nil,
163 extraEnvironment: stub.environment)
164 #expect(r.status == 0)
165 #expect(stub.contents() == "other")
166 }
167 #endif
168}