gitsu 1.0.0 → 2.0.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 (52) hide show
  1. checksums.yaml +7 -0
  2. data/.licenseignore +2 -0
  3. data/.simplecov +16 -0
  4. data/.travis.yml +19 -2
  5. data/Gemfile +17 -1
  6. data/README.md +36 -7
  7. data/Rakefile +37 -3
  8. data/TODO +4 -2
  9. data/bin/git-su +15 -0
  10. data/bin/git-whoami +15 -0
  11. data/features/add_user.feature +16 -0
  12. data/features/change_user_in_different_scopes.feature +16 -0
  13. data/features/clear_user.feature +16 -0
  14. data/features/configure_default_scope.feature +22 -2
  15. data/features/edit_config.feature +16 -0
  16. data/features/list_users.feature +16 -0
  17. data/features/print_current_user.feature +16 -0
  18. data/features/print_options.feature +16 -0
  19. data/features/step_definitions/gitsu_steps.rb +33 -12
  20. data/features/support/env.rb +16 -0
  21. data/features/switch_to_fully_qualified_user.feature +21 -0
  22. data/features/switch_to_multiple_users.feature +64 -0
  23. data/features/switch_to_stored_user.feature +16 -0
  24. data/gitsu.gemspec +20 -3
  25. data/lib/gitsu.rb +17 -0
  26. data/lib/gitsu/array.rb +31 -0
  27. data/lib/gitsu/config_repository.rb +49 -0
  28. data/lib/gitsu/factory.rb +26 -3
  29. data/lib/gitsu/git.rb +44 -35
  30. data/lib/gitsu/gitsu.rb +27 -3
  31. data/lib/gitsu/runner.rb +16 -0
  32. data/lib/gitsu/shell.rb +17 -2
  33. data/lib/gitsu/switcher.rb +68 -30
  34. data/lib/gitsu/user.rb +80 -11
  35. data/lib/gitsu/user_file.rb +18 -2
  36. data/lib/gitsu/user_list.rb +68 -12
  37. data/lib/gitsu/version.rb +17 -1
  38. data/man/git-su.1.ronn +36 -4
  39. data/spec/gitsu/array_spec.rb +71 -0
  40. data/spec/gitsu/bin_spec.rb +16 -0
  41. data/spec/gitsu/config_repository_spec.rb +67 -0
  42. data/spec/gitsu/git_spec.rb +36 -15
  43. data/spec/gitsu/gitsu_spec.rb +41 -18
  44. data/spec/gitsu/runner_spec.rb +16 -0
  45. data/spec/gitsu/shell_spec.rb +55 -0
  46. data/spec/gitsu/switcher_spec.rb +53 -13
  47. data/spec/gitsu/user_file_spec.rb +91 -0
  48. data/spec/gitsu/user_list_spec.rb +112 -41
  49. data/spec/gitsu/user_spec.rb +97 -0
  50. data/spec/gitsu/version_spec.rb +16 -0
  51. data/spec/spec_helper.rb +16 -0
  52. metadata +45 -28
@@ -0,0 +1,55 @@
1
+ # Gitsu
2
+ # Copyright (C) 2013 drrb
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'spec_helper'
18
+
19
+ module GitSu
20
+ describe Shell do
21
+ let(:shell) { Shell.new }
22
+
23
+ describe "#capture" do
24
+ it "executes the command and returns the output" do
25
+ output = shell.capture 'echo hello'
26
+ output.should == "hello"
27
+ end
28
+ it "calls the supplied block with the exit status and output" do
29
+ command = 'echo hello && false'
30
+ block_called = false
31
+ return_value = shell.capture(command) do |output, result|
32
+ block_called = true
33
+ output.should == "hello"
34
+ result.exitstatus.should be 1
35
+ end
36
+ return_value.should == "hello"
37
+ block_called.should be true
38
+ end
39
+ end
40
+
41
+ describe "#execute" do
42
+ context "when a command returns zero" do
43
+ it "executes the command and returns true" do
44
+ shell.execute("true").should be true
45
+ end
46
+ end
47
+ context "when a command returns nonzero" do
48
+ it "executes the command and returns false" do
49
+ shell.execute("false").should be false
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+
@@ -1,30 +1,68 @@
1
+ # Gitsu
2
+ # Copyright (C) 2013 drrb
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
1
17
  require 'spec_helper'
2
18
 
3
19
  module GitSu
4
20
 
5
21
  describe Switcher do
22
+ let(:config_repository) { double('config_repository') }
6
23
  let(:git) { double('git') }
7
24
  let(:user_list) { double('user_list') }
8
25
  let(:output) { double('output').as_null_object }
9
- let(:switcher) { Switcher.new(git, user_list, output) }
10
- let(:a_user) { User.new("Johnny Bravo", "jbravo@example.com") }
26
+ let(:switcher) { Switcher.new(config_repository, git, user_list, output) }
11
27
 
12
28
  describe '#request' do
13
29
  context "when request is a fully-qualified user string (e.g. 'John Galt <jgalt@example.com>'" do
14
- it "switches to user" do
30
+ it "switches to user and adds them" do
15
31
  user = User.new('John Galt', 'jgalt@example.com')
32
+ config_repository.should_receive(:group_email_address).and_return("dev@example.com")
16
33
  git.should_receive(:select_user).with(user, :global)
17
34
  git.should_receive(:render).with(user).and_return(user.to_s)
18
35
  output.should_receive(:puts).with("Switched global user to John Galt <jgalt@example.com>")
19
- switcher.request('John Galt <jgalt@example.com>', :global)
36
+ user_list.should_receive(:list).and_return []
37
+ user_list.should_receive(:add).with(user)
38
+ switcher.request(:global, 'John Galt <jgalt@example.com>')
39
+ end
40
+ end
41
+
42
+ context "when request is for multiple users" do
43
+ it "combines users" do
44
+ combined_user = User.new('Johnny A, Johnny B and Johnny C', 'dev+a+b+c@example.com')
45
+ config_repository.should_receive(:group_email_address).and_return("dev@example.com")
46
+ git.should_receive(:select_user).with(combined_user, :global)
47
+ git.should_receive(:render).with(combined_user).and_return(combined_user.to_s)
48
+ output.should_receive(:puts).with("Switched global user to Johnny A, Johnny B and Johnny C <dev+a+b+c@example.com>")
49
+
50
+ # TODO: how do you do times in rspec again?
51
+ user_list.should_receive(:list).and_return []
52
+ user_list.should_receive(:add).with User.new("Johnny A", "a@example.com")
53
+ user_list.should_receive(:list).and_return []
54
+ user_list.should_receive(:add).with User.new("Johnny B", "b@example.com")
55
+ user_list.should_receive(:list).and_return []
56
+ user_list.should_receive(:add).with User.new("Johnny C", "c@example.com")
57
+ switcher.request(:global, 'Johnny C <c@example.com>', 'Johnny B <b@example.com>', 'Johnny A <a@example.com>')
20
58
  end
21
59
  end
22
60
 
23
- context "when no matching user found" do
61
+ context "when at least one user isn't found" do
24
62
  it "does not switch user" do
25
- user_list.should_receive(:find).with('asdfasdf').and_return User::NONE
26
- output.should_receive(:puts).with("No user found matching 'asdfasdf'")
27
- switcher.request('asdfasdf', :global)
63
+ user_list.should_receive(:find).with('john', 'xx').and_raise "No user found matching 'xx'"
64
+ output.should_receive(:puts).with("No user found matching 'xx'")
65
+ switcher.request(:global, 'john', 'xx')
28
66
  end
29
67
  end
30
68
 
@@ -32,11 +70,12 @@ module GitSu
32
70
  it "switches to requested user" do
33
71
  user = User.new('John Galt', 'jgalt@example.com')
34
72
 
35
- user_list.should_receive(:find).with("john").and_return user
73
+ user_list.should_receive(:find).with("john").and_return [user ]
74
+ config_repository.should_receive(:group_email_address).and_return("dev@example.com")
36
75
  git.should_receive(:select_user).with user, :global
37
76
  git.should_receive(:render).with(user).and_return user.to_s
38
77
  output.should_receive(:puts).with("Switched global user to John Galt <jgalt@example.com>")
39
- switcher.request "john", :global
78
+ switcher.request :global, "john"
40
79
  end
41
80
  end
42
81
 
@@ -44,12 +83,13 @@ module GitSu
44
83
  it "switches user in configured default scope" do
45
84
  user = User.new('John Galt', 'jgalt@example.com')
46
85
 
47
- user_list.should_receive(:find).with("john").and_return user
48
- git.should_receive(:default_select_scope).and_return(:global)
86
+ user_list.should_receive(:find).with("john").and_return [user ]
87
+ config_repository.should_receive(:group_email_address).and_return("dev@example.com")
88
+ config_repository.should_receive(:default_select_scope).and_return(:global)
49
89
  git.should_receive(:select_user).with user, :global
50
90
  git.should_receive(:render).with(user).and_return user.to_s
51
91
  output.should_receive(:puts).with("Switched global user to John Galt <jgalt@example.com>")
52
- switcher.request "john", :default
92
+ switcher.request :default, "john"
53
93
  end
54
94
  end
55
95
  end
@@ -0,0 +1,91 @@
1
+ # Gitsu
2
+ # Copyright (C) 2013 drrb
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
17
+ require 'spec_helper'
18
+ require 'yaml'
19
+ require 'fileutils'
20
+
21
+ module GitSu
22
+ describe UserFile do
23
+ let(:user_file_name) { "/tmp/#{rand}" }
24
+ let(:user_file) { UserFile.new user_file_name }
25
+
26
+ after do
27
+ File.delete user_file_name if File.exist? user_file_name
28
+ end
29
+
30
+ describe "#initialize" do
31
+ context "when file is missing" do
32
+ it "creates the file" do
33
+ UserFile.new user_file_name
34
+ File.should exist user_file_name
35
+ end
36
+ end
37
+ context "when file is empty" do
38
+ it "writes a newline to the file" do
39
+ FileUtils.touch user_file_name
40
+ UserFile.new user_file_name
41
+ File.read(user_file_name).should == "\n"
42
+ end
43
+ end
44
+ context "otherwise" do
45
+ it "leaves the file alone" do
46
+ File.open(user_file_name, "w") {|f| f << "content" }
47
+ UserFile.new user_file_name
48
+ File.read(user_file_name).should == "content"
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "#write" do
54
+ it "adds a user to the user file as YAML" do
55
+ user_file.write User.new("John Galt", "jgalt@example.com")
56
+
57
+ user_map = YAML.load_file(user_file_name)
58
+ user_map.should == { "jgalt@example.com" => "John Galt" }
59
+ end
60
+ it "doesn't overwrite existing users" do
61
+ user_file.write User.new("John Galt", "jgalt@example.com")
62
+ user_file.write User.new("Joseph Porter", "porter@example.com")
63
+
64
+ user_map = YAML.load_file(user_file_name)
65
+ user_map.should == {
66
+ "jgalt@example.com" => "John Galt",
67
+ "porter@example.com" => "Joseph Porter"
68
+ }
69
+ end
70
+ end
71
+
72
+ describe "#read" do
73
+ it "parses user file as a list of users" do
74
+ user_1 = User.new("John Galt", "jgalt@example.com")
75
+ user_2 = User.new("Joseph Porter", "porter@example.com")
76
+ user_file.write user_1
77
+ user_file.write user_2
78
+
79
+ user_map = user_file.read
80
+ user_map.sort_by {|user| user.name}.should == [ user_1, user_2 ]
81
+ end
82
+
83
+ context "when the file is empty" do
84
+ it "returns an empty list" do
85
+ user_file.read.should == []
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+
@@ -1,81 +1,152 @@
1
+ # Gitsu
2
+ # Copyright (C) 2013 drrb
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
1
17
  require 'spec_helper'
2
- require 'yaml'
3
18
 
4
19
  module GitSu
5
20
  describe UserList do
6
21
 
7
- let(:user_file) { "/tmp/#{rand}" }
22
+ let(:user_file) { InMemoryUserFile.new }
8
23
  let(:user_list) { UserList.new(user_file) }
9
- after { File.delete user_file }
10
24
 
11
25
  describe "#add" do
12
- it "adds a user to the list" do
13
- user_list.add User.new("John Galt", "jgalt@example.com")
14
-
15
- user_map = YAML.parse_file(user_file).transform
16
- user_map.keys.should include("jgalt@example.com")
17
- user_map["jgalt@example.com"].should == "John Galt"
26
+ it "adds a user to the user file" do
27
+ user = User.new("John Galt", "jgalt@example.com")
28
+ user_list.add user
29
+ user_file.should include user
18
30
  end
19
31
  end
20
32
 
21
33
  describe "#list" do
22
- context "when there are no users configured" do
23
- it "returns an empty array" do
24
- user_list.list.should be_empty
25
- end
26
- end
27
-
28
- context "when there are users configured" do
29
- it "returns an array of all the users" do
30
- user_list.add User.new("John Galt", "jgalt@example.com")
31
-
32
- list = user_list.list
33
- list.size.should be 1
34
- list.should include User.new("John Galt", "jgalt@example.com")
35
- end
34
+ it "reads all users from the user file" do
35
+ user_file.stub(:read).and_return ["user1", "user2", "user3"]
36
+ user_list.list.should == ["user1", "user2", "user3"]
36
37
  end
37
38
  end
38
39
 
39
40
  describe "#find" do
40
41
  context "when a matching user exists" do
41
42
  it "returns the matching user by first name" do
42
- user_list.add User.new("John Galt", "jgalt@example.com")
43
+ user_file.add("John Galt", "jgalt@example.com")
43
44
 
44
- user_list.find("john").should == User.new("John Galt", "jgalt@example.com")
45
- user_list.find("John").should == User.new("John Galt", "jgalt@example.com")
45
+ user_list.find("john").should == [User.new("John Galt", "jgalt@example.com")]
46
+ user_list.find("John").should == [User.new("John Galt", "jgalt@example.com")]
46
47
  end
47
48
  it "returns the matching user by email snippet" do
48
- user_list.add User.new("John Galt", "jgalt@example.com")
49
+ user_file.add("John Galt", "jgalt@example.com")
49
50
 
50
- user_list.find("example").should == User.new("John Galt", "jgalt@example.com")
51
+ user_list.find("example").should == [User.new("John Galt", "jgalt@example.com")]
51
52
  end
52
53
  it "returns the matching user by initials" do
53
- user_list.add User.new("John Galt", "john.galt@example.com")
54
+ user_file.add("John Galt", "john@example.com")
54
55
 
55
- user_list.find("jg").should == User.new("John Galt", "john.galt@example.com")
56
- user_list.find("JG").should == User.new("John Galt", "john.galt@example.com")
56
+ user_list.find("jg").should == [User.new("John Galt", "john@example.com")]
57
+ user_list.find("JG").should == [User.new("John Galt", "john@example.com")]
57
58
  end
58
59
  it "favours prefix over innards" do
59
- user_list.add User.new("Matthew Jackson", "mj@example.com")
60
- user_list.add User.new("Thomas Hickleton", "tom@example.com")
61
- user_list.add User.new("John Smith", "js@example.com")
60
+ user_file.add("Matthew Jackson", "mj@example.com")
61
+ user_file.add("Thomas Hickleton", "tom@example.com")
62
+ user_file.add("John Smith", "js@example.com")
62
63
 
63
- user_list.find("th").should eq User.new("Thomas Hickleton", "tom@example.com")
64
- user_list.find("s").should eq User.new("John Smith", "js@example.com")
64
+ user_list.find("th").should == [User.new("Thomas Hickleton", "tom@example.com")]
65
+ user_list.find("s").should == [User.new("John Smith", "js@example.com")]
65
66
  end
66
67
  it "favours full firstname match over partial one" do
67
- user_list.add User.new("Matthew Jackson", "mj@example.com")
68
- user_list.add User.new("Mat Jackson", "zz@example.com")
68
+ user_file.add("Matthew Jackson", "mj@example.com")
69
+ user_file.add("Mat Jackson", "zz@example.com")
69
70
 
70
- user_list.find("mat").should eq User.new("Mat Jackson", "zz@example.com")
71
+ user_list.find("mat").should == [User.new("Mat Jackson", "zz@example.com")]
71
72
  end
72
73
  end
73
74
 
74
75
  context "when no matching user exists" do
75
- it "returns no user" do
76
- user_list.find("john").should be User::NONE
76
+ it "raises an error" do
77
+ expect {user_list.find("john")}.to raise_error "No user found matching 'john'"
77
78
  end
78
79
  end
80
+
81
+ context "when searching for two strings" do
82
+ it "returns two users if they both match the search strings" do
83
+ user_file.add("Johnny A", "ja@example.com")
84
+ user_file.add("Johnny B", "jb@example.com")
85
+
86
+ user_list.find("ja", "jb").should == [
87
+ User.new("Johnny A", "ja@example.com"),
88
+ User.new("Johnny B", "jb@example.com")
89
+ ]
90
+ end
91
+ it "returns other matched users when first match already found" do
92
+ user_file.add("Johnny A", "ja@example.com")
93
+ user_file.add("Johnny B", "jb@example.com")
94
+
95
+ user_list.find("j", "j").should == [
96
+ User.new("Johnny A", "ja@example.com"),
97
+ User.new("Johnny B", "jb@example.com")
98
+ ]
99
+ end
100
+
101
+ context "when no matching user exists for a search term" do
102
+ it "raises an error" do
103
+ user_file.add("Johnny A", "ja@example.com")
104
+
105
+ expect {user_list.find("ja", "jb")}.to raise_error "No user found matching 'jb'"
106
+ end
107
+ end
108
+ context "when a search term only matches users that were already matched" do
109
+ it "raises an error" do
110
+ user_file.add("Johnny A", "ja@example.com")
111
+ user_file.add("Johnny B", "jb@example.com")
112
+
113
+ expect {user_list.find("ja", "jb", "j")}.to raise_error "Couldn't find a combination of unique users matching 'ja', 'jb' and 'j'"
114
+ end
115
+ end
116
+ context "when a search term only matches a user that was already matched, but the matching search term matched multiple users" do
117
+ it "adjusts the selection so that both terms match" do
118
+ user_file.add("Johnny A", "ja@example.com")
119
+ user_file.add("Johnny B", "jb@example.com")
120
+
121
+ user_list.find("j", "ja").sort_by{|u| u.name}.should == [
122
+ User.new("Johnny A", "ja@example.com"),
123
+ User.new("Johnny B", "jb@example.com")
124
+ ]
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ class InMemoryUserFile
132
+ def add(name, email)
133
+ write User.new(name, email)
134
+ end
135
+
136
+ def users
137
+ @users ||= []
138
+ end
139
+
140
+ def read
141
+ users
142
+ end
143
+
144
+ def write(user)
145
+ users << user
146
+ end
147
+
148
+ def include?(user)
149
+ users.include? user
79
150
  end
80
151
  end
81
152
  end
@@ -1,3 +1,19 @@
1
+ # Gitsu
2
+ # Copyright (C) 2013 drrb
3
+ #
4
+ # This program is free software: you can redistribute it and/or modify
5
+ # it under the terms of the GNU General Public License as published by
6
+ # the Free Software Foundation, either version 3 of the License, or
7
+ # (at your option) any later version.
8
+ #
9
+ # This program is distributed in the hope that it will be useful,
10
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ # GNU General Public License for more details.
13
+ #
14
+ # You should have received a copy of the GNU General Public License
15
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
16
+
1
17
  module GitSu
2
18
  describe User do
3
19
  describe "#parse" do
@@ -23,11 +39,92 @@ module GitSu
23
39
  end
24
40
  end
25
41
 
42
+ describe "#hash" do
43
+ it "is different if name is different" do
44
+ user_a = User.new("a", "a@a.com")
45
+ user_b = User.new("b", "a@a.com")
46
+ user_a.hash.should_not == user_b.hash
47
+ end
48
+ it "is different if email is different" do
49
+ user_a = User.new("a", "a@a.com")
50
+ user_b = User.new("a", "b@a.com")
51
+ user_a.hash.should_not == user_b.hash
52
+ end
53
+ it "is the same if name and email are the same" do
54
+ user_a = User.new("a", "a@a.com")
55
+ user_b = User.new("a", "a@a.com")
56
+ user_a.hash.should == user_b.hash
57
+ end
58
+ end
59
+
26
60
  describe "#to_ansi_s" do
27
61
  it "returns a colored string representation of the user" do
28
62
  user = User.new("John Galt", "jg@example.com")
29
63
  user.to_ansi_s("\e[34m", "\e[35m", "\e[0m").should == "\e[34mJohn Galt\e[0m \e[35m<jg@example.com>\e[0m"
30
64
  end
65
+
66
+ context "when called on the null user" do
67
+ it "returns a colored string representation of the null user" do
68
+ user = User::NONE
69
+ user.to_ansi_s("\e[34m", "\e[35m", "\e[0m").should == "\e[34m(none)\e[0m"
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "#combine" do
75
+ it "combines the user with the provided other user" do
76
+ user = User.new("John A", "ja@example.com")
77
+ other = User.new("John B", "jb@example.com")
78
+ combined_user = user.combine other, "dev@example.com"
79
+ combined_user.name.should == "John A and John B"
80
+ combined_user.email.should == "dev+ja+jb@example.com"
81
+ end
82
+ it "accumulates users" do
83
+ user1 = User.new("John A", "ja@example.com")
84
+ user2 = User.new("John B", "jb@example.com")
85
+ user3 = User.new("John C", "jc@example.com")
86
+ combined_user = user1.combine(user2, "dev@example.com").combine(user3, "dev@example.com")
87
+ combined_user.name.should == "John A, John B and John C"
88
+ combined_user.email.should == "dev+ja+jb+jc@example.com"
89
+ end
90
+ it "can be called with combined users" do
91
+ user1 = User.new("John A", "ja@example.com")
92
+ user2 = User.new("John B", "jb@example.com")
93
+ user3 = User.new("John C", "jc@example.com")
94
+ combined_user = user1.combine(user2, "dev@example.com")
95
+ combined_user = user3.combine(combined_user, "dev@example.com")
96
+ combined_user.name.should == "John A, John B and John C"
97
+ combined_user.email.should == "dev+ja+jb+jc@example.com"
98
+ end
99
+ it "removes duplicate users by email" do
100
+ user = User.new("John A", "ja@example.com")
101
+ other = User.new("John B", "ja@example.com")
102
+ combined_user = user.combine other, "dev@example.com"
103
+ combined_user.name.should == "John B"
104
+ combined_user.email.should == "ja@example.com"
105
+ end
106
+ it "sorts combined users by email" do
107
+ user1 = User.new("John Z", "ja@example.com")
108
+ user2 = User.new("John X", "jc@example.com")
109
+ user3 = User.new("John Y", "jb@example.com")
110
+ combined_user = user1.combine(user2, "dev@example.com").combine(user3, "dev@example.com")
111
+ combined_user.name.should == "John Z, John Y and John X"
112
+ combined_user.email.should == "dev+ja+jb+jc@example.com"
113
+ end
114
+ it "doesn't have side-effects" do
115
+ ja = User.new("John A", "ja@example.com")
116
+ jb = User.new("John B", "jb@example.com")
117
+ combined_user = ja.combine(jb, "dev@example.com")
118
+ ja.name.should == "John A"
119
+ ja.email.should == "ja@example.com"
120
+ end
121
+ context "when combined with NONE" do
122
+ it "returns a clone of itself" do
123
+ ja = User.new("John A", "ja@example.com")
124
+ ja.combine(User::NONE, "dev@example.com").should == ja
125
+ User::NONE.combine(ja, "dev@example.com").should == ja
126
+ end
127
+ end
31
128
  end
32
129
  end
33
130
  end