codefumes 0.1.10 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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