scrapinghub-client 0.0.1

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