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