codefumes 0.1.10 → 0.2.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 (68) hide show
  1. data/Gemfile +19 -0
  2. data/Gemfile.lock +135 -0
  3. data/History.txt +12 -0
  4. data/LICENSE +20 -0
  5. data/Manifest.txt +40 -19
  6. data/README.txt +11 -29
  7. data/Rakefile +15 -10
  8. data/bin/fumes +214 -0
  9. data/config/website.yml +2 -0
  10. data/cucumber.yml +2 -0
  11. data/features/claiming_a_project.feature +46 -0
  12. data/features/deleting_a_project.feature +32 -0
  13. data/features/releasing_a_project.feature +50 -0
  14. data/features/step_definitions/cli_steps.rb +98 -0
  15. data/features/step_definitions/common_steps.rb +168 -0
  16. data/features/step_definitions/filesystem_steps.rb +19 -0
  17. data/features/storing_user_api_key.feature +41 -0
  18. data/features/support/common.rb +29 -0
  19. data/features/support/env.rb +24 -0
  20. data/features/support/matchers.rb +11 -0
  21. data/features/synchronizing_repository_with_project.feature +33 -0
  22. data/lib/codefumes.rb +10 -8
  23. data/lib/codefumes/api.rb +20 -11
  24. data/lib/codefumes/api/build.rb +139 -0
  25. data/lib/codefumes/api/claim.rb +74 -0
  26. data/lib/codefumes/api/commit.rb +150 -0
  27. data/lib/codefumes/api/payload.rb +93 -0
  28. data/lib/codefumes/api/project.rb +158 -0
  29. data/lib/codefumes/cli_helpers.rb +54 -0
  30. data/lib/codefumes/config_file.rb +3 -2
  31. data/lib/codefumes/errors.rb +21 -0
  32. data/lib/codefumes/exit_codes.rb +10 -0
  33. data/lib/codefumes/harvester.rb +113 -0
  34. data/lib/codefumes/quick_build.rb +43 -0
  35. data/lib/codefumes/quick_metric.rb +20 -0
  36. data/lib/codefumes/source_control.rb +137 -0
  37. data/lib/integrity_notifier/codefumes.haml +11 -0
  38. data/lib/integrity_notifier/codefumes.rb +62 -0
  39. data/spec/codefumes/{build_spec.rb → api/build_spec.rb} +14 -24
  40. data/spec/codefumes/{claim_spec.rb → api/claim_spec.rb} +42 -3
  41. data/spec/codefumes/{commit_spec.rb → api/commit_spec.rb} +34 -24
  42. data/spec/codefumes/api/payload_spec.rb +148 -0
  43. data/spec/codefumes/api/project_spec.rb +286 -0
  44. data/spec/codefumes/api_spec.rb +38 -15
  45. data/spec/codefumes/config_file_spec.rb +69 -13
  46. data/spec/codefumes/harvester_spec.rb +118 -0
  47. data/spec/codefumes/source_control_spec.rb +199 -0
  48. data/spec/codefumes_service_helpers.rb +23 -19
  49. data/spec/fixtures/sample_project_dirs/no_scm/description +4 -0
  50. data/spec/spec_helper.rb +1 -0
  51. data/tasks/cucumber.rake +11 -0
  52. metadata +145 -60
  53. data/bin/cf_claim_project +0 -9
  54. data/bin/cf_release_project +0 -10
  55. data/bin/cf_store_credentials +0 -10
  56. data/lib/cf_claim_project/cli.rb +0 -95
  57. data/lib/cf_release_project/cli.rb +0 -76
  58. data/lib/cf_store_credentials/cli.rb +0 -50
  59. data/lib/codefumes/build.rb +0 -131
  60. data/lib/codefumes/claim.rb +0 -57
  61. data/lib/codefumes/commit.rb +0 -144
  62. data/lib/codefumes/payload.rb +0 -103
  63. data/lib/codefumes/project.rb +0 -129
  64. data/spec/cf_claim_project/cli_spec.rb +0 -17
  65. data/spec/cf_release_project/cli_spec.rb +0 -41
  66. data/spec/cf_store_credentials/cli_spec.rb +0 -28
  67. data/spec/codefumes/payload_spec.rb +0 -155
  68. data/spec/codefumes/project_spec.rb +0 -274
@@ -0,0 +1,148 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ describe "API::Payload" do
4
+ include CodeFumesServiceHelpers::PayloadHelpers
5
+
6
+ after(:all) do
7
+ FakeWeb.allow_net_connect = false
8
+ FakeWeb.clean_registry
9
+ end
10
+
11
+ before(:each) do
12
+ setup_fixture_base
13
+ @payload = Payload.new(@project, @commit_data)
14
+ @payload_query = {:payload => {:commits => @commit_data}}
15
+ @basic_auth_params = {:username => @pub_key, :password => @priv_key}
16
+ end
17
+
18
+ describe "#save" do
19
+ it "sets basic auth with the public and private key" do
20
+ register_create_uri(["401", "Unauthorized"], "")
21
+ API.should_receive(:post).with("/projects/#{@project.public_key}/payloads", :query => @payload_query, :basic_auth => @basic_auth_params).and_return(mock("response", :code => 401))
22
+ @payload.save
23
+ end
24
+
25
+ context "with valid parameters" do
26
+ before(:each) do
27
+ register_create_uri(["201", "Created"])
28
+ end
29
+
30
+ it "sets a value for 'created_at'" do
31
+ @payload.created_at.should == nil
32
+ @payload.save.should == true
33
+ @payload.created_at.should_not == nil
34
+ end
35
+ end
36
+
37
+ context "with Unauthorized response" do
38
+ before(:each) do
39
+ register_create_uri(["401", "Unauthorized"], "")
40
+ end
41
+
42
+ it "does not set 'created_at'" do
43
+ @payload.created_at.should == nil
44
+ @payload.save.should == false
45
+ @payload.created_at.should == nil
46
+ end
47
+ end
48
+
49
+ context "when the payload does not have any commit information" do
50
+ before(:each) do
51
+ @payload = Payload.new(@project, "")
52
+ end
53
+
54
+ it "returns true without attempting to save to the site" do
55
+ Payload.should_not_receive(:post)
56
+ @payload.save.should == true
57
+ end
58
+
59
+ it "does not set the value of created_at" do
60
+ @payload.save
61
+ @payload.created_at.should == nil
62
+ end
63
+ end
64
+
65
+ context "with invalid parameters" do
66
+ before(:each) do
67
+ register_create_uri
68
+ end
69
+
70
+ it "does not set a value for 'created_at'" do
71
+ @payload.save.should == false
72
+ @payload.created_at.should == nil
73
+ end
74
+ end
75
+ end
76
+
77
+ describe "#prepare" do
78
+ it "is compatible with the entity returned from SourceControl#payload_between" do
79
+ git_repo_path = File.expand_path(File.dirname(__FILE__) + '/../../fixtures/sample_project_dirs/git_repository')
80
+ repository = SourceControl.new(git_repo_path)
81
+ project = Project.new('public_key', 'private_key')
82
+ lambda {
83
+ pl = Payload.prepare(project, repository.payload_between('HEAD^', 'HEAD'))
84
+ pl.length.should == 1
85
+ }.should_not raise_error
86
+ end
87
+
88
+ context "when supplying nil commit information" do
89
+ it "returns an empty array" do
90
+ Payload.prepare(@project, nil).should == []
91
+ end
92
+ end
93
+
94
+ context "when supplying an empty Hash" do
95
+ it "returns an empty array" do
96
+ Payload.prepare(@project, {}).should == []
97
+ end
98
+ end
99
+
100
+ context "when supplying a hash with less than 4,000 characters" do
101
+ before(:each) do
102
+ single_commit_ex = {:identifier => "92dd08477f0ca144ee0f12ba083760dd810760a2_000"}
103
+ commit_count = 4000 / single_commit_ex.to_s.length
104
+ commits = commit_count.times.map do |index|
105
+ {:identifier => "92dd08477f0ca144ee0f12ba083760dd810760a2_#{index}"}
106
+ end
107
+ @prepared = Payload.prepare(@project, {:commits => commits})
108
+ end
109
+
110
+ it "returns an Array with a single payload element" do
111
+ @prepared.should be_instance_of(Array)
112
+ @prepared.size.should == 1
113
+ @prepared.first.should be_instance_of(Payload)
114
+ end
115
+
116
+ it "associated the specified project with all generated payloads" do
117
+ @prepared.each do |payload|
118
+ payload.project.should == @project
119
+ end
120
+ end
121
+ end
122
+
123
+ context "when supplying a hash with approximately 15,000 characters" do
124
+ before(:each) do
125
+ single_commit_ex = {:identifier => "92dd08477f0ca144ee0f12ba083760dd810760a2_000"}
126
+ commit_count = 15000 / single_commit_ex.to_s.length + 1
127
+ commits = commit_count.times.map do |index|
128
+ {:identifier => "92dd08477f0ca144ee0f12ba083760dd810760a2_#{index}"}
129
+ end
130
+ raw_payload = {:commits => commits}
131
+ @prepared = Payload.prepare(@project, raw_payload)
132
+ end
133
+
134
+ it "returns an Array with a four payload elements" do
135
+ @prepared.should be_instance_of(Array)
136
+ @prepared.size.should == 4
137
+ all_are_payloads = @prepared.all? {|chunk| chunk.instance_of?(Payload)}
138
+ all_are_payloads.should == true
139
+ end
140
+
141
+ it "associated the specified project with all generated payloads" do
142
+ @prepared.each do |payload|
143
+ payload.project.should == @project
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,286 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper.rb'
2
+
3
+ describe "API::Project" do
4
+ include CodeFumesServiceHelpers::ProjectHelpers
5
+
6
+ before(:each) do
7
+ setup_fixture_base
8
+ end
9
+
10
+ after(:all) do
11
+ FakeWeb.allow_net_connect = false
12
+ FakeWeb.clean_registry
13
+ end
14
+
15
+ describe "#create" do
16
+ context "when successful" do
17
+ before(:each) do
18
+ register_create_uri
19
+ end
20
+
21
+ [:public_key,
22
+ :private_key,
23
+ :short_uri,
24
+ :community_uri,
25
+ :api_uri].each do |method_name|
26
+ it "sets the '#{method_name.to_s}'" do
27
+ project = Project.create
28
+ project.send(method_name).should_not be_nil
29
+ end
30
+ end
31
+ end
32
+
33
+ context "when unsuccessful" do
34
+ before(:each) do
35
+ register_create_uri(["404", "Not found"])
36
+ end
37
+
38
+ specify {Project.create.should be_false}
39
+ end
40
+ end
41
+
42
+ describe "#save" do
43
+ context "with valid parameters" do
44
+ it "sets basic auth with the public and private key" do
45
+ register_update_uri
46
+ API.should_receive(:put).with("/projects/#{@project.public_key}", :query => anything(), :basic_auth => {:username => @project.public_key, :password => @project.private_key}).and_return(mock("response", :code => 401))
47
+ @project.save
48
+ end
49
+
50
+ context "and the response is '200 Ok'" do
51
+ before(:each) do
52
+ register_update_uri
53
+ @project.name = @updated_name
54
+ @project.save.should be_true
55
+ end
56
+
57
+ [ :public_key,
58
+ :private_key,
59
+ :short_uri,
60
+ :community_uri,
61
+ :name,
62
+ :api_uri].each do |method_name|
63
+ it "sets the '#{method_name.to_s}'" do
64
+ @project.send(method_name).should_not be_nil
65
+ end
66
+ end
67
+ end
68
+
69
+ context "and the response is '401 Unauthorized'" do
70
+ it "returns false" do
71
+ @project.name = @updated_name
72
+ register_update_uri(["401", "Unauthorized"])
73
+ @project.save.should be_false
74
+ end
75
+ end
76
+ end
77
+
78
+ context "with invalid parameters" do
79
+ before(:each) do
80
+ register_show_uri(["404", "Not found"], "")
81
+ register_create_uri(["422", "Unprocessable Entity"], "")
82
+ end
83
+
84
+ it "returns false" do
85
+ Project.create.should be_false
86
+ end
87
+ end
88
+ end
89
+
90
+ context "#delete" do
91
+ before(:each) do
92
+ FakeWeb.register_uri(:delete, @authd_project_api_uri, :status => ["200", "Successful"], :body => "")
93
+ end
94
+
95
+ it "sets basic auth with the public and private key" do
96
+ API.should_receive(:delete).with("/projects/#{@project.public_key}", :basic_auth => {:username => @project.public_key, :password => @project.private_key}).and_return(mock("response", :code => 401))
97
+ @project.delete
98
+ end
99
+
100
+ context "without a private key specified" do
101
+ it "raises an InsufficientCredentials error" do
102
+ lambda {Project.new('public_key').delete}.should raise_error(Errors::InsufficientCredentials)
103
+ end
104
+ end
105
+
106
+ context "without a public key specified" do
107
+ it "raises an InsufficientCredentials error" do
108
+ lambda {Project.new(nil, 'private_key').delete}.should raise_error(Errors::InsufficientCredentials)
109
+ end
110
+ end
111
+
112
+ context "with Sucessful response" do
113
+ it "returns true" do
114
+ @project.delete.should be_true
115
+ end
116
+ end
117
+
118
+ context "with Unauthorized response" do
119
+ it "returns false when invalid Unauthorized response is received" do
120
+ register_delete_uri(["401", "Unauthorized"], "")
121
+ @project.delete.should be_false
122
+ end
123
+ end
124
+ end
125
+
126
+ describe "#to_config" do
127
+ before(:each) do
128
+ register_show_uri(["404", "Not Found"], "")
129
+ register_create_uri
130
+ @project = Project.create
131
+ end
132
+
133
+ it "returns an object keyed by the project's public_key as a symbol" do
134
+ @project.to_config.should include(@project.public_key.to_sym)
135
+ end
136
+
137
+ context "the content under the project's public_key element" do
138
+ [:private_key, :api_uri, :short_uri].each do |project_attribute|
139
+ it "includes a key-value pair of ':#{project_attribute.to_s} => [project's #{project_attribute.to_s}]'" do
140
+ value = @project.send(project_attribute)
141
+ @project.to_config[@project.public_key.to_sym].should include(project_attribute => value)
142
+ end
143
+ end
144
+ end
145
+
146
+ it "doesn't include the private_key in the hash if it is nil" do
147
+ public_key = "key_to_happiness"
148
+ Project.new(public_key).to_config[public_key.to_sym].should_not have_key(:private_key)
149
+ end
150
+ end
151
+
152
+ describe "#claim" do
153
+ let(:project) {Project.new('public_key_value', 'private_key_value')}
154
+ let(:api_key) {"USERS_API_KEY"}
155
+
156
+ before(:each) do
157
+ ConfigFile.stub!(:credentials).and_return({:api_key => api_key})
158
+ end
159
+
160
+ it "delegates the request to the Claim class" do
161
+ Claim.should_receive(:create).with(project, api_key)
162
+ project.claim
163
+ end
164
+ end
165
+
166
+ describe "#release" do
167
+ let(:project) {Project.new('public_key_value', 'private_key_value')}
168
+ let(:api_key) {"USERS_API_KEY"}
169
+
170
+ before(:each) do
171
+ ConfigFile.stub!(:credentials).and_return({:api_key => api_key})
172
+ end
173
+
174
+ it "delegates the request to the Claim class" do
175
+ Claim.should_receive(:destroy).with(project, api_key)
176
+ project.release
177
+ end
178
+ end
179
+
180
+ describe "protected attributes" do
181
+ [:api_uri, :community_uri, :short_uri].each do |attribute_name|
182
+ it "values passed in during initiazation for '#{attribute_name.to_s}' are silently ignored" do
183
+ p = Project.new(attribute_name => Time.now.to_s)
184
+ p.send(attribute_name).should be_nil
185
+ end
186
+
187
+ it "calling '#{attribute_name.to_s}= [value]' is not supported" do
188
+ p = Project.new
189
+ lambda {p.send("#{attribute_name}=", "my_value")}.should raise_error
190
+ end
191
+ end
192
+ end
193
+
194
+ describe "accessible attributes" do
195
+ let(:key_specified) {"public_key"}
196
+ let(:name_specified) {"project_name"}
197
+ let(:project) {Project.new(key_specified, '', :name => name_specified)}
198
+
199
+ specify {project.public_key.should == key_specified}
200
+ specify {project.name.should == name_specified}
201
+ end
202
+
203
+ describe "#find" do
204
+ context "specifying a public_key which is already associated to a project on the site" do
205
+ it "returns an initialized instance of the Project class" do
206
+ register_show_uri
207
+ expected_config = {
208
+ @pub_key.to_sym =>
209
+ {
210
+ :private_key=>"private_key_value",
211
+ :api_uri=>"http://codefumes.com/api/v1/xml/projects/#{@pub_key}.xml",
212
+ :short_uri=>"http://codefumes.com/p/#{@pub_key}"
213
+ }
214
+ }
215
+ Project.find(@pub_key).to_config.should == expected_config
216
+ end
217
+ end
218
+
219
+ # TODO: clean up duplication w/ non-Symbol test
220
+ context "specifying a public_key which is already associated to a project on the site as a Symbol" do
221
+ it "returns an initialized instance of the Project class" do
222
+ register_show_uri
223
+ expected_config = {
224
+ @pub_key.to_sym =>
225
+ {
226
+ :private_key=>"private_key_value",
227
+ :api_uri=>"http://codefumes.com/api/v1/xml/projects/#{@pub_key}.xml",
228
+ :short_uri=>"http://codefumes.com/p/#{@pub_key}"
229
+ }
230
+ }
231
+ Project.find(@pub_key.to_sym).to_config.should == expected_config
232
+ end
233
+ end
234
+
235
+ context "specifying a public_key which is not associated to any project on the site yet" do
236
+ before(:each) do
237
+ register_show_uri(["404", "Not Found"], "")
238
+ end
239
+
240
+ it "returns nil" do
241
+ Project.find(@pub_key).should == nil
242
+ end
243
+ end
244
+
245
+ context "supplying a nil public key" do
246
+ it "does not hit the CodeFumes server" do
247
+ API.should_not_receive(:get)
248
+ Project.find(nil).should == nil
249
+ end
250
+ it "returns nil" do
251
+ Project.find(nil).should == nil
252
+ end
253
+ end
254
+
255
+ context "supplying an empty string as a public key" do
256
+ it "does not hit the CodeFumes server" do
257
+ API.should_not_receive(:get)
258
+ Project.find('').should == nil
259
+ end
260
+
261
+ it "returns nil" do
262
+ Project.find('').should == nil
263
+ end
264
+ end
265
+ end
266
+
267
+ describe "#reinitialize_from_hash!" do
268
+ let(:option_keys) {[:name, :public_key, :private_key, :short_uri, :community_uri, :api_uri, :build_status]}
269
+
270
+ it "supports a Hash with Strings as keys" do
271
+ params = option_keys.inject({}) {|option_params,key| option_params.merge(key.to_s => key.to_s)}
272
+ project = Project.new.reinitialize_from_hash!(params)
273
+ option_keys.each do |attr_name|
274
+ project.send(attr_name).should == attr_name.to_s
275
+ end
276
+ end
277
+
278
+ it "supports a Hash with Symbols as keys" do
279
+ params = option_keys.inject({}) {|option_params,key| option_params.merge(key.to_sym => key.to_s)}
280
+ project = Project.new.reinitialize_from_hash!(params)
281
+ option_keys.each do |attr_name|
282
+ project.send(attr_name.to_sym).should == attr_name.to_s
283
+ end
284
+ end
285
+ end
286
+ end
@@ -1,40 +1,63 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper.rb'
2
2
 
3
- class APIClass < CodeFumes::API
4
- end
5
-
6
3
  describe "API" do
7
4
  after(:all) do
8
- CodeFumes::API.mode(:production)
5
+ CodeFumes::API.mode = :production
9
6
  end
10
7
 
11
8
  it "defaults the base uri to the production site" do
12
- APIClass.base_uri.should == 'http://codefumes.com/api/v1/xml'
9
+ API.base_uri.should == 'http://codefumes.com/api/v1/xml'
13
10
  end
14
11
 
15
12
  context "switching modes" do
16
13
  before(:each) do
17
- APIClass.mode(:test)
14
+ API.mode = :test
18
15
  end
19
16
 
20
17
  it "changes the base uri to the test site when switched to test mode" do
21
- APIClass.base_uri.should == 'http://test.codefumes.com/api/v1/xml'
18
+ API.base_uri.should == API::BASE_URIS[:test]
22
19
  end
23
20
 
24
21
  it "changes the base uri to the production site when switched to production mode" do
25
- APIClass.mode(:production)
26
- APIClass.base_uri.should == 'http://codefumes.com/api/v1/xml'
22
+ API.mode = :production
23
+ API.base_uri.should == API::BASE_URIS[:production]
27
24
  end
28
25
 
29
26
  it "ignores unrecognized modes" do
30
- APIClass.mode(:incomprehensible)
31
- APIClass.base_uri.should == 'http://test.codefumes.com/api/v1/xml'
27
+ API.mode = :incomprehensible
28
+ API.base_uri.should == API::BASE_URIS[:test]
29
+ end
30
+
31
+ it "treats empty Strings as an unrecognized mode" do
32
+ API.mode = ''
33
+ API.base_uri.should == API::BASE_URIS[:test]
34
+ end
35
+
36
+ it "treats nil as an unrecognized mode" do
37
+ API.mode = nil
38
+ API.base_uri.should == API::BASE_URIS[:test]
39
+ end
40
+
41
+ it "supports String versions of the supported modes" do
42
+ API.base_uri.should == API::BASE_URIS[:test]
43
+ API.mode = 'production'
44
+ API.base_uri.should == API::BASE_URIS[:production]
32
45
  end
33
46
 
34
- it "changes the base uri to 'localhost:3000' when switched to local mode (for developer testing)" do
35
- APIClass.mode(:local)
36
- APIClass.base_uri.should == 'http://codefumes.com.local/api/v1/xml'
47
+ it "changes the base uri to 'codefumes.com.local' when switched to local mode (for developer testing)" do
48
+ API.mode = :local
49
+ API.base_uri.should == API::BASE_URIS[:local]
50
+ end
51
+ end
52
+ context "#mode?" do
53
+ specify {API.mode?(:production).should == true}
54
+ specify {API.mode?('production').should == true}
55
+ specify {API.mode?(:test).should == false}
56
+ specify {API.mode?(:anything).should == false}
57
+ specify {API.mode?(nil).should == false}
58
+
59
+ it "does not modify the existing mode" do
60
+ lambda {API.mode?(:test)}.should_not change(API, :base_uri)
37
61
  end
38
62
  end
39
-
40
63
  end