Tests/KeycaskCLITests/AddShowTests.swift
124 lines · 5156 bytes
1import Foundation
2import Testing
3
4@Suite struct AddShowTests {
5 @Test func addReadsPasswordFromStdinAndPrintsID() throws {
6 let cli = try CLI.initialized()
7 let r = try cli.run(
8 ["add", "github", "-u", "cmc", "--url", "https://github.com", "--tag", "dev"],
9 stdin: "hunter2\n")
10 #expect(r.status == 0)
11 let id = r.stdout.trimmingCharacters(in: .whitespacesAndNewlines)
12 #expect(id.count == 8)
13
14 let shown = try cli.run(["show", id])
15 #expect(shown.status == 0)
16 #expect(shown.stdout.contains("name: github"))
17 #expect(shown.stdout.contains("username: cmc"))
18 #expect(shown.stdout.contains("password: ********"))
19 #expect(shown.stdout.contains("tags: dev"))
20 #expect(!shown.stdout.contains("hunter2"))
21 }
22
23 @Test func showByNameRevealAndField() throws {
24 let cli = try CLI.initialized()
25 try cli.run(["add", "github"], stdin: "hunter2\n")
26 let revealed = try cli.run(["show", "github", "--reveal"])
27 #expect(revealed.stdout.contains("password: hunter2"))
28 let field = try cli.run(["show", "github", "--field", "password"])
29 #expect(field.stdout == "hunter2\n")
30 let missing = try cli.run(["show", "github", "--field", "url"])
31 #expect(missing.status == 0)
32 #expect(missing.stdout == "\n")
33 let unknown = try cli.run(["show", "github", "--field", "nope"])
34 #expect(unknown.status == 2)
35 }
36
37 @Test func jsonMasksUnlessReveal() throws {
38 let cli = try CLI.initialized()
39 try cli.run(["add", "github", "-u", "cmc"], stdin: "hunter2\n")
40 let masked = try cli.run(["show", "github", "--json"])
41 let obj = try JSONSerialization.jsonObject(with: Data(masked.stdout.utf8)) as! [String: Any]
42 #expect(obj["name"] as? String == "github")
43 #expect(obj["username"] as? String == "cmc")
44 #expect(obj["password"] as? String == "********")
45 #expect((obj["id"] as? String)?.count == 8)
46 #expect((obj["created"] as? String)?.hasSuffix("Z") == true)
47 let revealed = try cli.run(["show", "github", "--json", "--reveal"])
48 let obj2 =
49 try JSONSerialization.jsonObject(with: Data(revealed.stdout.utf8)) as! [String: Any]
50 #expect(obj2["password"] as? String == "hunter2")
51 }
52
53 @Test func addGenerateAndWords() throws {
54 let cli = try CLI.initialized()
55 try cli.run(["add", "a", "--generate"])
56 try cli.run(["add", "b", "--generate", "--length", "40"])
57 try cli.run(["add", "c", "--words", "4"])
58 #expect(try cli.run(["show", "a", "--field", "password"]).stdout.count == 25)
59 #expect(try cli.run(["show", "b", "--field", "password"]).stdout.count == 41)
60 let words = try cli.run(["show", "c", "--field", "password"]).stdout
61 .trimmingCharacters(in: .newlines).split(separator: "-")
62 #expect(words.count == 4)
63 }
64
65 @Test func generateAndWordsTogetherIsUsageError() throws {
66 let cli = try CLI.initialized()
67 let r = try cli.run(["add", "a", "--generate", "--words", "3"])
68 #expect(r.status == 2)
69 }
70
71 @Test func duplicateNamesAreAllowedAndAmbiguousOnShow() throws {
72 let cli = try CLI.initialized()
73 let a = try cli.run(["add", "gh", "-u", "one", "--generate"]).stdout.trimmingCharacters(
74 in: .newlines)
75 let b = try cli.run(["add", "gh", "-u", "two", "--generate"]).stdout.trimmingCharacters(
76 in: .newlines)
77 let r = try cli.run(["show", "gh"])
78 #expect(r.status == 5)
79 #expect(r.stderr.contains(a) && r.stderr.contains(b))
80 #expect(try cli.run(["show", a]).stdout.contains("username: one"))
81 }
82
83 @Test func missingEntryIsNotFound() throws {
84 let cli = try CLI.initialized()
85 let r = try cli.run(["show", "nope"])
86 #expect(r.status == 3)
87 #expect(r.stderr == "nope: not found\n")
88 }
89
90 @Test func wrongPassphraseCannotDecrypt() throws {
91 let cli = try CLI.initialized()
92 let r = try cli.run(["show", "x"], passphrase: "wrong")
93 #expect(r.status == 4)
94 #expect(r.stderr.contains("cannot decrypt"))
95 }
96
97 @Test func missingVaultIsNotFound() throws {
98 let cli = try CLI()
99 let r = try cli.run(["show", "x"])
100 #expect(r.status == 3)
101 #expect(r.stderr.contains("keycask init"))
102 }
103
104 @Test func lengthWithWordsIsUsageError() throws {
105 let cli = try CLI.initialized()
106 let r = try cli.run(["add", "a", "--words", "4", "--length", "30"])
107 #expect(r.status == 2)
108 }
109
110 @Test func addWithoutPasswordSourceIsUsageError() throws {
111 let cli = try CLI.initialized()
112 let r = try cli.run(["add", "x"], stdin: "")
113 #expect(r.status == 2)
114 #expect(r.stderr.contains("no password"))
115 }
116
117 @Test func corruptVaultIsFailure() throws {
118 let cli = try CLI.initialized()
119 try Data("{}".utf8).write(to: cli.vault)
120 let r = try cli.run(["show", "x"])
121 #expect(r.status == 1)
122 #expect(r.stderr.hasPrefix("vault is corrupt"))
123 }
124}