quiz_api_client 4.2.0 → 4.2.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.
- checksums.yaml +4 -4
- data/lib/quiz_api_client/services/items_service.rb +11 -0
- data/lib/quiz_api_client/version.rb +1 -1
- data/spec/config_spec.rb +66 -0
- data/spec/contracts/interaction_types_service_spec.rb +22 -0
- data/spec/contracts/item_analyses_service_spec.rb +59 -0
- data/spec/contracts/items_service_spec.rb +59 -0
- data/spec/contracts/qti_imports_service_spec.rb +34 -0
- data/spec/contracts/quiz_clone_job_service_spec.rb +20 -0
- data/spec/contracts/quiz_clone_jobs_service_spec.rb +21 -0
- data/spec/contracts/quiz_entries_service_spec.rb +125 -0
- data/spec/contracts/quiz_service_spec.rb +68 -0
- data/spec/contracts/quiz_session_events_service_spec.rb +30 -0
- data/spec/contracts/quiz_session_result_service_spec.rb +42 -0
- data/spec/contracts/quiz_session_service_spec.rb +56 -0
- data/spec/contracts/quiz_sessions_service_spec.rb +28 -0
- data/spec/contracts/quizzes_service_spec.rb +80 -0
- data/spec/contracts/session_item_results_service_spec.rb +60 -0
- data/spec/contracts/session_items_service_spec.rb +21 -0
- data/spec/contracts/shared_banks_spec.rb +366 -0
- data/spec/contracts/shared_examples/http_delete_example.rb +56 -0
- data/spec/contracts/shared_examples/http_get_example.rb +139 -0
- data/spec/contracts/shared_examples/http_patch_example.rb +60 -0
- data/spec/contracts/shared_examples/http_post_example.rb +60 -0
- data/spec/contracts/shared_examples/http_put_example.rb +60 -0
- data/spec/http_client_spec.rb +347 -0
- data/spec/json_formatter_spec.rb +32 -0
- data/spec/quiz_api_client/http_request/failure_spec.rb +100 -0
- data/spec/quiz_api_client/http_request/metrics_spec.rb +75 -0
- data/spec/quiz_api_client_spec.rb +117 -0
- data/spec/services/base_api_service_spec.rb +50 -0
- data/spec/services/interaction_types_service_spec.rb +25 -0
- data/spec/services/item_analyses_service_spec.rb +76 -0
- data/spec/services/items_service_spec.rb +56 -0
- data/spec/services/jwt_service_spec.rb +66 -0
- data/spec/services/qti_imports_service_spec.rb +114 -0
- data/spec/services/quiz_analyses_service_spec.rb +44 -0
- data/spec/services/quiz_clone_job_service_spec.rb +41 -0
- data/spec/services/quiz_clone_jobs_service_spec.rb +41 -0
- data/spec/services/quiz_entries_service_spec.rb +71 -0
- data/spec/services/quiz_service_spec.rb +49 -0
- data/spec/services/quiz_session_events_service_spec.rb +42 -0
- data/spec/services/quiz_session_result_service_spec.rb +26 -0
- data/spec/services/quiz_session_service_spec.rb +49 -0
- data/spec/services/quiz_sessions_service_spec.rb +42 -0
- data/spec/services/quizzes_service_spec.rb +71 -0
- data/spec/services/session_item_results_service_spec.rb +33 -0
- data/spec/services/session_items_service_spec.rb +26 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/support/pact_config.rb +64 -0
- data/spec/support/pact_helper.rb +19 -0
- metadata +108 -39
- data/.dockerignore +0 -7
- data/.editorconfig +0 -16
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.rubocop.yml +0 -72
- data/CHANGELOG.md +0 -35
- data/Dockerfile +0 -12
- data/Gemfile +0 -5
- data/Jenkinsfile +0 -86
- data/bin/console +0 -7
- data/bin/contracts-generate +0 -26
- data/bin/setup +0 -65
- data/docker-compose.dev.override.yml +0 -11
- data/docker-compose.yml +0 -10
- data/quiz_api_client.gemspec +0 -60
@@ -0,0 +1,139 @@
|
|
1
|
+
shared_context 'http get' do
|
2
|
+
let(:quizzes_api_path) { raise 'Override in spec' }
|
3
|
+
let(:consumer_key) { 'consumer key' }
|
4
|
+
let(:consumer_request_id) { 'consumer request id' }
|
5
|
+
let(:host) { 'localhost:1234' }
|
6
|
+
let(:shared_secret) { 'secret' }
|
7
|
+
let(:scope) { raise 'Override in spec' }
|
8
|
+
let(:resource_id) { nil }
|
9
|
+
let(:response_headers) { { 'Content-Type' => 'application/json; charset=utf-8' } }
|
10
|
+
let(:response_body) { raise 'Override in spec' }
|
11
|
+
let(:service_name) { raise 'Override in spec' }
|
12
|
+
let(:status) { 200 }
|
13
|
+
let(:provider_state) { raise 'Override in spec' }
|
14
|
+
let(:user) { nil }
|
15
|
+
let(:params) { raise 'Override in spec' }
|
16
|
+
let(:query_params) { {} }
|
17
|
+
let(:request_description) { raise 'Override in spec (must be unique!)' }
|
18
|
+
|
19
|
+
let(:client) do
|
20
|
+
QuizApiClient::Client.new(
|
21
|
+
consumer_key: consumer_key,
|
22
|
+
consumer_request_id: consumer_request_id,
|
23
|
+
host: host,
|
24
|
+
shared_secret: shared_secret,
|
25
|
+
protocol: 'http'
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
shared_examples 'a http get request to quiz_api' do
|
31
|
+
include_context 'http get'
|
32
|
+
|
33
|
+
context 'retrieving a single resource' do
|
34
|
+
let(:token) do
|
35
|
+
client.jwt_service.grant_permission(
|
36
|
+
exp: token_expiration_one_year,
|
37
|
+
scope: scope,
|
38
|
+
uuid: user,
|
39
|
+
resource_id: resource_id
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
before do
|
44
|
+
quiz_api
|
45
|
+
.given(provider_state)
|
46
|
+
.upon_receiving(request_description)
|
47
|
+
.with(
|
48
|
+
method: :get,
|
49
|
+
path: quizzes_api_path,
|
50
|
+
headers: headers(token),
|
51
|
+
query: query_params
|
52
|
+
)
|
53
|
+
.will_respond_with(
|
54
|
+
status: status,
|
55
|
+
headers: response_headers,
|
56
|
+
body: response_body
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'verifies the request is valid' do
|
61
|
+
result = client.send(service_name).show(token: token, params: params)
|
62
|
+
expect(result).to be_truthy
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
shared_examples 'a http get request to quiz_api collection endpoint' do
|
68
|
+
include_context 'http get'
|
69
|
+
|
70
|
+
context 'retrieving a list of resources' do
|
71
|
+
let(:token) do
|
72
|
+
client.jwt_service.grant_permission(
|
73
|
+
exp: token_expiration_one_year,
|
74
|
+
scope: scope,
|
75
|
+
uuid: user,
|
76
|
+
resource_id: resource_id
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
before do
|
81
|
+
quiz_api
|
82
|
+
.given(provider_state)
|
83
|
+
.upon_receiving(request_description)
|
84
|
+
.with(
|
85
|
+
method: :get,
|
86
|
+
path: quizzes_api_path,
|
87
|
+
headers: headers(token),
|
88
|
+
query: query_params
|
89
|
+
)
|
90
|
+
.will_respond_with(
|
91
|
+
status: status,
|
92
|
+
headers: response_headers,
|
93
|
+
body: response_body
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'verifies the request is valid' do
|
98
|
+
result = client.send(service_name).list(token: token, params: params)
|
99
|
+
expect(result).to be_truthy
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
shared_examples 'a http get only request to quiz_api' do
|
105
|
+
include_context 'http get'
|
106
|
+
|
107
|
+
context 'retrieving a single resource' do
|
108
|
+
let(:token) do
|
109
|
+
client.jwt_service.grant_permission(
|
110
|
+
exp: token_expiration_one_year,
|
111
|
+
scope: scope,
|
112
|
+
uuid: user,
|
113
|
+
resource_id: resource_id
|
114
|
+
)
|
115
|
+
end
|
116
|
+
|
117
|
+
before do
|
118
|
+
quiz_api
|
119
|
+
.given(provider_state)
|
120
|
+
.upon_receiving(request_description)
|
121
|
+
.with(
|
122
|
+
method: :get,
|
123
|
+
path: quizzes_api_path,
|
124
|
+
headers: headers(token),
|
125
|
+
query: query_params
|
126
|
+
)
|
127
|
+
.will_respond_with(
|
128
|
+
status: status,
|
129
|
+
headers: response_headers,
|
130
|
+
body: response_body
|
131
|
+
)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'verifies the request is valid' do
|
135
|
+
result = client.send(service_name).get(token: token, params: params)
|
136
|
+
expect(result).to be_truthy
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
shared_examples 'a http patch request to quiz_api' do
|
2
|
+
let(:quizzes_api_path) { raise 'Override in spec' }
|
3
|
+
let(:consumer_key) { 'consumer key' }
|
4
|
+
let(:consumer_request_id) { 'consumer request id' }
|
5
|
+
let(:host) { 'localhost:1234' }
|
6
|
+
let(:shared_secret) { 'secret' }
|
7
|
+
let(:scope) { raise 'Override in spec' }
|
8
|
+
let(:user) { nil }
|
9
|
+
let(:resource_id) { nil }
|
10
|
+
let(:response_body) { raise 'Override in spec' }
|
11
|
+
let(:service_name) { raise 'Override in spec' }
|
12
|
+
let(:status) { 200 }
|
13
|
+
let(:provider_state) { raise 'Override in spec' }
|
14
|
+
let(:params) { raise 'Override in spec' }
|
15
|
+
let(:body) { raise 'Override in spec' }
|
16
|
+
let(:request_description) { raise 'Override in spec (must be unique!)' }
|
17
|
+
|
18
|
+
let(:client) do
|
19
|
+
QuizApiClient::Client.new(
|
20
|
+
consumer_key: consumer_key,
|
21
|
+
consumer_request_id: consumer_request_id,
|
22
|
+
host: host,
|
23
|
+
shared_secret: shared_secret,
|
24
|
+
protocol: 'http'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'updating a resource' do
|
29
|
+
let(:token) do
|
30
|
+
client.jwt_service.grant_permission(
|
31
|
+
exp: token_expiration_one_year,
|
32
|
+
scope: scope,
|
33
|
+
uuid: user,
|
34
|
+
resource_id: resource_id
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
quiz_api
|
40
|
+
.given(provider_state)
|
41
|
+
.upon_receiving(request_description)
|
42
|
+
.with(
|
43
|
+
method: :patch,
|
44
|
+
path: quizzes_api_path,
|
45
|
+
headers: headers(token),
|
46
|
+
body: body
|
47
|
+
)
|
48
|
+
.will_respond_with(
|
49
|
+
status: status,
|
50
|
+
headers: { 'Content-Type' => 'application/json; charset=utf-8' },
|
51
|
+
body: response_body
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'verifies the request is valid' do
|
56
|
+
result = client.send(service_name).update(token: token, params: params)
|
57
|
+
expect(result).to be_truthy
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
shared_examples 'a http post request to quiz_api' do
|
2
|
+
let(:quizzes_api_path) { raise 'Override in spec' }
|
3
|
+
let(:consumer_key) { 'consumer key' }
|
4
|
+
let(:consumer_request_id) { 'consumer request id' }
|
5
|
+
let(:host) { 'localhost:1234' }
|
6
|
+
let(:shared_secret) { 'secret' }
|
7
|
+
let(:scope) { raise 'Override in spec' }
|
8
|
+
let(:user) { nil }
|
9
|
+
let(:resource_id) { nil }
|
10
|
+
let(:response_body) { raise 'Override in spec' }
|
11
|
+
let(:service_name) { raise 'Override in spec' }
|
12
|
+
let(:status) { 201 }
|
13
|
+
let(:provider_state) { raise 'Override in spec' }
|
14
|
+
let(:params) { raise 'Override in spec' }
|
15
|
+
let(:body) { raise 'Override in spec' }
|
16
|
+
let(:request_description) { raise 'Override in spec (must be unique!)' }
|
17
|
+
|
18
|
+
let(:client) do
|
19
|
+
QuizApiClient::Client.new(
|
20
|
+
consumer_key: consumer_key,
|
21
|
+
consumer_request_id: consumer_request_id,
|
22
|
+
host: host,
|
23
|
+
shared_secret: shared_secret,
|
24
|
+
protocol: 'http'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'creating a new resource' do
|
29
|
+
let(:token) do
|
30
|
+
client.jwt_service.grant_permission(
|
31
|
+
exp: token_expiration_one_year,
|
32
|
+
scope: scope,
|
33
|
+
uuid: user,
|
34
|
+
resource_id: resource_id
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
quiz_api
|
40
|
+
.given(provider_state)
|
41
|
+
.upon_receiving(request_description)
|
42
|
+
.with(
|
43
|
+
method: :post,
|
44
|
+
path: quizzes_api_path,
|
45
|
+
headers: headers(token),
|
46
|
+
body: body
|
47
|
+
)
|
48
|
+
.will_respond_with(
|
49
|
+
status: status,
|
50
|
+
headers: { 'Content-Type' => 'application/json; charset=utf-8' },
|
51
|
+
body: response_body
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'verifies the request is valid' do
|
56
|
+
result = client.send(service_name).create(token: token, params: params)
|
57
|
+
expect(result).to be_truthy
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
shared_examples 'a http put request to quiz_api' do
|
2
|
+
let(:quizzes_api_path) { raise 'Override in spec' }
|
3
|
+
let(:consumer_key) { 'consumer key' }
|
4
|
+
let(:consumer_request_id) { 'consumer request id' }
|
5
|
+
let(:host) { 'localhost:1234' }
|
6
|
+
let(:shared_secret) { 'secret' }
|
7
|
+
let(:scope) { raise 'Override in spec' }
|
8
|
+
let(:resource_id) { nil }
|
9
|
+
let(:response_body) { raise 'Override in spec' }
|
10
|
+
let(:service_name) { raise 'Override in spec' }
|
11
|
+
let(:status) { 200 }
|
12
|
+
let(:provider_state) { raise 'Override in spec' }
|
13
|
+
let(:user) { nil }
|
14
|
+
let(:params) { raise 'Override in spec' }
|
15
|
+
let(:body) { raise 'Override in spec' }
|
16
|
+
let(:request_description) { raise 'Override in spec (must be unique!)' }
|
17
|
+
|
18
|
+
let(:client) do
|
19
|
+
QuizApiClient::Client.new(
|
20
|
+
consumer_key: consumer_key,
|
21
|
+
consumer_request_id: consumer_request_id,
|
22
|
+
host: host,
|
23
|
+
shared_secret: shared_secret,
|
24
|
+
protocol: 'http'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
context 'updating a resource' do
|
29
|
+
let(:token) do
|
30
|
+
client.jwt_service.grant_permission(
|
31
|
+
exp: token_expiration_one_year,
|
32
|
+
scope: scope,
|
33
|
+
uuid: user,
|
34
|
+
resource_id: resource_id
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
before do
|
39
|
+
quiz_api
|
40
|
+
.given(provider_state)
|
41
|
+
.upon_receiving(request_description)
|
42
|
+
.with(
|
43
|
+
method: :put,
|
44
|
+
path: quizzes_api_path,
|
45
|
+
headers: headers(token),
|
46
|
+
body: body
|
47
|
+
)
|
48
|
+
.will_respond_with(
|
49
|
+
status: status,
|
50
|
+
headers: { 'Content-Type' => 'application/json; charset=utf-8' },
|
51
|
+
body: response_body
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'verifies the request is valid' do
|
56
|
+
result = client.send(service_name).update(token: token, params: params)
|
57
|
+
expect(result).to be_truthy
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,347 @@
|
|
1
|
+
describe QuizApiClient::HttpClient do
|
2
|
+
let(:uri) { 'http://api.quiz.docker' }
|
3
|
+
let(:jwt) { JWT.encode({ jwt: 'payload' }, 'secret') }
|
4
|
+
let(:default_request_data) { client.send(:default_request_data) }
|
5
|
+
let(:config) { QuizApiClient::Config.new { |c| c.consumer_request_id = 'hi' } }
|
6
|
+
|
7
|
+
subject(:client) { QuizApiClient::HttpClient.new(uri: uri, jwt: jwt, config: config) }
|
8
|
+
|
9
|
+
def url_for_path(path)
|
10
|
+
"http://api.quiz.docker#{path}"
|
11
|
+
end
|
12
|
+
|
13
|
+
def stub_quiz_api(path, item: 1, query: {}, headers: {}, status: 200)
|
14
|
+
stub_request(:get, url_for_path(path))
|
15
|
+
.with(query: query)
|
16
|
+
.to_return(
|
17
|
+
body: [item].to_json,
|
18
|
+
status: status,
|
19
|
+
headers: headers.merge(
|
20
|
+
'Content-Type' => 'application/json'
|
21
|
+
)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
def link_header(path, page, last_page)
|
26
|
+
link_header = "<http://api.quiz.docker#{path}?page=#{last_page}>; rel=\"last\""
|
27
|
+
link_header += ", <http://api.quiz.docker#{path}?page=#{page + 1}>; rel=\"next\"" if page < last_page
|
28
|
+
link_header
|
29
|
+
end
|
30
|
+
|
31
|
+
def mock_time(duration)
|
32
|
+
mock = Time.now
|
33
|
+
allow(Time).to receive(:now).and_return(mock, mock + duration)
|
34
|
+
mock
|
35
|
+
end
|
36
|
+
|
37
|
+
def mock_metrics(mock_time)
|
38
|
+
mock = instance_double(QuizApiClient::HttpRequest::Metrics)
|
39
|
+
expect(mock).to receive(:increment)
|
40
|
+
expect(mock).to receive(:duration).with(mock_time, mock_time + 10)
|
41
|
+
mock
|
42
|
+
end
|
43
|
+
|
44
|
+
def expect_metrics_calls(config, method, url, code)
|
45
|
+
expect(QuizApiClient::HttpRequest::Metrics).to receive(:new)
|
46
|
+
.with(config, method, url, code)
|
47
|
+
.and_return(mock_metrics(mock_time(10)))
|
48
|
+
end
|
49
|
+
|
50
|
+
def expect_raise_error_call(config, method, url, response, current_error)
|
51
|
+
mock_failure = instance_double(QuizApiClient::HttpRequest::Failure)
|
52
|
+
|
53
|
+
expect(QuizApiClient::HttpRequest::Failure).to receive(:new).with(config).and_return(mock_failure)
|
54
|
+
|
55
|
+
expect(mock_failure).to(
|
56
|
+
receive(:raise_error)
|
57
|
+
.with(method, url, response: response, current_error: current_error)
|
58
|
+
.and_raise(QuizApiClient::HttpClient::RequestFailed.new(:context))
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
it { is_expected.to be_a HTTParty }
|
63
|
+
|
64
|
+
describe 'get' do
|
65
|
+
it 'makes a get request' do
|
66
|
+
path = '/api/quizzes'
|
67
|
+
stub_quiz_api path, query: { sort: 'alpha' }
|
68
|
+
expect_metrics_calls(client.config, :get, url_for_path(path), 200)
|
69
|
+
response = client.get(path, all: false, query: { sort: 'alpha' })
|
70
|
+
expect(response.parsed_response).to eq [1]
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'all' do
|
74
|
+
it 'retrieves single page when all is true' do
|
75
|
+
stub_quiz_api '/api/quizzes'
|
76
|
+
expect(client.get('/api/quizzes', all: true)).to eq [1]
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'link pagination' do
|
80
|
+
it 'retrieves subsequent pages when all is true' do
|
81
|
+
path = '/api/quizzes'
|
82
|
+
stub_quiz_api path, headers: { link: link_header(path, 1, 2) }
|
83
|
+
stub_quiz_api path, item: 2, query: { page: 2 }, headers: { link: link_header(path, 2, 2) }
|
84
|
+
expect(client.get('/api/quizzes', all: true)).to eq [1, 2]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'dynamo pagination' do
|
89
|
+
let(:dynamo_headers) { { 'x-last-evaluated-hash-key' => 'foo', 'x-last-evaluated-range-key' => 'bar' } }
|
90
|
+
let(:dynamo_params) { { last_evaluated_hash_key: 'foo', last_evaluated_range_key: 'bar' } }
|
91
|
+
|
92
|
+
it 'retrieves subsequent pages when all is true' do
|
93
|
+
path = '/api/quizzes'
|
94
|
+
stub_quiz_api path, headers: dynamo_headers
|
95
|
+
stub_quiz_api path, item: 2, query: dynamo_params
|
96
|
+
expect(client.get('/api/quizzes', all: true)).to eq [1, 2]
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'retrieves subsequent pages when all is true and query is present' do
|
100
|
+
path = '/api/quizzes'
|
101
|
+
stub_quiz_api path, query: { my_id: 12 }, headers: dynamo_headers
|
102
|
+
stub_quiz_api path, item: 2, query: { my_id: 12, **dynamo_params }
|
103
|
+
expect(client.get('/api/quizzes', query: { my_id: 12 }, all: true)).to eq [1, 2]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'post' do
|
110
|
+
it 'makes a post request' do
|
111
|
+
url = 'http://api.quiz.docker/api/quizzes'
|
112
|
+
expect(client.class).to receive(:post).with(
|
113
|
+
url,
|
114
|
+
body: { title: 'ohai' }.to_json,
|
115
|
+
**default_request_data
|
116
|
+
).and_return(instance_double('HTTParty::Response', success?: true, code: 200))
|
117
|
+
expect_metrics_calls(client.config, :post, url, 200)
|
118
|
+
client.post('/api/quizzes', title: 'ohai')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'patch' do
|
123
|
+
it 'makes a patch request' do
|
124
|
+
url = 'http://api.quiz.docker/api/quizzes/1'
|
125
|
+
expect(client.class).to receive(:patch).with(
|
126
|
+
url,
|
127
|
+
body: { title: 'new title' }.to_json,
|
128
|
+
**default_request_data
|
129
|
+
).and_return(instance_double('HTTParty::Response', success?: true, code: 200))
|
130
|
+
expect_metrics_calls(client.config, :patch, url, 200)
|
131
|
+
client.patch('/api/quizzes/1', title: 'new title')
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe 'put' do
|
136
|
+
it 'makes a put request' do
|
137
|
+
url = 'http://api.quiz.docker/api/quizzes/1'
|
138
|
+
expect(client.class).to receive(:put).with(
|
139
|
+
url,
|
140
|
+
body: { title: 'new title' }.to_json,
|
141
|
+
**default_request_data
|
142
|
+
).and_return(instance_double('HTTParty::Response', success?: true, code: 200))
|
143
|
+
expect_metrics_calls(client.config, :put, url, 200)
|
144
|
+
client.put('/api/quizzes/1', title: 'new title')
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
describe 'delete' do
|
149
|
+
it 'makes a delete request' do
|
150
|
+
url = 'http://api.quiz.docker/api/quizzes/1'
|
151
|
+
expect(client.class).to receive(:delete).with(
|
152
|
+
url,
|
153
|
+
**default_request_data
|
154
|
+
).and_return(instance_double('HTTParty::Response', success?: true, code: 200))
|
155
|
+
expect_metrics_calls(client.config, :delete, url, 200)
|
156
|
+
client.delete('/api/quizzes/1')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe 'headers' do
|
161
|
+
it 'includes the correct headers' do
|
162
|
+
expect(default_request_data[:headers]).to eq(
|
163
|
+
'Authorization' => jwt,
|
164
|
+
'AuthType' => 'Signature',
|
165
|
+
'Accept' => 'application/json',
|
166
|
+
'Content-Type' => 'application/json',
|
167
|
+
'X-Consumer-Request-Id' => 'hi'
|
168
|
+
)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe '#successful_response?' do
|
173
|
+
it 'returns true if the response is successful' do
|
174
|
+
resp = instance_double('HTTParty::Response', success?: true)
|
175
|
+
expect(client.send(:successful_response?, resp)).to be_truthy
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'returns true if the response code is 401' do
|
179
|
+
resp = instance_double('HTTParty::Response', success?: false, code: 401)
|
180
|
+
expect(client.send(:successful_response?, resp)).to be_truthy
|
181
|
+
end
|
182
|
+
|
183
|
+
it 'returns false if the response is not successful' do
|
184
|
+
resp = instance_double('HTTParty::Response', success?: false, code: 404)
|
185
|
+
expect(client.send(:successful_response?, resp)).to be_falsey
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
describe 'error handling' do
|
190
|
+
context 'with :sentry_raven error handler' do
|
191
|
+
let(:error_handler) { :sentry_raven }
|
192
|
+
let(:path) { '/' }
|
193
|
+
let(:url) { "http://api.quiz.docker#{path}" }
|
194
|
+
let(:error_context) do
|
195
|
+
{
|
196
|
+
quiz_api_client: {
|
197
|
+
request: {
|
198
|
+
method: :get,
|
199
|
+
url: url
|
200
|
+
}
|
201
|
+
}
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
before do
|
206
|
+
client.config.error_handler = error_handler
|
207
|
+
end
|
208
|
+
|
209
|
+
context 'with non-success responses' do
|
210
|
+
let(:body) { '[2]' }
|
211
|
+
let(:code) { 404 }
|
212
|
+
let(:path) { '/api/quizzes' }
|
213
|
+
let(:context_url) { url }
|
214
|
+
let(:method) { :get }
|
215
|
+
let(:error_context) do
|
216
|
+
{
|
217
|
+
quiz_api_client: {
|
218
|
+
request: {
|
219
|
+
method: method,
|
220
|
+
url: context_url
|
221
|
+
},
|
222
|
+
response: {
|
223
|
+
body: body,
|
224
|
+
code: code
|
225
|
+
}
|
226
|
+
}
|
227
|
+
}
|
228
|
+
end
|
229
|
+
let(:mock_response) do
|
230
|
+
instance_double(
|
231
|
+
'HTTParty::Response',
|
232
|
+
success?: false,
|
233
|
+
body: body,
|
234
|
+
code: code
|
235
|
+
)
|
236
|
+
end
|
237
|
+
let(:success_response) do
|
238
|
+
# rubocop:disable Metrics/LineLength
|
239
|
+
instance_double(
|
240
|
+
'HTTParty::Response',
|
241
|
+
body: body,
|
242
|
+
code: 200,
|
243
|
+
parsed_response: [1],
|
244
|
+
headers: {
|
245
|
+
'link' => '<http://api.quiz.docker/api/quizzes?page=2>; rel="last", <http://api.quiz.docker/api/quizzes?page=2>; rel="next"',
|
246
|
+
'content-type' => ['application/json']
|
247
|
+
}
|
248
|
+
)
|
249
|
+
# rubocop:enable Metrics/LineLength
|
250
|
+
end
|
251
|
+
|
252
|
+
context ':get request' do
|
253
|
+
it 'raises error' do
|
254
|
+
expect(client).to receive(:successful_response?).with(mock_response).and_return(false)
|
255
|
+
expect(client.class).to receive(:get).and_return(mock_response)
|
256
|
+
expect_metrics_calls(client.config, method, url_for_path(path), code)
|
257
|
+
expect_raise_error_call(client.config, method, url_for_path(path), mock_response, nil)
|
258
|
+
expect { client.get(path, all: false, query: { sort: 'alpha' }) }
|
259
|
+
.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
context ':post request' do
|
264
|
+
let(:method) { :post }
|
265
|
+
|
266
|
+
it 'raises error' do
|
267
|
+
expect(client).to receive(:successful_response?).with(mock_response).and_return(false)
|
268
|
+
expect(client.class).to receive(:post).and_return(mock_response)
|
269
|
+
expect_metrics_calls(client.config, method, url_for_path(path), code)
|
270
|
+
expect_raise_error_call(client.config, method, url_for_path(path), mock_response, nil)
|
271
|
+
expect { client.post(path) }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
context ':put request' do
|
276
|
+
let(:method) { :put }
|
277
|
+
|
278
|
+
it 'raises error' do
|
279
|
+
expect(client).to receive(:successful_response?).with(mock_response).and_return(false)
|
280
|
+
expect(client.class).to receive(:put).and_return(mock_response)
|
281
|
+
expect_metrics_calls(client.config, method, url_for_path(path), code)
|
282
|
+
expect_raise_error_call(client.config, method, url_for_path(path), mock_response, nil)
|
283
|
+
expect { client.put(path) }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context ':patch request' do
|
288
|
+
let(:method) { :patch }
|
289
|
+
|
290
|
+
it 'raises error' do
|
291
|
+
expect(client).to receive(:successful_response?).with(mock_response).and_return(false)
|
292
|
+
expect(client.class).to receive(:patch).and_return(mock_response)
|
293
|
+
expect_metrics_calls(client.config, method, url_for_path(path), code)
|
294
|
+
expect_raise_error_call(client.config, method, url_for_path(path), mock_response, nil)
|
295
|
+
expect { client.patch(path) }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
context ':delete request' do
|
300
|
+
let(:method) { :delete }
|
301
|
+
|
302
|
+
it 'raises error' do
|
303
|
+
expect(client).to receive(:successful_response?).with(mock_response).and_return(false)
|
304
|
+
expect(client.class).to receive(:delete).and_return(mock_response)
|
305
|
+
expect_metrics_calls(client.config, method, url_for_path(path), code)
|
306
|
+
expect_raise_error_call(client.config, method, url_for_path(path), mock_response, nil)
|
307
|
+
expect { client.delete(path) }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
context 'with pagination' do
|
312
|
+
let(:context_url) { "#{url}?page=2" }
|
313
|
+
|
314
|
+
it 'raises error if one of the linked pages does not return 200 response' do
|
315
|
+
stub_quiz_api path, headers: { link: link_header(path, 1, 2) }
|
316
|
+
stub_quiz_api path, item: 2, query: { page: 2 }, headers: { link: link_header(path, 2, 2) }, status: code
|
317
|
+
allow(HTTParty::Response).to receive(:new).and_return(success_response, mock_response)
|
318
|
+
expect(client).to receive(:successful_response?).and_return(true, false).twice
|
319
|
+
expect_raise_error_call(client.config, method, context_url, mock_response, nil)
|
320
|
+
expect { client.get('/api/quizzes', all: true) }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'handles an HTTParty::Error error' do
|
326
|
+
stub_request(:get, url).to_raise(HTTParty::Error)
|
327
|
+
expect_metrics_calls(client.config, :get, url_for_path(path), 0)
|
328
|
+
expect_raise_error_call(client.config, :get, url_for_path(path), nil, kind_of(HTTParty::Error))
|
329
|
+
expect { client.get('/') }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'handles an Errno::ECONNREFUSED error' do
|
333
|
+
stub_request(:get, url).to_raise(Errno::ECONNREFUSED)
|
334
|
+
expect_metrics_calls(client.config, :get, url_for_path(path), 0)
|
335
|
+
expect_raise_error_call(client.config, :get, url_for_path(path), nil, kind_of(Errno::ECONNREFUSED))
|
336
|
+
expect { client.get('/') }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'handles an Net::ReadTimeout error' do
|
340
|
+
stub_request(:get, url).to_raise(Net::ReadTimeout)
|
341
|
+
expect_metrics_calls(client.config, :get, url_for_path(path), 0)
|
342
|
+
expect_raise_error_call(client.config, :get, url_for_path(path), nil, kind_of(Net::ReadTimeout))
|
343
|
+
expect { client.get('/') }.to raise_error(QuizApiClient::HttpClient::RequestFailed)
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
describe QuizApiClient::JSONFormatter do
|
2
|
+
let(:logger) { double('logger') }
|
3
|
+
let(:level) { :info }
|
4
|
+
let(:uri) { 'uri_here' }
|
5
|
+
let(:request) do
|
6
|
+
r = double('request')
|
7
|
+
allow(r).to receive(:last_uri) { uri }
|
8
|
+
r
|
9
|
+
end
|
10
|
+
let(:status_code) { 200 }
|
11
|
+
let(:request_id) { 'request_id' }
|
12
|
+
let(:response) do
|
13
|
+
r = double('response')
|
14
|
+
allow(r).to receive(:code) { status_code }
|
15
|
+
allow(r).to receive(:headers) { { 'x-request-id' => [request_id] } }
|
16
|
+
r
|
17
|
+
end
|
18
|
+
|
19
|
+
subject(:formatter) { QuizApiClient::JSONFormatter.new(logger, level) }
|
20
|
+
|
21
|
+
describe 'logging formatter' do
|
22
|
+
it 'processes logging calls into a JSON format' do
|
23
|
+
expect(logger).to receive(level).with(
|
24
|
+
client_request_id: request_id,
|
25
|
+
request_url: uri,
|
26
|
+
response_code: status_code
|
27
|
+
)
|
28
|
+
|
29
|
+
formatter.format(request, response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|