scrapinghub-client 0.0.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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +6 -0
  5. data/Gemfile +5 -0
  6. data/LICENSE +21 -0
  7. data/README.md +46 -0
  8. data/Rakefile +22 -0
  9. data/lib/scrapinghub-client.rb +3 -0
  10. data/lib/scrapinghub/jobs.rb +175 -0
  11. data/lib/scrapinghub/version.rb +3 -0
  12. data/scrapinghub.gemspec +26 -0
  13. data/spec/fixtures/vcr_cassettes/jobs/delete/bad_auth.yml +38 -0
  14. data/spec/fixtures/vcr_cassettes/jobs/delete/job/invalid.yml +38 -0
  15. data/spec/fixtures/vcr_cassettes/jobs/delete/job/multiple.yml +38 -0
  16. data/spec/fixtures/vcr_cassettes/jobs/delete/job/single.yml +38 -0
  17. data/spec/fixtures/vcr_cassettes/jobs/delete/project/invalid.yml +39 -0
  18. data/spec/fixtures/vcr_cassettes/jobs/delete/project/valid.yml +38 -0
  19. data/spec/fixtures/vcr_cassettes/jobs/list/bad_auth.yml +38 -0
  20. data/spec/fixtures/vcr_cassettes/jobs/list/count/3.yml +53 -0
  21. data/spec/fixtures/vcr_cassettes/jobs/list/has_tag/invalid.yml +38 -0
  22. data/spec/fixtures/vcr_cassettes/jobs/list/has_tag/multiple.yml +53 -0
  23. data/spec/fixtures/vcr_cassettes/jobs/list/has_tag/single.yml +48 -0
  24. data/spec/fixtures/vcr_cassettes/jobs/list/job/invalid.yml +38 -0
  25. data/spec/fixtures/vcr_cassettes/jobs/list/job/multiple.yml +48 -0
  26. data/spec/fixtures/vcr_cassettes/jobs/list/job/single.yml +43 -0
  27. data/spec/fixtures/vcr_cassettes/jobs/list/lacks_tag/invalid.yml +81 -0
  28. data/spec/fixtures/vcr_cassettes/jobs/list/lacks_tag/multiple.yml +67 -0
  29. data/spec/fixtures/vcr_cassettes/jobs/list/lacks_tag/single.yml +72 -0
  30. data/spec/fixtures/vcr_cassettes/jobs/list/project/invalid.yml +39 -0
  31. data/spec/fixtures/vcr_cassettes/jobs/list/project/valid.yml +81 -0
  32. data/spec/fixtures/vcr_cassettes/jobs/list/spider/invalid.yml +38 -0
  33. data/spec/fixtures/vcr_cassettes/jobs/list/spider/valid.yml +62 -0
  34. data/spec/fixtures/vcr_cassettes/jobs/list/state/finished.yml +81 -0
  35. data/spec/fixtures/vcr_cassettes/jobs/list/state/pending.yml +38 -0
  36. data/spec/fixtures/vcr_cassettes/jobs/schedule/bad_auth.yml +38 -0
  37. data/spec/fixtures/vcr_cassettes/jobs/schedule/project/invalid.yml +39 -0
  38. data/spec/fixtures/vcr_cassettes/jobs/schedule/spider/add_tag.yml +38 -0
  39. data/spec/fixtures/vcr_cassettes/jobs/schedule/spider/already-running.yml +39 -0
  40. data/spec/fixtures/vcr_cassettes/jobs/schedule/spider/extra.yml +38 -0
  41. data/spec/fixtures/vcr_cassettes/jobs/schedule/spider/minimal.yml +38 -0
  42. data/spec/fixtures/vcr_cassettes/jobs/schedule/spider/priority.yml +38 -0
  43. data/spec/fixtures/vcr_cassettes/jobs/stop/bad_auth.yml +38 -0
  44. data/spec/fixtures/vcr_cassettes/jobs/stop/job/already-stopped.yml +38 -0
  45. data/spec/fixtures/vcr_cassettes/jobs/stop/job/invalid.yml +38 -0
  46. data/spec/fixtures/vcr_cassettes/jobs/stop/job/valid.yml +38 -0
  47. data/spec/fixtures/vcr_cassettes/jobs/stop/project/invalid.yml +39 -0
  48. data/spec/fixtures/vcr_cassettes/jobs/update/bad_auth.yml +38 -0
  49. data/spec/fixtures/vcr_cassettes/jobs/update/has_tag.yml +38 -0
  50. data/spec/fixtures/vcr_cassettes/jobs/update/job.yml +38 -0
  51. data/spec/fixtures/vcr_cassettes/jobs/update/lacks_tag.yml +38 -0
  52. data/spec/fixtures/vcr_cassettes/jobs/update/no-query-filters.yml +38 -0
  53. data/spec/fixtures/vcr_cassettes/jobs/update/no-update-params.yml +38 -0
  54. data/spec/fixtures/vcr_cassettes/jobs/update/project/invalid.yml +39 -0
  55. data/spec/fixtures/vcr_cassettes/jobs/update/spider.yml +38 -0
  56. data/spec/fixtures/vcr_cassettes/jobs/update/state.yml +38 -0
  57. data/spec/integration/jobs_spec.rb +567 -0
  58. data/spec/spec_helper.rb +22 -0
  59. data/spec/unit/jobs_spec.rb +188 -0
  60. data/spec/unit/scrapinghub_spec.rb +8 -0
  61. metadata +200 -0
@@ -0,0 +1,567 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ shared_examples "connection_refused_returns_try" do
4
+ before do
5
+ stub_request(:any, "dash.scrapinghub.com").to_timeout
6
+ end
7
+
8
+ it "returns a Try::Failure when host is down" do
9
+ expect(jobs.send(action, args)).to be_a Kleisli::Try::Failure
10
+ end
11
+ end
12
+
13
+ shared_examples "bad_auth_returns_try" do |cassette|
14
+ use_vcr_cassette cassette
15
+
16
+ it "returns a Left when bad authentication is used" do
17
+ js = jobs.send(action, args)
18
+ expect(js).to be_a Kleisli::Either::Left
19
+ expect(js.left.class).to eq HTTParty::Response
20
+ expect(js.left.response).to be_a Net::HTTPForbidden
21
+ expect(js.left['status']).to eq("error")
22
+ expect(js.left["message"]).to match(/^Authentication failed$/)
23
+ end
24
+ end
25
+
26
+ describe "jobs integration" do
27
+ let(:api_key) { "XXX" }
28
+ let(:jobs) { Scrapinghub::Jobs.new(api_key: api_key) }
29
+
30
+ describe "list" do
31
+ let(:action) { :list }
32
+ let(:valid_project) { 1 }
33
+ let(:args) { {project: valid_project} }
34
+
35
+ it_behaves_like "connection_refused_returns_try"
36
+ it_behaves_like "bad_auth_returns_try", "jobs/list/bad_auth"
37
+
38
+ context "project" do
39
+ context "given a valid project ID" do
40
+ use_vcr_cassette "jobs/list/project/valid"
41
+
42
+ it "returns a Right" do
43
+ expect(jobs.send(action, args)).to be_a Kleisli::Either::Right
44
+ end
45
+ end
46
+
47
+ context "given an invalid / non-owned project ID" do
48
+ use_vcr_cassette "jobs/list/project/invalid"
49
+ let(:invalid_project) { 2 }
50
+ let(:args) { {project: invalid_project} }
51
+
52
+ it "returns a Left" do
53
+ expect(jobs.send(action, args)).to be_a Kleisli::Either::Left
54
+ end
55
+ end
56
+ end
57
+
58
+ context "job" do
59
+ context "given a single job" do
60
+ use_vcr_cassette "jobs/list/job/single"
61
+ let(:job) { "1/1/6" }
62
+ let(:args) { {project: valid_project, job: job} }
63
+
64
+ it "returns the job" do
65
+ js = jobs.send(action, args)
66
+ expect(js).to be_a Kleisli::Either::Right
67
+ expect(js.fmap{|j| j["total"]}.right).to eq(1)
68
+ end
69
+ end
70
+
71
+ context "given a list of multiple jobs" do
72
+ use_vcr_cassette "jobs/list/job/multiple"
73
+ let(:job) { ["1/1/1", "1/1/2"] }
74
+ let(:args) { {project: valid_project, job: job} }
75
+
76
+ it "returns the jobs" do
77
+ js = jobs.send(action, args)
78
+ expect(js).to be_a Kleisli::Either::Right
79
+ expect(js.fmap{|j| j["total"]}.right).to eq(2)
80
+ end
81
+ end
82
+
83
+ context "given an invalid/non-existent job" do
84
+ use_vcr_cassette "jobs/list/job/invalid"
85
+ let(:job) { "1/1/123" }
86
+ let(:args) { {project: valid_project, job: job} }
87
+
88
+ it "returns no jobs" do
89
+ js = jobs.send(action, args)
90
+ expect(js).to be_a Kleisli::Either::Right
91
+ expect(js.fmap{|j| j["total"]}.right).to eq(0)
92
+ end
93
+ end
94
+ end
95
+
96
+ context "spider" do
97
+ context "given a valid spider with ran jobs" do
98
+ use_vcr_cassette "jobs/list/spider/valid"
99
+ let(:spider) { "atlantic_firearms_crawl" }
100
+ let(:args) { {project: valid_project, spider: spider} }
101
+
102
+ it "returns a Right with the list of spider jobs" do
103
+ js = jobs.send(action, args)
104
+ expect(js).to be_a Kleisli::Either::Right
105
+ expect(js.fmap{|j| j["total"]}.right).to eq(5)
106
+ expect(js.fmap{|j| j["jobs"]}.fmap(&:size).right).to eq(5)
107
+ end
108
+ end
109
+
110
+ context "given an invalid or not-ran spider" do
111
+ use_vcr_cassette "jobs/list/spider/invalid"
112
+ let(:spider) { "bar" }
113
+ let(:args) { {project: valid_project, spider: spider} }
114
+
115
+ it "returns a Right with no results" do
116
+ js = jobs.send(action, args)
117
+ expect(js).to be_a Kleisli::Either::Right
118
+ expect(js.fmap{|j| j["total"]}.right).to eq(0)
119
+ expect(js.fmap{|j| j["jobs"]}.fmap(&:size).right).to eq(0)
120
+ end
121
+ end
122
+ end
123
+
124
+ context "state" do
125
+ context "given 'finished' with completed jobs" do
126
+ use_vcr_cassette "jobs/list/state/finished"
127
+ let(:state) { "finished" }
128
+ let(:args) { {project: valid_project, state: state} }
129
+
130
+ it "returns a Right with the list of spider jobs" do
131
+ js = jobs.send(action, args)
132
+ expect(js).to be_a Kleisli::Either::Right
133
+ expect(js.fmap{|j| j["total"]}.right).to eq(9)
134
+ expect(js.fmap{|j| j["jobs"]}.fmap(&:size).right).to eq(9)
135
+ end
136
+ end
137
+
138
+ context "given 'pending' without pending jobs" do
139
+ use_vcr_cassette "jobs/list/state/pending"
140
+ let(:state) { "pending" }
141
+ let(:args) { {project: valid_project, state: state} }
142
+
143
+ it "returns a Right without any jobs" do
144
+ js = jobs.send(action, args)
145
+ expect(js).to be_a Kleisli::Either::Right
146
+ expect(js.fmap{|j| j["total"]}.right).to eq(0)
147
+ expect(js.fmap{|j| j["jobs"]}.fmap(&:size).right).to eq(0)
148
+ end
149
+ end
150
+ end
151
+
152
+ context "has_tag" do
153
+ context "given a single tag" do
154
+ use_vcr_cassette "jobs/list/has_tag/single"
155
+ let(:has_tag) { "foo" }
156
+ let(:args) { {project: valid_project, has_tag: has_tag} }
157
+
158
+ it "returns jobs with that tag" do
159
+ js = jobs.send(action, args)
160
+ expect(js).to be_a Kleisli::Either::Right
161
+ expect(js.fmap{|j| j["total"]}.right).to eq(2)
162
+ js.fmap{|j| j["jobs"].map{|j| j["tags"]} }.right.each do |tags|
163
+ expect(tags).to include(has_tag)
164
+ end
165
+ end
166
+ end
167
+
168
+ context "given a list of multiple tags" do
169
+ use_vcr_cassette "jobs/list/has_tag/multiple"
170
+ let(:has_tag) { ["foo", "bar"] }
171
+ let(:args) { {project: valid_project, has_tag: has_tag} }
172
+
173
+ it "returns all jobs with either tag" do
174
+ js = jobs.send(action, args)
175
+ expect(js).to be_a Kleisli::Either::Right
176
+ expect(js.fmap{|j| j["total"]}.right).to eq(3)
177
+ js.fmap{|j| j["jobs"].map{|j| j["tags"]} }.right.each do |tags|
178
+ in_common = tags & has_tag
179
+ expect(in_common).not_to be_empty
180
+ end
181
+ end
182
+ end
183
+
184
+ context "given an invalid/non-existent tag" do
185
+ use_vcr_cassette "jobs/list/has_tag/invalid"
186
+ let(:has_tag) { "baz" }
187
+ let(:args) { {project: valid_project, has_tag: has_tag} }
188
+
189
+ it "returns no jobs" do
190
+ js = jobs.send(action, args)
191
+ expect(js).to be_a Kleisli::Either::Right
192
+ expect(js.fmap{|j| j["total"]}.right).to eq(0)
193
+ end
194
+ end
195
+ end
196
+
197
+ context "lacks_tag" do
198
+ context "given a single tag" do
199
+ use_vcr_cassette "jobs/list/lacks_tag/single"
200
+ let(:lacks_tag) { "foo" }
201
+ let(:args) { {project: valid_project, lacks_tag: lacks_tag} }
202
+
203
+ it "returns jobs with that tag" do
204
+ js = jobs.send(action, args)
205
+ expect(js).to be_a Kleisli::Either::Right
206
+ expect(js.fmap{|j| j["total"]}.right).to eq(7)
207
+ js.fmap{|j| j["jobs"].map{|j| j["tags"]} }.right.each do |tags|
208
+ expect(tags).not_to include(lacks_tag)
209
+ end
210
+ end
211
+ end
212
+
213
+ context "given a list of tags" do
214
+ use_vcr_cassette "jobs/list/lacks_tag/multiple"
215
+ let(:lacks_tag) { ["foo", "bar"] }
216
+ let(:args) { {project: valid_project, lacks_tag: lacks_tag} }
217
+
218
+ it "returns all jobs without either tag" do
219
+ js = jobs.send(action, args)
220
+ expect(js).to be_a Kleisli::Either::Right
221
+ expect(js.fmap{|j| j["total"]}.right).to eq(6)
222
+ js.fmap{|j| j["jobs"].map{|j| j["tags"]} }.right.each do |tags|
223
+ in_common = tags & lacks_tag
224
+ expect(in_common).to be_empty
225
+ end
226
+ end
227
+ end
228
+
229
+ context "given an invalid/non-existent tag" do
230
+ use_vcr_cassette "jobs/list/lacks_tag/invalid"
231
+ let(:lacks_tag) { "baz" }
232
+ let(:args) { {project: valid_project, lacks_tag: lacks_tag} }
233
+
234
+ it "returns no jobs" do
235
+ js = jobs.send(action, args)
236
+ expect(js).to be_a Kleisli::Either::Right
237
+ expect(js.fmap{|j| j["total"]}.right).to eq(9)
238
+ js.fmap{|j| j["jobs"].map{|j| j["tags"]} }.right.each do |tags|
239
+ expect(tags).not_to include(lacks_tag)
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ context "count" do
246
+ context "given 3" do
247
+ use_vcr_cassette "jobs/list/count/3"
248
+ let(:count) { 3 }
249
+ let(:args) { {project: valid_project, count: count} }
250
+
251
+ it "returns 3 responses" do
252
+ js = jobs.send(action, args)
253
+ expect(js).to be_a Kleisli::Either::Right
254
+ expect(js.fmap{|j| j["total"]}.right).to eq(3)
255
+ end
256
+ end
257
+ end
258
+ end
259
+
260
+ describe "schedule" do
261
+ let(:action) { :schedule }
262
+ let(:valid_project) { 1 }
263
+ let(:args) { {project: valid_project, spider: "atlantic_firearms_crawl"} }
264
+
265
+ it_behaves_like "connection_refused_returns_try"
266
+ it_behaves_like "bad_auth_returns_try", "jobs/schedule/bad_auth"
267
+
268
+ context "project" do
269
+ context "given an invalid / non-owned project ID" do
270
+ use_vcr_cassette "jobs/schedule/project/invalid"
271
+ let(:invalid_project) { 2 }
272
+
273
+ it "returns a Left" do
274
+ js = jobs.send(action, args.merge(project: invalid_project))
275
+ expect(js).to be_a Kleisli::Either::Left
276
+ expect(js.left['status']).to eq("badrequest")
277
+ end
278
+ end
279
+ end
280
+
281
+ context "spider" do
282
+ context "given minimal parameters" do
283
+ use_vcr_cassette "jobs/schedule/spider/minimal"
284
+
285
+ it "returns ok status and the created jobid" do
286
+ js = jobs.send(action, args)
287
+ expect(js).to be_a Kleisli::Either::Right
288
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
289
+ expect(js.fmap{|j| j["jobid"]}.right).to eq("1/1/11")
290
+ end
291
+ end
292
+
293
+ context "when an instance of the spider is already running" do
294
+ use_vcr_cassette "jobs/schedule/spider/already-running"
295
+
296
+ it "returns ok status and the error message" do
297
+ js = jobs.send(action, args)
298
+ expect(js).to be_a Kleisli::Either::Left
299
+ expect(js.left["status"]).to eq("error")
300
+ expect(js.left["message"]).to match(/^Spider.*already scheduled$/)
301
+ end
302
+ end
303
+
304
+ context "given an add_tag argument" do
305
+ use_vcr_cassette "jobs/schedule/spider/add_tag"
306
+
307
+ it "returns ok status and the created jobid" do
308
+ js = jobs.send(action, args.merge(add_tag: "foo"))
309
+ expect(js).to be_a Kleisli::Either::Right
310
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
311
+ expect(js.fmap{|j| j["jobid"]}.right).to eq("1/1/14")
312
+ end
313
+ end
314
+
315
+ context "given a priority argument" do
316
+ use_vcr_cassette "jobs/schedule/spider/priority"
317
+
318
+ it "returns ok status and the created jobid" do
319
+ js = jobs.send(action, args.merge(priority: 4))
320
+ expect(js).to be_a Kleisli::Either::Right
321
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
322
+ expect(js.fmap{|j| j["jobid"]}.right).to eq("1/1/16")
323
+ end
324
+ end
325
+
326
+ context "given an extra argument" do
327
+ use_vcr_cassette "jobs/schedule/spider/extra"
328
+
329
+ it "returns ok status and the created jobid" do
330
+ js = jobs.send(action, args.merge(extra: {:"DOWNLOAD_DELAY" => "0.5"}))
331
+ expect(js).to be_a Kleisli::Either::Right
332
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
333
+ expect(js.fmap{|j| j["jobid"]}.right).to eq("1/1/17")
334
+ end
335
+ end
336
+ end
337
+ end
338
+
339
+ describe "delete" do
340
+ let(:action) { :delete }
341
+ let(:valid_project) { 1 }
342
+ let(:args) { {project: valid_project, job: "#{valid_project}/1/7"} }
343
+
344
+ it_behaves_like "connection_refused_returns_try"
345
+ it_behaves_like "bad_auth_returns_try", "jobs/delete/bad_auth"
346
+
347
+ context "project" do
348
+ context "given an invalid / non-owned project ID" do
349
+ use_vcr_cassette "jobs/delete/project/invalid"
350
+ let(:invalid_project) { 2 }
351
+
352
+ it "returns a Left" do
353
+ expect(jobs.send(action, args.merge(project: invalid_project))).to be_a Kleisli::Either::Left
354
+ end
355
+ end
356
+ end
357
+
358
+ context "job" do
359
+ context "given a single job" do
360
+ use_vcr_cassette "jobs/delete/job/single"
361
+ let(:job) { "#{valid_project}/3/4" }
362
+ let(:args) { {project: valid_project, job: job} }
363
+
364
+ it "returns the right count" do
365
+ js = jobs.send(action, args)
366
+ expect(js).to be_a Kleisli::Either::Right
367
+ expect(js.fmap{|j| j["count"]}.right).to eq(1)
368
+ end
369
+ end
370
+
371
+ context "given a list of multiple jobs" do
372
+ use_vcr_cassette "jobs/delete/job/multiple"
373
+ let(:job) { ["#{valid_project}/1/7", "#{valid_project}/1/8"] }
374
+ let(:args) { {project: valid_project, job: job} }
375
+
376
+ it "returns the right count" do
377
+ js = jobs.send(action, args)
378
+ expect(js).to be_a Kleisli::Either::Right
379
+ expect(js.fmap{|j| j["count"]}.right).to eq(2)
380
+ end
381
+ end
382
+
383
+ context "given an invalid/non-existent job" do
384
+ use_vcr_cassette "jobs/delete/job/invalid"
385
+ let(:job) { "#{valid_project}/1/123" }
386
+ let(:args) { {project: valid_project, job: job} }
387
+
388
+ it "returns count of 0" do
389
+ js = jobs.send(action, args)
390
+ expect(js).to be_a Kleisli::Either::Right
391
+ expect(js.fmap{|j| j["count"]}.right).to eq(0)
392
+ end
393
+ end
394
+ end
395
+ end
396
+
397
+ describe "stop" do
398
+ let(:action) { :stop }
399
+ let(:valid_project) { 1 }
400
+ let(:args) { {project: valid_project, job: "#{valid_project}/1/9"} }
401
+
402
+ it_behaves_like "connection_refused_returns_try"
403
+ it_behaves_like "bad_auth_returns_try", "jobs/stop/bad_auth"
404
+
405
+ context "project" do
406
+ context "given an invalid / non-owned project ID" do
407
+ use_vcr_cassette "jobs/stop/project/invalid"
408
+ let(:invalid_project) { 2 }
409
+
410
+ it "returns a Left" do
411
+ expect(jobs.send(action, args.merge(project: invalid_project))).to be_a Kleisli::Either::Left
412
+ end
413
+ end
414
+ end
415
+
416
+ context "job" do
417
+ context "given a valid job" do
418
+ use_vcr_cassette "jobs/stop/job/valid"
419
+ let(:job) { "#{valid_project}/1/9" }
420
+ let(:args) { {project: valid_project, job: job} }
421
+
422
+ it "returns ok" do
423
+ js = jobs.send(action, args)
424
+ expect(js).to be_a Kleisli::Either::Right
425
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
426
+ end
427
+ end
428
+
429
+ context "given a non-existent job" do
430
+ use_vcr_cassette "jobs/stop/job/invalid"
431
+ let(:job) { "#{valid_project}/123/123" }
432
+ let(:args) { {project: valid_project, job: job} }
433
+
434
+ it "returns ok" do
435
+ js = jobs.send(action, args)
436
+ expect(js).to be_a Kleisli::Either::Right
437
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
438
+ end
439
+ end
440
+
441
+ context "given an already-stopped job" do
442
+ use_vcr_cassette "jobs/stop/job/already-stopped"
443
+ let(:job) { "#{valid_project}/1/6" }
444
+ let(:args) { {project: valid_project, job: job} }
445
+
446
+ it "returns ok" do
447
+ js = jobs.send(action, args)
448
+ expect(js).to be_a Kleisli::Either::Right
449
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
450
+ end
451
+ end
452
+
453
+ end
454
+ end
455
+
456
+ describe "update" do
457
+ let(:action) { :update }
458
+ let(:valid_project) { 1 }
459
+ let(:args) { {project: valid_project, job: "1/1/18"} }
460
+
461
+ it_behaves_like "connection_refused_returns_try"
462
+ it_behaves_like "bad_auth_returns_try", "jobs/update/bad_auth"
463
+
464
+ context "project" do
465
+ context "given an invalid / non-owned project ID" do
466
+ use_vcr_cassette "jobs/update/project/invalid"
467
+ let(:invalid_project) { 2 }
468
+ let(:args) { {project: invalid_project} }
469
+
470
+ it "returns an error" do
471
+ js = jobs.send(action, args)
472
+ expect(js).to be_a Kleisli::Either::Left
473
+ expect(js.left['status']).to eq("badrequest")
474
+ expect(js.left["message"]).to match(/^User.*doesn\'t have access to project/)
475
+ end
476
+ end
477
+ end
478
+
479
+ context "query filters" do
480
+ context "without query filters" do
481
+ use_vcr_cassette "jobs/update/no-query-filters"
482
+ let(:args) { {project: valid_project} }
483
+
484
+ it "returns an error" do
485
+ js = jobs.send(action, args)
486
+ expect(js).to be_a Kleisli::Either::Left
487
+ expect(js.left["status"]).to eq("badrequest")
488
+ expect(js.left["message"]).to match(/^No query filters provided$/)
489
+ end
490
+ end
491
+
492
+ context "filtering on job" do
493
+ use_vcr_cassette "jobs/update/job"
494
+ let(:args) { {project: valid_project, job: ["1/3/7", "1/1/18"], add_tag: "baz"} }
495
+
496
+ it "returns ok status and the affected count" do
497
+ js = jobs.send(action, args)
498
+ expect(js).to be_a Kleisli::Either::Right
499
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
500
+ expect(js.fmap{|j| j["count"]}.right).to eq(2)
501
+ end
502
+ end
503
+
504
+ context "filtering on spider" do
505
+ use_vcr_cassette "jobs/update/spider"
506
+ let(:args) { {project: valid_project, spider: "atlantic_firearms_crawl", add_tag: "foo"} }
507
+
508
+ it "returns ok status and the affected count" do
509
+ js = jobs.send(action, args)
510
+ expect(js).to be_a Kleisli::Either::Right
511
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
512
+ expect(js.fmap{|j| j["count"]}.right).to eq(11)
513
+ end
514
+ end
515
+
516
+ context "filtering on state" do
517
+ use_vcr_cassette "jobs/update/state"
518
+ let(:args) { {project: valid_project, state: "running", add_tag: "bar"} }
519
+
520
+ it "returns ok status and the affected count" do
521
+ js = jobs.send(action, args)
522
+ expect(js).to be_a Kleisli::Either::Right
523
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
524
+ expect(js.fmap{|j| j["count"]}.right).to eq(2)
525
+ end
526
+ end
527
+
528
+ context "filtering on has_tag" do
529
+ use_vcr_cassette "jobs/update/has_tag"
530
+ let(:args) { {project: valid_project, has_tag: "bar", remove_tag: "bar"} }
531
+
532
+ it "returns ok status and the affected count" do
533
+ js = jobs.send(action, args)
534
+ expect(js).to be_a Kleisli::Either::Right
535
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
536
+ expect(js.fmap{|j| j["count"]}.right).to eq(4)
537
+ end
538
+ end
539
+
540
+ context "filtering on lacks_tag" do
541
+ use_vcr_cassette "jobs/update/lacks_tag"
542
+ let(:args) { {project: valid_project, lacks_tag: "bar", add_tag: "bar"} }
543
+
544
+ it "returns ok status and the affected count" do
545
+ js = jobs.send(action, args)
546
+ expect(js).to be_a Kleisli::Either::Right
547
+ expect(js.fmap{|j| j["status"]}.right).to eq("ok")
548
+ expect(js.fmap{|j| j["count"]}.right).to eq(18)
549
+ end
550
+ end
551
+ end
552
+
553
+ context "update parameters" do
554
+ context "without update parameters" do
555
+ use_vcr_cassette "jobs/update/no-update-params"
556
+
557
+ it "returns an error" do
558
+ js = jobs.send(action, args)
559
+ expect(js).to be_a Kleisli::Either::Left
560
+ expect(js.left["status"]).to eq("badrequest")
561
+ expect(js.left["message"]).to match(/^No update modifiers provided$/)
562
+ end
563
+ end
564
+ end
565
+ end
566
+
567
+ end