kensa 1.2.0rc7 → 1.2.0

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.
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