kensa 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/server.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'sinatra'
2
+ require 'yajl'
3
+ require 'restclient'
4
+
5
+ post "/heroku/resources" do
6
+ resp = { :id => 123, :config => { "FOO" => "bar" } }
7
+ #resp = { :id => 123 }
8
+ Yajl::Encoder.encode(resp)
9
+ end
10
+
11
+ delete "/heroku/resources/:id" do
12
+ "ok"
13
+ end
data/set-env.sh ADDED
@@ -0,0 +1,4 @@
1
+ PATH=$(pwd)/bin:$PATH
2
+ RUBYOPT=rubygems
3
+ RUBYLIB=$(pwd)/lib:$RUBYLIB
4
+ export PATH RUBYOPT RUBYLIB
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class DeprovisionCheckTest < Test::Unit::TestCase
4
+ include Heroku::Sensei
5
+
6
+ setup do
7
+ @data = Manifest.skeleton.merge :id => 123
8
+ @responses = [
9
+ [200, ""],
10
+ [401, ""],
11
+ ]
12
+ end
13
+
14
+ def check ; DeprovisionCheck ; end
15
+
16
+ test "valid on 200" do
17
+ assert_valid do |check|
18
+ stub :delete, check, @responses
19
+ end
20
+ end
21
+
22
+ test "status other than 200" do
23
+ @responses[0] = [500, ""]
24
+ assert_invalid do |check|
25
+ stub :delete, check, @responses
26
+ end
27
+ end
28
+
29
+ test "runs auth check" do
30
+ @responses[1] = [200, ""]
31
+ assert_invalid do |check|
32
+ stub :delete, check, @responses
33
+ end
34
+ end
35
+
36
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,48 @@
1
+ require 'heroku/kensa'
2
+ require 'contest'
3
+
4
+ class Test::Unit::TestCase
5
+
6
+ def assert_valid(data=@data, &blk)
7
+ check = create_check(data, &blk)
8
+ assert check.call
9
+ end
10
+
11
+ def assert_invalid(data=@data, &blk)
12
+ check = create_check(data, &blk)
13
+ assert !check.call
14
+ end
15
+
16
+ def create_check(data, &blk)
17
+ check = self.check.new(data)
18
+ blk.call(check) if blk
19
+ check
20
+ end
21
+
22
+ module Headerize
23
+ attr_accessor :headers
24
+ end
25
+
26
+ def to_json(data, headers={})
27
+ body = Yajl::Encoder.encode(data)
28
+ add_headers(body, headers)
29
+ end
30
+
31
+ def add_headers(o, headers={})
32
+ o.extend Headerize
33
+ o.headers = {}
34
+ o.headers["Content-Type"] ||= "application/json"
35
+ o.headers.merge!(headers)
36
+ o
37
+ end
38
+
39
+ def stub(meth, o, returns)
40
+ o.instance_eval { @returns = Array(returns) }
41
+ eval <<-EVAL
42
+ def o.#{meth}(*args)
43
+ @returns.shift || fail("Nothing else to return from stub'ed method")
44
+ end
45
+ EVAL
46
+ end
47
+
48
+ end
@@ -0,0 +1,142 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class ManifestCheckTest < Test::Unit::TestCase
4
+ include Heroku::Sensei
5
+
6
+ def check ; ManifestCheck ; end
7
+
8
+ setup do
9
+ @data = Manifest.skeleton
10
+ @data["plans"] << {
11
+ "id" => "advanced",
12
+ "name" => "Advanced",
13
+ "price" => "100",
14
+ "price_unit" => "month"
15
+ }
16
+ end
17
+
18
+ test "is valid if no errors" do
19
+ assert_valid
20
+ end
21
+
22
+ test "has an id" do
23
+ @data.delete("id")
24
+ assert_invalid
25
+ end
26
+
27
+ test "has a name" do
28
+ @data.delete("name")
29
+ assert_invalid
30
+ end
31
+
32
+ test "api key exists" do
33
+ @data.delete("api")
34
+ assert_invalid
35
+ end
36
+
37
+ test "api is a Hash" do
38
+ @data["api"] = ""
39
+ assert_invalid
40
+ end
41
+
42
+ test "api has a username" do
43
+ @data["api"].delete("username")
44
+ assert_invalid
45
+ end
46
+
47
+ test "api has a password" do
48
+ @data["api"].delete("password")
49
+ assert_invalid
50
+ end
51
+
52
+ test "api contains test" do
53
+ @data["api"].delete("test")
54
+ assert_invalid
55
+ end
56
+
57
+ test "api contains production" do
58
+ @data["api"].delete("production")
59
+ assert_invalid
60
+ end
61
+
62
+ test "api contains production of https" do
63
+ @data["api"]["production"] = "http://foo.com"
64
+ assert_invalid
65
+ end
66
+
67
+ test "api contains config_vars array" do
68
+ @data["api"]["config_vars"] = "test"
69
+ assert_invalid
70
+ end
71
+
72
+ test "api contains at least one config var" do
73
+ @data["api"]["config_vars"].clear
74
+ assert_invalid
75
+ end
76
+
77
+ test "all config vars are in upper case" do
78
+ @data["api"]["config_vars"] << 'MYADDON_invalid_var'
79
+ assert_invalid
80
+ end
81
+
82
+ test "assert config var prefixes match addon id" do
83
+ @data["api"]["config_vars"] << 'MONGO_URL'
84
+ assert_invalid
85
+ end
86
+
87
+ test "plans key must exist" do
88
+ @data.delete("plans")
89
+ assert_invalid
90
+ end
91
+
92
+ test "plans key must be an Array" do
93
+ @data["plans"] = ""
94
+ assert_invalid
95
+ end
96
+
97
+ test "has at least one plan" do
98
+ @data["plans"] = []
99
+ assert_invalid
100
+ end
101
+
102
+ test "all plans are a hash" do
103
+ @data["plans"][0] = ""
104
+ assert_invalid
105
+ end
106
+
107
+ test "all plans have an id" do
108
+ @data["plans"].first.delete("id")
109
+ assert_invalid
110
+ end
111
+
112
+ test "all plans have an unique id" do
113
+ @data["plans"].first["id"] = @data["plans"].last["id"]
114
+ assert_invalid
115
+ end
116
+
117
+ test "all plans have a name" do
118
+ @data["plans"].first.delete("name")
119
+ assert_invalid
120
+ end
121
+
122
+ test "all plans have a unique name" do
123
+ @data["plans"].first["name"] = @data["plans"].last["name"]
124
+ assert_invalid
125
+ end
126
+
127
+ test "plans have a price" do
128
+ @data["plans"].first.delete("price")
129
+ assert_invalid
130
+ end
131
+
132
+ test "plans have an integer price" do
133
+ @data["plans"].first["price"] = "fiddy cent"
134
+ assert_invalid
135
+ end
136
+
137
+ test "plans have a valid price_unit" do
138
+ @data["plans"].first["price_unit"] = "first ov da munth"
139
+ assert_invalid
140
+ end
141
+
142
+ end
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class ProvisionCheckTest < Test::Unit::TestCase
4
+ include Heroku::Sensei
5
+
6
+ setup do
7
+ @data = Manifest.skeleton
8
+ @responses = [
9
+ [200, to_json({ :id => 456 })],
10
+ [401, "Unauthorized"]
11
+ ]
12
+ end
13
+
14
+ def check ; ProvisionCheck ; end
15
+
16
+ test "valid on 200 for the regular check, and 401 for the auth check" do
17
+ assert_valid do |check|
18
+ stub :post, check, @responses
19
+ end
20
+ end
21
+
22
+ test "invalid JSON" do
23
+ @responses[0] = [200, "---"]
24
+ assert_invalid do |check|
25
+ stub :post, check, @responses
26
+ end
27
+ end
28
+
29
+ test "status other than 200" do
30
+ @responses[0] = [500, to_json({ :id => 456 })]
31
+ assert_invalid do |check|
32
+ stub :post, check, @responses
33
+ end
34
+ end
35
+
36
+ test "runs provision response check" do
37
+ @responses[0] = [200, to_json({ :noid => 456 })]
38
+ assert_invalid do |check|
39
+ stub :post, check, @responses
40
+ end
41
+ end
42
+
43
+ test "runs auth check" do
44
+ @responses[1] = [200, to_json({ :id => 456 })]
45
+ assert_invalid do |check|
46
+ stub :post, check, @responses
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,72 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class ProvisionResponseCheckTest < Test::Unit::TestCase
4
+ include Heroku::Sensei
5
+
6
+ def check ; ProvisionResponseCheck ; end
7
+
8
+ setup do
9
+ @response = { "id" => "123" }
10
+ @data = Manifest.skeleton.merge(:provision_response => @response)
11
+ @data['api']['config_vars'] << "MYADDON_CONFIG"
12
+ end
13
+
14
+ test "is valid if no errors" do
15
+ assert_valid
16
+ end
17
+
18
+ test "has an id" do
19
+ @response.delete("id")
20
+ assert_invalid
21
+ end
22
+
23
+ describe "when config is present" do
24
+
25
+ test "is a hash" do
26
+ @response["config"] = ""
27
+ assert_invalid
28
+ end
29
+
30
+ test "each key is previously set in the manifest" do
31
+ @response["config"] = { "MYSQL_URL" => "http://..." }
32
+ assert_invalid
33
+ end
34
+
35
+ test "each value is a string" do
36
+ @response["config"] = { "MYADDON_URL" => {} }
37
+ assert_invalid
38
+ end
39
+
40
+ test "asserts _URL vars are valid URIs" do
41
+ @response["config"] = { "MYADDON_URL" => "abc:" }
42
+ assert_invalid
43
+ end
44
+
45
+ test "asserts _URL vars have a host" do
46
+ @response["config"] = { "MYADDON_URL" => "path" }
47
+ assert_invalid
48
+ end
49
+
50
+ test "asserts _URL vars have a scheme" do
51
+ @response["config"] = { "MYADDON_URL" => "//host/path" }
52
+ assert_invalid
53
+ end
54
+
55
+ test "doesn't run URI test against other vars" do
56
+ @response["config"] = { "MYADDON_CONFIG" => "abc:" }
57
+ assert_valid
58
+ end
59
+
60
+ test "doesn't allow localhost URIs on production" do
61
+ @data[:env] = 'production'
62
+ @response["config"] = { "MYADDON_URL" => "http://localhost/abc" }
63
+ assert_invalid
64
+ end
65
+
66
+ test "is valid otherwise" do
67
+ @response["config"] = { "MYADDON_URL" => "http://localhost/abc" }
68
+ assert_valid
69
+ end
70
+ end
71
+
72
+ end
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'sinatra'
3
+
4
+ enable :sessions
5
+
6
+ helpers do
7
+ def unauthorized!(status=403)
8
+ throw(:halt, [status, "Not authorized\n"])
9
+ end
10
+
11
+ def make_token
12
+ Digest::SHA1.hexdigest([params[:id], 'SSO_SALT', params[:timestamp]].join(':'))
13
+ end
14
+
15
+ def login
16
+ session[:logged_in] = true
17
+ redirect '/'
18
+ end
19
+ end
20
+
21
+ get '/working/heroku/resources/:id' do
22
+ unauthorized! unless params[:id] && params[:token]
23
+ unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
24
+ unauthorized! unless params[:token] == make_token
25
+ login
26
+ end
27
+
28
+ get '/notoken/heroku/resources/:id' do
29
+ unauthorized! unless params[:id] && params[:token]
30
+ unauthorized! unless params[:timestamp].to_i > (Time.now-60*2).to_i
31
+ login
32
+ end
33
+
34
+ get '/notimestamp/heroku/resources/:id' do
35
+ unauthorized! unless params[:id] && params[:token]
36
+ unauthorized! unless params[:token] == make_token
37
+ login
38
+ end
39
+
40
+ get '/' do
41
+ unauthorized! unless session[:logged_in]
42
+ "OK"
43
+ end
@@ -0,0 +1,28 @@
1
+ require File.dirname(__FILE__) + "/helper"
2
+
3
+ class SsoCheckTest < Test::Unit::TestCase
4
+ include Heroku::Sensei
5
+
6
+ setup do
7
+ @data = Manifest.skeleton.merge :id => 123
8
+ @data['api']['sso_salt'] = 'SSO_SALT'
9
+ end
10
+
11
+ def check ; SsoCheck ; end
12
+
13
+ test "working sso request" do
14
+ @data['api']['test'] += "working"
15
+ assert_valid
16
+ end
17
+
18
+ test "rejects bad token" do
19
+ @data['api']['test'] += "notoken"
20
+ assert_invalid
21
+ end
22
+
23
+ test "rejects old timestamp" do
24
+ @data['api']['test'] += "notimestamp"
25
+ assert_invalid
26
+ end
27
+
28
+ end