keyrack 0.3.0.pre → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +8 -15
  4. data/Guardfile +6 -0
  5. data/LICENSE.txt +4 -2
  6. data/Rakefile +2 -56
  7. data/keyrack.gemspec +22 -104
  8. data/lib/keyrack.rb +9 -1
  9. data/lib/keyrack/database.rb +64 -98
  10. data/lib/keyrack/event.rb +13 -0
  11. data/lib/keyrack/exceptions.rb +4 -0
  12. data/lib/keyrack/group.rb +225 -0
  13. data/lib/keyrack/migrator.rb +45 -0
  14. data/lib/keyrack/runner.rb +98 -44
  15. data/lib/keyrack/site.rb +92 -0
  16. data/lib/keyrack/ui/console.rb +234 -95
  17. data/lib/keyrack/utils.rb +0 -18
  18. data/lib/keyrack/version.rb +3 -0
  19. data/test/fixtures/database-3.dat +0 -0
  20. data/test/helper.rb +11 -1
  21. data/test/integration/test_interactive_console.rb +139 -0
  22. data/test/unit/store/test_filesystem.rb +21 -0
  23. data/test/unit/store/test_ssh.rb +32 -0
  24. data/test/unit/test_database.rb +201 -0
  25. data/test/unit/test_event.rb +41 -0
  26. data/test/unit/test_group.rb +703 -0
  27. data/test/unit/test_migrator.rb +105 -0
  28. data/test/unit/test_runner.rb +407 -0
  29. data/test/unit/test_site.rb +181 -0
  30. data/test/unit/test_store.rb +13 -0
  31. data/test/unit/test_utils.rb +8 -0
  32. data/test/unit/ui/test_console.rb +495 -0
  33. metadata +98 -94
  34. data/Gemfile.lock +0 -45
  35. data/TODO +0 -4
  36. data/VERSION +0 -1
  37. data/features/console.feature +0 -103
  38. data/features/step_definitions/keyrack_steps.rb +0 -61
  39. data/features/support/env.rb +0 -16
  40. data/test/fixtures/aes +0 -0
  41. data/test/fixtures/config.yml +0 -4
  42. data/test/fixtures/id_rsa +0 -54
  43. data/test/keyrack/store/test_filesystem.rb +0 -25
  44. data/test/keyrack/store/test_ssh.rb +0 -36
  45. data/test/keyrack/test_database.rb +0 -111
  46. data/test/keyrack/test_runner.rb +0 -111
  47. data/test/keyrack/test_store.rb +0 -15
  48. data/test/keyrack/test_utils.rb +0 -41
  49. data/test/keyrack/ui/test_console.rb +0 -308
@@ -0,0 +1,181 @@
1
+ require 'helper'
2
+
3
+ class TestSite < Test::Unit::TestCase
4
+ def new_site(*args)
5
+ Keyrack::Site.new(*args)
6
+ end
7
+
8
+ test "creating new site" do
9
+ site = new_site("Enterprise", "picard", "livingston")
10
+ assert_equal "Enterprise", site.name
11
+ end
12
+
13
+ test "name setter" do
14
+ site = new_site("Enterprise", "picard", "livingston")
15
+ site.name = "NCC1701D"
16
+ assert_equal "NCC1701D", site.name
17
+ end
18
+
19
+ test "username" do
20
+ site = new_site("Enterprise", "picard", "livingston")
21
+ assert_equal "picard", site.username
22
+ end
23
+
24
+ test "username setter" do
25
+ site = new_site("Enterprise", "picard", "livingston")
26
+ site.username = "jean_luc"
27
+ assert_equal "jean_luc", site.username
28
+ end
29
+
30
+ test "password" do
31
+ site = new_site("Enterprise", "picard", "livingston")
32
+ assert_equal "livingston", site.password
33
+ end
34
+
35
+ test "password setter" do
36
+ site = new_site("Enterprise", "picard", "livingston")
37
+ site.password = "crusher"
38
+ assert_equal "crusher", site.password
39
+ end
40
+
41
+ test "loading site from hash" do
42
+ hash = {
43
+ 'name' => 'Enterprise',
44
+ 'username' => 'picard',
45
+ 'password' => 'livingston'
46
+ }
47
+ site = new_site(hash)
48
+ assert_equal 'Enterprise', site.name
49
+ assert_equal 'picard', site.username
50
+ assert_equal 'livingston', site.password
51
+ end
52
+
53
+ test "loading site from hash with missing name" do
54
+ hash = {
55
+ 'username' => 'picard',
56
+ 'password' => 'livingston'
57
+ }
58
+ assert_raises(ArgumentError) do
59
+ site = new_site(hash)
60
+ end
61
+ end
62
+
63
+ test "loading site from hash with non-string name" do
64
+ hash = {
65
+ 'name' => [123],
66
+ 'username' => 'picard',
67
+ 'password' => 'livingston'
68
+ }
69
+ assert_raises(ArgumentError) do
70
+ site = new_site(hash)
71
+ end
72
+ end
73
+
74
+ test "loading site from hash with missing username" do
75
+ hash = {
76
+ 'name' => 'Enterprise',
77
+ 'password' => 'livingston'
78
+ }
79
+ assert_raises(ArgumentError) do
80
+ site = new_site(hash)
81
+ end
82
+ end
83
+
84
+ test "loading site from hash with non-string username" do
85
+ hash = {
86
+ 'name' => 'Enterprise',
87
+ 'username' => [123],
88
+ 'password' => 'livingston'
89
+ }
90
+ assert_raises(ArgumentError) do
91
+ site = new_site(hash)
92
+ end
93
+ end
94
+
95
+ test "loading site from hash with missing password" do
96
+ hash = {
97
+ 'name' => 'Enterprise',
98
+ 'username' => 'picard'
99
+ }
100
+ assert_raises(ArgumentError) do
101
+ site = new_site(hash)
102
+ end
103
+ end
104
+
105
+ test "loading site from hash with non-string password" do
106
+ hash = {
107
+ 'name' => 'Enterprise',
108
+ 'username' => 'picard',
109
+ 'password' => [123]
110
+ }
111
+ assert_raises(ArgumentError) do
112
+ site = new_site(hash)
113
+ end
114
+ end
115
+
116
+ test "name change event" do
117
+ site = new_site("Enterprise", "picard", "livingston")
118
+
119
+ called = false
120
+ site.after_event do |event|
121
+ called = true
122
+ assert_equal 'change', event.name
123
+ assert_same site, event.owner
124
+ assert_equal 'name', event.attribute_name
125
+ assert_equal 'Enterprise', event.previous_value
126
+ assert_equal 'NCC1701D', event.new_value
127
+ end
128
+ site.name = "NCC1701D"
129
+ assert called
130
+ end
131
+
132
+ test "password changed callback" do
133
+ site = new_site("Enterprise", "picard", "livingston")
134
+
135
+ called = false
136
+ site.after_event do |event|
137
+ called = true
138
+ assert_same site, event.owner
139
+ assert_equal 'change', event.name
140
+ assert_equal 'password', event.attribute_name
141
+ assert_equal 'livingston', event.previous_value
142
+ assert_equal 'crusher', event.new_value
143
+ end
144
+ site.password = "crusher"
145
+ assert called
146
+ end
147
+
148
+ test "username changed callback" do
149
+ site = new_site("Enterprise", "picard", "livingston")
150
+
151
+ called = false
152
+ site.after_event do |event|
153
+ called = true
154
+ assert_same site, event.owner
155
+ assert_equal 'change', event.name
156
+ assert_equal 'username', event.attribute_name
157
+ assert_equal 'picard', event.previous_value
158
+ assert_equal 'jean_luc', event.new_value
159
+ end
160
+ site.username = "jean_luc"
161
+ assert called
162
+ end
163
+
164
+ test "serializing to yaml" do
165
+ site = new_site("Enterprise", "picard", "livingston")
166
+ expected = {
167
+ 'name' => 'Enterprise',
168
+ 'username' => 'picard',
169
+ 'password' => 'livingston'
170
+ }.to_yaml
171
+ assert_equal expected, site.to_yaml
172
+ end
173
+
174
+ test "sites with same name and username are equal" do
175
+ site_1 = new_site("Enterprise", "picard", "livingston")
176
+ site_2 = new_site("Enterprise", "picard", "crusher")
177
+ site_3 = new_site("Enterprise", "jean_luc", "crusher")
178
+ assert_equal site_1, site_2
179
+ assert_not_equal site_1, site_3
180
+ end
181
+ end
@@ -0,0 +1,13 @@
1
+ require 'helper'
2
+
3
+ class TestStore < Test::Unit::TestCase
4
+ def test_get_filesystem
5
+ assert_equal Keyrack::Store::Filesystem, Keyrack::Store[:filesystem]
6
+ assert_equal Keyrack::Store::Filesystem, Keyrack::Store['filesystem']
7
+ end
8
+
9
+ def test_get_ssh
10
+ assert_equal Keyrack::Store::SSH, Keyrack::Store[:ssh]
11
+ assert_equal Keyrack::Store::SSH, Keyrack::Store['ssh']
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ require 'helper'
2
+
3
+ class TestUtils < Test::Unit::TestCase
4
+ def test_generate_password
5
+ result = Keyrack::Utils.generate_password
6
+ assert_match /^[!-~]{8}$/, result
7
+ end
8
+ end
@@ -0,0 +1,495 @@
1
+ require 'helper'
2
+
3
+ class TestConsole < Test::Unit::TestCase
4
+ def setup
5
+ @twitter = stub('Twitter', {
6
+ :name => 'Twitter',
7
+ :username => 'tweeb',
8
+ :password => 'secret'
9
+ })
10
+ @google_1 = stub('Google 1', {
11
+ :name => 'Google',
12
+ :username => 'catfan',
13
+ :password => 'kitty'
14
+ })
15
+ @google_2 = stub('Google 2', {
16
+ :name => 'Google',
17
+ :username => 'dogfan',
18
+ :password => 'puppy'
19
+ })
20
+ @top_group = stub('top group', {
21
+ :sites => [@twitter, @google_1, @google_2],
22
+ :group_names => []
23
+ })
24
+ @top_group.stubs(:site).with(0).returns(@twitter)
25
+ @top_group.stubs(:site).with(1).returns(@google_1)
26
+ @top_group.stubs(:site).with(2).returns(@google_2)
27
+
28
+ @highline = stub('highline')
29
+ @highline.stubs(:color).with("Keyrack Main Menu", instance_of(Symbol)).
30
+ returns("Keyrack Main Menu")
31
+ #@highline.stubs(:say)
32
+ HighLine.expects(:new).returns(@highline)
33
+ @console = Keyrack::UI::Console.new
34
+
35
+ HighLine::SystemExtensions.stubs(:terminal_size).returns([20, 20])
36
+ end
37
+
38
+ test "select login from menu" do
39
+ seq = sequence('say')
40
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
41
+ @highline.expects(:say).with("1. Twitter [tweeb]")
42
+ @highline.expects(:say).with("2. Google [catfan]")
43
+ @highline.expects(:say).with("3. Google [dogfan]")
44
+ @highline.expects(:say).with("Mode: copy")
45
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
46
+
47
+ question = mock('question')
48
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g}) }).returns('1')
49
+ Clipboard.expects(:copy).with('secret')
50
+ @highline.expects(:say).with("The password has been copied to your clipboard.")
51
+ assert_nil @console.menu(:group => @top_group, :at_top => true)
52
+ end
53
+
54
+ test "select entry from menu in print mode" do
55
+ seq = sequence('say')
56
+ @console.mode = :print
57
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
58
+ @highline.expects(:say).with("1. Twitter [tweeb]")
59
+ @highline.expects(:say).with("2. Google [catfan]")
60
+ @highline.expects(:say).with("3. Google [dogfan]")
61
+ @highline.expects(:say).with("Mode: print")
62
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
63
+
64
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g}) }).returns('1')
65
+ @highline.expects(:color).with('secret', instance_of(Symbol)).
66
+ returns('secret').in_sequence(seq)
67
+ question = mock do
68
+ expects(:echo=).with(false)
69
+ if HighLine::SystemExtensions::CHARACTER_MODE != 'stty'
70
+ expects(:character=).with(true)
71
+ expects(:overwrite=).with(true)
72
+ end
73
+ end
74
+ @highline.expects(:ask).
75
+ with('Here you go: secret. Done? ').
76
+ yields(question).
77
+ returns('')
78
+
79
+ assert_nil @console.menu(:group => @top_group, :at_top => true)
80
+ end
81
+
82
+ test "select new from menu" do
83
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
84
+ @highline.expects(:say).with("1. Twitter [tweeb]")
85
+ @highline.expects(:say).with("2. Google [catfan]")
86
+ @highline.expects(:say).with("3. Google [dogfan]")
87
+ @highline.expects(:say).with("Mode: copy")
88
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
89
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g}) }).returns('n')
90
+ assert_equal :new, @console.menu(:group => @top_group, :at_top => true)
91
+ end
92
+
93
+ test "select edit from menu" do
94
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
95
+ @highline.expects(:say).with("1. Twitter [tweeb]")
96
+ @highline.expects(:say).with("2. Google [catfan]")
97
+ @highline.expects(:say).with("3. Google [dogfan]")
98
+ @highline.expects(:say).with("Mode: copy")
99
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
100
+ question = mock('question')
101
+ question.expects(:in=).with(%w{n q m 1 2 3 e g})
102
+ @highline.expects(:ask).yields(question).returns('e')
103
+ assert_equal :edit, @console.menu(:group => @top_group, :at_top => true)
104
+ end
105
+
106
+ test "select quit from menu" do
107
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
108
+ @highline.expects(:say).with("1. Twitter [tweeb]")
109
+ @highline.expects(:say).with("2. Google [catfan]")
110
+ @highline.expects(:say).with("3. Google [dogfan]")
111
+ @highline.expects(:say).with("Mode: copy")
112
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
113
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g}) }).returns('q')
114
+ assert_equal :quit, @console.menu(:group => @top_group, :at_top => true)
115
+ end
116
+
117
+ test "select quit from menu when database is dirty" do
118
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
119
+ @highline.expects(:say).with("1. Twitter [tweeb]")
120
+ @highline.expects(:say).with("2. Google [catfan]")
121
+ @highline.expects(:say).with("3. Google [dogfan]")
122
+ @highline.expects(:say).with("Mode: copy")
123
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [s]ave [m]ode [q]uit")
124
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g s}) }).returns('q')
125
+ @highline.expects(:agree).with("Really quit? You have unsaved changes! [yn] ").returns(false)
126
+ assert_equal nil, @console.menu(:group => @top_group, :at_top => true, :dirty => true)
127
+ end
128
+
129
+ test "select save from menu" do
130
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
131
+ @highline.expects(:say).with("1. Twitter [tweeb]")
132
+ @highline.expects(:say).with("2. Google [catfan]")
133
+ @highline.expects(:say).with("3. Google [dogfan]")
134
+ @highline.expects(:say).with("Mode: copy")
135
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [s]ave [m]ode [q]uit")
136
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g s}) }).returns('s')
137
+ assert_equal :save, @console.menu(:group => @top_group, :at_top => true, :dirty => true)
138
+ end
139
+
140
+ test "select group from menu" do
141
+ @top_group.stubs(:group_names).returns(["Blargh"])
142
+ blargh = stub('Blargh group')
143
+ @top_group.stubs(:group).with('Blargh').returns(blargh)
144
+
145
+ @highline.stubs(:color).with('Blargh', instance_of(Symbol)).
146
+ returns('Blargh')
147
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
148
+ @highline.expects(:say).with("1. Blargh")
149
+ @highline.expects(:say).with("2. Twitter [tweeb]")
150
+ @highline.expects(:say).with("3. Google [catfan]")
151
+ @highline.expects(:say).with("4. Google [dogfan]")
152
+ @highline.expects(:say).with("Mode: copy")
153
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
154
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 4 e g}) }).returns('1')
155
+ assert_equal({:group => blargh}, @console.menu(:group => @top_group, :at_top => true))
156
+ end
157
+
158
+ test "select entry from group menu" do
159
+ facebook = stub('Facebook site', {
160
+ :name => 'Facebook',
161
+ :username => 'username',
162
+ :password => 'password'
163
+ })
164
+ foo_group = stub('Foo group', {
165
+ :name => 'Foo',
166
+ :sites => [facebook],
167
+ :group_names => []
168
+ })
169
+
170
+ @highline.expects(:color).with("Foo", instance_of(Symbol)).
171
+ returns("Foo")
172
+ @highline.expects(:say).with("======== Foo =========")
173
+ @highline.expects(:say).with("1. Facebook [username]")
174
+ @highline.expects(:say).with("Mode: copy")
175
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [t]op [m]ode [q]uit")
176
+
177
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 e g t}) }).returns('1')
178
+ Clipboard.expects(:copy).with('password')
179
+ @highline.expects(:say).with("The password has been copied to your clipboard.")
180
+ assert_nil @console.menu(:group => foo_group, :at_top => false)
181
+ end
182
+
183
+ test "get password" do
184
+ question = mock('question')
185
+ question.expects(:echo=).with(false)
186
+ @highline.expects(:ask).with("Keyrack password: ").yields(question).returns("foobar")
187
+ assert_equal "foobar", @console.get_password
188
+ end
189
+
190
+ test "get new entry with manual password" do
191
+ seq = sequence("new entry")
192
+ @highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
193
+ @highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
194
+ @highline.expects(:ask).with("Generate password? [ync] ").returns("n").in_sequence(seq)
195
+ @highline.expects(:ask).with("Password: ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
196
+ @highline.expects(:ask).with("Password (again): ").yields(mock { expects(:echo=).with(false) }).returns("bar").in_sequence(seq)
197
+ @highline.expects(:say).with("Passwords didn't match. Try again!").in_sequence(seq)
198
+ @highline.expects(:ask).with("Password: ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
199
+ @highline.expects(:ask).with("Password (again): ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
200
+ assert_equal({:site => "Foo", :username => "bar", :password => "baz"}, @console.get_new_entry)
201
+ end
202
+
203
+ test "get new entry with generated password" do
204
+ seq = sequence("new entry")
205
+ @highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
206
+ @highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
207
+ @highline.expects(:ask).with("Generate password? [ync] ").returns("y").in_sequence(seq)
208
+ Keyrack::Utils.expects(:generate_password).returns('foobar').in_sequence(seq)
209
+ @highline.expects(:color).with('foobar', instance_of(Symbol)).
210
+ returns('foobar').in_sequence(seq)
211
+ @highline.expects(:ask).with("Generated foobar. Sound good? [ync] ").returns("n").in_sequence(seq)
212
+ Keyrack::Utils.expects(:generate_password).returns('foobar').in_sequence(seq)
213
+ @highline.expects(:color).with('foobar', instance_of(Symbol)).
214
+ returns('foobar').in_sequence(seq)
215
+ @highline.expects(:ask).with("Generated foobar. Sound good? [ync] ").returns("y").in_sequence(seq)
216
+ assert_equal({:site => "Foo", :username => "bar", :password => "foobar"}, @console.get_new_entry)
217
+ end
218
+
219
+ test "get new entry with cancel" do
220
+ seq = sequence("new entry")
221
+ @highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
222
+ @highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
223
+
224
+ question = mock('question')
225
+ question.expects(:in=).with(%w{y n c})
226
+ @highline.expects(:ask).with("Generate password? [ync] ").yields(question).returns('c').in_sequence(seq)
227
+ assert_nil @console.get_new_entry
228
+ end
229
+
230
+ test "get new entry with generated password cancel" do
231
+ seq = sequence("new entry")
232
+ @highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
233
+ @highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
234
+ @highline.expects(:ask).with("Generate password? [ync] ").returns("y").in_sequence(seq)
235
+ Keyrack::Utils.expects(:generate_password).returns('foobar').in_sequence(seq)
236
+ @highline.expects(:color).with('foobar', instance_of(Symbol)).
237
+ returns('foobar').in_sequence(seq)
238
+ @highline.expects(:ask).with("Generated foobar. Sound good? [ync] ").returns('c').in_sequence(seq)
239
+ @highline.expects(:ask).with("Password: ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
240
+ @highline.expects(:ask).with("Password (again): ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
241
+ assert_equal({:site => "Foo", :username => "bar", :password => "baz"}, @console.get_new_entry)
242
+ end
243
+
244
+ test "display first time notice" do
245
+ @highline.expects(:say).with("This looks like your first time using Keyrack. I'll need to ask you a few questions first.")
246
+ @console.display_first_time_notice
247
+ end
248
+
249
+ test "password setup" do
250
+ seq = sequence("password setup")
251
+ @highline.expects(:ask).with("New passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
252
+ @highline.expects(:ask).with("Confirm passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('small').in_sequence(seq)
253
+ @highline.expects(:say).with("Passphrases didn't match.").in_sequence(seq)
254
+ @highline.expects(:ask).with("New passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
255
+ @highline.expects(:ask).with("Confirm passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
256
+ assert_equal 'huge', @console.password_setup
257
+ end
258
+
259
+ test "store setup for filesystem" do
260
+ @highline.expects(:choose).yields(mock {
261
+ expects(:header=).with("Choose storage type")
262
+ expects(:choices).with("filesystem", "ssh")
263
+ }).returns("filesystem")
264
+
265
+ expected = {'type' => 'filesystem', 'path' => 'database'}
266
+ assert_equal expected, @console.store_setup
267
+ end
268
+
269
+ test "store setup for ssh" do
270
+ seq = sequence("store setup")
271
+ @highline.expects(:choose).yields(mock {
272
+ expects(:header=).with("Choose storage type")
273
+ expects(:choices).with("filesystem", "ssh")
274
+ }).returns("ssh").in_sequence(seq)
275
+ @highline.expects(:ask).with("Host: ").returns("example.com").in_sequence(seq)
276
+ @highline.expects(:ask).with("User: ").returns("dudeguy").in_sequence(seq)
277
+ @highline.expects(:ask).with("Remote path: ").returns(".keyrack/database").in_sequence(seq)
278
+
279
+ expected = {'type' => 'ssh', 'host' => 'example.com', 'user' => 'dudeguy', 'path' => '.keyrack/database'}
280
+ assert_equal expected, @console.store_setup
281
+ end
282
+
283
+ test "get new group" do
284
+ @highline.expects(:ask).with("Group: ").yields(mock {
285
+ expects(:validate=).with(/^\w[\w\s]*$/)
286
+ }).returns("Foo")
287
+ assert_equal('Foo', @console.get_new_group)
288
+ end
289
+
290
+ test "choose entry to edit" do
291
+ seq = SequenceHelper.new("editing")
292
+ @highline.expects(:color).with("Choose entry", instance_of(Symbol)).returns("Choose entry")
293
+ seq << @highline.expects(:say).with('=== Choose entry ===')
294
+ seq << @highline.expects(:say).with("1. Twitter [tweeb]")
295
+ seq << @highline.expects(:say).with("2. Google [catfan]")
296
+ seq << @highline.expects(:say).with("3. Google [dogfan]")
297
+ seq << @highline.expects(:say).with("c. Cancel")
298
+ seq << @highline.expects(:ask).yields(mock { expects(:in=).with(%w{c 1 2 3}) }).returns('1')
299
+ assert_equal({:site => @twitter}, @console.choose_entry_to_edit(@top_group))
300
+ end
301
+
302
+ test "choose group to edit" do
303
+ seq = SequenceHelper.new("editing")
304
+ @top_group.expects(:group_names).returns(%w{Foo})
305
+ @highline.expects(:color).with("Choose entry", instance_of(Symbol)).
306
+ returns("Choose entry")
307
+ @highline.expects(:color).with("Foo", instance_of(Symbol)).
308
+ returns("Foo")
309
+ seq << @highline.expects(:say).with('=== Choose entry ===')
310
+ seq << @highline.expects(:say).with("1. Foo")
311
+ seq << @highline.expects(:say).with("2. Twitter [tweeb]")
312
+ seq << @highline.expects(:say).with("3. Google [catfan]")
313
+ seq << @highline.expects(:say).with("4. Google [dogfan]")
314
+ seq << @highline.expects(:say).with("c. Cancel")
315
+ seq << @highline.expects(:ask).yields(mock { expects(:in=).with(%w{c 1 2 3 4}) }).returns('1')
316
+ assert_equal({:group => "Foo"}, @console.choose_entry_to_edit(@top_group))
317
+ end
318
+
319
+ test "choose entry to edit with cancel" do
320
+ seq = SequenceHelper.new("editing")
321
+ @highline.expects(:color).with("Choose entry", instance_of(Symbol)).returns("Choose entry")
322
+ seq << @highline.expects(:say).with('=== Choose entry ===')
323
+ seq << @highline.expects(:say).with("1. Twitter [tweeb]")
324
+ seq << @highline.expects(:say).with("2. Google [catfan]")
325
+ seq << @highline.expects(:say).with("3. Google [dogfan]")
326
+ seq << @highline.expects(:say).with("c. Cancel")
327
+ seq << @highline.expects(:ask).yields(mock { expects(:in=).with(%w{c 1 2 3}) }).returns('c')
328
+ assert_nil @console.choose_entry_to_edit(@top_group)
329
+ end
330
+
331
+ test "selecting username from edit menu" do
332
+ seq = sequence("editing")
333
+
334
+ @highline.expects(:color).with("Twitter [tweeb]", instance_of(Symbol)).
335
+ returns("Twitter [tweeb]")
336
+ @highline.expects(:say).with("Editing entry: Twitter [tweeb]").in_sequence(seq)
337
+ @highline.expects(:say).with("u. Change username").in_sequence(seq)
338
+ @highline.expects(:say).with("p. Change password").in_sequence(seq)
339
+ @highline.expects(:say).with("d. Delete").in_sequence(seq)
340
+ @highline.expects(:say).with("c. Cancel").in_sequence(seq)
341
+ @highline.expects(:ask).yields(mock {
342
+ expects(:in=).with(%w{u p d c})
343
+ }).returns('u').in_sequence(seq)
344
+
345
+ actual = @console.edit_entry(@twitter)
346
+ assert_equal :change_username, actual
347
+ end
348
+
349
+ test "selecting password from edit menu" do
350
+ seq = sequence("editing")
351
+
352
+ @highline.expects(:color).with("Twitter [tweeb]", instance_of(Symbol)).
353
+ returns("Twitter [tweeb]")
354
+ @highline.expects(:say).with("Editing entry: Twitter [tweeb]").in_sequence(seq)
355
+ @highline.expects(:say).with("u. Change username").in_sequence(seq)
356
+ @highline.expects(:say).with("p. Change password").in_sequence(seq)
357
+ @highline.expects(:say).with("d. Delete").in_sequence(seq)
358
+ @highline.expects(:say).with("c. Cancel").in_sequence(seq)
359
+ @highline.expects(:ask).yields(mock {
360
+ expects(:in=).with(%w{u p d c})
361
+ }).returns('p').in_sequence(seq)
362
+
363
+ actual = @console.edit_entry(@twitter)
364
+ assert_equal :change_password, actual
365
+ end
366
+
367
+ test "selecting delete from edit menu" do
368
+ seq = sequence("editing")
369
+
370
+ @highline.expects(:color).with("Twitter [tweeb]", instance_of(Symbol)).
371
+ returns("Twitter [tweeb]")
372
+ @highline.expects(:say).with("Editing entry: Twitter [tweeb]").in_sequence(seq)
373
+ @highline.expects(:say).with("u. Change username").in_sequence(seq)
374
+ @highline.expects(:say).with("p. Change password").in_sequence(seq)
375
+ @highline.expects(:say).with("d. Delete").in_sequence(seq)
376
+ @highline.expects(:say).with("c. Cancel").in_sequence(seq)
377
+ @highline.expects(:ask).yields(mock {
378
+ expects(:in=).with(%w{u p d c})
379
+ }).returns('d').in_sequence(seq)
380
+
381
+ actual = @console.edit_entry(@twitter)
382
+ assert_equal :delete, actual
383
+ end
384
+
385
+ test "cancel edit selection" do
386
+ seq = sequence("editing")
387
+
388
+ @highline.expects(:color).with("Twitter [tweeb]", instance_of(Symbol)).
389
+ returns("Twitter [tweeb]")
390
+ @highline.expects(:say).with("Editing entry: Twitter [tweeb]").in_sequence(seq)
391
+ @highline.expects(:say).with("u. Change username").in_sequence(seq)
392
+ @highline.expects(:say).with("p. Change password").in_sequence(seq)
393
+ @highline.expects(:say).with("d. Delete").in_sequence(seq)
394
+ @highline.expects(:say).with("c. Cancel").in_sequence(seq)
395
+ @highline.expects(:ask).yields(mock {
396
+ expects(:in=).with(%w{u p d c})
397
+ }).returns('c').in_sequence(seq)
398
+
399
+ assert_nil @console.edit_entry(@twitter)
400
+ end
401
+
402
+ test "changing username" do
403
+ seq = SequenceHelper.new("changing username")
404
+ @highline.expects(:color).with("username", instance_of(Symbol)).returns("username")
405
+ seq << @highline.expects(:say).with("Current username: username")
406
+ seq << @highline.expects(:ask).with("New username (blank to cancel): ").returns("foo")
407
+ assert_equal "foo", @console.change_username('username')
408
+ end
409
+
410
+ test "switching mode from menu" do
411
+ @console.mode = :copy
412
+
413
+ @highline.expects(:say).with("=== Keyrack Main Menu ===")
414
+ @highline.expects(:say).with("1. Twitter [tweeb]")
415
+ @highline.expects(:say).with("2. Google [catfan]")
416
+ @highline.expects(:say).with("3. Google [dogfan]")
417
+ @highline.expects(:say).with("Mode: copy")
418
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
419
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g}) }).returns('m')
420
+ assert_nil @console.menu(:group => @top_group, :at_top => true)
421
+ assert_equal :print, @console.mode
422
+ end
423
+
424
+ test "confirm overwrite entry" do
425
+ @highline.expects(:color).with("Twitter [tweeb]", instance_of(Symbol)).
426
+ returns("Twitter [tweeb]")
427
+ @highline.expects(:agree).with("There's already an entry for: Twitter [tweeb]. Do you want to overwrite it? [yn] ").returns(true)
428
+ assert_equal true, @console.confirm_overwrite_entry(@twitter)
429
+ end
430
+
431
+ test "confirm delete entry" do
432
+ @highline.expects(:color).with("Twitter [tweeb]", instance_of(Symbol)).
433
+ returns("Twitter [tweeb]")
434
+ @highline.expects(:agree).with("You're about to delete Twitter [tweeb]. Are you sure? [yn] ").returns(true)
435
+ assert_equal true, @console.confirm_delete_entry(@twitter)
436
+ end
437
+
438
+ test "top command" do
439
+ foo_group = stub('Foo group', :name => 'Foo', :sites => [], :group_names => [])
440
+ @highline.stubs(:color).with('Foo', instance_of(Symbol)).returns('Foo')
441
+ @highline.expects(:say).with("=== Foo ===")
442
+ @highline.expects(:say).with("Mode: copy")
443
+ @highline.expects(:say).with("Commands: [n]ew [g]roup [t]op [m]ode [q]uit")
444
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m g t}) }).returns('t')
445
+ assert_equal :top, @console.menu(:group => foo_group, :at_top => false)
446
+ end
447
+
448
+ test "up command" do
449
+ foo_group = stub('Foo group', :name => 'Foo', :sites => [], :group_names => [])
450
+ @highline.stubs(:color).with('Foo', instance_of(Symbol)).returns('Foo')
451
+ @highline.expects(:say).with("=== Foo ===")
452
+ @highline.expects(:say).with("Mode: copy")
453
+ @highline.expects(:say).with("Commands: [n]ew [g]roup [u]p [t]op [m]ode [q]uit")
454
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m g u t}) }).returns('u')
455
+ assert_equal :up, @console.menu(:group => foo_group, :at_top => false, :enable_up => true)
456
+ end
457
+
458
+ test "invalid password" do
459
+ @highline.expects(:say).with("Invalid password.")
460
+ @console.display_invalid_password_notice
461
+ end
462
+
463
+ test "menu prints two columns" do
464
+ # ========== Keyrack Main Menu =========
465
+ # 1. Twitter [tweeb] 2. Google [catfan]
466
+ # 3. Google [dogfan]
467
+
468
+ HighLine::SystemExtensions.expects(:terminal_size).returns([40, 32])
469
+ seq = sequence('say')
470
+ @highline.expects(:say).with('========= Keyrack Main Menu =========')
471
+ @highline.expects(:say).with("1. Twitter [tweeb] ")
472
+ @highline.expects(:say).with("2. Google [catfan]")
473
+ @highline.expects(:say).with("3. Google [dogfan]")
474
+ @highline.expects(:say).with("Mode: copy")
475
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
476
+
477
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g}) }).returns('q')
478
+ assert_equal :quit, @console.menu(:group => @top_group, :at_top => true)
479
+ end
480
+
481
+ test "menu prints three columns" do
482
+ HighLine::SystemExtensions.expects(:terminal_size).returns([80, 32])
483
+ @twitter.stubs(:usernames).returns(%w{foo})
484
+ seq = sequence('say')
485
+ @highline.expects(:say).with('============================ Keyrack Main Menu ============================')
486
+ @highline.expects(:say).with("1. Twitter [tweeb] ")
487
+ @highline.expects(:say).with("2. Google [catfan] ")
488
+ @highline.expects(:say).with("3. Google [dogfan]")
489
+ @highline.expects(:say).with("Mode: copy")
490
+ @highline.expects(:say).with("Commands: [n]ew [e]dit [g]roup [m]ode [q]uit")
491
+
492
+ @highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q m 1 2 3 e g}) }).returns('q')
493
+ assert_equal :quit, @console.menu(:group => @top_group, :at_top => true)
494
+ end
495
+ end