wcc-data 0.3.9 → 0.4.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.env.example +3 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +6 -0
- data/Rakefile +36 -0
- data/bin/rspec +17 -0
- data/circle.yml +6 -0
- data/lib/wcc/data/faraday_client_app_token_auth.rb +63 -44
- data/lib/wcc/data/nucleus/campus.rb +3 -25
- data/lib/wcc/data/version.rb +1 -1
- data/spec/spec_helper.rb +24 -0
- data/spec/support/inheritable_class_attribute_examples.rb +16 -0
- data/spec/wcc/data/config_spec.rb +113 -0
- data/spec/wcc/data/enumerated_type_spec.rb +176 -0
- data/spec/wcc/data/faraday_client_app_token_auth_spec.rb +237 -0
- data/spec/wcc/data/mapper/attributes_spec.rb +94 -0
- data/spec/wcc/data/mapper/json_response_spec.rb +57 -0
- data/spec/wcc/data/mapper/rest_configuration_spec.rb +43 -0
- data/spec/wcc/data/mapper/rest_query_spec.rb +50 -0
- data/spec/wcc/data/model_spec.rb +33 -0
- data/spec/wcc/data/nucleus/campus_spec.rb +34 -0
- data/spec/wcc/data/rack_client_app_token_auth_spec.rb +219 -0
- data/spec/wcc/data/response_spec.rb +57 -0
- data/spec/wcc/data/rest_endpoint_spec.rb +71 -0
- data/spec/wcc/data/service_spec.rb +128 -0
- data/spec/wcc/data_spec.rb +25 -0
- data/wcc-data.gemspec +5 -3
- metadata +47 -7
@@ -0,0 +1,237 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe WCC::Data::FaradayClientAppTokenAuth do
|
4
|
+
let(:app) { spy(:application) }
|
5
|
+
|
6
|
+
describe WCC::Data::FaradayClientAppTokenAuth::RedisCache do
|
7
|
+
let(:connection) { instance_spy(Redis) }
|
8
|
+
let(:connection_lambda) { -> (&blk) { blk.call(connection) } }
|
9
|
+
|
10
|
+
describe "#initialize" do
|
11
|
+
it "requires a callable for Redis connection" do
|
12
|
+
expect { described_class.new }.to raise_error(ArgumentError)
|
13
|
+
obj = described_class.new(connection_lambda)
|
14
|
+
expect(obj.connection).to eq(connection_lambda)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "allows setting a :cache_key option to change the token store" do
|
18
|
+
obj = described_class.new(connection_lambda, cache_key: "store")
|
19
|
+
expect(obj.cache_key).to eq("store")
|
20
|
+
end
|
21
|
+
|
22
|
+
it "defaults to the value of the DEFAULT_CACHE_KEY constant" do
|
23
|
+
obj = described_class.new(connection_lambda)
|
24
|
+
expect(obj.cache_key).to eq(described_class::DEFAULT_CACHE_KEY)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#[]" do
|
29
|
+
subject(:cache) { described_class.new(connection_lambda) }
|
30
|
+
|
31
|
+
it "returns HGET on the cache_key for the specified field" do
|
32
|
+
expect(connection)
|
33
|
+
.to receive(:hget).with(cache.cache_key, "example.com").and_return("123abc")
|
34
|
+
expect(cache["example.com"]).to eq("123abc")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#[]=" do
|
39
|
+
subject(:cache) { described_class.new(connection_lambda) }
|
40
|
+
|
41
|
+
it "does HSET on the cache_key for the specified field and value" do
|
42
|
+
cache["example.com"] = "abc123"
|
43
|
+
expect(connection)
|
44
|
+
.to have_received(:hset).with(cache.cache_key, "example.com", "abc123")
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with a nil value" do
|
48
|
+
it "does HDEL on the cache_key for the specified field" do
|
49
|
+
cache["example.com"] = nil
|
50
|
+
expect(connection)
|
51
|
+
.to have_received(:hdel).with(cache.cache_key, "example.com")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#initialize" do
|
58
|
+
it "takes an app as param one and sets to @app" do
|
59
|
+
obj = described_class.new(:app)
|
60
|
+
expect(obj.send(:instance_variable_get, :@app)).to eq(:app)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "takes a :cache option" do
|
64
|
+
obj = described_class.new(:app, cache: :cache)
|
65
|
+
expect(obj.cache).to eq(:cache)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "takes a :token_factory option" do
|
69
|
+
obj = described_class.new(:app, token_factory: :factory)
|
70
|
+
expect(obj.token_factory).to eq(:factory)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "defaults :cache option to an instance of RedisCache" do
|
74
|
+
obj = described_class.new(:app)
|
75
|
+
expect(obj.cache).to be_a(described_class::RedisCache)
|
76
|
+
expect(obj.cache.connection).to eq(Sidekiq.method(:redis))
|
77
|
+
end
|
78
|
+
|
79
|
+
it "defaults :token_factory option to a callable" do
|
80
|
+
obj = described_class.new(:app)
|
81
|
+
expect(obj.token_factory).to respond_to(:call)
|
82
|
+
end
|
83
|
+
|
84
|
+
describe "default token_factory" do
|
85
|
+
it "returns #token property from a Nucleus::ClientAppToken create" do
|
86
|
+
token = instance_spy(WCC::Data::Nucleus::ClientAppToken)
|
87
|
+
|
88
|
+
expect(WCC::Data::Nucleus::ClientAppToken)
|
89
|
+
.to receive(:create).with(hostname: "example.com").and_return(token)
|
90
|
+
expect(token).to receive(:token).and_return("abc123")
|
91
|
+
expect(described_class.new(:app).token_factory.("example.com"))
|
92
|
+
.to eq("abc123")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "#call" do
|
98
|
+
let(:app) { spy(:application) }
|
99
|
+
subject(:auth) { described_class.new(app) }
|
100
|
+
let(:env) {
|
101
|
+
{
|
102
|
+
url: URI("http://example.com"),
|
103
|
+
request_headers: {},
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
before do
|
108
|
+
allow(auth)
|
109
|
+
.to receive(:token_for).with("example.com").and_return("abc123")
|
110
|
+
end
|
111
|
+
|
112
|
+
it "sets env's Authorization header using value from #token_for" do
|
113
|
+
auth.call(env)
|
114
|
+
expect(env[:request_headers]["Authorization"])
|
115
|
+
.to eq("Bearer abc123")
|
116
|
+
end
|
117
|
+
|
118
|
+
it "calls @app with env" do
|
119
|
+
expect(app).to receive(:call).with(env)
|
120
|
+
auth.call(env)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "adds an on_complete handler to @app" do
|
124
|
+
allow(app).to receive(:call).and_return(app).ordered
|
125
|
+
expect(app).to receive(:on_complete).ordered
|
126
|
+
auth.call(env)
|
127
|
+
end
|
128
|
+
|
129
|
+
describe "on_complete handler" do
|
130
|
+
subject(:auth) { described_class.new(app, cache: cache) }
|
131
|
+
let(:cache) { instance_spy(described_class::RedisCache) }
|
132
|
+
let(:handler) {
|
133
|
+
handler = nil
|
134
|
+
allow(app).to receive(:call).and_return(app)
|
135
|
+
allow(app).to receive(:on_complete) { |&block| handler = block }
|
136
|
+
|
137
|
+
auth.call(env)
|
138
|
+
|
139
|
+
handler
|
140
|
+
}
|
141
|
+
|
142
|
+
it "removes value from cache when response is unsuccessful" do
|
143
|
+
expect(cache).to receive(:[]=).with("example.com", nil).exactly(2).times
|
144
|
+
handler.(status: 200)
|
145
|
+
handler.(status: 400)
|
146
|
+
handler.(status: 500)
|
147
|
+
end
|
148
|
+
|
149
|
+
it "retries the request after clearing the cache on a 403 response" do
|
150
|
+
expect(app).to receive(:call).with(env).ordered
|
151
|
+
expect(cache).to receive(:[]=).with("example.com", nil).ordered
|
152
|
+
expect(app).to receive(:call).with(env).ordered
|
153
|
+
handler.(status: 403)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
class FakeApp
|
158
|
+
attr_accessor :response
|
159
|
+
def initialize(response)
|
160
|
+
@response = response
|
161
|
+
end
|
162
|
+
def call(request)
|
163
|
+
@request = request
|
164
|
+
self
|
165
|
+
end
|
166
|
+
def on_complete
|
167
|
+
@request[:body] = "response"
|
168
|
+
yield(response)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "multiple 403 errors" do
|
173
|
+
subject(:auth) { described_class.new(app, cache: cache, token_factory: -> (_) { "value" }) }
|
174
|
+
let(:cache) { instance_spy(described_class::RedisCache) }
|
175
|
+
let(:app) { FakeApp.new(body: "response", status: 403) }
|
176
|
+
|
177
|
+
it "doesn't retry forever" do
|
178
|
+
auth.call(env)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "resets the body to original request body" do
|
182
|
+
expect(app).to receive(:call).with(hash_including(body: "request")).twice.and_call_original
|
183
|
+
auth.call(env.merge(body: "request"))
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
describe "#token_for" do
|
189
|
+
let(:cache) { instance_spy(described_class::RedisCache) }
|
190
|
+
subject(:auth) { described_class.new(:app, cache: cache) }
|
191
|
+
|
192
|
+
context "when cache is non-nil" do
|
193
|
+
before do
|
194
|
+
allow(cache).to receive(:[]).with("example.com").and_return("abc123")
|
195
|
+
end
|
196
|
+
|
197
|
+
it "returns value from cache" do
|
198
|
+
expect(auth.token_for("example.com")).to eq("abc123")
|
199
|
+
end
|
200
|
+
|
201
|
+
it "does not call @token_factory" do
|
202
|
+
expect(auth.token_factory).to_not receive(:call)
|
203
|
+
auth.token_for("example.com")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
context "when cache is empty" do
|
208
|
+
before do
|
209
|
+
allow(cache).to receive(:[]).and_return(nil)
|
210
|
+
allow(auth.token_factory)
|
211
|
+
.to receive(:call).with("example.com").and_return("abc123")
|
212
|
+
end
|
213
|
+
|
214
|
+
it "calls @token_factory with passed host" do
|
215
|
+
expect(auth.token_for("example.com")).to eq("abc123")
|
216
|
+
end
|
217
|
+
|
218
|
+
it "stores the value in the cache" do
|
219
|
+
expect(cache).to receive(:[]=).with("example.com", "abc123")
|
220
|
+
auth.token_for("example.com")
|
221
|
+
end
|
222
|
+
|
223
|
+
it "passes error up the chain when service unavailable" do
|
224
|
+
allow(auth.token_factory)
|
225
|
+
.to receive(:call)
|
226
|
+
.and_raise(WCC::Data::Mapper::ServiceUnavailable)
|
227
|
+
|
228
|
+
expect {
|
229
|
+
auth.call({
|
230
|
+
url: URI("http://example.com"),
|
231
|
+
request_headers: {},
|
232
|
+
})
|
233
|
+
}.to raise_error(WCC::Data::Mapper::ServiceUnavailable)
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WCC::Data::Mapper::Attributes do
|
4
|
+
let(:klass) {
|
5
|
+
Class.new do
|
6
|
+
include WCC::Data::Mapper::Attributes
|
7
|
+
end
|
8
|
+
}
|
9
|
+
|
10
|
+
describe "::attribute" do
|
11
|
+
subject { klass }
|
12
|
+
it_behaves_like :inheritable_class_attributes, :attributes
|
13
|
+
|
14
|
+
it "takes a name and optional options and stores them in ::attributes" do
|
15
|
+
subject.attribute :name, foo: :bar
|
16
|
+
expect(subject.attributes).to eq("name" => { foo: :bar })
|
17
|
+
end
|
18
|
+
|
19
|
+
it "freezes the options hash" do
|
20
|
+
subject.attribute :name
|
21
|
+
expect { subject.attributes["name"][:foo] = "foo" }.to raise_error(RuntimeError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "defines an instance method by the same name" do
|
25
|
+
expect(subject.new).to_not respond_to(:name)
|
26
|
+
subject.attribute :name
|
27
|
+
expect(subject.new).to respond_to(:name)
|
28
|
+
end
|
29
|
+
|
30
|
+
context "defined instance method" do
|
31
|
+
it "returns the value set on the instance for this attribute" do
|
32
|
+
subject.attribute :name
|
33
|
+
obj = subject.new("name" => "foo")
|
34
|
+
expect(obj.name).to eq("foo")
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "with writer option" do
|
39
|
+
it "defines a writer instance method" do
|
40
|
+
subject.attribute :name, writer: true
|
41
|
+
obj = subject.new("name" => "foo")
|
42
|
+
obj.name = "bar"
|
43
|
+
expect(obj.name).to eq("bar")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#initialize" do
|
49
|
+
subject { klass }
|
50
|
+
|
51
|
+
it "takes a hash of attributes and sets it to @attributes" do
|
52
|
+
obj = subject.new(foo: :bar)
|
53
|
+
expect(obj.attributes).to eq(foo: :bar)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#[]" do
|
58
|
+
before(:each) do
|
59
|
+
klass.attribute :name
|
60
|
+
end
|
61
|
+
subject { klass.new("name" => "foo", "other" => :value) }
|
62
|
+
|
63
|
+
it "returns raw values of defined attributes" do
|
64
|
+
expect(subject[:name]).to eq("foo")
|
65
|
+
expect(subject["name"]).to eq("foo")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns a key error for undefined attributes" do
|
69
|
+
expect { subject[:other] }.to raise_error(KeyError)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#[]=" do
|
74
|
+
before(:each) do
|
75
|
+
klass.attribute :name
|
76
|
+
end
|
77
|
+
subject { klass.new("name" => "foo", "other" => :value) }
|
78
|
+
|
79
|
+
it "sets raw values of defined attributes" do
|
80
|
+
subject[:name] = "value1"
|
81
|
+
expect(subject[:name]).to eq("value1")
|
82
|
+
end
|
83
|
+
|
84
|
+
it "allows string values as key" do
|
85
|
+
subject["name"] = "value1"
|
86
|
+
expect(subject[:name]).to eq("value1")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "returns a key error for undefined attributes" do
|
90
|
+
expect { subject[:other] = "value" }.to raise_error(KeyError)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WCC::Data::Mapper::JSONResponse do
|
4
|
+
let(:klass) {
|
5
|
+
Class.new do
|
6
|
+
extend WCC::Data::Mapper::JSONResponse
|
7
|
+
|
8
|
+
attr_accessor :val
|
9
|
+
def initialize(val=nil) @val = val end
|
10
|
+
end
|
11
|
+
}
|
12
|
+
|
13
|
+
describe "::new_from_response" do
|
14
|
+
let(:singular_response) {
|
15
|
+
double(json: { key: "value" }, status: 200)
|
16
|
+
}
|
17
|
+
let(:plural_response) {
|
18
|
+
double(json: [
|
19
|
+
{ name: "abc" },
|
20
|
+
{ name: "def" },
|
21
|
+
], status: 200)
|
22
|
+
}
|
23
|
+
|
24
|
+
it "returns a new instance from singular response" do
|
25
|
+
obj = klass.new_from_response(singular_response)
|
26
|
+
expect(obj).to be_a(klass)
|
27
|
+
expect(obj.val).to eq(singular_response.json)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns array of instances from array response" do
|
31
|
+
obj = klass.new_from_response(plural_response)
|
32
|
+
expect(obj).to be_a(Array)
|
33
|
+
expect(obj[0].val).to eq(plural_response.json[0])
|
34
|
+
expect(obj[1].val).to eq(plural_response.json[1])
|
35
|
+
end
|
36
|
+
|
37
|
+
it "raises RecordNotFound when response status is 404" do
|
38
|
+
allow(singular_response).to receive(:status).and_return(404)
|
39
|
+
expect { klass.new_from_response(singular_response) }
|
40
|
+
.to raise_error(WCC::Data::Mapper::RecordNotFound)
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
it "raises ServiceUnavailable when response status is 503" do
|
45
|
+
allow(singular_response).to receive(:status).and_return(503)
|
46
|
+
expect { klass.new_from_response(singular_response) }
|
47
|
+
.to raise_error(WCC::Data::Mapper::ServiceUnavailable)
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
it "raises InvalidResponse error for non-json objects" do
|
52
|
+
expect { klass.new_from_response(double(json: 1, status: 200)) }
|
53
|
+
.to raise_error(WCC::Data::Mapper::InvalidResponse)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WCC::Data::Mapper::RESTConfiguration do
|
4
|
+
let(:klass) {
|
5
|
+
Class.new.tap do |klass|
|
6
|
+
klass.extend described_class
|
7
|
+
end
|
8
|
+
}
|
9
|
+
subject { klass }
|
10
|
+
before(:each) do
|
11
|
+
WCC::Data.config.applications[:rest_configuration_test].uri = "http://test.app/"
|
12
|
+
subject.set_endpoint(:rest_configuration_test, "bar/")
|
13
|
+
end
|
14
|
+
after(:each) do
|
15
|
+
WCC::Data.config.applications.delete(:rest_configuration_test)
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "::set_endpoint" do
|
19
|
+
it_behaves_like :inheritable_class_attributes, :endpoint_config
|
20
|
+
|
21
|
+
it "sets @endpoint_config hash with values from args" do
|
22
|
+
expect(subject.endpoint_config).to eq(app: :rest_configuration_test, uri: "bar/")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "::endpoint" do
|
27
|
+
it "returns instance of RESTEndpoint" do
|
28
|
+
expect(subject.endpoint).to be_a(WCC::Data::RESTEndpoint)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "sets service using appname and provided URI string" do
|
32
|
+
expect(subject.endpoint.service.uri).to eq(URI("http://test.app/bar/"))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "lazy configuration" do
|
37
|
+
it "sets endpoint data only when endpoint is called" do
|
38
|
+
WCC::Data.config.applications[:rest_configuration_test].uri = "http://new.test.app/"
|
39
|
+
expect(subject.endpoint.service.uri).to eq(URI("http://new.test.app/bar/"))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WCC::Data::Mapper::RESTQuery do
|
4
|
+
let(:klass) {
|
5
|
+
Class.new.tap do |klass|
|
6
|
+
klass.extend described_class
|
7
|
+
end
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:endpoint) { double(:endpoint) }
|
11
|
+
before(:each) do
|
12
|
+
allow(klass).to receive(:endpoint) { endpoint }
|
13
|
+
end
|
14
|
+
|
15
|
+
shared_examples_for :handles_undefined_endpoint do |method, args|
|
16
|
+
let(:endpoint) { nil }
|
17
|
+
it "raises an EndpointUndefined exception" do
|
18
|
+
expect { klass.send(method, *args) }.to raise_error(WCC::Data::Mapper::EndpointUndefined)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "::find" do
|
23
|
+
it_behaves_like :handles_undefined_endpoint, :find, :id_param
|
24
|
+
|
25
|
+
it "calls show(id) on defined endpoint and builds on response" do
|
26
|
+
expect(endpoint).to receive(:show).with(:id_param).and_return(:response)
|
27
|
+
expect(klass).to receive(:new_from_response).with(:response).and_return(:new_response)
|
28
|
+
expect(klass.find(:id_param)).to eq(:new_response)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "::list" do
|
33
|
+
it_behaves_like :handles_undefined_endpoint, :list, :params
|
34
|
+
|
35
|
+
it "calls index(params) on defined endpoint and builds on response" do
|
36
|
+
expect(endpoint).to receive(:index).with(params: :params).and_return(:response)
|
37
|
+
expect(klass).to receive(:new_from_response).with(:response).and_return(:new_response)
|
38
|
+
expect(klass.list(:params)).to eq(:new_response)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "allows calling with no params" do
|
42
|
+
expect(endpoint).to receive(:index).with(params: {})
|
43
|
+
expect(klass).to receive(:new_from_response)
|
44
|
+
klass.list
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WCC::Data::Model do
|
4
|
+
|
5
|
+
describe "inherited behavior" do
|
6
|
+
subject { described_class }
|
7
|
+
|
8
|
+
includes = [
|
9
|
+
WCC::Data::Mapper::Attributes,
|
10
|
+
]
|
11
|
+
|
12
|
+
extensions = [
|
13
|
+
WCC::Data::Mapper::JSONResponse,
|
14
|
+
WCC::Data::Mapper::RESTConfiguration,
|
15
|
+
WCC::Data::Mapper::RESTQuery,
|
16
|
+
]
|
17
|
+
|
18
|
+
includes.each do |mod|
|
19
|
+
it "includes #{mod}" do
|
20
|
+
expect(subject.ancestors).to include(mod)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
extensions.each do |mod|
|
25
|
+
it "extends #{mod}" do
|
26
|
+
extended_modules = (class << subject; self; end).included_modules
|
27
|
+
expect(extended_modules).to include(mod)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WCC::Data::Nucleus::Campus do
|
4
|
+
it 'inherits EnumeratedType functionality' do
|
5
|
+
expect(described_class.ancestors).to include(WCC::Data::EnumeratedType)
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'with defined data' do
|
9
|
+
it 'defines Dallas campus' do
|
10
|
+
campus = described_class[:dallas]
|
11
|
+
expect(campus).to be_a(described_class)
|
12
|
+
expect(campus.name).to eq('Dallas')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'defines Fort Worth campus' do
|
16
|
+
campus = described_class[:ft_worth]
|
17
|
+
expect(campus).to be_a(described_class)
|
18
|
+
expect(campus.name).to eq('Fort Worth')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'defines Plano campus' do
|
22
|
+
campus = described_class[:plano]
|
23
|
+
expect(campus).to be_a(described_class)
|
24
|
+
expect(campus.name).to eq('Plano')
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'defines Frisco campus' do
|
28
|
+
campus = described_class[:frisco]
|
29
|
+
expect(campus).to be_a(described_class)
|
30
|
+
expect(campus.name).to eq('Frisco')
|
31
|
+
expect(campus.code).to eq('FRS')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|