sauce_whisk 0.0.3 → 0.0.4

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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OGZmYWI0OTdkOGZhN2Y4MTJlOGI3NmIwZDkwODA4MTBhZmViOTlmYg==
4
+ NDgyMTEzNDI3YWYzZTg2ZDBlODEwYWEwNjE3ODBjMTgzMDNlZmUzMQ==
5
5
  data.tar.gz: !binary |-
6
- MTMzNDg5Y2Q5YjdjNjlkMWIwZDNkZTBjNDc2Y2FhYzhmODIxZmQ4MA==
6
+ MzM0YWI4OGNjMGU4YTJkZGMzMTE2ZWE0YWRmYzVmZmQ4ODk2ODMxOQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- ZmVlZjYzMDY5YjUwYzE2ZDBjMTc5OGZhN2Y1Njk3MjUyODk2NTgyZDBkM2Nk
10
- ZjdlYTBiODc1OTFkNGVmOGYxNGYyMjBiZmE4ODVjOWFhOTMxMjE0MjQ4MGE3
11
- MWM5YWE5MTU5NGEyOWU3ODNhZTA1NDUyNGZmYmZhYWRmNDQ3Nzc=
9
+ NzM4YjkzNDMzMTM1OGFiZTFhZDk3YmM3ZjdiZGUxNGVkMDE1NzdhODhhNGYx
10
+ NzlkMDI2MTBhMjEwZGU2NzAwYTk1OGI4NDhiOWRlYWViMTMyNDMwOGJjOTJh
11
+ M2ZlZDM5YWM1ZWZiZjczZTNjOTA2MjQwZjY0ZDdiOTY1NTU1Mjc=
12
12
  data.tar.gz: !binary |-
13
- NWUwYjk5MjJjYzE0MGY4MzliODhhZGIwMGZhZThlYzNiYTEzZmY1ZTQxMjM2
14
- OGE1MDE2MGNiZjIyYzk5NzNlMjA2OGEzMDhkYjA4NGY0YzNiZTk4Y2IwN2Y0
15
- OTEwMDI1N2Q2MGM5ZWEyYjFjYzQzMTg3OGI1ZTU2MTU3NTVhOTU=
13
+ MmUyMWU5MjE1YWJiOWJkN2Y0YTlhY2Y0YjdlMjU1YjRkZTczNDM4OGZlYTk0
14
+ MTliNWI2M2EyMGM1NGYyOGQ4Y2VlOWUxM2FjMzU4YWI0MDBhM2VlZmRhNjNl
15
+ Y2I3ZDA2YjM0YzFmYzJlMTgzOTg1NDU3MWY0M2I1YzNjNGM2MDI=
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- group :test do
4
- gem 'psych', "2.0.0", :path => "./vendor/psych"
5
- end
3
+ #group :test do
4
+ # gem 'psych', "2.0.0", :path => "./vendor/psych"
5
+ #end
6
6
 
7
7
  gemspec
data/README.md CHANGED
@@ -35,6 +35,7 @@ SAUCE_ACCESS_KEY=Your Access Key, found on the lower left of your Account page
35
35
  ```ruby
36
36
  SauceWhisk::Jobs.pass_job job_id
37
37
  SauceWhisk::Jobs.fail_job job_id
38
+ SauceWhisk::Jobs.change_status job_id, true_for_passed_false_for_failed
38
39
  ```
39
40
 
40
41
  ### Creating Job Objects
@@ -0,0 +1,3 @@
1
+ # Major Version 0
2
+ ### 0.0.4
3
+ * Added configuration loading from the Sauce gem, if present
@@ -45,6 +45,10 @@ module SauceWhisk
45
45
  Job.new(job_hash)
46
46
  end
47
47
 
48
+ def self.stop(job_id)
49
+ put "#{job_id}/stop", {}
50
+ end
51
+
48
52
  def self.fetch_asset(job_id, asset)
49
53
  asset = get "#{job_id}/assets/#{asset}"
50
54
  end
@@ -91,6 +95,10 @@ module SauceWhisk
91
95
  Jobs.save(self)
92
96
  end
93
97
 
98
+ def stop
99
+ Jobs.stop id
100
+ end
101
+
94
102
  def updated_fields
95
103
  @updated_fields ||= []
96
104
  end
@@ -7,7 +7,7 @@ module SauceWhisk
7
7
  RestClient::Request.execute({:method => :get, :url => resource_url}.merge auth_details)
8
8
  end
9
9
 
10
- def put(resource_id, body)
10
+ def put(resource_id, body={})
11
11
  url = "#{fully_qualified_resource}/#{resource_id}"
12
12
  length = body.length
13
13
  headers = {"Content-Length" => length}
@@ -15,11 +15,17 @@ module SauceWhisk
15
15
  :method => :put,
16
16
  :url => url,
17
17
  :payload => body,
18
- :content_type => "application/json"
18
+ :content_type => "application/json",
19
+ :headers => headers
19
20
  }
20
21
  RestClient::Request.execute(req_params.merge auth_details)
21
22
  end
22
23
 
24
+ def delete(resource_id)
25
+ resource_to_delete = fully_qualified_resource << "/#{resource_id}"
26
+ RestClient::Request.execute({:method => :delete, :url => resource_to_delete}.merge auth_details)
27
+ end
28
+
23
29
  def auth_details
24
30
  {:user => SauceWhisk.username, :password => SauceWhisk.password}
25
31
  end
@@ -0,0 +1,48 @@
1
+ require 'sauce_whisk/rest_request_builder'
2
+
3
+ module SauceWhisk
4
+ class Tunnels
5
+ extend RestRequestBuilder
6
+
7
+ def self.resource
8
+ "#{SauceWhisk.username}/tunnels"
9
+ end
10
+
11
+ def self.all(fetch_each = false)
12
+ all_tunnels = JSON.parse get
13
+
14
+ unless fetch_each
15
+ return all_tunnels
16
+ end
17
+
18
+ tunnels = all_tunnels.map do |tunnel|
19
+ fetch tunnel
20
+ end
21
+
22
+ return tunnels
23
+ end
24
+
25
+ def self.stop tunnel_id
26
+ delete tunnel_id
27
+ end
28
+
29
+ def self.fetch tunnel_id
30
+ tunnel_parameters = JSON.parse get tunnel_id
31
+ Tunnel.new tunnel_parameters
32
+ end
33
+ end
34
+
35
+ class Tunnel
36
+ attr_reader :id, :owner, :status, :host, :creation_time
37
+
38
+ def initialize(params)
39
+ params.each do |param, val|
40
+ self.instance_variable_set("@#{param}", val)
41
+ end
42
+ end
43
+
44
+ def stop
45
+ SauceWhisk::Tunnels.stop self.id
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  module SauceWhisk
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
data/lib/sauce_whisk.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "sauce_whisk/version"
2
2
  require "sauce_whisk/jobs"
3
3
  require "sauce_whisk/assets"
4
+ require "sauce_whisk/tunnels"
4
5
  require "sauce_whisk/rest_request_builder"
5
6
 
6
7
  module SauceWhisk
@@ -10,11 +11,19 @@ module SauceWhisk
10
11
  end
11
12
 
12
13
  def self.username
13
- ENV["SAUCE_USERNAME"]
14
+ if defined? Sauce
15
+ return Sauce.get_config.username
16
+ else
17
+ return ENV["SAUCE_USERNAME"]
18
+ end
14
19
  end
15
20
 
16
21
  def self.password
17
- ENV["SAUCE_ACCESS_KEY"]
22
+ if defined? Sauce
23
+ return Sauce.get_config.password
24
+ else
25
+ return ENV["SAUCE_ACCESS_KEY"]
26
+ end
18
27
  end
19
28
 
20
29
  def self.pass_job(job_id)
@@ -8,7 +8,7 @@ http_interactions:
8
8
  string: ''
9
9
  headers:
10
10
  Accept:
11
- - ! '*/*; q=0.5, application/xml'
11
+ - '*/*; q=0.5, application/xml'
12
12
  Accept-Encoding:
13
13
  - gzip, deflate
14
14
  User-Agent:
@@ -16,44 +16,31 @@ http_interactions:
16
16
  response:
17
17
  status:
18
18
  code: 200
19
- message: !binary |-
20
- T0s=
19
+ message: OK
21
20
  headers:
22
- !binary "U2VydmVy":
23
- - !binary |-
24
- bmdpbngvMC43LjYy
25
- !binary "RGF0ZQ==":
26
- - !binary |-
27
- RnJpLCAyNCBNYXkgMjAxMyAwNjowNjozOSBHTVQ=
28
- !binary "Q29udGVudC1UeXBl":
29
- - !binary |-
30
- YXBwbGljYXRpb24vanNvbg==
31
- !binary "VHJhbnNmZXItRW5jb2Rpbmc=":
32
- - !binary |-
33
- Y2h1bmtlZA==
34
- !binary "Q29ubmVjdGlvbg==":
35
- - !binary |-
36
- a2VlcC1hbGl2ZQ==
37
- !binary "UHJhZ21h":
38
- - !binary |-
39
- bm8tY2FjaGU=
40
- !binary "Q2FjaGUtQ29udHJvbA==":
41
- - !binary |-
42
- bm8tY2FjaGU=
43
- !binary "WC1GcmFtZS1PcHRpb25z":
44
- - !binary |-
45
- REVOWQ==
46
- !binary "U2V0LUNvb2tpZQ==":
47
- - !binary |-
48
- Y3NyZl90b2tlbj01M2I2Nzc4MDk4NjdkZmMyZGRkNjU4MjI4YzYzYTc2Njsg
49
- ZXhwaXJlcz0iRnJpLCAyNC1NYXktMjAxMyAwNjoyMzoxOSBHTVQiOyBNYXgt
50
- QWdlPTYwMDsgUGF0aD0v
51
- - !binary |-
52
- bWV0cmljcz0zMTNlYzg5ZmFlMzhmNDQwOGI3MzY1ODEzZjI1MGMyMzk2YWIw
53
- Nzk2ZGZmZjI3ODhkODY3NDcyYmJjM2Q4YmIzOTRiMDRjYzg7IFBhdGg9Lw==
54
- !binary "Q29udGVudC1FbmNvZGluZw==":
55
- - !binary |-
56
- Z3ppcA==
21
+ Server:
22
+ - nginx/0.7.62
23
+ Date:
24
+ - Fri, 24 May 2013 06:06:39 GMT
25
+ Content-Type:
26
+ - application/json
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Pragma:
32
+ - no-cache
33
+ Cache-Control:
34
+ - no-cache
35
+ X-Frame-Options:
36
+ - DENY
37
+ Set-Cookie:
38
+ - csrf_token=53b677809867dfc2ddd658228c63a766; expires="Fri, 24-May-2013 06:23:19
39
+ GMT"; Max-Age=600; Path=/
40
+ - metrics=313ec89fae38f4408b7365813f250c2396ab0796dfff2788d867472bbc3d8bb394b04cc8;
41
+ Path=/
42
+ Content-Encoding:
43
+ - gzip
57
44
  body:
58
45
  encoding: ASCII-8BIT
59
46
  string: !binary |-
@@ -110,10 +97,10 @@ http_interactions:
110
97
  uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/jobs/bd9c43dd6b5549f1b942d1d581d98cac
111
98
  body:
112
99
  encoding: UTF-8
113
- string: ! '{"passed":true}'
100
+ string: '{"passed":true}'
114
101
  headers:
115
102
  Accept:
116
- - ! '*/*; q=0.5, application/xml'
103
+ - '*/*; q=0.5, application/xml'
117
104
  Accept-Encoding:
118
105
  - gzip, deflate
119
106
  Content-Length:
@@ -148,7 +135,7 @@ http_interactions:
148
135
  - '611'
149
136
  body:
150
137
  encoding: US-ASCII
151
- string: ! '{"status": "complete", "commands_not_successful": 2, "name": "Ruby
138
+ string: '{"status": "complete", "commands_not_successful": 2, "name": "Ruby
152
139
  Example for Appium", "video_url": "http://saucelabs.com/jobs/bd9c43dd6b5549f1b942d1d581d98cac/video.flv",
153
140
  "tags": [], "proxied": false, "start_time": 1362100867, "log_url": "http://saucelabs.com/jobs/bd9c43dd6b5549f1b942d1d581d98cac/selenium-server.log",
154
141
  "creation_time": 1362100864, "custom-data": null, "public": null, "browser_version":
@@ -165,7 +152,7 @@ http_interactions:
165
152
  string: ''
166
153
  headers:
167
154
  Accept:
168
- - ! '*/*; q=0.5, application/xml'
155
+ - '*/*; q=0.5, application/xml'
169
156
  Accept-Encoding:
170
157
  - gzip, deflate
171
158
  User-Agent:
@@ -198,7 +185,7 @@ http_interactions:
198
185
  - '611'
199
186
  body:
200
187
  encoding: US-ASCII
201
- string: ! '{"status": "complete", "commands_not_successful": 2, "name": "Ruby
188
+ string: '{"status": "complete", "commands_not_successful": 2, "name": "Ruby
202
189
  Example for Appium", "video_url": "http://saucelabs.com/jobs/bd9c43dd6b5549f1b942d1d581d98cac/video.flv",
203
190
  "tags": [], "proxied": false, "start_time": 1362100867, "log_url": "http://saucelabs.com/jobs/bd9c43dd6b5549f1b942d1d581d98cac/selenium-server.log",
204
191
  "creation_time": 1362100864, "custom-data": null, "public": null, "browser_version":
@@ -215,7 +202,7 @@ http_interactions:
215
202
  string: ''
216
203
  headers:
217
204
  Accept:
218
- - ! '*/*; q=0.5, application/xml'
205
+ - '*/*; q=0.5, application/xml'
219
206
  Accept-Encoding:
220
207
  - gzip, deflate
221
208
  User-Agent:
@@ -248,9 +235,267 @@ http_interactions:
248
235
  - '191'
249
236
  body:
250
237
  encoding: US-ASCII
251
- string: ! '{"sauce-log": "log.json", "video": "video.flv", "selenium-log": "selenium-server.log",
238
+ string: '{"sauce-log": "log.json", "video": "video.flv", "selenium-log": "selenium-server.log",
252
239
  "screenshots": ["0000screenshot.png", "0001screenshot.png", "0002screenshot.png",
253
240
  "0003screenshot.png"]}'
254
241
  http_version:
255
242
  recorded_at: Fri, 24 May 2013 06:13:26 GMT
243
+ - request:
244
+ method: put
245
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/jobs/job_id/stop
246
+ body:
247
+ encoding: ASCII-8BIT
248
+ string: ''
249
+ headers:
250
+ Accept:
251
+ - '*/*; q=0.5, application/xml'
252
+ Accept-Encoding:
253
+ - gzip, deflate
254
+ Content-Length:
255
+ - '0'
256
+ Content-Type:
257
+ - application/x-www-form-urlencoded
258
+ User-Agent:
259
+ - Ruby
260
+ response:
261
+ status:
262
+ code: 404
263
+ message: Not Found
264
+ headers:
265
+ Server:
266
+ - nginx/0.7.62
267
+ Date:
268
+ - Mon, 10 Jun 2013 07:14:49 GMT
269
+ Content-Type:
270
+ - application/json
271
+ Connection:
272
+ - keep-alive
273
+ Pragma:
274
+ - no-cache
275
+ Cache-Control:
276
+ - no-cache
277
+ X-Frame-Options:
278
+ - DENY
279
+ Set-Cookie:
280
+ - csrf_token=4298b6049c24168640f07d791dcf5b65; expires="Mon, 10-Jun-2013 07:31:53
281
+ GMT"; Max-Age=600; Path=/
282
+ - metrics=b0b8f1841b3c8208349be86ea61357059b0e4ed4c26ca98213b74728815a4ec47d432151;
283
+ Path=/
284
+ - metrics=f36ba36dd62295ed19dfa7023109af257d3744e6901106f8b3254c599af08d386c3a857e;
285
+ Path=/
286
+ Content-Length:
287
+ - '23'
288
+ body:
289
+ encoding: US-ASCII
290
+ string: '{"errors": "Not Found"}'
291
+ http_version:
292
+ recorded_at: Mon, 10 Jun 2013 07:21:54 GMT
293
+ - request:
294
+ method: put
295
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/jobs/986bafd435c341b7bf8a1ee995606183/stop
296
+ body:
297
+ encoding: ASCII-8BIT
298
+ string: ''
299
+ headers:
300
+ Accept:
301
+ - '*/*; q=0.5, application/xml'
302
+ Accept-Encoding:
303
+ - gzip, deflate
304
+ Content-Length:
305
+ - '0'
306
+ Content-Type:
307
+ - application/x-www-form-urlencoded
308
+ User-Agent:
309
+ - Ruby
310
+ response:
311
+ status:
312
+ code: 404
313
+ message: Not Found
314
+ headers:
315
+ Server:
316
+ - nginx/0.7.62
317
+ Date:
318
+ - Mon, 10 Jun 2013 07:30:03 GMT
319
+ Content-Type:
320
+ - application/json
321
+ Connection:
322
+ - keep-alive
323
+ Pragma:
324
+ - no-cache
325
+ Cache-Control:
326
+ - no-cache
327
+ X-Frame-Options:
328
+ - DENY
329
+ Set-Cookie:
330
+ - csrf_token=6b2be9b2c3e8a2b606b10944adfa767d; expires="Mon, 10-Jun-2013 07:47:08
331
+ GMT"; Max-Age=600; Path=/
332
+ - metrics=8a5fd4768016507bd6281762cf9f4056814fa577ae1b658c62a745078c750aa67afeab47;
333
+ Path=/
334
+ - metrics=c92deb1233a3ae422dbbbdb2ff820a436485cd867ac5a39655234d99aa91fbfed6336887;
335
+ Path=/
336
+ Content-Length:
337
+ - '23'
338
+ body:
339
+ encoding: US-ASCII
340
+ string: '{"errors": "Not Found"}'
341
+ http_version:
342
+ recorded_at: Mon, 10 Jun 2013 07:37:09 GMT
343
+ - request:
344
+ method: put
345
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/jobs/3edc8fe6d52645bf931b1003da65af1f/stop
346
+ body:
347
+ encoding: ASCII-8BIT
348
+ string: ''
349
+ headers:
350
+ Accept:
351
+ - '*/*; q=0.5, application/xml'
352
+ Accept-Encoding:
353
+ - gzip, deflate
354
+ Content-Length:
355
+ - '0'
356
+ Content-Type:
357
+ - application/x-www-form-urlencoded
358
+ User-Agent:
359
+ - Ruby
360
+ response:
361
+ status:
362
+ code: 200
363
+ message: OK
364
+ headers:
365
+ Server:
366
+ - nginx/0.7.62
367
+ Date:
368
+ - Mon, 10 Jun 2013 07:31:41 GMT
369
+ Content-Type:
370
+ - application/json
371
+ Connection:
372
+ - keep-alive
373
+ Pragma:
374
+ - no-cache
375
+ Cache-Control:
376
+ - no-cache
377
+ X-Frame-Options:
378
+ - DENY
379
+ Set-Cookie:
380
+ - csrf_token=35e126296b2dc908d1694a45051b4a4b; expires="Mon, 10-Jun-2013 07:48:45
381
+ GMT"; Max-Age=600; Path=/
382
+ - metrics=ac238c38215972b4e2b21e106220620bcda4daf3f066f7410d2f451dba13b5df645fc89d;
383
+ Path=/
384
+ Content-Length:
385
+ - '652'
386
+ body:
387
+ encoding: US-ASCII
388
+ string: '{"status": "complete", "commands_not_successful": 0, "name": "Manual
389
+ Session at \\011http://www.artindustrial.com", "video_url": "http://saucelabs.com/jobs/3edc8fe6d52645bf931b1003da65af1f/video.flv",
390
+ "tags": [], "proxied": false, "start_time": 1370848961, "log_url": "http://saucelabs.com/jobs/3edc8fe6d52645bf931b1003da65af1f/selenium-server.log",
391
+ "creation_time": 1370848961, "custom-data": null, "public": null, "browser_version":
392
+ "4.0.4", "end_time": 1370849925, "passed": null, "error": "User terminated",
393
+ "owner": "<SAUCE_USERNAME>", "build": null, "os": "Linux", "id": "3edc8fe6d52645bf931b1003da65af1f",
394
+ "breakpointed": null, "browser": "android"}'
395
+ http_version:
396
+ recorded_at: Mon, 10 Jun 2013 07:38:46 GMT
397
+ - request:
398
+ method: put
399
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/jobs/9591ce8519f043f8b3bea0462923c883/stop
400
+ body:
401
+ encoding: ASCII-8BIT
402
+ string: ''
403
+ headers:
404
+ Accept:
405
+ - '*/*; q=0.5, application/xml'
406
+ Accept-Encoding:
407
+ - gzip, deflate
408
+ Content-Length:
409
+ - '0'
410
+ Content-Type:
411
+ - application/x-www-form-urlencoded
412
+ User-Agent:
413
+ - Ruby
414
+ response:
415
+ status:
416
+ code: 200
417
+ message: OK
418
+ headers:
419
+ Server:
420
+ - nginx/0.7.62
421
+ Date:
422
+ - Mon, 10 Jun 2013 07:58:27 GMT
423
+ Content-Type:
424
+ - application/json
425
+ Connection:
426
+ - keep-alive
427
+ Pragma:
428
+ - no-cache
429
+ Cache-Control:
430
+ - no-cache
431
+ X-Frame-Options:
432
+ - DENY
433
+ Set-Cookie:
434
+ - csrf_token=2d1a8d0e7f5eeb39b148d343fbdbf94a; expires="Mon, 10-Jun-2013 08:15:31
435
+ GMT"; Max-Age=600; Path=/
436
+ - metrics=9fe34163bd1c71b54125f30a824b987b8b4293c71041f8b4df454d86a4c89cdf27da352d;
437
+ Path=/
438
+ Content-Length:
439
+ - '619'
440
+ body:
441
+ encoding: US-ASCII
442
+ string: '{"status": "complete", "commands_not_successful": 1, "name": "Wikipedia''s
443
+ Miso Page fails", "video_url": "http://saucelabs.com/jobs/9591ce8519f043f8b3bea0462923c883/video.flv",
444
+ "tags": [], "proxied": true, "start_time": 1370846866, "log_url": "http://saucelabs.com/jobs/9591ce8519f043f8b3bea0462923c883/selenium-server.log",
445
+ "creation_time": 1370846866, "custom-data": null, "public": null, "browser_version":
446
+ "17.0.1", "end_time": 1370846883, "passed": false, "error": null, "owner":
447
+ "<SAUCE_USERNAME>", "build": null, "os": "Mac 10.6", "id": "9591ce8519f043f8b3bea0462923c883",
448
+ "breakpointed": null, "browser": "firefox"}'
449
+ http_version:
450
+ recorded_at: Mon, 10 Jun 2013 08:05:32 GMT
451
+ - request:
452
+ method: put
453
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/jobs/dsfargeg/stop
454
+ body:
455
+ encoding: ASCII-8BIT
456
+ string: ''
457
+ headers:
458
+ Accept:
459
+ - '*/*; q=0.5, application/xml'
460
+ Accept-Encoding:
461
+ - gzip, deflate
462
+ Content-Length:
463
+ - '0'
464
+ Content-Type:
465
+ - application/x-www-form-urlencoded
466
+ User-Agent:
467
+ - Ruby
468
+ response:
469
+ status:
470
+ code: 404
471
+ message: Not Found
472
+ headers:
473
+ Server:
474
+ - nginx/0.7.62
475
+ Date:
476
+ - Mon, 10 Jun 2013 08:47:43 GMT
477
+ Content-Type:
478
+ - application/json
479
+ Connection:
480
+ - keep-alive
481
+ Pragma:
482
+ - no-cache
483
+ Cache-Control:
484
+ - no-cache
485
+ X-Frame-Options:
486
+ - DENY
487
+ Set-Cookie:
488
+ - csrf_token=400011d970af000bde75fc33d3c2fa66; expires="Mon, 10-Jun-2013 09:04:48
489
+ GMT"; Max-Age=600; Path=/
490
+ - metrics=65c699bb137a29a1d8b4dc9250854eb35608035468aa780535534c269393f16a7ea3ca35;
491
+ Path=/
492
+ - metrics=c18c09599c25a7fba270b5c639dc185240c5cb72c92f89a4e9d547d48c6b0079ca4714b3;
493
+ Path=/
494
+ Content-Length:
495
+ - '23'
496
+ body:
497
+ encoding: US-ASCII
498
+ string: '{"errors": "Not Found"}'
499
+ http_version:
500
+ recorded_at: Mon, 10 Jun 2013 08:54:49 GMT
256
501
  recorded_with: VCR 2.5.0
@@ -0,0 +1,47 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/tunnels
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - '*/*; q=0.5, application/xml'
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Server:
22
+ - nginx/0.7.62
23
+ Date:
24
+ - Tue, 11 Jun 2013 04:14:00 GMT
25
+ Content-Type:
26
+ - application/json
27
+ Connection:
28
+ - keep-alive
29
+ Pragma:
30
+ - no-cache
31
+ Cache-Control:
32
+ - no-cache
33
+ X-Frame-Options:
34
+ - DENY
35
+ Set-Cookie:
36
+ - csrf_token=6448c9d779f6621c4baa59707aea63d; expires="Tue, 11-Jun-2013 04:31:05
37
+ GMT"; Max-Age=600; Path=/
38
+ - metrics=f7d9eb6e751429503ce76107b06ca1ea52cdc81556b48fbb6e8442cdaf2bd845ea18f402;
39
+ Path=/
40
+ Content-Length:
41
+ - '2'
42
+ body:
43
+ encoding: US-ASCII
44
+ string: '[]'
45
+ http_version:
46
+ recorded_at: Tue, 11 Jun 2013 04:21:09 GMT
47
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,229 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: delete
5
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/tunnels/7a4815f52407435581517ffd4d71c3a7
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - '*/*; q=0.5, application/xml'
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Server:
22
+ - nginx/0.7.62
23
+ Date:
24
+ - Tue, 11 Jun 2013 05:19:12 GMT
25
+ Content-Type:
26
+ - application/json
27
+ Connection:
28
+ - keep-alive
29
+ Pragma:
30
+ - no-cache
31
+ Cache-Control:
32
+ - no-cache
33
+ X-Frame-Options:
34
+ - DENY
35
+ Set-Cookie:
36
+ - csrf_token=987dda6d5a72ac5e15e804c6f81417f; expires="Tue, 11-Jun-2013 05:36:18
37
+ GMT"; Max-Age=600; Path=/
38
+ - metrics=8c3aec73d026c2739e78176dc7e48da9da88b13df2efaffa9d5442ca9d3bf56ee75f008c;
39
+ Path=/
40
+ Content-Length:
41
+ - '58'
42
+ body:
43
+ encoding: US-ASCII
44
+ string: '{"result": true, "id": "7a4815f52407435581517ffd4d71c3a7"}'
45
+ http_version:
46
+ recorded_at: Tue, 11 Jun 2013 05:26:22 GMT
47
+ - request:
48
+ method: get
49
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/tunnels/fcf7b980037b4a37aa5ff19808e46da7
50
+ body:
51
+ encoding: US-ASCII
52
+ string: ''
53
+ headers:
54
+ Accept:
55
+ - '*/*; q=0.5, application/xml'
56
+ Accept-Encoding:
57
+ - gzip, deflate
58
+ User-Agent:
59
+ - Ruby
60
+ response:
61
+ status:
62
+ code: 200
63
+ message: OK
64
+ headers:
65
+ Server:
66
+ - nginx/0.7.62
67
+ Date:
68
+ - Tue, 11 Jun 2013 06:29:23 GMT
69
+ Content-Type:
70
+ - application/json
71
+ Connection:
72
+ - keep-alive
73
+ Pragma:
74
+ - no-cache
75
+ Cache-Control:
76
+ - no-cache
77
+ X-Frame-Options:
78
+ - DENY
79
+ Set-Cookie:
80
+ - csrf_token=cb99c690ba4dec71e689ccc2368aee20; expires="Tue, 11-Jun-2013 06:46:29
81
+ GMT"; Max-Age=600; Path=/
82
+ - metrics=5f26b4576e57ec0c75993853df481a41035c26d7768a87c9e90c4212bf075d7cfe42cc17;
83
+ Path=/
84
+ Content-Length:
85
+ - '699'
86
+ body:
87
+ encoding: US-ASCII
88
+ string: '{"status": "terminated", "direct_domains": null, "shutdown_time": null,
89
+ "ssh_port": 443, "user_shutdown": true, "use_caching_proxy": true, "creation_time":
90
+ 1370924241, "domain_names": ["sauce-connect.proxy"], "shared_tunnel": false,
91
+ "tunnel_identifier": null, "host": "maki10141.miso.saucelabs.com", "owner":
92
+ "<SAUCE_USERNAME>", "use_kgp": true, "id": "fcf7b980037b4a37aa5ff19808e46da7",
93
+ "metadata": {"ScriptRelease": 35, "PythonVersion": "2.5.1", "Platform": "Java-1.7.0_13-Java_HotSpot-TM-_64-Bit_Server_VM,_23.7-b01,_Oracle_Corporation-on-Mac_OS_X-10.8.3-x86_64",
94
+ "OwnerHost": "127.0.0.1", "Build": "35", "Release": "3.0-r24", "OwnerPorts":
95
+ ["57539"], "ScriptName": "sauce_connect", "Ports": ["80"]}}'
96
+ http_version:
97
+ recorded_at: Tue, 11 Jun 2013 06:36:33 GMT
98
+ - request:
99
+ method: get
100
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/tunnels
101
+ body:
102
+ encoding: US-ASCII
103
+ string: ''
104
+ headers:
105
+ Accept:
106
+ - '*/*; q=0.5, application/xml'
107
+ Accept-Encoding:
108
+ - gzip, deflate
109
+ User-Agent:
110
+ - Ruby
111
+ response:
112
+ status:
113
+ code: 200
114
+ message: OK
115
+ headers:
116
+ Server:
117
+ - nginx/0.7.62
118
+ Date:
119
+ - Tue, 11 Jun 2013 06:49:07 GMT
120
+ Content-Type:
121
+ - application/json
122
+ Connection:
123
+ - keep-alive
124
+ Pragma:
125
+ - no-cache
126
+ Cache-Control:
127
+ - no-cache
128
+ X-Frame-Options:
129
+ - DENY
130
+ Set-Cookie:
131
+ - csrf_token=90135ac06149df70abf84010839f0d84; expires="Tue, 11-Jun-2013 07:06:13
132
+ GMT"; Max-Age=600; Path=/
133
+ - metrics=74fb9ada210b5a86c395fa69c78b7ece36c584aab137e19870e94bcdb0a2e17bbd96f144;
134
+ Path=/
135
+ Content-Length:
136
+ - '36'
137
+ body:
138
+ encoding: US-ASCII
139
+ string: &1 !ruby/string
140
+ str: '["5da3256813fd409ba7927004b499a5cf"]'
141
+ net_http_res: !ruby/object:Net::HTTPOK
142
+ http_version: '1.0'
143
+ code: '200'
144
+ message: OK
145
+ header:
146
+ server:
147
+ - nginx/0.7.62
148
+ date:
149
+ - Tue, 11 Jun 2013 06:49:07 GMT
150
+ content-type:
151
+ - application/json
152
+ connection:
153
+ - keep-alive
154
+ pragma:
155
+ - no-cache
156
+ cache-control:
157
+ - no-cache
158
+ x-frame-options:
159
+ - DENY
160
+ set-cookie:
161
+ - csrf_token=90135ac06149df70abf84010839f0d84; expires="Tue, 11-Jun-2013
162
+ 07:06:13 GMT"; Max-Age=600; Path=/
163
+ - metrics=74fb9ada210b5a86c395fa69c78b7ece36c584aab137e19870e94bcdb0a2e17bbd96f144;
164
+ Path=/
165
+ content-length:
166
+ - '36'
167
+ body: *1
168
+ read: true
169
+ __read_body_previously_called: true
170
+ args:
171
+ :method: :get
172
+ :url: https://saucelabs.com/rest/v1/dylanatsauce/tunnels
173
+ :user: dylanatsauce
174
+ :password: 8f5210dd-f0cf-477f-b720-9f721a9679a4
175
+ code: 200
176
+ http_version:
177
+ recorded_at: Tue, 11 Jun 2013 06:56:16 GMT
178
+ - request:
179
+ method: get
180
+ uri: https://<SAUCE_USERNAME>:<SAUCE_ACCESS_KEY>@saucelabs.com/rest/v1/<SAUCE_USERNAME>/tunnels/5da3256813fd409ba7927004b499a5cf
181
+ body:
182
+ encoding: US-ASCII
183
+ string: ''
184
+ headers:
185
+ Accept:
186
+ - '*/*; q=0.5, application/xml'
187
+ Accept-Encoding:
188
+ - gzip, deflate
189
+ User-Agent:
190
+ - Ruby
191
+ response:
192
+ status:
193
+ code: 200
194
+ message: OK
195
+ headers:
196
+ Server:
197
+ - nginx/0.7.62
198
+ Date:
199
+ - Tue, 11 Jun 2013 07:03:16 GMT
200
+ Content-Type:
201
+ - application/json
202
+ Connection:
203
+ - keep-alive
204
+ Pragma:
205
+ - no-cache
206
+ Cache-Control:
207
+ - no-cache
208
+ X-Frame-Options:
209
+ - DENY
210
+ Set-Cookie:
211
+ - csrf_token=501ca123c01fa4ac294d3640990b433f; expires="Tue, 11-Jun-2013 07:20:22
212
+ GMT"; Max-Age=600; Path=/
213
+ - metrics=11c55a5a4a201f53c400274f179bc79003c0dc64d853714ac40b489994e546aba7c7f16a;
214
+ Path=/
215
+ Content-Length:
216
+ - '697'
217
+ body:
218
+ encoding: US-ASCII
219
+ string: '{"status": "terminated", "direct_domains": null, "shutdown_time": null,
220
+ "ssh_port": 443, "user_shutdown": true, "use_caching_proxy": true, "creation_time":
221
+ 1370933412, "domain_names": ["sauce-connect.proxy"], "shared_tunnel": false,
222
+ "tunnel_identifier": "", "host": "maki10141.miso.saucelabs.com", "owner":
223
+ "<SAUCE_USERNAME>", "use_kgp": true, "id": "5da3256813fd409ba7927004b499a5cf",
224
+ "metadata": {"ScriptRelease": 38, "PythonVersion": "2.5.1", "Platform": "Java-1.7.0_13-Java_HotSpot-TM-_64-Bit_Server_VM,_23.7-b01,_Oracle_Corporation-on-Mac_OS_X-10.8.3-x86_64",
225
+ "OwnerHost": "127.0.0.1", "Build": "38", "Release": "3.0-r25", "OwnerPorts":
226
+ ["64703"], "ScriptName": "sauce_connect", "Ports": ["80"]}}'
227
+ http_version:
228
+ recorded_at: Tue, 11 Jun 2013 07:10:26 GMT
229
+ recorded_with: VCR 2.5.0
@@ -66,6 +66,15 @@ describe SauceWhisk::Job do
66
66
  end
67
67
  end
68
68
 
69
+ describe "#stop" do
70
+ subject {SauceWhisk::Job.new(params.merge({"id" => "3edc8fe6d52645bf931b1003da65af1f"}))}
71
+
72
+ it "Calls the correct REST API method", :vcr => {:cassette_name => "jobs"} do
73
+ subject.stop
74
+ assert_requested :put, "https://#{ENV["SAUCE_USERNAME"]}:#{ENV["SAUCE_ACCESS_KEY"]}@saucelabs.com/rest/v1/dylanatsauce/jobs/#{subject.id}/stop"
75
+ end
76
+ end
77
+
69
78
  describe "parameters" do
70
79
  it "lets you set only the parameters which are mutable" do
71
80
  [:name, :build, :passed, :tags, :custom_data, :visibility].each do |param|
@@ -75,4 +75,20 @@ describe SauceWhisk::Jobs do
75
75
  job.should be_an_instance_of SauceWhisk::Job
76
76
  end
77
77
  end
78
+
79
+ describe "##stop", :vcr => {:cassette_name => "jobs"} do
80
+ it "calls the API correctly" do
81
+ SauceWhisk::Jobs.stop "3edc8fe6d52645bf931b1003da65af1f"
82
+ assert_requested :put, "https://#{auth}@saucelabs.com/rest/v1/dylanatsauce/jobs/3edc8fe6d52645bf931b1003da65af1f/stop", :content_type => "application/json"
83
+ end
84
+
85
+ it "does something interesting when the job is already stopped" do
86
+ SauceWhisk::Jobs.stop "9591ce8519f043f8b3bea0462923c883"
87
+ end
88
+
89
+ it "does something else interesting when the job does not exist" do
90
+ SauceWhisk::Jobs.stop "job_id"
91
+ end
92
+
93
+ end
78
94
  end
@@ -39,11 +39,45 @@ describe SauceWhisk::RestRequestBuilder do
39
39
  end
40
40
  end
41
41
 
42
+ describe "#delete", :vcr => {:cassette_name => 'jobs'} do
43
+ let(:expected_url) {"#{SauceWhisk.base_url}/#{dummy_client.resource}/identifier"}
44
+
45
+ it "calls the base URL with the delete method" do
46
+ expected_params = {:method => :delete, :url => expected_url}.merge mock_auth
47
+ RestClient::Request.should_receive(:execute).with(expected_params)
48
+ dummy_client.delete "identifier"
49
+ end
50
+ end
51
+
42
52
  describe "#put", :vcr => {:cassette_name => 'jobs'} do
53
+ let(:expected_url) {"#{SauceWhisk.base_url}/#{dummy_client.resource}/something"}
43
54
  it "calls the base URL with the put method" do
44
- expected_url = "#{SauceWhisk.base_url}/#{dummy_client.resource}/something"
45
- expected_params = {:method => :put, :url => expected_url, :payload => "another_thing", :content_type => "application/json"}.merge mock_auth
46
- RestClient::Request.should_receive(:execute).with(expected_params)
55
+ RestClient::Request.should_receive(:execute).with(hash_including({:url => expected_url}))
56
+ dummy_client.put "something", "another_thing"
57
+ end
58
+
59
+ it "includes the right content_type" do
60
+ RestClient::Request.should_receive(:execute).with(hash_including({:content_type => "application/json"}))
61
+ dummy_client.put "something", "another_thing"
62
+ end
63
+
64
+ it "includes the right method" do
65
+ RestClient::Request.should_receive(:execute).with(hash_including({:method => :put}))
66
+ dummy_client.put "something", "another_thing"
67
+ end
68
+
69
+ it "includes the content length" do
70
+ RestClient::Request.should_receive(:execute).with(hash_including(:headers => {"Content-Length" => 13}))
71
+ dummy_client.put "something", "another_thing"
72
+ end
73
+
74
+ it "includes authentication details" do
75
+ RestClient::Request.should_receive(:execute).with(hash_including(mock_auth))
76
+ dummy_client.put "something", "another_thing"
77
+ end
78
+
79
+ it "sends the payload" do
80
+ RestClient::Request.should_receive(:execute).with(hash_including({:payload => "another_thing"}))
47
81
  dummy_client.put "something", "another_thing"
48
82
  end
49
83
  end
@@ -0,0 +1,32 @@
1
+ require "spec_helper"
2
+
3
+ describe SauceWhisk::Tunnel do
4
+ let(:params) {{
5
+ :id => "tunnel_id",
6
+ :owner => "test_user",
7
+ :status => "open",
8
+ :host => "yacko.wacko.dot",
9
+ :creation_time => Time.now
10
+ }}
11
+
12
+ describe "#new" do
13
+ it "sets all parameters passed in" do
14
+ tunnel = SauceWhisk::Tunnel.new params
15
+ tunnel.id.should eq "tunnel_id"
16
+ tunnel.owner.should eq "test_user"
17
+ tunnel.status.should eq "open"
18
+ tunnel.host.should eq "yacko.wacko.dot"
19
+ tunnel.creation_time.should eq params[:creation_time]
20
+ end
21
+ end
22
+
23
+ describe "#stop" do
24
+ it "calls the Repository class" do
25
+ tunnel = SauceWhisk::Tunnel.new params
26
+ SauceWhisk::Tunnels.should_receive(:stop).with("tunnel_id")
27
+
28
+ tunnel.stop
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,54 @@
1
+ require "spec_helper"
2
+
3
+ describe SauceWhisk::Tunnels, :vcr => {:cassette_name => "tunnels"} do
4
+ let(:auth) {"#{ENV["SAUCE_USERNAME"]}:#{ENV["SAUCE_ACCESS_KEY"]}"}
5
+
6
+ describe "##all" do
7
+ it "lists all tunnels a user has open" do
8
+ SauceWhisk::Tunnels.all
9
+ assert_requested :get, "https://#{auth}@saucelabs.com/rest/v1/dylanatsauce/tunnels"
10
+ end
11
+
12
+ it "returns nothing when no tunnels are found", :vcr => {:cassette_name => "no_tunnels", :exclusive => true} do
13
+ tunnels = SauceWhisk::Tunnels.all
14
+ tunnels.should eq []
15
+ end
16
+
17
+ context "called without the 'fetch' parameter" do
18
+ it "returns an array of strings" do
19
+ tunnels = SauceWhisk::Tunnels.all
20
+ tunnels.should be_an_instance_of Array
21
+ tunnels.each {|tunnel| tunnel.should be_an_instance_of String }
22
+ end
23
+ end
24
+
25
+ context "called with the fetch parameter" do
26
+ it "returns an array of Tunnels" do
27
+ tunnels = SauceWhisk::Tunnels.all(:fetch)
28
+ tunnels.should be_an_instance_of Array
29
+ tunnels.each {|tunnel| tunnel.should be_an_instance_of SauceWhisk::Tunnel}
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "##fetch" do
35
+ let(:job_id) {"fcf7b980037b4a37aa5ff19808e46da7"}
36
+ it "fetches a single instance of a tunnel" do
37
+ SauceWhisk::Tunnels.fetch job_id
38
+ assert_requested :get, "https://#{auth}@saucelabs.com/rest/v1/dylanatsauce/tunnels/#{job_id}"
39
+ end
40
+
41
+ it "returns instances of Tunnel" do
42
+ tunnel = SauceWhisk::Tunnels.fetch job_id
43
+ tunnel.should be_an_instance_of SauceWhisk::Tunnel
44
+ end
45
+ end
46
+
47
+ describe "##stop" do
48
+ it "calls the correct API method" do
49
+ tunnel_id = "7a4815f52407435581517ffd4d71c3a7"
50
+ SauceWhisk::Tunnels.stop tunnel_id
51
+ assert_requested :delete, "https://#{auth}@saucelabs.com/rest/v1/dylanatsauce/tunnels/#{tunnel_id}"
52
+ end
53
+ end
54
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sauce_whisk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dylan Lacey
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-06-02 00:00:00.000000000 Z
11
+ date: 2013-06-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rest-client
@@ -92,22 +92,28 @@ files:
92
92
  - LICENSE.txt
93
93
  - README.md
94
94
  - Rakefile
95
+ - changelog.markdown
95
96
  - lib/sauce_whisk.rb
96
97
  - lib/sauce_whisk/assets.rb
97
98
  - lib/sauce_whisk/jobs.rb
98
99
  - lib/sauce_whisk/rest_request_builder.rb
100
+ - lib/sauce_whisk/tunnels.rb
99
101
  - lib/sauce_whisk/version.rb
100
102
  - sauce_whisk.gemspec
101
103
  - spec/fixtures/vcr_cassettes/assets.yml
102
104
  - spec/fixtures/vcr_cassettes/jobs.yml
103
105
  - spec/fixtures/vcr_cassettes/no_jobs.yml
106
+ - spec/fixtures/vcr_cassettes/no_tunnels.yml
104
107
  - spec/fixtures/vcr_cassettes/rest_request.yml
108
+ - spec/fixtures/vcr_cassettes/tunnels.yml
105
109
  - spec/lib/sauce_whisk/asset_spec.rb
106
110
  - spec/lib/sauce_whisk/assets_spec.rb
107
111
  - spec/lib/sauce_whisk/job_spec.rb
108
112
  - spec/lib/sauce_whisk/jobs_spec.rb
109
113
  - spec/lib/sauce_whisk/rest_request_builder_spec.rb
110
114
  - spec/lib/sauce_whisk/sauce_whisk_spec.rb
115
+ - spec/lib/sauce_whisk/tunnel_spec.rb
116
+ - spec/lib/sauce_whisk/tunnels_spec.rb
111
117
  - spec/spec_helper.rb
112
118
  - vendor/psych/lib/psych.bundle
113
119
  - vendor/psych/lib/psych.rb
@@ -184,11 +190,15 @@ test_files:
184
190
  - spec/fixtures/vcr_cassettes/assets.yml
185
191
  - spec/fixtures/vcr_cassettes/jobs.yml
186
192
  - spec/fixtures/vcr_cassettes/no_jobs.yml
193
+ - spec/fixtures/vcr_cassettes/no_tunnels.yml
187
194
  - spec/fixtures/vcr_cassettes/rest_request.yml
195
+ - spec/fixtures/vcr_cassettes/tunnels.yml
188
196
  - spec/lib/sauce_whisk/asset_spec.rb
189
197
  - spec/lib/sauce_whisk/assets_spec.rb
190
198
  - spec/lib/sauce_whisk/job_spec.rb
191
199
  - spec/lib/sauce_whisk/jobs_spec.rb
192
200
  - spec/lib/sauce_whisk/rest_request_builder_spec.rb
193
201
  - spec/lib/sauce_whisk/sauce_whisk_spec.rb
202
+ - spec/lib/sauce_whisk/tunnel_spec.rb
203
+ - spec/lib/sauce_whisk/tunnels_spec.rb
194
204
  - spec/spec_helper.rb