itrp-client 1.1.3 → 1.1.4
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 +4 -4
- data/Gemfile.lock +15 -13
- data/README.md +7 -0
- data/lib/itrp/client.rb +3 -1
- data/lib/itrp/client/version.rb +1 -1
- data/spec/lib/itrp/attachments_spec.rb +178 -0
- data/spec/lib/itrp/certificate_spec.rb +28 -0
- data/spec/lib/itrp/client_spec.rb +550 -0
- data/spec/lib/itrp/response_spec.rb +271 -0
- data/spec/lib/itrp_spec.rb +33 -0
- data/spec/spec_helper.rb +48 -0
- data/spec/support/fixtures/people.csv +3 -0
- data/spec/support/fixtures/upload.txt +1 -0
- data/spec/support/matchers/never_raise.rb +46 -0
- data/spec/support/util.rb +3 -0
- metadata +24 -5
@@ -0,0 +1,271 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itrp::Response do
|
4
|
+
before(:each) do
|
5
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
6
|
+
@person_hash = {
|
7
|
+
addresses:[],
|
8
|
+
contacts:[ {id: 1365, label: 'work', telephone: '7139872946'} ],
|
9
|
+
id: 562,
|
10
|
+
information: 'Info about John.',
|
11
|
+
job_title: 'rolling stone',
|
12
|
+
locale: 'en-US',
|
13
|
+
location: 'Top of John Hill',
|
14
|
+
name: 'John',
|
15
|
+
organization: {id: 20, name: 'ITRP Institute'},
|
16
|
+
picture_uri: nil,
|
17
|
+
primary_email: 'john@example.com',
|
18
|
+
site: {id:14, name: 'IT Training Facility'},
|
19
|
+
time_format_24h: false,
|
20
|
+
time_zone: 'Central Time (US & Canada)'
|
21
|
+
}
|
22
|
+
stub_request(:get, 'https://api.itrp.com/v1/me').with(basic_auth: ['secret', 'x']).to_return(body: @person_hash.to_json)
|
23
|
+
@response_hash = @client.get('me')
|
24
|
+
|
25
|
+
@client = Itrp::Client.new(api_token: 'secret', max_retry_time: -1)
|
26
|
+
@people_array = [
|
27
|
+
{id: 562, name: 'John', organization: { id: 20, name: 'ITRP Institute'}, site: {id: 14, name: 'IT Training Facility'} },
|
28
|
+
{id: 560, name: 'Lucas', organization: { id: 20, name: 'ITRP Institute', office: { name: 'The Office'}}, site: {id: 14, name: 'IT Training Facility'} },
|
29
|
+
{id: 561, name: 'Sheryl', organization: { id: 20, name: 'ITRP Institute'}, site: {id: 14, name: 'IT Training Facility'} }
|
30
|
+
]
|
31
|
+
stub_request(:get, 'https://api.itrp.com/v1/people').to_return(body: @people_array.to_json).with(basic_auth: ['secret', 'x'])
|
32
|
+
@response_array = @client.get('people')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should contain the request' do
|
36
|
+
expect(@response_hash.request.class.name).to eq('Net::HTTP::Get')
|
37
|
+
expect(@response_hash.request.path).to eq('/v1/me')
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should contain the full request' do
|
41
|
+
expect(@response_hash.response.class.name).to eq('Net::HTTPOK')
|
42
|
+
expect(@response_hash.response).to respond_to(:body)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'should provide easy access to the body' do
|
46
|
+
expect(@response_hash.body).to include(%("primary_email":"john@example.com"))
|
47
|
+
end
|
48
|
+
|
49
|
+
context 'json/message' do
|
50
|
+
it 'should provide the JSON value for single records' do
|
51
|
+
be_json_eql(@response_hash.json, @person_hash)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should provide the JSON value for lists' do
|
55
|
+
be_json_eql(@response_array.json, @people_array)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should provide indifferent access for single records' do
|
59
|
+
expect(@response_hash.json['organization']['name']).to eq('ITRP Institute')
|
60
|
+
expect(@response_hash.json[:organization][:name]).to eq('ITRP Institute')
|
61
|
+
expect(@response_hash.json[:organization]['name']).to eq('ITRP Institute')
|
62
|
+
expect(@response_hash.json['organization'][:name]).to eq('ITRP Institute')
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should provide indifferent access for lists' do
|
66
|
+
expect(@response_array.json.first['site']['name']).to eq('IT Training Facility')
|
67
|
+
expect(@response_array.json.first[:site][:name]).to eq('IT Training Facility')
|
68
|
+
expect(@response_array.json.last[:site]['name']).to eq('IT Training Facility')
|
69
|
+
expect(@response_array.json.last['site'][:name]).to eq('IT Training Facility')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should add a message if the body is empty' do
|
73
|
+
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: nil)
|
74
|
+
response = @client.get('organizations')
|
75
|
+
|
76
|
+
message = '429: empty body'
|
77
|
+
expect(response.json[:message]).to eq(message)
|
78
|
+
expect(response.json['message']).to eq(message)
|
79
|
+
expect(response.message).to eq(message)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should add a message if the HTTP response is not OK' do
|
83
|
+
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: {message: 'Too Many Requests'}.to_json)
|
84
|
+
response = @client.get('organizations')
|
85
|
+
|
86
|
+
message = '429: Too Many Requests'
|
87
|
+
expect(response.json[:message]).to eq(message)
|
88
|
+
expect(response.json['message']).to eq(message)
|
89
|
+
expect(response.message).to eq(message)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'should add a message if the JSON body cannot be parsed' do
|
93
|
+
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(body: '==$$!invalid')
|
94
|
+
response = @client.get('organizations')
|
95
|
+
|
96
|
+
message = "Invalid JSON - 757: unexpected token at '==$$!invalid' for:\n#{response.body}"
|
97
|
+
expect(response.json[:message]).to eq(message)
|
98
|
+
expect(response.json['message']).to eq(message)
|
99
|
+
expect(response.message).to eq(message)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should have a blank message when single record is succesfully retrieved' do
|
103
|
+
expect(@response_hash.message).to be_nil
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should have a blank message when single record is succesfully retrieved' do
|
107
|
+
expect(@response_array.message).to be_nil
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should define empty' do
|
113
|
+
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: nil)
|
114
|
+
response = @client.get('organizations')
|
115
|
+
|
116
|
+
expect(response.empty?).to be_truthy
|
117
|
+
expect(@person_hash.empty?).to be_falsey
|
118
|
+
expect(@people_array.empty?).to be_falsey
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'valid' do
|
122
|
+
it 'should be valid when the message is nil' do
|
123
|
+
expect(@response_hash).to receive(:message){ nil }
|
124
|
+
expect(@response_hash.valid?).to be_truthy
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should not be valid when the message is not nil' do
|
128
|
+
expect(@response_array).to receive(:message){ 'invalid' }
|
129
|
+
expect(@response_array.valid?).to be_falsey
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context '[] access' do
|
134
|
+
context 'single records' do
|
135
|
+
it 'should delegate [] to the json' do
|
136
|
+
expect(@response_hash[:name]).to eq('John')
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'should allow multiple keys' do
|
140
|
+
expect(@response_hash[:organization, 'name']).to eq('ITRP Institute')
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should allow nils when using multiple keys' do
|
144
|
+
expect(@response_hash[:organization, :missing, 'name']).to be_nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'list of records' do
|
149
|
+
it 'should delegate [] to the json of each record' do
|
150
|
+
expect(@response_array['name']).to eq(['John', 'Lucas', 'Sheryl'])
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should allow multiple keys' do
|
154
|
+
expect(@response_array[:organization, 'name']).to eq(['ITRP Institute', 'ITRP Institute', 'ITRP Institute'])
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should allow nils when using multiple keys' do
|
158
|
+
expect(@response_array[:organization, :office, 'name']).to eq([nil, 'The Office', nil])
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'size' do
|
164
|
+
it 'should return 1 for single records' do
|
165
|
+
expect(@response_hash.size).to eq(1)
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should return the array size for list records' do
|
169
|
+
expect(@response_array.size).to eq(3)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should return nil if an error message is present' do
|
173
|
+
expect(@response_hash).to receive(:message){ 'error message' }
|
174
|
+
expect(@response_hash.size).to eq(0)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'count' do
|
179
|
+
it 'should return 1 for single records' do
|
180
|
+
expect(@response_hash.count).to eq(1)
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'should return the array size for list records' do
|
184
|
+
expect(@response_array.count).to eq(3)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should return nil if an error message is present' do
|
188
|
+
expect(@response_hash).to receive(:message){ 'error message' }
|
189
|
+
expect(@response_hash.count).to eq(0)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'pagination' do
|
194
|
+
before(:each) do
|
195
|
+
@pagination_header = {
|
196
|
+
'X-Pagination-Per-Page' => 3,
|
197
|
+
'X-Pagination-Current-Page' => 1,
|
198
|
+
'X-Pagination-Total-Pages' => 2,
|
199
|
+
'X-Pagination-Total-Entries' => 5,
|
200
|
+
'Link' => '<https://api.itrp.com/v1/people?page=1&per_page=3>; rel="first",<https://api.itrp.com/v1/people?page=2&per_page=3>; rel="next", <https://api.itrp.com/v1/people?page=2&per_page=3>; rel="last"',
|
201
|
+
}
|
202
|
+
allow(@response_array.response).to receive('header'){ @pagination_header }
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should retrieve per_page from the 'X-Pagination-Per-Page' header" do
|
206
|
+
expect(@response_array.per_page).to eq(3)
|
207
|
+
end
|
208
|
+
|
209
|
+
it "should retrieve current_page from the 'X-Pagination-Current-Page' header" do
|
210
|
+
expect(@response_array.current_page).to eq(1)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "should retrieve total_pages from the 'X-Pagination-Total-Pages' header" do
|
214
|
+
expect(@response_array.total_pages).to eq(2)
|
215
|
+
end
|
216
|
+
|
217
|
+
it "should retrieve total_entries from the 'X-Pagination-Total-Entries' header" do
|
218
|
+
expect(@response_array.total_entries).to eq(5)
|
219
|
+
end
|
220
|
+
|
221
|
+
{first: 'https://api.itrp.com/v1/people?page=1&per_page=3',
|
222
|
+
next: 'https://api.itrp.com/v1/people?page=2&per_page=3',
|
223
|
+
last: 'https://api.itrp.com/v1/people?page=2&per_page=3'}.each do |relation, link|
|
224
|
+
|
225
|
+
it "should define pagination link for :#{relation}" do
|
226
|
+
expect(@response_array.pagination_link(relation)).to eq(link)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
{first: '/v1/people?page=1&per_page=3',
|
231
|
+
next: '/v1/people?page=2&per_page=3',
|
232
|
+
last: '/v1/people?page=2&per_page=3'}.each do |relation, link|
|
233
|
+
|
234
|
+
it "should define pagination relative link for :#{relation}" do
|
235
|
+
expect(@response_array.pagination_relative_link(relation)).to eq(link)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context 'throttled?' do
|
241
|
+
it 'should not be trhottled by default' do
|
242
|
+
expect(@response_hash.throttled?).to be_falsey
|
243
|
+
expect(@response_array.throttled?).to be_falsey
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should check the return code' do
|
247
|
+
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: nil)
|
248
|
+
response = @client.get('organizations')
|
249
|
+
expect(response.throttled?).to be_truthy
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'should check the return message' do
|
253
|
+
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(status: 500, body: {message: 'Too Many Requests'}.to_json )
|
254
|
+
response = @client.get('organizations')
|
255
|
+
expect(response.throttled?).to be_truthy
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context 'to_s' do
|
260
|
+
it 'should return the JSON as a string' do
|
261
|
+
expect(@response_hash.to_s).to eq(JSON.parse(@person_hash.to_json).to_s)
|
262
|
+
end
|
263
|
+
|
264
|
+
it 'should return the message in case the response is not valid' do
|
265
|
+
stub_request(:get, 'https://api.itrp.com/v1/organizations').with(basic_auth: ['secret', 'x']).to_return(status: 429, body: nil)
|
266
|
+
response = @client.get('organizations')
|
267
|
+
expect(response.to_s).to eq('429: empty body')
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Itrp do
|
4
|
+
it "should define a default configuration" do
|
5
|
+
conf = Itrp.configuration.current
|
6
|
+
|
7
|
+
expect(conf.keys.sort).to eq([:account, :api_token, :api_version, :block_at_rate_limit, :ca_file, :host, :logger, :max_retry_time, :proxy_host, :proxy_password, :proxy_port, :proxy_user, :read_timeout, :source])
|
8
|
+
|
9
|
+
expect(conf[:logger].class).to eq(::Logger)
|
10
|
+
expect(conf[:host]).to eq('https://api.itrp.com')
|
11
|
+
expect(conf[:api_version]).to eq('v1')
|
12
|
+
|
13
|
+
expect(conf[:max_retry_time]).to eq(5400)
|
14
|
+
expect(conf[:read_timeout]).to eq(25)
|
15
|
+
expect(conf[:block_at_rate_limit]).to be_falsey
|
16
|
+
|
17
|
+
expect(conf[:proxy_port]).to eq(8080)
|
18
|
+
|
19
|
+
[:api_token, :account, :source, :proxy_host, :proxy_user, :proxy_password].each do |no_default|
|
20
|
+
expect(conf[no_default]).to be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
expect(conf[:ca_file]).to eq('../ca-bundle.crt')
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should define a logger" do
|
27
|
+
expect(Itrp.logger.class).to eq(::Logger)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should define an exception class" do
|
31
|
+
expect { raise ::Itrp::Exception.new('test') }.to raise_error('test')
|
32
|
+
end
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
dir = File.dirname(__FILE__)
|
3
|
+
$LOAD_PATH.unshift dir + '/../lib'
|
4
|
+
$LOAD_PATH.unshift dir
|
5
|
+
|
6
|
+
STDERR.puts("Running specs using ruby version #{RUBY_VERSION}")
|
7
|
+
|
8
|
+
require 'simplecov'
|
9
|
+
SimpleCov.start
|
10
|
+
|
11
|
+
require 'rspec'
|
12
|
+
require 'webmock/rspec'
|
13
|
+
|
14
|
+
# Patch for https://github.com/bblimke/webmock/issues/623
|
15
|
+
module WebMock
|
16
|
+
class BodyPattern
|
17
|
+
def assert_non_multipart_body(content_type)
|
18
|
+
# if content_type =~ %r{^multipart/form-data}
|
19
|
+
# raise ArgumentError.new("WebMock does not support matching body for multipart/form-data requests yet :(")
|
20
|
+
# end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'itrp/client'
|
26
|
+
|
27
|
+
# Requires supporting ruby files with custom matchers and macros, etc,
|
28
|
+
# in spec/support/ and its subdirectories.
|
29
|
+
Dir["#{dir}/support/**/*.rb"].each { |f| require f }
|
30
|
+
|
31
|
+
RSpec.configure do |config|
|
32
|
+
config.before(:each) do
|
33
|
+
log_dir = File.dirname(__FILE__) + '/log'
|
34
|
+
Dir.mkdir(log_dir) unless File.exists?(log_dir)
|
35
|
+
Itrp.configuration.logger = Logger.new("#{log_dir}/test.log")
|
36
|
+
@spec_dir = dir
|
37
|
+
@fixture_dir = "#{dir}/support/fixtures"
|
38
|
+
end
|
39
|
+
config.after(:each) { Itrp.configuration.reset }
|
40
|
+
|
41
|
+
# Run specs in random order to surface order dependencies. If you find an
|
42
|
+
# order dependency and want to debug it, you can fix the order by providing
|
43
|
+
# the seed, which is printed after each run.
|
44
|
+
# --seed 1234
|
45
|
+
config.order = "random"
|
46
|
+
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
content
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# This matcher will return:
|
2
|
+
# 1. TRUE if the code was run without exceptions
|
3
|
+
# 2. FALSE if the code was run but raised (only) the specified exception
|
4
|
+
#
|
5
|
+
# It *will* raise an exception if the block of code raises an exception other than
|
6
|
+
# (the exception specified)
|
7
|
+
#
|
8
|
+
# To use it
|
9
|
+
#
|
10
|
+
# expect {
|
11
|
+
# code
|
12
|
+
# }.to never_raise(MySpecificException)
|
13
|
+
#
|
14
|
+
RSpec::Matchers.define :never_raise do |exception_class|
|
15
|
+
global_result = nil
|
16
|
+
|
17
|
+
def supports_block_expectations?
|
18
|
+
true # or some logic
|
19
|
+
end
|
20
|
+
|
21
|
+
match do |block|
|
22
|
+
begin
|
23
|
+
block.call
|
24
|
+
rescue exception_class => e
|
25
|
+
global_result = "expected #{block.source_location[0]}:#{block.source_location[1]} to never raise #{exception_class.name}, but did: #{e.message}"
|
26
|
+
false # we did NOT never raise this exception
|
27
|
+
|
28
|
+
rescue RSpec::Expectations::ExpectationNotMetError => exception
|
29
|
+
global_result = "expectation failed inside block at #{block.source_location[0]}:#{block.source_location[1]}: #{exception}"
|
30
|
+
# give us a pretty error message in addition to the error message from the exception
|
31
|
+
raise exception
|
32
|
+
|
33
|
+
rescue
|
34
|
+
# handle other exceptions by reraising them. They are exceptional!!!
|
35
|
+
# (also, no pretty error messages here)
|
36
|
+
raise
|
37
|
+
|
38
|
+
else
|
39
|
+
true # everything ran, nothing raised at all, thus code did in fact not raise anything
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
failure_message do |player|
|
44
|
+
global_result
|
45
|
+
end
|
46
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: itrp-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ITRP
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: gem_config
|
@@ -140,6 +140,16 @@ files:
|
|
140
140
|
- lib/itrp/client/multipart.rb
|
141
141
|
- lib/itrp/client/response.rb
|
142
142
|
- lib/itrp/client/version.rb
|
143
|
+
- spec/lib/itrp/attachments_spec.rb
|
144
|
+
- spec/lib/itrp/certificate_spec.rb
|
145
|
+
- spec/lib/itrp/client_spec.rb
|
146
|
+
- spec/lib/itrp/response_spec.rb
|
147
|
+
- spec/lib/itrp_spec.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
- spec/support/fixtures/people.csv
|
150
|
+
- spec/support/fixtures/upload.txt
|
151
|
+
- spec/support/matchers/never_raise.rb
|
152
|
+
- spec/support/util.rb
|
143
153
|
homepage: http://github.com/itrp/itrp-client
|
144
154
|
licenses:
|
145
155
|
- MIT
|
@@ -161,9 +171,18 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
161
171
|
version: '0'
|
162
172
|
requirements: []
|
163
173
|
rubyforge_project:
|
164
|
-
rubygems_version: 2.
|
174
|
+
rubygems_version: 2.4.6
|
165
175
|
signing_key:
|
166
176
|
specification_version: 4
|
167
177
|
summary: Client for accessing the ITRP REST API
|
168
|
-
test_files:
|
169
|
-
|
178
|
+
test_files:
|
179
|
+
- spec/lib/itrp/attachments_spec.rb
|
180
|
+
- spec/lib/itrp/certificate_spec.rb
|
181
|
+
- spec/lib/itrp/client_spec.rb
|
182
|
+
- spec/lib/itrp/response_spec.rb
|
183
|
+
- spec/lib/itrp_spec.rb
|
184
|
+
- spec/spec_helper.rb
|
185
|
+
- spec/support/fixtures/people.csv
|
186
|
+
- spec/support/fixtures/upload.txt
|
187
|
+
- spec/support/matchers/never_raise.rb
|
188
|
+
- spec/support/util.rb
|