keyrack 0.2.0 → 0.2.1
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.
- data/TODO +4 -0
- data/VERSION +1 -1
- data/features/console.feature +43 -3
- data/features/step_definitions/keyrack_steps.rb +11 -0
- data/lib/keyrack/database.rb +23 -6
- data/lib/keyrack/runner.rb +13 -2
- data/lib/keyrack/ui/console.rb +76 -22
- data/test/keyrack/test_database.rb +28 -0
- data/test/keyrack/test_runner.rb +18 -6
- data/test/keyrack/ui/test_console.rb +170 -123
- metadata +6 -4
data/TODO
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
data/features/console.feature
CHANGED
@@ -11,7 +11,12 @@ Feature: Console runner
|
|
11
11
|
* I wait a few seconds
|
12
12
|
* the output should contain "Choose storage type:"
|
13
13
|
* I type "filesystem"
|
14
|
-
|
14
|
+
|
15
|
+
* the output should contain "g. New group"
|
16
|
+
* I type "g" to add a new group
|
17
|
+
* the output should contain "Group:"
|
18
|
+
* I type "Social"
|
19
|
+
* the output should contain "n. New entry"
|
15
20
|
* I type "n" to add a new entry
|
16
21
|
* the output should contain "Label:"
|
17
22
|
* I type "Twitter"
|
@@ -23,9 +28,28 @@ Feature: Console runner
|
|
23
28
|
* I type "kittens"
|
24
29
|
* the output should contain "Password (again):"
|
25
30
|
* I type "kittens"
|
26
|
-
* the output should contain "1. Twitter"
|
31
|
+
* the output should contain "1. Twitter [dudeguy]"
|
27
32
|
* the output should also contain "s. Save"
|
28
33
|
* I type "s" to save the database
|
34
|
+
* the output should contain "t. Top level menu"
|
35
|
+
* I type "t"
|
36
|
+
* the output should match /1\. .+Social.+/
|
37
|
+
|
38
|
+
* the output should also contain "n. New entry"
|
39
|
+
* I type "n" to add a new entry
|
40
|
+
* the output should contain "Label:"
|
41
|
+
* I type "Company X"
|
42
|
+
* the output should contain "Username:"
|
43
|
+
* I type "buddypal"
|
44
|
+
* the output should contain "Generate password?"
|
45
|
+
* I type "y" for yes
|
46
|
+
* the output should contain "Sound good? [yn]"
|
47
|
+
* I type "y" for yes
|
48
|
+
* the output should contain "2. Company X [buddypal]"
|
49
|
+
* the output should also contain "s. Save"
|
50
|
+
* I type "s" to save the database
|
51
|
+
|
52
|
+
* the output should contain "q. Quit"
|
29
53
|
* I type "q" to quit
|
30
54
|
* I wait a few seconds
|
31
55
|
* I run keyrack interactively again
|
@@ -33,7 +57,23 @@ Feature: Console runner
|
|
33
57
|
* the output should contain "Keyrack password:"
|
34
58
|
* I type "secret"
|
35
59
|
* I wait a few seconds
|
36
|
-
* the output should
|
60
|
+
* the output should match /1\. .+Social.+/
|
61
|
+
* the output should also contain "2. Company X [buddypal]"
|
62
|
+
* I type "1" for Social
|
63
|
+
* the output should contain "1. Twitter [dudeguy]"
|
37
64
|
* I type "1" for Twitter
|
38
65
|
* my clipboard should contain "kittens"
|
66
|
+
|
67
|
+
* the output should contain "d. Delete entry"
|
68
|
+
* I type "d"
|
69
|
+
* the output should contain "1. Twitter [dudeguy]"
|
70
|
+
* I type "1"
|
71
|
+
* the output should contain "Are you sure?"
|
72
|
+
* I type "y"
|
73
|
+
* the output should contain "t. Top level menu"
|
74
|
+
* I type "t"
|
75
|
+
|
76
|
+
* the output should contain "Main Menu"
|
39
77
|
* I type "q" to quit
|
78
|
+
* the output should contain "Really quit?" (since the database is dirty)
|
79
|
+
* I type "y"
|
@@ -25,6 +25,17 @@ Then /the output should contain "([^"]+)"/ do |expected|
|
|
25
25
|
@output.should include(expected)
|
26
26
|
end
|
27
27
|
|
28
|
+
Then %r{the output should match /([^/]+)/} do |expected|
|
29
|
+
# This won't work for escaped backslashes
|
30
|
+
if @slept
|
31
|
+
@slept = false
|
32
|
+
else
|
33
|
+
sleep 1
|
34
|
+
end
|
35
|
+
@output = @out.read_nonblock(255)
|
36
|
+
@output.should match(Regexp.new(expected))
|
37
|
+
end
|
38
|
+
|
28
39
|
Then /the output should also contain "([^"]+)"/ do |expected|
|
29
40
|
@output.should include(expected)
|
30
41
|
end
|
data/lib/keyrack/database.rb
CHANGED
@@ -8,17 +8,28 @@ module Keyrack
|
|
8
8
|
@dirty = false
|
9
9
|
end
|
10
10
|
|
11
|
-
def add(site, username, password)
|
12
|
-
@data[
|
11
|
+
def add(site, username, password, options = {})
|
12
|
+
hash = options[:group] ? @data[options[:group]] ||= {} : @data
|
13
|
+
hash[site] = { :username => username, :password => password }
|
13
14
|
@dirty = true
|
14
15
|
end
|
15
16
|
|
16
|
-
def get(site)
|
17
|
-
@data[site]
|
17
|
+
def get(site, options = {})
|
18
|
+
(options[:group] ? @data[options[:group]] : @data)[site]
|
18
19
|
end
|
19
20
|
|
20
|
-
def sites
|
21
|
-
@data
|
21
|
+
def sites(options = {})
|
22
|
+
hash = options[:group] ? @data[options[:group]] : @data
|
23
|
+
if hash
|
24
|
+
hash.keys.select { |k| hash[k].keys.include?(:username) }.sort
|
25
|
+
else
|
26
|
+
# new groups are empty
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def groups
|
32
|
+
@data.keys.reject { |k| @data[k].keys.include?(:username) }.sort
|
22
33
|
end
|
23
34
|
|
24
35
|
def dirty?
|
@@ -32,6 +43,12 @@ module Keyrack
|
|
32
43
|
@dirty = false
|
33
44
|
end
|
34
45
|
|
46
|
+
def delete(site, options = {})
|
47
|
+
hash = options[:group] ? @data[options[:group]] : @data
|
48
|
+
hash.delete(site)
|
49
|
+
@dirty = true
|
50
|
+
end
|
51
|
+
|
35
52
|
private
|
36
53
|
def decrypt
|
37
54
|
data = @store.read
|
data/lib/keyrack/runner.rb
CHANGED
@@ -54,15 +54,26 @@ module Keyrack
|
|
54
54
|
end
|
55
55
|
|
56
56
|
def main_loop
|
57
|
+
options = {}
|
57
58
|
loop do
|
58
|
-
|
59
|
+
choice = @ui.menu(options)
|
60
|
+
|
61
|
+
case choice
|
59
62
|
when :new
|
60
63
|
result = @ui.get_new_entry
|
61
|
-
@database.add(result[:site], result[:username], result[:password])
|
64
|
+
@database.add(result[:site], result[:username], result[:password], options)
|
65
|
+
when :delete
|
66
|
+
@ui.delete_entry(options)
|
67
|
+
when :new_group
|
68
|
+
options = @ui.get_new_group
|
62
69
|
when :save
|
63
70
|
@database.save
|
64
71
|
when :quit
|
65
72
|
break
|
73
|
+
when Hash
|
74
|
+
options = choice
|
75
|
+
when :top
|
76
|
+
options = {}
|
66
77
|
end
|
67
78
|
end
|
68
79
|
end
|
data/lib/keyrack/ui/console.rb
CHANGED
@@ -10,40 +10,72 @@ module Keyrack
|
|
10
10
|
@highline.ask("Keyrack password: ") { |q| q.echo = false }
|
11
11
|
end
|
12
12
|
|
13
|
-
def menu
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@
|
13
|
+
def menu(options = {})
|
14
|
+
choices = {'n' => :new, 'q' => :quit}
|
15
|
+
index = 1
|
16
|
+
|
17
|
+
if !options[:group]
|
18
|
+
# Can't have subgroups (yet?).
|
19
|
+
@highline.say("=== #{@highline.color("Keyrack Main Menu", :yellow)} ===")
|
20
|
+
@database.groups.each do |group|
|
21
|
+
choices[index.to_s] = {:group => group}
|
22
|
+
@highline.say("% 2d. %s" % [index, @highline.color(group, :green)])
|
23
|
+
index += 1
|
24
|
+
end
|
25
|
+
else
|
26
|
+
@highline.say("===== #{@highline.color(options[:group], :green)} =====")
|
27
|
+
end
|
28
|
+
|
29
|
+
sites = @database.sites(options)
|
30
|
+
sites.each do |site|
|
31
|
+
entry = @database.get(site, options)
|
32
|
+
choices[index.to_s] = entry
|
33
|
+
@highline.say("% 2d. %s [%s]" % [index, site, entry[:username]])
|
34
|
+
index += 1
|
35
|
+
end
|
36
|
+
|
37
|
+
@highline.say(" n. New entry")
|
38
|
+
if !sites.empty?
|
39
|
+
choices['d'] = :delete
|
40
|
+
@highline.say(" d. Delete entry")
|
41
|
+
end
|
42
|
+
if !options[:group]
|
43
|
+
choices['g'] = :new_group
|
44
|
+
@highline.say(" g. New group")
|
45
|
+
else
|
46
|
+
choices['t'] = :top
|
47
|
+
@highline.say(" t. Top level menu")
|
21
48
|
end
|
22
|
-
@highline.say(" n. Add new")
|
23
49
|
if @database.dirty?
|
50
|
+
choices['s'] = :save
|
24
51
|
@highline.say(" s. Save")
|
25
|
-
choices << "s"
|
26
52
|
end
|
27
53
|
@highline.say(" q. Quit")
|
28
|
-
|
54
|
+
answer = @highline.ask(" ? ") { |q| q.in = choices.keys }
|
55
|
+
result = choices[answer]
|
29
56
|
case result
|
30
|
-
when
|
31
|
-
:
|
32
|
-
when "s"
|
33
|
-
:save
|
34
|
-
when "q"
|
35
|
-
if @database.dirty? && !@highline.agree("Really quit? You have unsaved changes! [yn] ")
|
57
|
+
when Symbol
|
58
|
+
if result == :quit && @database.dirty? && !@highline.agree("Really quit? You have unsaved changes! [yn] ")
|
36
59
|
nil
|
37
60
|
else
|
38
|
-
|
61
|
+
result
|
62
|
+
end
|
63
|
+
when Hash
|
64
|
+
if result.has_key?(:group)
|
65
|
+
result
|
66
|
+
else
|
67
|
+
Copier(result[:password])
|
68
|
+
@highline.say("The password has been copied to your clipboard.")
|
69
|
+
nil
|
39
70
|
end
|
40
|
-
else
|
41
|
-
Copier(entries[result.to_i - 1][:password])
|
42
|
-
@highline.say("The password has been copied to your clipboard.")
|
43
|
-
nil
|
44
71
|
end
|
45
72
|
end
|
46
73
|
|
74
|
+
def get_new_group
|
75
|
+
group = @highline.ask("Group: ") { |q| q.validate = /^\w[\w\s]*$/ }
|
76
|
+
{:group => group}
|
77
|
+
end
|
78
|
+
|
47
79
|
def get_new_entry
|
48
80
|
result = {}
|
49
81
|
result[:site] = @highline.ask("Label: ")
|
@@ -103,6 +135,28 @@ module Keyrack
|
|
103
135
|
|
104
136
|
result
|
105
137
|
end
|
138
|
+
|
139
|
+
def delete_entry(options = {})
|
140
|
+
choices = {'c' => :cancel}
|
141
|
+
index = 1
|
142
|
+
@highline.say("Choose entry to delete:")
|
143
|
+
@database.sites(options).each do |site|
|
144
|
+
entry = @database.get(site, options)
|
145
|
+
choices[index.to_s] = {:site => site, :username => entry[:username]}
|
146
|
+
@highline.say("% 2d. %s [%s]" % [index, site, entry[:username]])
|
147
|
+
index += 1
|
148
|
+
end
|
149
|
+
@highline.say(" c. Cancel")
|
150
|
+
|
151
|
+
answer = @highline.ask(" ? ") { |q| q.in = choices.keys }
|
152
|
+
result = choices[answer]
|
153
|
+
if result != :cancel
|
154
|
+
entry = @highline.color("#{result[:site]} [#{result[:username]}]", :red)
|
155
|
+
if @highline.agree("You're about to delete #{entry}. Are you sure? [yn] ")
|
156
|
+
@database.delete(result[:site], options)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
106
160
|
end
|
107
161
|
end
|
108
162
|
end
|
@@ -30,7 +30,16 @@ module Keyrack
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def test_sites
|
33
|
+
@database.add('Blargh', 'dudeguy', 'secret', :group => "Junk")
|
33
34
|
assert_equal(%w{Twitter}, @database.sites)
|
35
|
+
assert_equal(%w{Blargh}, @database.sites(:group => "Junk"))
|
36
|
+
assert_equal([], @database.sites(:group => "New group"))
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_groups
|
40
|
+
assert_equal [], @database.groups
|
41
|
+
@database.add('Blargh', 'dudeguy', 'secret', :group => "Junk")
|
42
|
+
assert_equal %w{Junk}, @database.groups
|
34
43
|
end
|
35
44
|
|
36
45
|
def test_dirty
|
@@ -48,5 +57,24 @@ module Keyrack
|
|
48
57
|
@database.save
|
49
58
|
assert_equal 501, @database.sites.length
|
50
59
|
end
|
60
|
+
|
61
|
+
def test_add_with_top_level_group
|
62
|
+
@database.add('Twitter', 'dudeguy', 'secret', :group => "Social")
|
63
|
+
expected = {:username => 'dudeguy', :password => 'secret'}
|
64
|
+
assert_equal expected, @database.get('Twitter', :group => "Social")
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_delete
|
68
|
+
@database.delete('Twitter')
|
69
|
+
assert_equal [], @database.sites
|
70
|
+
assert @database.dirty?
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_delete_group_entry
|
74
|
+
@database.add('Facebook', 'dudeguy', 'secret', :group => "Social")
|
75
|
+
@database.delete('Facebook', :group => 'Social')
|
76
|
+
assert_equal [], @database.sites(:group => 'Social')
|
77
|
+
assert_equal ['Twitter'], @database.sites
|
78
|
+
end
|
51
79
|
end
|
52
80
|
end
|
data/test/keyrack/test_runner.rb
CHANGED
@@ -5,7 +5,7 @@ module Keyrack
|
|
5
5
|
def setup
|
6
6
|
@console = stub('console', {
|
7
7
|
:get_password => 'secret',
|
8
|
-
:database= => nil, :menu =>
|
8
|
+
:database= => nil, :menu => :quit,
|
9
9
|
:get_new_entry => {:site => "Foo", :username => "bar", :password => "baz"}
|
10
10
|
})
|
11
11
|
UI::Console.stubs(:new).returns(@console)
|
@@ -40,10 +40,23 @@ module Keyrack
|
|
40
40
|
|
41
41
|
@console.expects(:menu).returns(:new).in_sequence(seq)
|
42
42
|
@console.expects(:get_new_entry).returns({:site => "Foo", :username => "bar", :password => "baz"}).in_sequence(seq)
|
43
|
-
@database.expects(:add).with("Foo", "bar", "baz")
|
43
|
+
@database.expects(:add).with("Foo", "bar", "baz", {})
|
44
44
|
@console.expects(:menu).returns(nil).in_sequence(seq)
|
45
45
|
@console.expects(:menu).returns(:save).in_sequence(seq)
|
46
46
|
@database.expects(:save).in_sequence(seq)
|
47
|
+
@console.expects(:menu).returns(:delete).in_sequence(seq)
|
48
|
+
@console.expects(:delete_entry).with({}).in_sequence(seq)
|
49
|
+
@console.expects(:menu).returns(:new_group).in_sequence(seq)
|
50
|
+
@console.expects(:get_new_group).returns(:group => 'Blah').in_sequence(seq)
|
51
|
+
@console.expects(:menu).with(:group => 'Blah').returns(:top).in_sequence(seq)
|
52
|
+
@console.expects(:menu).returns(:group => 'Huge').in_sequence(seq)
|
53
|
+
@console.expects(:menu).with(:group => 'Huge').returns(nil).in_sequence(seq)
|
54
|
+
@console.expects(:menu).with(:group => 'Huge').returns(:new).in_sequence(seq)
|
55
|
+
@console.expects(:get_new_entry).returns({:site => "Bar", :username => "bar", :password => "baz"}).in_sequence(seq)
|
56
|
+
@database.expects(:add).with("Bar", "bar", "baz", :group => 'Huge')
|
57
|
+
@console.expects(:menu).with(:group => "Huge").returns(:delete).in_sequence(seq)
|
58
|
+
@console.expects(:delete_entry).with(:group => 'Huge').in_sequence(seq)
|
59
|
+
@console.expects(:menu).with(:group => 'Huge').returns(:top).in_sequence(seq)
|
47
60
|
@console.expects(:menu).returns(:quit).in_sequence(seq)
|
48
61
|
|
49
62
|
runner = Runner.new(["-d", keyrack_dir])
|
@@ -67,10 +80,9 @@ module Keyrack
|
|
67
80
|
rsa.expects(:public_encrypt).with(dump).returns("encrypted dump")
|
68
81
|
|
69
82
|
# Store setup
|
70
|
-
|
71
|
-
@console.expects(:store_setup).returns('type' => 'filesystem', 'path' => store_path).in_sequence(seq)
|
83
|
+
@console.expects(:store_setup).returns('type' => 'filesystem', 'path' => 'database').in_sequence(seq)
|
72
84
|
store = mock('filesystem store')
|
73
|
-
Store::Filesystem.expects(:new).with('path' =>
|
85
|
+
Store::Filesystem.expects(:new).with('path' => File.join(keyrack_dir, 'database')).returns(store).in_sequence(seq)
|
74
86
|
|
75
87
|
Database.expects(:new).with('foobar', 'barfoo', store).returns(@database).in_sequence(seq)
|
76
88
|
@console.expects(:database=).with(@database).in_sequence(seq)
|
@@ -91,7 +103,7 @@ module Keyrack
|
|
91
103
|
assert File.exist?(expected_config_file)
|
92
104
|
expected_config = {
|
93
105
|
'rsa' => expected_rsa_file, 'aes' => expected_aes_file,
|
94
|
-
'store' => { 'type' => 'filesystem', 'path' =>
|
106
|
+
'store' => { 'type' => 'filesystem', 'path' => File.join(keyrack_dir, 'database') }
|
95
107
|
}
|
96
108
|
assert_equal expected_config, YAML.load_file(expected_config_file)
|
97
109
|
end
|
@@ -4,199 +4,246 @@ module Keyrack
|
|
4
4
|
module UI
|
5
5
|
class TestConsole < Test::Unit::TestCase
|
6
6
|
def setup
|
7
|
-
@database = stub('database', :sites => %w{Twitter}, :dirty? => false) do
|
8
|
-
stubs(:get).with('Twitter').returns({
|
7
|
+
@database = stub('database', :sites => %w{Twitter}, :groups => [], :dirty? => false) do
|
8
|
+
stubs(:get).with('Twitter', {}).returns({
|
9
9
|
:username => 'username', :password => 'password'
|
10
10
|
})
|
11
11
|
end
|
12
|
+
@highline = stub('highline')
|
13
|
+
@highline.stubs(:color).with("Keyrack Main Menu", :yellow).returns("yellowKeyrack Main Menu")
|
14
|
+
@highline.stubs(:say)
|
15
|
+
HighLine.expects(:new).returns(@highline)
|
16
|
+
@console = Console.new
|
12
17
|
end
|
13
18
|
|
14
19
|
def test_select_entry_from_menu
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
highline.expects(:say).with("
|
21
|
-
highline.expects(:say).with("
|
22
|
-
highline.expects(:say).with(" q. Quit")
|
20
|
+
@console.database = @database
|
21
|
+
@highline.expects(:say).with("=== yellowKeyrack Main Menu ===")
|
22
|
+
@highline.expects(:say).with(" 1. Twitter [username]")
|
23
|
+
@highline.expects(:say).with(" n. New entry")
|
24
|
+
@highline.expects(:say).with(" d. Delete entry")
|
25
|
+
@highline.expects(:say).with(" g. New group")
|
26
|
+
@highline.expects(:say).with(" q. Quit")
|
23
27
|
|
24
28
|
question = mock('question')
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
assert_nil console.menu
|
29
|
+
@highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q 1 d g}) }).returns('1')
|
30
|
+
@console.expects(:Copier).with('password')
|
31
|
+
@highline.expects(:say).with("The password has been copied to your clipboard.")
|
32
|
+
assert_nil @console.menu
|
30
33
|
end
|
31
34
|
|
32
35
|
def test_select_new_from_menu
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
@console.database = @database
|
37
|
+
|
38
|
+
# === yellowKeyrack Main Menu ===
|
39
|
+
# 1. Twitter [username]
|
40
|
+
# n. New entry
|
41
|
+
# d. Delete entry
|
42
|
+
# g. New group
|
43
|
+
# q. Quit
|
44
|
+
|
45
|
+
@highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q 1 d g}) }).returns('n')
|
46
|
+
assert_equal :new, @console.menu
|
47
|
+
end
|
37
48
|
|
38
|
-
|
39
|
-
|
40
|
-
|
49
|
+
def test_select_delete_from_menu
|
50
|
+
@console.database = @database
|
51
|
+
|
52
|
+
# === yellowKeyrack Main Menu ===
|
53
|
+
# 1. Twitter [username]
|
54
|
+
# n. New entry
|
55
|
+
# d. Delete entry
|
56
|
+
# g. New group
|
57
|
+
# q. Quit
|
41
58
|
|
42
59
|
question = mock('question')
|
43
|
-
question.expects(:in=).with(%w{n q 1})
|
44
|
-
highline.expects(:ask).yields(question).returns('
|
45
|
-
assert_equal :
|
60
|
+
question.expects(:in=).with(%w{n q 1 d g})
|
61
|
+
@highline.expects(:ask).yields(question).returns('d')
|
62
|
+
assert_equal :delete, @console.menu
|
46
63
|
end
|
47
64
|
|
48
65
|
def test_select_quit_from_menu
|
49
|
-
|
50
|
-
HighLine.expects(:new).returns(highline)
|
51
|
-
console = Console.new
|
52
|
-
console.database = @database
|
66
|
+
@console.database = @database
|
53
67
|
|
54
|
-
|
55
|
-
|
56
|
-
|
68
|
+
# === yellowKeyrack Main Menu ===
|
69
|
+
# 1. Twitter [username]
|
70
|
+
# n. New entry
|
71
|
+
# d. Delete entry
|
72
|
+
# g. New group
|
73
|
+
# q. Quit
|
57
74
|
|
58
|
-
|
59
|
-
|
60
|
-
highline.expects(:ask).yields(question).returns('q')
|
61
|
-
assert_equal :quit, console.menu
|
75
|
+
@highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q 1 d g}) }).returns('q')
|
76
|
+
assert_equal :quit, @console.menu
|
62
77
|
end
|
63
78
|
|
64
79
|
def test_select_quit_from_menu_when_database_is_dirty
|
65
|
-
|
66
|
-
HighLine.expects(:new).returns(highline)
|
67
|
-
console = Console.new
|
68
|
-
console.database = @database
|
80
|
+
@console.database = @database
|
69
81
|
@database.stubs(:dirty?).returns(true)
|
70
82
|
|
71
|
-
highline.expects(:
|
72
|
-
highline.expects(:
|
73
|
-
|
74
|
-
highline.expects(:say).with(" q. Quit")
|
75
|
-
|
76
|
-
question = mock('question', :in= => nil)
|
77
|
-
highline.expects(:ask).yields(question).returns('q')
|
78
|
-
highline.expects(:agree).with("Really quit? You have unsaved changes! [yn] ").returns(false)
|
79
|
-
assert_equal nil, console.menu
|
83
|
+
@highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q 1 d g s}) }).returns('q')
|
84
|
+
@highline.expects(:agree).with("Really quit? You have unsaved changes! [yn] ").returns(false)
|
85
|
+
assert_equal nil, @console.menu
|
80
86
|
end
|
81
87
|
|
82
88
|
def test_select_save_from_menu
|
83
|
-
|
84
|
-
HighLine.expects(:new).returns(highline)
|
85
|
-
console = Console.new
|
86
|
-
console.database = @database
|
89
|
+
@console.database = @database
|
87
90
|
@database.stubs(:dirty?).returns(true)
|
88
91
|
|
89
|
-
highline.expects(:say).with("
|
90
|
-
highline.expects(:
|
91
|
-
|
92
|
-
|
92
|
+
@highline.expects(:say).with(" s. Save")
|
93
|
+
@highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q 1 d g s}) }).returns('s')
|
94
|
+
assert_equal :save, @console.menu
|
95
|
+
end
|
93
96
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
97
|
+
def test_select_group_from_menu
|
98
|
+
@console.database = @database
|
99
|
+
@database.stubs(:groups).returns(["Blargh"])
|
100
|
+
|
101
|
+
@highline.expects(:color).with('Blargh', :green).returns('greenBlargh')
|
102
|
+
@highline.expects(:say).with(" 1. greenBlargh")
|
103
|
+
@highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q 1 2 d g}) }).returns('1')
|
104
|
+
assert_equal({:group => 'Blargh'}, @console.menu)
|
98
105
|
end
|
99
106
|
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
107
|
+
def test_select_entry_from_group_menu
|
108
|
+
@console.database = @database
|
109
|
+
@database.expects(:sites).with(:group => "Foo").returns(["Facebook"])
|
110
|
+
@database.expects(:get).with('Facebook', :group => "Foo").returns({:username => 'username', :password => 'password'})
|
111
|
+
|
112
|
+
@highline.expects(:color).with("Foo", :green).returns("greenFoo")
|
113
|
+
@highline.expects(:say).with("===== greenFoo =====")
|
114
|
+
@highline.expects(:say).with(" 1. Facebook [username]")
|
115
|
+
@highline.expects(:say).with(" n. New entry")
|
116
|
+
@highline.expects(:say).with(" d. Delete entry")
|
117
|
+
@highline.expects(:say).with(" t. Top level menu")
|
118
|
+
@highline.expects(:say).with(" q. Quit")
|
119
|
+
|
120
|
+
@highline.expects(:ask).yields(mock { expects(:in=).with(%w{n q 1 d t}) }).returns('1')
|
121
|
+
@console.expects(:Copier).with('password')
|
122
|
+
@highline.expects(:say).with("The password has been copied to your clipboard.")
|
123
|
+
assert_nil @console.menu(:group => 'Foo')
|
124
|
+
end
|
104
125
|
|
126
|
+
def test_get_password
|
105
127
|
question = mock('question')
|
106
128
|
question.expects(:echo=).with(false)
|
107
|
-
highline.expects(:ask).with("Keyrack password: ").yields(question).returns("foobar")
|
108
|
-
assert_equal "foobar", console.get_password
|
129
|
+
@highline.expects(:ask).with("Keyrack password: ").yields(question).returns("foobar")
|
130
|
+
assert_equal "foobar", @console.get_password
|
109
131
|
end
|
110
132
|
|
111
133
|
def test_get_new_entry_with_manual_password
|
112
|
-
highline = mock('highline')
|
113
|
-
HighLine.expects(:new).returns(highline)
|
114
|
-
console = Console.new
|
115
|
-
|
116
134
|
seq = sequence("new entry")
|
117
|
-
highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
|
118
|
-
highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
|
119
|
-
highline.expects(:agree).with("Generate password? [yn] ").returns(false).in_sequence(seq)
|
120
|
-
highline.expects(:ask).with("Password: ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
|
121
|
-
highline.expects(:ask).with("Password (again): ").yields(mock { expects(:echo=).with(false) }).returns("bar").in_sequence(seq)
|
122
|
-
highline.expects(:say).with("Passwords didn't match. Try again!").in_sequence(seq)
|
123
|
-
highline.expects(:ask).with("Password: ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
|
124
|
-
highline.expects(:ask).with("Password (again): ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
|
125
|
-
assert_equal({:site => "Foo", :username => "bar", :password => "baz"}, console.get_new_entry)
|
135
|
+
@highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
|
136
|
+
@highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
|
137
|
+
@highline.expects(:agree).with("Generate password? [yn] ").returns(false).in_sequence(seq)
|
138
|
+
@highline.expects(:ask).with("Password: ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
|
139
|
+
@highline.expects(:ask).with("Password (again): ").yields(mock { expects(:echo=).with(false) }).returns("bar").in_sequence(seq)
|
140
|
+
@highline.expects(:say).with("Passwords didn't match. Try again!").in_sequence(seq)
|
141
|
+
@highline.expects(:ask).with("Password: ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
|
142
|
+
@highline.expects(:ask).with("Password (again): ").yields(mock { expects(:echo=).with(false) }).returns("baz").in_sequence(seq)
|
143
|
+
assert_equal({:site => "Foo", :username => "bar", :password => "baz"}, @console.get_new_entry)
|
126
144
|
end
|
127
145
|
|
128
146
|
def test_get_new_entry_generated_password
|
129
|
-
highline = mock('highline')
|
130
|
-
HighLine.expects(:new).returns(highline)
|
131
|
-
console = Console.new
|
132
|
-
|
133
147
|
seq = sequence("new entry")
|
134
|
-
highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
|
135
|
-
highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
|
136
|
-
highline.expects(:agree).with("Generate password? [yn] ").returns(true).in_sequence(seq)
|
148
|
+
@highline.expects(:ask).with("Label: ").returns("Foo").in_sequence(seq)
|
149
|
+
@highline.expects(:ask).with("Username: ").returns("bar").in_sequence(seq)
|
150
|
+
@highline.expects(:agree).with("Generate password? [yn] ").returns(true).in_sequence(seq)
|
137
151
|
Utils.expects(:generate_password).returns('foobar').in_sequence(seq)
|
138
|
-
highline.expects(:color).with('foobar', :blue).returns('bluefoobar').in_sequence(seq)
|
139
|
-
highline.expects(:agree).with("Generated bluefoobar. Sound good? [yn] ").returns(false).in_sequence(seq)
|
152
|
+
@highline.expects(:color).with('foobar', :blue).returns('bluefoobar').in_sequence(seq)
|
153
|
+
@highline.expects(:agree).with("Generated bluefoobar. Sound good? [yn] ").returns(false).in_sequence(seq)
|
140
154
|
Utils.expects(:generate_password).returns('foobar').in_sequence(seq)
|
141
|
-
highline.expects(:color).with('foobar', :blue).returns('bluefoobar').in_sequence(seq)
|
142
|
-
highline.expects(:agree).with("Generated bluefoobar. Sound good? [yn] ").returns(true).in_sequence(seq)
|
143
|
-
assert_equal({:site => "Foo", :username => "bar", :password => "foobar"}, console.get_new_entry)
|
155
|
+
@highline.expects(:color).with('foobar', :blue).returns('bluefoobar').in_sequence(seq)
|
156
|
+
@highline.expects(:agree).with("Generated bluefoobar. Sound good? [yn] ").returns(true).in_sequence(seq)
|
157
|
+
assert_equal({:site => "Foo", :username => "bar", :password => "foobar"}, @console.get_new_entry)
|
144
158
|
end
|
145
159
|
|
146
160
|
def test_display_first_time_notice
|
147
|
-
highline
|
148
|
-
|
149
|
-
console = Console.new
|
150
|
-
|
151
|
-
highline.expects(:say).with("This looks like your first time using Keyrack. I'll need to ask you a few questions first.")
|
152
|
-
console.display_first_time_notice
|
161
|
+
@highline.expects(:say).with("This looks like your first time using Keyrack. I'll need to ask you a few questions first.")
|
162
|
+
@console.display_first_time_notice
|
153
163
|
end
|
154
164
|
|
155
165
|
def test_rsa_setup
|
156
|
-
highline = mock('highline')
|
157
|
-
HighLine.expects(:new).returns(highline)
|
158
|
-
console = Console.new
|
159
|
-
|
160
166
|
seq = sequence("rsa setup")
|
161
|
-
highline.expects(:ask).with("New passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
|
162
|
-
highline.expects(:ask).with("Confirm passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('small').in_sequence(seq)
|
163
|
-
highline.expects(:say).with("Passphrases didn't match.").in_sequence(seq)
|
164
|
-
highline.expects(:ask).with("New passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
|
165
|
-
highline.expects(:ask).with("Confirm passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
|
167
|
+
@highline.expects(:ask).with("New passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
|
168
|
+
@highline.expects(:ask).with("Confirm passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('small').in_sequence(seq)
|
169
|
+
@highline.expects(:say).with("Passphrases didn't match.").in_sequence(seq)
|
170
|
+
@highline.expects(:ask).with("New passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
|
171
|
+
@highline.expects(:ask).with("Confirm passphrase: ").yields(mock{expects(:echo=).with(false)}).returns('huge').in_sequence(seq)
|
166
172
|
expected = {'password' => 'huge', 'path' => 'rsa'}
|
167
|
-
assert_equal expected, console.rsa_setup
|
173
|
+
assert_equal expected, @console.rsa_setup
|
168
174
|
end
|
169
175
|
|
170
176
|
def test_store_setup_for_filesystem
|
171
|
-
highline
|
172
|
-
HighLine.expects(:new).returns(highline)
|
173
|
-
console = Console.new
|
174
|
-
|
175
|
-
highline.expects(:choose).yields(mock {
|
177
|
+
@highline.expects(:choose).yields(mock {
|
176
178
|
expects(:header=).with("Choose storage type:")
|
177
179
|
expects(:choices).with("filesystem", "ssh")
|
178
180
|
}).returns("filesystem")
|
179
181
|
|
180
182
|
expected = {'type' => 'filesystem', 'path' => 'database'}
|
181
|
-
assert_equal expected, console.store_setup
|
183
|
+
assert_equal expected, @console.store_setup
|
182
184
|
end
|
183
185
|
|
184
186
|
def test_store_setup_for_ssh
|
185
|
-
highline = mock('highline')
|
186
|
-
HighLine.expects(:new).returns(highline)
|
187
|
-
console = Console.new
|
188
|
-
|
189
187
|
seq = sequence("store setup")
|
190
|
-
highline.expects(:choose).yields(mock {
|
188
|
+
@highline.expects(:choose).yields(mock {
|
191
189
|
expects(:header=).with("Choose storage type:")
|
192
190
|
expects(:choices).with("filesystem", "ssh")
|
193
191
|
}).returns("ssh").in_sequence(seq)
|
194
|
-
highline.expects(:ask).with("Host: ").returns("example.com").in_sequence(seq)
|
195
|
-
highline.expects(:ask).with("User: ").returns("dudeguy").in_sequence(seq)
|
196
|
-
highline.expects(:ask).with("Remote path: ").returns(".keyrack/database").in_sequence(seq)
|
192
|
+
@highline.expects(:ask).with("Host: ").returns("example.com").in_sequence(seq)
|
193
|
+
@highline.expects(:ask).with("User: ").returns("dudeguy").in_sequence(seq)
|
194
|
+
@highline.expects(:ask).with("Remote path: ").returns(".keyrack/database").in_sequence(seq)
|
197
195
|
|
198
196
|
expected = {'type' => 'ssh', 'host' => 'example.com', 'user' => 'dudeguy', 'path' => '.keyrack/database'}
|
199
|
-
assert_equal expected, console.store_setup
|
197
|
+
assert_equal expected, @console.store_setup
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_get_new_group
|
201
|
+
@highline.expects(:ask).with("Group: ").yields(mock {
|
202
|
+
expects(:validate=).with(/^\w[\w\s]*$/)
|
203
|
+
}).returns("Foo")
|
204
|
+
assert_equal({:group => "Foo"}, @console.get_new_group)
|
205
|
+
end
|
206
|
+
|
207
|
+
def test_delete_entry
|
208
|
+
@console.database = @database
|
209
|
+
@database.stubs(:sites).returns(%w{Twitter Facebook})
|
210
|
+
@database.stubs(:get).with('Facebook', {}).returns({
|
211
|
+
:username => 'username', :password => 'password'
|
212
|
+
})
|
213
|
+
|
214
|
+
seq = sequence("deleting")
|
215
|
+
@highline.expects(:say).with("Choose entry to delete:").in_sequence(seq)
|
216
|
+
@highline.expects(:say).with(" 1. Twitter [username]").in_sequence(seq)
|
217
|
+
@highline.expects(:say).with(" 2. Facebook [username]").in_sequence(seq)
|
218
|
+
@highline.expects(:say).with(" c. Cancel").in_sequence(seq)
|
219
|
+
@highline.expects(:ask).yields(mock {
|
220
|
+
expects(:in=).with(%w{c 1 2})
|
221
|
+
}).returns('1').in_sequence(seq)
|
222
|
+
@highline.expects(:color).with("Twitter [username]", :red).returns("redTwitter").in_sequence(seq)
|
223
|
+
@highline.expects(:agree).with("You're about to delete redTwitter. Are you sure? [yn] ").returns(true).in_sequence(seq)
|
224
|
+
@database.expects(:delete).with("Twitter", {}).in_sequence(seq)
|
225
|
+
@console.delete_entry
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_delete_group_entry
|
229
|
+
@console.database = @database
|
230
|
+
@database.stubs(:sites).returns(%w{Quora Foursquare})
|
231
|
+
@database.stubs(:get).with(kind_of(String), {:group => 'Social'}).returns({
|
232
|
+
:username => 'username', :password => 'password'
|
233
|
+
})
|
234
|
+
|
235
|
+
seq = sequence("deleting")
|
236
|
+
@highline.expects(:say).with("Choose entry to delete:").in_sequence(seq)
|
237
|
+
@highline.expects(:say).with(" 1. Quora [username]").in_sequence(seq)
|
238
|
+
@highline.expects(:say).with(" 2. Foursquare [username]").in_sequence(seq)
|
239
|
+
@highline.expects(:say).with(" c. Cancel").in_sequence(seq)
|
240
|
+
@highline.expects(:ask).yields(mock {
|
241
|
+
expects(:in=).with(%w{c 1 2})
|
242
|
+
}).returns('2').in_sequence(seq)
|
243
|
+
@highline.expects(:color).with("Foursquare [username]", :red).returns("redFoursquare").in_sequence(seq)
|
244
|
+
@highline.expects(:agree).with("You're about to delete redFoursquare. Are you sure? [yn] ").returns(true).in_sequence(seq)
|
245
|
+
@database.expects(:delete).with("Foursquare", :group => 'Social').in_sequence(seq)
|
246
|
+
@console.delete_entry(:group => 'Social')
|
200
247
|
end
|
201
248
|
end
|
202
249
|
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
8
|
+
- 1
|
9
|
+
version: 0.2.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Jeremy Stephens
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2011-01-
|
17
|
+
date: 2011-01-05 00:00:00 -06:00
|
18
18
|
default_executable: keyrack
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
@@ -134,6 +134,7 @@ extensions: []
|
|
134
134
|
extra_rdoc_files:
|
135
135
|
- LICENSE.txt
|
136
136
|
- README.rdoc
|
137
|
+
- TODO
|
137
138
|
files:
|
138
139
|
- .document
|
139
140
|
- .rvmrc
|
@@ -143,6 +144,7 @@ files:
|
|
143
144
|
- LICENSE.txt
|
144
145
|
- README.rdoc
|
145
146
|
- Rakefile
|
147
|
+
- TODO
|
146
148
|
- VERSION
|
147
149
|
- bin/keyrack
|
148
150
|
- features/console.feature
|
@@ -184,7 +186,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
184
186
|
requirements:
|
185
187
|
- - ">="
|
186
188
|
- !ruby/object:Gem::Version
|
187
|
-
hash: -
|
189
|
+
hash: -45771557779572669
|
188
190
|
segments:
|
189
191
|
- 0
|
190
192
|
version: "0"
|