cassette 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/README.md +106 -0
- data/lib/cassette/authentication/authorities.rb +37 -0
- data/lib/cassette/authentication/cache.rb +30 -0
- data/lib/cassette/authentication/filter.rb +41 -0
- data/lib/cassette/authentication/user.rb +27 -0
- data/lib/cassette/authentication.rb +72 -0
- data/lib/cassette/cache.rb +42 -0
- data/lib/cassette/client/cache.rb +43 -0
- data/lib/cassette/client.rb +68 -0
- data/lib/cassette/errors/not_a_customer.rb +14 -0
- data/lib/cassette/errors/not_an_employee.rb +14 -0
- data/lib/cassette/errors.rb +44 -0
- data/lib/cassette/rubycas/helper.rb +78 -0
- data/lib/cassette/rubycas/not_single_sign_out_constraint.rb +14 -0
- data/lib/cassette/rubycas/single_sign_out_constraint.rb +27 -0
- data/lib/cassette/rubycas.rb +11 -0
- data/lib/cassette/version.rb +15 -0
- data/lib/cassette.rb +75 -0
- data/spec/cas/authentication/authorities_spec.rb +82 -0
- data/spec/cas/authentication/cache_spec.rb +8 -0
- data/spec/cas/authentication/filter_spec.rb +172 -0
- data/spec/cas/authentication/user_spec.rb +70 -0
- data/spec/cas/authentication_spec.rb +84 -0
- data/spec/cas/cache_spec.rb +40 -0
- data/spec/cas/client/cache_spec.rb +7 -0
- data/spec/cas/errors_spec.rb +29 -0
- data/spec/cas_spec.rb +78 -0
- data/spec/config.yml +5 -0
- data/spec/fixtures/cas/fail.xml +6 -0
- data/spec/fixtures/cas/success.xml +12 -0
- data/spec/integration/cas/client_spec.rb +50 -0
- data/spec/spec_helper.rb +27 -0
- metadata +257 -0
data/lib/cassette.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "cassette/errors"
|
4
|
+
require "cassette/cache"
|
5
|
+
require "cassette/client/cache"
|
6
|
+
require "cassette/client"
|
7
|
+
require "cassette/authentication"
|
8
|
+
require "cassette/authentication/authorities"
|
9
|
+
require "cassette/authentication/user"
|
10
|
+
require "cassette/authentication/cache"
|
11
|
+
require "cassette/authentication/filter"
|
12
|
+
|
13
|
+
require "faraday"
|
14
|
+
require "logger"
|
15
|
+
|
16
|
+
module Cassette
|
17
|
+
extend self
|
18
|
+
|
19
|
+
DEFAULT_TIMEOUT = 10
|
20
|
+
|
21
|
+
def logger
|
22
|
+
@@logger ||= begin
|
23
|
+
if defined?(Rails) && Rails.logger
|
24
|
+
Rails.logger
|
25
|
+
else
|
26
|
+
Logger.new("/dev/null")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def logger=(logger)
|
32
|
+
@@logger = logger
|
33
|
+
end
|
34
|
+
|
35
|
+
def config
|
36
|
+
@@config
|
37
|
+
end
|
38
|
+
|
39
|
+
def config=(config)
|
40
|
+
@@config = config
|
41
|
+
end
|
42
|
+
|
43
|
+
def new_request(uri, timeout)
|
44
|
+
Faraday.new(url: uri, ssl: { verify: false, version: "TLSv1" }) do |builder|
|
45
|
+
builder.adapter :httpclient
|
46
|
+
builder.options.timeout = timeout
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def get(uri, payload, timeout = DEFAULT_TIMEOUT)
|
51
|
+
perform(:get, uri, payload, timeout) do |req|
|
52
|
+
req.params = payload
|
53
|
+
logger.debug "Request: #{req.inspect}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def post(uri, payload, timeout = DEFAULT_TIMEOUT)
|
58
|
+
perform(:post, uri, payload, timeout) do |req|
|
59
|
+
req.body = payload
|
60
|
+
logger.debug "Request: #{req.inspect}"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
def perform(op, uri, payload, timeout = DEFAULT_TIMEOUT, &block)
|
67
|
+
request = new_request(uri, timeout)
|
68
|
+
res = request.send(op, &block)
|
69
|
+
|
70
|
+
res.tap do |response|
|
71
|
+
logger.debug "Got response: #{response.body.inspect} (#{response.status}), #{response.headers.inspect}"
|
72
|
+
Cassette::Errors.raise_by_code(response.status) unless response.success?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cassette::Authentication::Authorities do
|
4
|
+
subject do
|
5
|
+
Cassette::Authentication::Authorities
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#has_role?" do
|
9
|
+
let(:input) { "[#{Cassette.config.base_authority}, SAPI, #{Cassette.config.base_authority}_CREATE-USER]" }
|
10
|
+
let(:authorities) { subject.parse(input) }
|
11
|
+
|
12
|
+
it "adds the application prefix to roles" do
|
13
|
+
expect(authorities.has_role?("CREATE-USER")).to eql(true)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "ignores role case" do
|
17
|
+
expect(authorities.has_role?("create-user")).to eql(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "replaces underscores with dashes" do
|
21
|
+
expect(authorities.has_role?("create_user")).to eql(true)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "with a defined base authority" do
|
26
|
+
let(:base_authority) { "SOMEAPI" }
|
27
|
+
|
28
|
+
it "stores the base authority" do
|
29
|
+
input = "CUSTOMERAPI"
|
30
|
+
expect(subject.parse(input, base_authority).base).to eql(base_authority)
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#has_role?" do
|
34
|
+
let(:input) { "[#{Cassette.config.base_authority}_TEST2, SOMEAPI_TEST]" }
|
35
|
+
|
36
|
+
it "returns true for a role that is using the base authority" do
|
37
|
+
expect(subject.parse(input, base_authority)).to have_role(:test)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "returns false for a role that is not using the base authority" do
|
41
|
+
expect(subject.parse(input, base_authority)).not_to have_role(:test2)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "CAS authorities parsing" do
|
47
|
+
it "handles single authority" do
|
48
|
+
input = "CUSTOMERAPI"
|
49
|
+
expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI))
|
50
|
+
end
|
51
|
+
|
52
|
+
it "handles multiple authorities with surrounding []" do
|
53
|
+
input = "[CUSTOMERAPI, SAPI]"
|
54
|
+
expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI SAPI))
|
55
|
+
end
|
56
|
+
|
57
|
+
it "ignores whitespace in multiple authorities" do
|
58
|
+
input = "[CUSTOMERAPI,SAPI]"
|
59
|
+
expect(subject.parse(input).authorities).to eq(%w(CUSTOMERAPI SAPI))
|
60
|
+
end
|
61
|
+
|
62
|
+
it "returns an empty array when input is nil" do
|
63
|
+
expect(subject.parse(nil).authorities).to eq([])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "with authentication disabled" do
|
68
|
+
before { ENV["NOAUTH"] = "true" }
|
69
|
+
after { ENV.delete("NOAUTH") }
|
70
|
+
subject { Cassette::Authentication::Authorities.new("[]") }
|
71
|
+
|
72
|
+
it "#has_role? returns true for every role" do
|
73
|
+
expect(subject.authorities).to be_empty
|
74
|
+
expect(subject.has_role?(:can_manage)).to eql(true)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "#has_raw_role? returns true for every role" do
|
78
|
+
expect(subject.authorities).to be_empty
|
79
|
+
expect(subject.has_raw_role?("SAPI_CUSTOMER-CREATOR")).to eql(true)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
require "active_support/core_ext/hash/indifferent_access"
|
5
|
+
|
6
|
+
describe Cassette::Authentication::Filter do
|
7
|
+
before do
|
8
|
+
allow(Cassette::Authentication).to receive(:validate_ticket)
|
9
|
+
end
|
10
|
+
|
11
|
+
class ControllerMock
|
12
|
+
attr_accessor :params, :request, :current_user
|
13
|
+
def self.before_filter(*); end
|
14
|
+
include Cassette::Authentication::Filter
|
15
|
+
|
16
|
+
def initialize(params = {}, headers = {})
|
17
|
+
self.params = params.with_indifferent_access
|
18
|
+
self.request = OpenStruct.new(headers: headers.with_indifferent_access)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
shared_context "with NOAUTH" do
|
23
|
+
before do
|
24
|
+
ENV["NOAUTH"] = "yes"
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
ENV.delete("NOAUTH")
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#validate_raw_role!" do
|
33
|
+
let(:controller) { ControllerMock.new }
|
34
|
+
let(:current_user) { instance_double(Cassette::Authentication::User) }
|
35
|
+
|
36
|
+
before do
|
37
|
+
allow(controller).to receive(:current_user).and_return(current_user)
|
38
|
+
end
|
39
|
+
|
40
|
+
it_behaves_like "with NOAUTH" do
|
41
|
+
it "never checks the role" do
|
42
|
+
expect(current_user).not_to receive(:has_raw_role?)
|
43
|
+
controller.validate_raw_role!(:something)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "does not raise error" do
|
47
|
+
expect { controller.validate_raw_role!(:something) }.not_to raise_error
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it "forwards to current_user" do
|
52
|
+
role = instance_double(String)
|
53
|
+
|
54
|
+
expect(current_user).to receive(:has_raw_role?).with(role).and_return(true)
|
55
|
+
controller.validate_raw_role!(role)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "raises a Cassette::Errors::Forbidden when current_user does not have the role" do
|
59
|
+
role = instance_double(String)
|
60
|
+
|
61
|
+
expect(current_user).to receive(:has_raw_role?).with(role).and_return(false)
|
62
|
+
expect { controller.validate_raw_role!(role) }.to raise_error(Cassette::Errors::Forbidden)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#validate_role!" do
|
67
|
+
let(:controller) { ControllerMock.new }
|
68
|
+
let(:current_user) { instance_double(Cassette::Authentication::User) }
|
69
|
+
|
70
|
+
before do
|
71
|
+
allow(controller).to receive(:current_user).and_return(current_user)
|
72
|
+
end
|
73
|
+
|
74
|
+
it_behaves_like "with NOAUTH" do
|
75
|
+
it "never checks the role" do
|
76
|
+
expect(current_user).not_to receive(:has_role?)
|
77
|
+
controller.validate_role!(:something)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "does not raise error" do
|
81
|
+
expect { controller.validate_role!(:something) }.not_to raise_error
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "forwards to current_user" do
|
86
|
+
role = instance_double(String)
|
87
|
+
|
88
|
+
expect(current_user).to receive(:has_role?).with(role).and_return(true)
|
89
|
+
controller.validate_role!(role)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "raises a Cassette::Errors::Forbidden when current_user does not have the role" do
|
93
|
+
role = instance_double(String)
|
94
|
+
|
95
|
+
expect(current_user).to receive(:has_role?).with(role).and_return(false)
|
96
|
+
expect { controller.validate_role!(role) }.to raise_error(Cassette::Errors::Forbidden)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#validate_authentication_ticket" do
|
101
|
+
it_behaves_like "with NOAUTH" do
|
102
|
+
context "and no ticket" do
|
103
|
+
let(:controller) { ControllerMock.new }
|
104
|
+
|
105
|
+
it "should not validate tickets" do
|
106
|
+
controller.validate_authentication_ticket
|
107
|
+
expect(Cassette::Authentication).not_to have_received(:validate_ticket)
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should set current_user" do
|
111
|
+
controller.validate_authentication_ticket
|
112
|
+
expect(controller.current_user).to be_present
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "and a ticket header" do
|
117
|
+
let(:controller) do
|
118
|
+
ControllerMock.new({}, "Service-Ticket" => "le ticket")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should validate tickets" do
|
122
|
+
controller.validate_authentication_ticket
|
123
|
+
expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "and a ticket param" do
|
128
|
+
let(:controller) do
|
129
|
+
ControllerMock.new(ticket: "le ticket")
|
130
|
+
end
|
131
|
+
|
132
|
+
it "should validate tickets" do
|
133
|
+
controller.validate_authentication_ticket
|
134
|
+
expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "with a ticket in the query string *AND* headers" do
|
140
|
+
let(:controller) do
|
141
|
+
ControllerMock.new({"ticket" => "le other ticket"}, "Service-Ticket" => "le ticket")
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should send only the header ticket to validation" do
|
145
|
+
controller.validate_authentication_ticket
|
146
|
+
expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "with a ticket in the query string" do
|
151
|
+
let(:controller) do
|
152
|
+
ControllerMock.new("ticket" => "le ticket")
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should send the ticket to validation" do
|
156
|
+
controller.validate_authentication_ticket
|
157
|
+
expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
context "with a ticket in the Service-Ticket header" do
|
162
|
+
let(:controller) do
|
163
|
+
ControllerMock.new({}, "Service-Ticket" => "le ticket")
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should send the ticket to validation" do
|
167
|
+
controller.validate_authentication_ticket
|
168
|
+
expect(Cassette::Authentication).to have_received(:validate_ticket).with("le ticket", Cassette.config.service)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Cassette::Authentication::User do
|
4
|
+
let(:base_authority) do
|
5
|
+
Cassette.config.base_authority
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
context "without a config" do
|
10
|
+
it "forwards authorities parsing" do
|
11
|
+
expect(Cassette::Authentication::Authorities).to receive(:new).with("[CUSTOMERAPI, SAPI]", nil)
|
12
|
+
Cassette::Authentication::User.new(login: "john.doe", name: "John Doe", authorities: "[CUSTOMERAPI, SAPI]")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
context "with a config" do
|
17
|
+
it "forwards authorities parsing passing along the base authority" do
|
18
|
+
config = object_double(Cassette.config)
|
19
|
+
|
20
|
+
expect(config).to receive(:base_authority).and_return("TESTAPI")
|
21
|
+
expect(Cassette::Authentication::Authorities).to receive(:new).with("[CUSTOMERAPI, SAPI]", "TESTAPI")
|
22
|
+
|
23
|
+
Cassette::Authentication::User.new(login: "john.doe", name: "John Doe", authorities: "[CUSTOMERAPI, SAPI]", config: config)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#has_role?" do
|
29
|
+
let (:user) do
|
30
|
+
Cassette::Authentication::User.new(login: "john.doe", name: "John Doe",
|
31
|
+
authorities: "[#{base_authority}, SAPI, #{base_authority}_CREATE-USER]")
|
32
|
+
end
|
33
|
+
|
34
|
+
it "adds the application prefix to roles" do
|
35
|
+
expect(user.has_role?("CREATE-USER")).to eql(true)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "ignores role case" do
|
39
|
+
expect(user.has_role?("create-user")).to eql(true)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "replaces underscores with dashes" do
|
43
|
+
expect(user.has_role?("create_user")).to eql(true)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "user types" do
|
48
|
+
context "#employee?" do
|
49
|
+
it "returns true when user is an employee" do
|
50
|
+
expect(Cassette::Authentication::User.new(type: "employee")).to be_employee
|
51
|
+
expect(Cassette::Authentication::User.new(type: "Employee")).to be_employee
|
52
|
+
expect(Cassette::Authentication::User.new(type: :employee)).to be_employee
|
53
|
+
expect(Cassette::Authentication::User.new(type: "customer")).not_to be_employee
|
54
|
+
expect(Cassette::Authentication::User.new(type: nil)).not_to be_employee
|
55
|
+
expect(Cassette::Authentication::User.new(type: "")).not_to be_employee
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "#customer?" do
|
60
|
+
it "returns true when the user is a customer" do
|
61
|
+
expect(Cassette::Authentication::User.new(type: "customer")).to be_customer
|
62
|
+
expect(Cassette::Authentication::User.new(type: "Customer")).to be_customer
|
63
|
+
expect(Cassette::Authentication::User.new(type: :customer)).to be_customer
|
64
|
+
expect(Cassette::Authentication::User.new(type: "employee")).not_to be_customer
|
65
|
+
expect(Cassette::Authentication::User.new(type: nil)).not_to be_customer
|
66
|
+
expect(Cassette::Authentication::User.new(type: "")).not_to be_customer
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Cassette::Authentication do
|
6
|
+
let(:cache) { instance_double(Cassette::Authentication::Cache) }
|
7
|
+
let(:http) { class_double(Cassette) }
|
8
|
+
|
9
|
+
subject do
|
10
|
+
Cassette::Authentication.new(cache: cache, http_client: http)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#ticket_user" do
|
14
|
+
context "when cached" do
|
15
|
+
it "returns the cached value when cached" do
|
16
|
+
cached = double('cached')
|
17
|
+
|
18
|
+
expect(cache).to receive(:fetch_authentication) do |ticket, &block|
|
19
|
+
expect(ticket).to eql("ticket")
|
20
|
+
expect(block).to be_present
|
21
|
+
cached
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(subject.ticket_user("ticket")).to eql(cached)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when not cached" do
|
29
|
+
before do
|
30
|
+
expect(cache).to receive(:fetch_authentication) do |ticket, &block|
|
31
|
+
block.call
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "raises a Forbidden exception on any exceptions" do
|
36
|
+
allow(http).to receive(:post).with(anything, anything).and_raise(Cassette::Errors::BadRequest)
|
37
|
+
expect { subject.ticket_user("ticket") }.to raise_error(Cassette::Errors::Forbidden)
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with a failed CAS response" do
|
41
|
+
before do
|
42
|
+
allow(http).to receive(:post).with(anything, anything)
|
43
|
+
.and_return(OpenStruct.new(body: fixture("cas/fail.xml")))
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns nil" do
|
47
|
+
expect(subject.ticket_user("ticket")).to be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with a successful CAS response" do
|
52
|
+
before do
|
53
|
+
allow(http).to receive(:post).with(anything, anything)
|
54
|
+
.and_return(OpenStruct.new(body: fixture("cas/success.xml")))
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns an User" do
|
58
|
+
expect(subject.ticket_user("ticket")).to be_instance_of(Cassette::Authentication::User)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "#validate_ticket" do
|
65
|
+
it "raises a authorization required error when no ticket is provided" do
|
66
|
+
expect { subject.validate_ticket(nil) }.to raise_error(Cassette::Errors::AuthorizationRequired)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "raises a authorization required error when ticket is blank" do
|
70
|
+
expect { subject.validate_ticket("") }.to raise_error(Cassette::Errors::AuthorizationRequired)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "raises a forbidden error when the associated user is not found" do
|
74
|
+
expect(subject).to receive(:ticket_user).with("ticket", Cassette.config.service).and_return(nil)
|
75
|
+
expect { subject.validate_ticket("ticket") }.to raise_error(Cassette::Errors::Forbidden)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "returns the associated user" do
|
79
|
+
user = double('User')
|
80
|
+
expect(subject).to receive(:ticket_user).with("ticket", Cassette.config.service).and_return(user)
|
81
|
+
expect(subject.validate_ticket("ticket")).to eql(user)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Cassette::Cache do
|
6
|
+
subject do
|
7
|
+
c = Class.new
|
8
|
+
c.send(:include, Cassette::Cache)
|
9
|
+
c.new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "backend" do
|
13
|
+
before { subject.backend = nil }
|
14
|
+
after { subject.backend = nil }
|
15
|
+
|
16
|
+
it "sets the backend" do
|
17
|
+
backend = double('Backend')
|
18
|
+
subject.backend = backend
|
19
|
+
expect(subject.backend).to eql(backend)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "defaults to rails backend" do
|
23
|
+
rails = double("Rails")
|
24
|
+
allow(rails).to receive(:cache).and_return(rails)
|
25
|
+
stub_const("Rails", rails)
|
26
|
+
|
27
|
+
expect(subject.backend).to eql(rails)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
it "invalidates the cache after the configured number of uses" do
|
32
|
+
generator = double('Generator')
|
33
|
+
expect(generator).to receive(:generate).twice
|
34
|
+
|
35
|
+
6.times do
|
36
|
+
subject.fetch("Generator", max_uses: 5) { generator.generate }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
describe Cassette::Errors do
|
6
|
+
describe Cassette::Errors::Base do
|
7
|
+
describe "#code" do
|
8
|
+
it "returns the HTTP status code accordlingly" do
|
9
|
+
expect(Cassette::Errors::Forbidden.new.code).to eql(403)
|
10
|
+
expect(Cassette::Errors::NotFound.new.code).to eql(404)
|
11
|
+
expect(Cassette::Errors::InternalServerError.new.code).to eql(500)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe ".raise_by_code" do
|
17
|
+
it "raises the correct exception for the status code" do
|
18
|
+
expect { Cassette::Errors.raise_by_code(404) }.to raise_error(Cassette::Errors::NotFound)
|
19
|
+
expect { Cassette::Errors.raise_by_code(403) }.to raise_error(Cassette::Errors::Forbidden)
|
20
|
+
expect { Cassette::Errors.raise_by_code(412) }.to raise_error(Cassette::Errors::PreconditionFailed)
|
21
|
+
expect { Cassette::Errors.raise_by_code(500) }.to raise_error(Cassette::Errors::InternalServerError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "raises internal server error for unmapped errors" do
|
25
|
+
expect { Cassette::Errors.raise_by_code(406) }.to raise_error(Cassette::Errors::InternalServerError)
|
26
|
+
expect { Cassette::Errors.raise_by_code(200) }.to raise_error(Cassette::Errors::InternalServerError)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/spec/cas_spec.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Cassette do
|
4
|
+
let(:uri) { "http://example.org/" }
|
5
|
+
let(:response) do
|
6
|
+
Faraday.new do |builder|
|
7
|
+
builder.adapter :test do |stub|
|
8
|
+
stub.post(uri, 'data') do |env|
|
9
|
+
headers = env.request_headers
|
10
|
+
[200, {}, "{ok: true}"]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:failed_response) do
|
17
|
+
Faraday.new do |builder|
|
18
|
+
builder.adapter :test do |stub|
|
19
|
+
stub.post(uri, 'data') do |env|
|
20
|
+
headers = env.request_headers
|
21
|
+
[500, {}, "{ok: false}"]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ".new_request" do
|
28
|
+
it "returns an instance" do
|
29
|
+
# damn coverage
|
30
|
+
expect(Cassette.new_request(uri, 5)).to be_instance_of(Faraday::Connection)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe ".post" do
|
35
|
+
it "forwards requests" do
|
36
|
+
allow(Cassette).to receive(:new_request).with(uri, 5).and_return(response)
|
37
|
+
Cassette.post(uri, "data", 5)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises an exception when failed" do
|
41
|
+
allow(Cassette).to receive(:new_request).with(uri, 5).and_return(failed_response)
|
42
|
+
expect { Cassette.post(uri, "data", 5) }.to raise_error(Cassette::Errors::InternalServerError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def keeping_logger(&block)
|
47
|
+
original_logger = Cassette.logger
|
48
|
+
block.call
|
49
|
+
Cassette.logger = original_logger
|
50
|
+
end
|
51
|
+
|
52
|
+
describe ".logger" do
|
53
|
+
it "returns a default instance" do
|
54
|
+
expect(Cassette.logger).not_to be_nil
|
55
|
+
expect(Cassette.logger.kind_of?(Logger)).to eql(true)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns rails logger when Rails is available" do
|
59
|
+
keeping_logger do
|
60
|
+
Cassette.logger = nil
|
61
|
+
rails = double("Rails")
|
62
|
+
expect(rails).to receive(:logger).and_return(rails).at_least(:once)
|
63
|
+
stub_const("Rails", rails)
|
64
|
+
expect(Cassette.logger).to eql(rails)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe ".logger=" do
|
70
|
+
let(:logger) { Logger.new(STDOUT) }
|
71
|
+
it "defines the logger instance" do
|
72
|
+
keeping_logger do
|
73
|
+
Cassette.logger = logger
|
74
|
+
expect(Cassette.logger).to eq(logger)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/spec/config.yml
ADDED