kensa 1.2.0rc7 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,4 +2,4 @@ addon-manifest.json
2
2
  pkg/
3
3
  .rvmrc
4
4
  tags
5
- bundle/
5
+ test_log.txt
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
- source "http://rubygems.org"
1
+ source :rubygems
2
2
  gemspec
3
- group :development do
4
- gem 'ruby-debug', :platform => :mri_18
5
- gem 'ruby-debug19', :platform => :mri_19
3
+ gem 'rake'
4
+ group :development do
5
+ gem 'ruby-debug', :platforms => [:ruby_18]
6
+ gem 'ruby-debug19', :platforms => [:ruby_19]
6
7
  end
data/Gemfile.lock CHANGED
@@ -1,27 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kensa (1.2.0rc7)
5
- artifice (~> 0.6.0)
6
- launchy (~> 2.0.5)
4
+ kensa (1.2.0)
5
+ launchy (>= 0.3.2)
7
6
  mechanize (~> 1.0.0)
8
- rest-client (~> 1.6.7)
9
- term-ansicolor (~> 1.0.6)
10
- test-unit (~> 1.2.3)
11
- yajl-ruby (~> 0.8.3)
7
+ rest-client (>= 1.4.0, < 1.7.0)
8
+ term-ansicolor (~> 1.0)
9
+ yajl-ruby (~> 0.6)
12
10
 
13
11
  GEM
14
12
  remote: http://rubygems.org/
15
13
  specs:
16
14
  addressable (2.2.6)
17
15
  archive-tar-minitar (0.5.2)
18
- artifice (0.6)
19
- rack-test
20
16
  columnize (0.3.4)
21
- haml (3.1.3)
22
- hoe (2.12.3)
23
- rake (~> 0.8)
24
- json (1.6.1)
17
+ contest (0.1.3)
18
+ fakefs (0.4.0)
19
+ haml (3.1.2)
20
+ json (1.5.3)
25
21
  launchy (2.0.5)
26
22
  addressable (~> 2.2.6)
27
23
  linecache (0.43)
@@ -29,15 +25,13 @@ GEM
29
25
  ruby_core_source (>= 0.1.4)
30
26
  mechanize (1.0.0)
31
27
  nokogiri (>= 1.2.1)
32
- mime-types (1.16)
28
+ mime-types (1.17.2)
33
29
  nokogiri (1.5.0)
34
- rack (1.3.3)
35
- rack-test (0.6.1)
36
- rack (>= 1.0)
37
- rake (0.9.2)
30
+ rack (1.3.2)
31
+ rake (0.9.2.2)
38
32
  rest-client (1.6.7)
39
33
  mime-types (>= 1.16)
40
- rr (1.0.4)
34
+ rr (1.0.3)
41
35
  ruby-debug (0.10.4)
42
36
  columnize (>= 0.1)
43
37
  ruby-debug-base (~> 0.10.4.0)
@@ -57,9 +51,7 @@ GEM
57
51
  rack (~> 1.1)
58
52
  tilt (>= 1.2.2, < 2.0)
59
53
  term-ansicolor (1.0.7)
60
- test-unit (1.2.3)
61
- hoe (>= 1.5.1)
62
- tilt (1.3.3)
54
+ tilt (1.3.2)
63
55
  timecop (0.3.5)
64
56
  yajl-ruby (0.8.3)
65
57
 
@@ -67,12 +59,14 @@ PLATFORMS
67
59
  ruby
68
60
 
69
61
  DEPENDENCIES
70
- artifice (~> 0.6)
71
- haml (~> 3.1.3)
62
+ contest
63
+ fakefs
64
+ haml
72
65
  json
73
66
  kensa!
74
- rr (~> 1.0.4)
67
+ rake
68
+ rr
75
69
  ruby-debug
76
70
  ruby-debug19
77
- sinatra (~> 1.2.6)
78
- timecop (~> 0.3.5)
71
+ sinatra (>= 0.9)
72
+ timecop (>= 0.3.5)
data/README.md CHANGED
@@ -24,6 +24,6 @@ http://provider.heroku.com/resources/technical/build/provisioning
24
24
 
25
25
  ## Meta #######################################################################
26
26
 
27
- Maintained by Glenn Gillen and Chris Continanza.
27
+ Maintained by Pedro Belo.
28
28
 
29
29
  Released under the MIT license. http://github.com/heroku/kensa
data/Rakefile CHANGED
@@ -1,12 +1,20 @@
1
- $:.unshift File.dirname(__FILE__)
2
- require "bundler/gem_tasks"
3
-
4
- desc 'Run all unit tests'
5
- task :test do
6
- puts require "test/helper"
7
- Dir["test/*_test.rb"].each do |test_file|
8
- require test_file
9
- end
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << '.'
6
+ t.verbose = true
7
+ t.test_files = FileList["test/*_test.rb"]
8
+ end
9
+
10
+ desc 'Start the server'
11
+ task :start do
12
+ fork { exec "ruby test/resources/server.rb > test_log.txt 2>&1" }
13
+ end
14
+
15
+ desc 'Stop the server'
16
+ task :stop do
17
+ system "ps -ax | grep test/resources/server.rb | grep -v grep | awk '{print $1}' | xargs kill"
10
18
  end
11
19
 
12
- task :default => :test
20
+ task :default => [:start, :test, :stop]
data/bin/kensa CHANGED
@@ -4,47 +4,28 @@ lib = File.expand_path(File.dirname(__FILE__) + '/../lib')
4
4
  $LOAD_PATH.unshift(lib) if File.directory?(lib) && !$LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'rubygems'
7
- require 'optparse'
8
7
  require 'heroku/kensa'
9
8
  require 'heroku/kensa/client'
10
9
 
11
10
  $stdout.sync = true
12
11
 
13
- options = {
14
- :filename => 'addon-manifest.json',
15
- :env => "test",
16
- :async => false,
17
- }
18
-
19
- ARGV.options do |o|
20
- o.on("-f file", "--file") {|filename| options[:filename] = filename }
21
- o.on("--async") { options[:async] = true }
22
- o.on("--production") { options[:env] = "production" }
23
- o.on("--without-sso") { options[:sso] = false }
24
- o.on("-h", "--help") { command = "help" }
25
- o.on("-p plan", "--plan") { |plan| options[:plan] = plan }
26
- o.on("-v", "--version") { options[:command] = "version" }
27
- o.on("-t template", "--template") { |t| options[:template] = t }
28
- o.parse!
29
- end
30
-
31
12
  include Heroku::Kensa
32
13
 
33
14
  begin
34
15
  args = ARGV.dup
35
16
  ARGV.clear
36
- Client.new(args, options).run!
37
- rescue Client::CommandFailed => e
38
- puts "We're sorry, but #{e.message} "
39
- rescue Client::CommandInvalid
17
+ Client.new(args).run!
18
+ rescue Client::CommandInvalid => e
19
+ puts e.message unless e.message.empty?
40
20
  abort File.read(__FILE__).split('__END__').last
41
21
  end
42
22
 
43
23
  __END__
44
24
  Usage: kensa [OPTIONS] command
45
25
  kensa init
46
- kensa test <type> [arg1 arg2 ...]
47
- kensa run <command> [arg1 arg1 ...]
26
+ kensa create <app_name> --template
27
+ kensa test <type> [arg1 arg2 ...]
28
+ kensa run <command> [arg1 arg1 ...]
48
29
 
49
30
  OPTIONS
50
31
 
@@ -66,16 +47,14 @@ OPTIONS
66
47
  --post
67
48
  Use HTTP POST for single sign-on instead of GET
68
49
 
69
- -t, --template name
70
- Desired template to clone for create command from:
71
- git://github.com/heroku/kensa-template-<name>
72
- try sinatra, clojure, or node
50
+ --template
51
+ Name of git template on github or full url of git repo.
73
52
 
74
53
  COMMANDS
75
54
 
76
55
  init Creates a skeleton manifest
77
56
 
78
- create <name> Creates a skeleton application from --template
57
+ create <app> Clone a git repo that contains a template add-on
79
58
 
80
59
  test <type> Simulate call from Heroku (provision or deprovision)
81
60
 
@@ -103,3 +82,4 @@ TEST TYPES
103
82
 
104
83
  manifest
105
84
  Confirm that the manifest is valid. Automatically runs before all tests.
85
+
data/kensa.gemspec CHANGED
@@ -1,14 +1,16 @@
1
+ # -*- encoding: utf-8 -*-
1
2
  $:.push File.expand_path("../lib", __FILE__)
2
3
  require "heroku/kensa/version"
3
4
 
4
5
  Gem::Specification.new do |s|
5
- s.name = "kensa"
6
- s.summary = "Tool to help Heroku add-on providers integrating their services"
7
- s.description = "Kensa is a command-line tool to help add-on providers integrating their services with Heroku. It manages manifest files, and provides a TDD-like approach for programmers to test and develop their APIs."
8
- s.email = "glenn@heroku.com"
9
- s.homepage = "http://provider.heroku.com/resources"
10
- s.authors = ["Blake Mizerany", "Pedro Belo", "Adam Wiggins", "Chris Continanza", "Glenn Gillen"]
11
- s.version = Heroku::Kensa::VERSION
6
+ s.name = %q{kensa}
7
+ s.version = Heroku::Kensa::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+
10
+ s.authors = ["Blake Mizerany", "Pedro Belo", "Adam Wiggins", 'Glenn Gillen', 'Chris Continanza']
11
+ s.default_executable = %q{kensa}
12
+ s.description = %q{Kensa is a command-line tool to help add-on providers integrating their services with Heroku. It manages manifest files, and provides a TDD-like approach for programmers to test and develop their APIs.}
13
+ s.email = %q{pedro@heroku.com}
12
14
 
13
15
  s.rubyforge_project = "kensa"
14
16
 
@@ -17,17 +19,22 @@ Gem::Specification.new do |s|
17
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
20
  s.require_paths = ["lib"]
19
21
 
20
- s.add_development_dependency('json', [">= 0"])
21
- s.add_development_dependency('sinatra', ["~> 1.2.6"])
22
- s.add_development_dependency('timecop', ["~> 0.3.5"])
23
- s.add_development_dependency('rr', ["~> 1.0.4"])
24
- s.add_development_dependency('artifice', ["~> 0.6"])
25
- s.add_development_dependency('haml', ["~> 3.1.3"])
26
- s.add_runtime_dependency('test-unit', ["~> 1.2.3"])
27
- s.add_runtime_dependency('rest-client', ["~> 1.6.7"])
28
- s.add_runtime_dependency('yajl-ruby', ["~> 0.8.3"])
29
- s.add_runtime_dependency('term-ansicolor', ["~> 1.0.6"])
30
- s.add_runtime_dependency('launchy', ["~> 2.0.5"])
31
- s.add_runtime_dependency('mechanize', ["~> 1.0.0"])
32
- s.add_runtime_dependency('artifice', ["~> 0.6.0"])
22
+ s.homepage = %q{http://provider.heroku.com/resources}
23
+ s.rubygems_version = %q{1.6.2}
24
+ s.summary = %q{Tool to help Heroku add-on providers integrating their services}
25
+
26
+ s.add_development_dependency(%q<contest>, [">= 0"])
27
+ s.add_development_dependency(%q<timecop>, [">= 0.3.5"])
28
+ s.add_development_dependency(%q<sinatra>, [">= 0.9"])
29
+ s.add_development_dependency(%q<json>, [">= 0"])
30
+ s.add_development_dependency(%q<contest>, [">= 0"])
31
+ s.add_development_dependency(%q<haml>, [">= 0"])
32
+ s.add_development_dependency(%q<rr>, [">= 0"])
33
+ s.add_development_dependency(%q<fakefs>, [">= 0"])
34
+ s.add_runtime_dependency(%q<rest-client>, ["< 1.7.0", ">= 1.4.0"])
35
+ s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.6"])
36
+ s.add_runtime_dependency(%q<term-ansicolor>, ["~> 1.0"])
37
+ s.add_runtime_dependency(%q<launchy>, [">= 0.3.2"])
38
+ s.add_runtime_dependency(%q<mechanize>, ["~> 1.0.0"])
33
39
  end
40
+
data/lib/heroku/kensa.rb CHANGED
@@ -1,10 +1,8 @@
1
- require 'yajl'
2
- require 'mechanize'
3
- require 'socket'
4
- require 'timeout'
5
- require 'uri'
6
- base_path = File.dirname(__FILE__)
7
- %w{http manifest sso post_proxy}.each do |lib|
8
- require "#{base_path}/kensa/#{lib}"
9
- end
10
1
  require 'heroku/kensa/version'
2
+ require 'heroku/kensa/http'
3
+ require 'heroku/kensa/manifest'
4
+ require 'heroku/kensa/check'
5
+ require 'heroku/kensa/sso'
6
+ require 'heroku/kensa/post_proxy'
7
+ require 'heroku/kensa/screen'
8
+ require 'heroku/kensa/git'
@@ -0,0 +1,499 @@
1
+ require 'yajl'
2
+ require 'mechanize'
3
+ require 'socket'
4
+ require 'timeout'
5
+ require 'uri'
6
+
7
+ module Heroku
8
+ module Kensa
9
+ class Check
10
+ attr_accessor :screen, :data
11
+
12
+ class CheckError < StandardError ; end
13
+
14
+ def initialize(data, screen=NilScreen.new)
15
+ @data = data
16
+ @screen = screen
17
+ end
18
+
19
+ def env
20
+ @data.fetch(:env, 'test')
21
+ end
22
+
23
+ def test(msg)
24
+ screen.test msg
25
+ end
26
+
27
+ def check(msg)
28
+ screen.check(msg)
29
+ if yield
30
+ screen.result(true)
31
+ else
32
+ raise CheckError
33
+ end
34
+ end
35
+
36
+ def run(klass, data)
37
+ c = klass.new(data, screen)
38
+ instance_eval(&c)
39
+ end
40
+
41
+ def error(msg)
42
+ raise CheckError, msg
43
+ end
44
+
45
+ def call
46
+ call!
47
+ true
48
+ rescue CheckError => boom
49
+ screen.result(false)
50
+ screen.error boom.message if boom.message != boom.class.name
51
+
52
+ false
53
+ end
54
+
55
+ def to_proc
56
+ me = self
57
+ Proc.new { me.call! }
58
+ end
59
+
60
+ def url
61
+ if data['api'][env].is_a? Hash
62
+ base = data['api'][env]['base_url']
63
+ uri = URI.parse(base)
64
+ base.sub!(uri.query, '') if uri.query
65
+ base.sub(uri.path, '')
66
+ else
67
+ data['api'][env].chomp("/")
68
+ end
69
+ end
70
+ end
71
+
72
+
73
+ class ManifestCheck < Check
74
+
75
+ ValidPriceUnits = %w[month dyno_hour]
76
+
77
+ def call!
78
+ test "manifest id key"
79
+ check "if exists" do
80
+ data.has_key?("id")
81
+ end
82
+ check "is a string" do
83
+ data["id"].is_a?(String)
84
+ end
85
+ check "is not blank" do
86
+ !data["id"].empty?
87
+ end
88
+
89
+ test "manifest api key"
90
+ check "if exists" do
91
+ data.has_key?("api")
92
+ end
93
+ check "is a hash" do
94
+ data["api"].is_a?(Hash)
95
+ end
96
+ check "contains password" do
97
+ data["api"].has_key?("password") && data["api"]["password"] != ""
98
+ end
99
+ check "contains test url" do
100
+ data["api"].has_key?("test")
101
+ end
102
+ check "contains production url" do
103
+ data["api"].has_key?("production")
104
+ end
105
+ if data['api']['production'].is_a? Hash
106
+ check "production url uses SSL" do
107
+ data['api']['production']['base_url'] =~ /^https:/
108
+ end
109
+ check "sso url uses SSL" do
110
+ data['api']['production']['sso_url'] =~ /^https:/
111
+ end
112
+ else
113
+ check "production url uses SSL" do
114
+ data['api']['production'] =~ /^https:/
115
+ end
116
+ end
117
+ check "contains config_vars array" do
118
+ data["api"].has_key?("config_vars") && data["api"]["config_vars"].is_a?(Array)
119
+ end
120
+ check "containst at least one config var" do
121
+ !data["api"]["config_vars"].empty?
122
+ end
123
+ check "all config vars are uppercase strings" do
124
+ data["api"]["config_vars"].each do |k, v|
125
+ if k =~ /^[A-Z][0-9A-Z_]+$/
126
+ true
127
+ else
128
+ error "#{k.inspect} is not a valid ENV key"
129
+ end
130
+ end
131
+ end
132
+ check "all config vars are prefixed with the addon id" do
133
+ data["api"]["config_vars"].each do |k|
134
+ addon_key = data['id'].upcase.gsub('-', '_')
135
+ if k =~ /^#{addon_key}_/
136
+ true
137
+ else
138
+ error "#{k} is not a valid ENV key - must be prefixed with #{addon_key}_"
139
+ end
140
+ end
141
+ end
142
+ check "deprecated fields" do
143
+ if data["api"].has_key?("username")
144
+ error "username is deprecated: Please authenticate using the add-on id."
145
+ end
146
+ true
147
+ end
148
+ end
149
+ end
150
+
151
+
152
+ class ProvisionResponseCheck < Check
153
+
154
+ def call!
155
+ response = data[:provision_response]
156
+ test "response"
157
+
158
+ check "contains an id" do
159
+ response.is_a?(Hash) && response.has_key?("id")
160
+ end
161
+
162
+ screen.message " (id #{response['id']})"
163
+
164
+ if response.has_key?("config")
165
+ test "config data"
166
+ check "is a hash" do
167
+ response["config"].is_a?(Hash)
168
+ end
169
+
170
+ check "all config keys were previously defined in the manifest" do
171
+ response["config"].keys.each do |key|
172
+ error "#{key} is not in the manifest" unless data["api"]["config_vars"].include?(key)
173
+ end
174
+ true
175
+ end
176
+
177
+ check "all keys in the manifest are present" do
178
+ difference = data['api']['config_vars'] - response['config'].keys
179
+ unless difference.empty?
180
+ verb = (difference.size == 1) ? "is" : "are"
181
+ error "#{difference.join(', ')} #{verb} missing from the manifest"
182
+ end
183
+ true
184
+ end
185
+
186
+ check "all config values are strings" do
187
+ response["config"].each do |k, v|
188
+ if v.is_a?(String)
189
+ true
190
+ else
191
+ error "the key #{k} doesn't contain a string (#{v.inspect})"
192
+ end
193
+ end
194
+ end
195
+
196
+ check "URL configs vars" do
197
+ response["config"].each do |key, value|
198
+ next unless key =~ /_URL$/
199
+ begin
200
+ uri = URI.parse(value)
201
+ error "#{value} is not a valid URI - missing host" unless uri.host
202
+ error "#{value} is not a valid URI - missing scheme" unless uri.scheme
203
+ error "#{value} is not a valid URI - pointing to localhost" if env == 'production' && uri.host == 'localhost'
204
+ rescue URI::Error
205
+ error "#{value} is not a valid URI"
206
+ end
207
+ end
208
+ end
209
+
210
+ end
211
+ end
212
+
213
+ end
214
+
215
+
216
+ class ApiCheck < Check
217
+ def base_path
218
+ if data['api'][env].is_a? Hash
219
+ URI.parse(data['api'][env]['base_url']).path
220
+ else
221
+ '/heroku/resources'
222
+ end
223
+ end
224
+
225
+ def heroku_id
226
+ "app#{rand(10000)}@kensa.heroku.com"
227
+ end
228
+
229
+ def credentials
230
+ [ data['id'], data['api']['password'] ]
231
+ end
232
+ end
233
+
234
+ class ProvisionCheck < ApiCheck
235
+ include HTTP
236
+
237
+ READLEN = 1024 * 10
238
+
239
+ def call!
240
+ json = nil
241
+ response = nil
242
+
243
+ code = nil
244
+ json = nil
245
+ callback = "http://localhost:7779/callback/999"
246
+ reader, writer = nil
247
+
248
+ payload = {
249
+ :heroku_id => heroku_id,
250
+ :plan => data[:plan] || 'test',
251
+ :callback_url => callback,
252
+ :logplex_token => nil,
253
+ :options => {}
254
+ }
255
+
256
+ if data[:async]
257
+ reader, writer = IO.pipe
258
+ end
259
+
260
+ test "POST /heroku/resources"
261
+ check "response" do
262
+ if data[:async]
263
+ child = fork do
264
+ reader.close
265
+ server = TCPServer.open(7779)
266
+ client = server.accept
267
+ writer.write(client.readpartial(READLEN))
268
+ client.write("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n")
269
+ client.close
270
+ writer.close
271
+ end
272
+ sleep(1)
273
+ end
274
+
275
+ code, json = post(credentials, base_path, payload)
276
+
277
+ if code == 200
278
+ # noop
279
+ elsif code == -1
280
+ error("unable to connect to #{url}")
281
+ else
282
+ error("expected 200, got #{code}")
283
+ end
284
+
285
+ true
286
+ end
287
+
288
+ if data[:async]
289
+ check "async response to PUT #{callback}" do
290
+ out = reader.readpartial(READLEN)
291
+ _, json = out.split("\r\n\r\n")
292
+ end
293
+ end
294
+
295
+ check "valid JSON" do
296
+ begin
297
+ response = Yajl::Parser.parse(json)
298
+ rescue Yajl::ParseError => boom
299
+ error boom.message
300
+ end
301
+ true
302
+ end
303
+
304
+ check "authentication" do
305
+ wrong_credentials = ['wrong', 'secret']
306
+ code, _ = post(wrong_credentials, base_path, payload)
307
+ error("expected 401, got #{code}") if code != 401
308
+ true
309
+ end
310
+
311
+ data[:provision_response] = response
312
+
313
+ run ProvisionResponseCheck, data
314
+ end
315
+
316
+ ensure
317
+ reader.close rescue nil
318
+ writer.close rescue nil
319
+ end
320
+
321
+
322
+ class DeprovisionCheck < ApiCheck
323
+ include HTTP
324
+
325
+ def call!
326
+ id = data[:id]
327
+ raise ArgumentError, "No id specified" if id.nil?
328
+
329
+ path = "#{base_path}/#{CGI::escape(id.to_s)}"
330
+
331
+ test "DELETE #{path}"
332
+ check "response" do
333
+ code, _ = delete(credentials, path, nil)
334
+ if code == 200
335
+ true
336
+ elsif code == -1
337
+ error("unable to connect to #{url}")
338
+ else
339
+ error("expected 200, got #{code}")
340
+ end
341
+ end
342
+
343
+ check "authentication" do
344
+ wrong_credentials = ['wrong', 'secret']
345
+ code, _ = delete(wrong_credentials, path, nil)
346
+ error("expected 401, got #{code}") if code != 401
347
+ true
348
+ end
349
+
350
+ end
351
+
352
+ end
353
+
354
+
355
+ class PlanChangeCheck < ApiCheck
356
+ include HTTP
357
+
358
+ def call!
359
+ id = data[:id]
360
+ raise ArgumentError, "No id specified" if id.nil?
361
+
362
+ new_plan = data[:plan]
363
+ raise ArgumentError, "No plan specified" if new_plan.nil?
364
+
365
+ path = "#{base_path}/#{CGI::escape(id.to_s)}"
366
+ payload = {:plan => new_plan, :heroku_id => heroku_id}
367
+
368
+ test "PUT #{path}"
369
+ check "response" do
370
+ code, _ = put(credentials, path, payload)
371
+ if code == 200
372
+ true
373
+ elsif code == -1
374
+ error("unable to connect to #{url}")
375
+ else
376
+ error("expected 200, got #{code}")
377
+ end
378
+ end
379
+
380
+ check "authentication" do
381
+ wrong_credentials = ['wrong', 'secret']
382
+ code, _ = put(wrong_credentials, path, payload)
383
+ error("expected 401, got #{code}") if code != 401
384
+ true
385
+ end
386
+ end
387
+ end
388
+
389
+
390
+ class SsoCheck < ApiCheck
391
+ include HTTP
392
+
393
+ def agent
394
+ @agent ||= Mechanize.new
395
+ end
396
+
397
+ def mechanize_get
398
+ if @sso.POST?
399
+ page = agent.post(@sso.post_url, @sso.query_params)
400
+ else
401
+ page = agent.get(@sso.get_url)
402
+ end
403
+ return page, 200
404
+ rescue Mechanize::ResponseCodeError => error
405
+ return nil, error.response_code.to_i
406
+ rescue Errno::ECONNREFUSED
407
+ error("connection refused to #{url}")
408
+ end
409
+
410
+ def check(msg)
411
+ @sso = Sso.new(data)
412
+ super
413
+ end
414
+
415
+ def call!
416
+ error("need an sso salt to perform sso test") unless data['api']['sso_salt']
417
+
418
+ sso = Sso.new(data)
419
+ verb = sso.POST? ? 'POST' : 'GET'
420
+ test "#{verb} #{sso.path}"
421
+
422
+ check "validates token" do
423
+ @sso.token = 'invalid'
424
+ page, respcode = mechanize_get
425
+ error("expected 403, got #{respcode}") unless respcode == 403
426
+ true
427
+ end
428
+
429
+ check "validates timestamp" do
430
+ @sso.timestamp = (Time.now - 60*6).to_i
431
+ page, respcode = mechanize_get
432
+ error("expected 403, got #{respcode}") unless respcode == 403
433
+ true
434
+ end
435
+
436
+ page_logged_in = nil
437
+ check "logs in" do
438
+ page_logged_in, respcode = mechanize_get
439
+ error("expected 200, got #{respcode}") unless respcode == 200
440
+ true
441
+ end
442
+
443
+ check "creates the heroku-nav-data cookie" do
444
+ cookie = agent.cookie_jar.cookies(URI.parse(@sso.full_url)).detect { |c| c.name == 'heroku-nav-data' }
445
+ error("could not find cookie heroku-nav-data") unless cookie
446
+ error("expected #{@sso.sample_nav_data}, got #{cookie.value}") unless cookie.value == @sso.sample_nav_data
447
+ true
448
+ end
449
+
450
+ check "displays the heroku layout" do
451
+ error("could not find Heroku layout") if page_logged_in.search('div#heroku-header').empty?
452
+ true
453
+ end
454
+ end
455
+ end
456
+
457
+
458
+ ##
459
+ # On Testing:
460
+ # I've opted to not write tests for this
461
+ # due to the simple nature it's currently in.
462
+ # If this becomes more complex in even the
463
+ # least amount, find me (blake) and I'll
464
+ # help get tests in.
465
+ class AllCheck < Check
466
+
467
+ def call!
468
+ args = data[:args]
469
+ run ProvisionCheck, data
470
+
471
+ response = data[:provision_response]
472
+ data.merge!(:id => response["id"])
473
+ config = response["config"] || Hash.new
474
+
475
+ if args
476
+ screen.message "\n\n"
477
+ screen.message "Starting #{args.first}..."
478
+ screen.message "\n\n"
479
+
480
+ run_in_env(config) { system(*args) }
481
+ error("run exited abnormally, expected 0, got #{$?.to_i}") unless $?.to_i == 0
482
+
483
+ screen.message "\n"
484
+ screen.message "End of #{args.first}\n"
485
+ end
486
+
487
+ run DeprovisionCheck, data
488
+ end
489
+
490
+ def run_in_env(env)
491
+ env.each {|key, value| ENV[key] = value }
492
+ yield
493
+ env.keys.each {|key| ENV.delete(key) }
494
+ end
495
+
496
+ end
497
+
498
+ end
499
+ end