quiz_api_client 4.2.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/lib/quiz_api_client/services/items_service.rb +11 -0
  3. data/lib/quiz_api_client/version.rb +1 -1
  4. data/spec/config_spec.rb +66 -0
  5. data/spec/contracts/interaction_types_service_spec.rb +22 -0
  6. data/spec/contracts/item_analyses_service_spec.rb +59 -0
  7. data/spec/contracts/items_service_spec.rb +59 -0
  8. data/spec/contracts/qti_imports_service_spec.rb +34 -0
  9. data/spec/contracts/quiz_clone_job_service_spec.rb +20 -0
  10. data/spec/contracts/quiz_clone_jobs_service_spec.rb +21 -0
  11. data/spec/contracts/quiz_entries_service_spec.rb +125 -0
  12. data/spec/contracts/quiz_service_spec.rb +68 -0
  13. data/spec/contracts/quiz_session_events_service_spec.rb +30 -0
  14. data/spec/contracts/quiz_session_result_service_spec.rb +42 -0
  15. data/spec/contracts/quiz_session_service_spec.rb +56 -0
  16. data/spec/contracts/quiz_sessions_service_spec.rb +28 -0
  17. data/spec/contracts/quizzes_service_spec.rb +80 -0
  18. data/spec/contracts/session_item_results_service_spec.rb +60 -0
  19. data/spec/contracts/session_items_service_spec.rb +21 -0
  20. data/spec/contracts/shared_banks_spec.rb +366 -0
  21. data/spec/contracts/shared_examples/http_delete_example.rb +56 -0
  22. data/spec/contracts/shared_examples/http_get_example.rb +139 -0
  23. data/spec/contracts/shared_examples/http_patch_example.rb +60 -0
  24. data/spec/contracts/shared_examples/http_post_example.rb +60 -0
  25. data/spec/contracts/shared_examples/http_put_example.rb +60 -0
  26. data/spec/http_client_spec.rb +347 -0
  27. data/spec/json_formatter_spec.rb +32 -0
  28. data/spec/quiz_api_client/http_request/failure_spec.rb +100 -0
  29. data/spec/quiz_api_client/http_request/metrics_spec.rb +75 -0
  30. data/spec/quiz_api_client_spec.rb +117 -0
  31. data/spec/services/base_api_service_spec.rb +50 -0
  32. data/spec/services/interaction_types_service_spec.rb +25 -0
  33. data/spec/services/item_analyses_service_spec.rb +76 -0
  34. data/spec/services/items_service_spec.rb +56 -0
  35. data/spec/services/jwt_service_spec.rb +66 -0
  36. data/spec/services/qti_imports_service_spec.rb +114 -0
  37. data/spec/services/quiz_analyses_service_spec.rb +44 -0
  38. data/spec/services/quiz_clone_job_service_spec.rb +41 -0
  39. data/spec/services/quiz_clone_jobs_service_spec.rb +41 -0
  40. data/spec/services/quiz_entries_service_spec.rb +71 -0
  41. data/spec/services/quiz_service_spec.rb +49 -0
  42. data/spec/services/quiz_session_events_service_spec.rb +42 -0
  43. data/spec/services/quiz_session_result_service_spec.rb +26 -0
  44. data/spec/services/quiz_session_service_spec.rb +49 -0
  45. data/spec/services/quiz_sessions_service_spec.rb +42 -0
  46. data/spec/services/quizzes_service_spec.rb +71 -0
  47. data/spec/services/session_item_results_service_spec.rb +33 -0
  48. data/spec/services/session_items_service_spec.rb +26 -0
  49. data/spec/spec_helper.rb +42 -0
  50. data/spec/support/pact_config.rb +64 -0
  51. data/spec/support/pact_helper.rb +19 -0
  52. metadata +108 -39
  53. data/.dockerignore +0 -7
  54. data/.editorconfig +0 -16
  55. data/.gitignore +0 -13
  56. data/.rspec +0 -3
  57. data/.rubocop.yml +0 -72
  58. data/CHANGELOG.md +0 -35
  59. data/Dockerfile +0 -12
  60. data/Gemfile +0 -5
  61. data/Jenkinsfile +0 -86
  62. data/bin/console +0 -7
  63. data/bin/contracts-generate +0 -26
  64. data/bin/setup +0 -65
  65. data/docker-compose.dev.override.yml +0 -11
  66. data/docker-compose.yml +0 -10
  67. data/quiz_api_client.gemspec +0 -60
@@ -0,0 +1,100 @@
1
+ require 'sentry-raven'
2
+
3
+ describe QuizApiClient::HttpRequest::Failure do
4
+ let(:config) { QuizApiClient::Config.new }
5
+ let(:method) { 'GET' }
6
+ let(:url) { 'https://something.com' }
7
+ let(:response_code) { 500 }
8
+ let(:response_body) { 'I am a response body!' }
9
+ let(:response) { instance_double('HTTParty::Response', code: response_code, body: response_body) }
10
+
11
+ subject { described_class.new(config) }
12
+
13
+ describe '#raise_error' do
14
+ it 'raises the QuizApiClient::HttpClient::RequestFailed error' do
15
+ expect do
16
+ subject.raise_error(method, url)
17
+ end.to raise_error(QuizApiClient::HttpClient::RequestFailed)
18
+ end
19
+
20
+ context 'when there is a current exception' do
21
+ let(:error_context) do
22
+ {
23
+ quiz_api_client: {
24
+ request: {
25
+ method: method,
26
+ url: url
27
+ }
28
+ }
29
+ }
30
+ end
31
+ let(:current_error_message) { 'standard error message' }
32
+ let(:current_error) { StandardError.new(current_error_message) }
33
+ let(:raise_error_call) { -> { subject.raise_error(method, url, current_error: current_error) } }
34
+
35
+ it 'sets the context on the error' do
36
+ expect(raise_error_call).to raise_error(QuizApiClient::HttpClient::RequestFailed) do |error|
37
+ expect(error.context).to eq error_context
38
+ end
39
+ end
40
+
41
+ it 'sets the error message' do
42
+ expect(raise_error_call).to raise_error(QuizApiClient::HttpClient::RequestFailed) do |error|
43
+ expect(error.message).to eq current_error_message
44
+ end
45
+ end
46
+
47
+ context 'when error_handler is set to :sentry_raven' do
48
+ before do
49
+ config.error_handler = :sentry_raven
50
+ end
51
+
52
+ it 'sets the Raven context to the context on the error' do
53
+ expect(Raven).to receive(:extra_context).with(error_context)
54
+ expect(raise_error_call).to raise_error(QuizApiClient::HttpClient::RequestFailed)
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'when there is a response' do
60
+ let(:error_context) do
61
+ {
62
+ quiz_api_client: {
63
+ request: {
64
+ method: method,
65
+ url: url
66
+ },
67
+ response: {
68
+ body: response_body,
69
+ code: response_code
70
+ }
71
+ }
72
+ }
73
+ end
74
+ let(:raise_error_call) { -> { subject.raise_error(method, url, response: response) } }
75
+
76
+ it 'sets the context on the error' do
77
+ expect(raise_error_call).to raise_error(QuizApiClient::HttpClient::RequestFailed) do |error|
78
+ expect(error.context).to eq error_context
79
+ end
80
+ end
81
+
82
+ it 'sets the error message' do
83
+ expect(raise_error_call).to raise_error(QuizApiClient::HttpClient::RequestFailed) do |error|
84
+ expect(error.message).to eq "#{url} responded #{response_body} (#{response_code})"
85
+ end
86
+ end
87
+
88
+ context 'when error_handler is set to :sentry_raven' do
89
+ before do
90
+ config.error_handler = :sentry_raven
91
+ end
92
+
93
+ it 'sets the Raven context to the context on the error' do
94
+ expect(Raven).to receive(:extra_context).with(error_context)
95
+ expect(raise_error_call).to raise_error(QuizApiClient::HttpClient::RequestFailed)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,75 @@
1
+ require 'inst_statsd'
2
+
3
+ describe QuizApiClient::HttpRequest::Metrics do
4
+ let(:config) { QuizApiClient::Config.new }
5
+ let(:method) { 'GET' }
6
+ let(:url) { 'https://something.com' }
7
+ let(:code) { 200 }
8
+
9
+ subject { described_class.new(config, method, url, code) }
10
+
11
+ describe '#increment' do
12
+ it 'does nothing when the metrics handler is nil' do
13
+ allow(config).to receive(:metrics_handler).and_return(nil)
14
+ expect(InstStatsd::Statsd).to receive(:increment).never
15
+
16
+ subject.increment
17
+ end
18
+
19
+ it 'does nothing when the metrics namespace is nil' do
20
+ allow(config).to receive(:metrics_namespace).and_return(nil)
21
+ expect(InstStatsd::Statsd).to receive(:increment).never
22
+
23
+ subject.increment
24
+ end
25
+
26
+ it 'calls the #increment method for InstStatsd' do
27
+ config.setup_metrics(:inststatsd, 'fake-namespace')
28
+ expect(InstStatsd::Statsd).to receive(:increment).with(
29
+ 'fake-namespace.quiz_api_client.request.count',
30
+ tags: {
31
+ method: method,
32
+ status: code,
33
+ url: url
34
+ }
35
+ )
36
+
37
+ subject.increment
38
+ end
39
+ end
40
+
41
+ describe '#duration' do
42
+ let(:duration_in_secs) { 10 }
43
+ let(:start_time) { Time.parse('2021-11-09 15:11:48 -0500') }
44
+ let(:end_time) { start_time + duration_in_secs }
45
+
46
+ it 'does nothing when the metrics handler is nil' do
47
+ allow(config).to receive(:metrics_handler).and_return(nil)
48
+ expect(InstStatsd::Statsd).to receive(:timing).never
49
+
50
+ subject.duration(start_time, end_time)
51
+ end
52
+
53
+ it 'does nothing when the metrics namespace is nil' do
54
+ allow(config).to receive(:metrics_namespace).and_return(nil)
55
+ expect(InstStatsd::Statsd).to receive(:timing).never
56
+
57
+ subject.duration(start_time, end_time)
58
+ end
59
+
60
+ it 'calls the #timing method for InstStatsd' do
61
+ config.setup_metrics(:inststatsd, 'fake-namespace')
62
+ expect(InstStatsd::Statsd).to receive(:timing).with(
63
+ 'fake-namespace.quiz_api_client.request.duration_ms',
64
+ duration_in_secs * 1_000,
65
+ tags: {
66
+ method: method,
67
+ status: code,
68
+ url: url
69
+ }
70
+ )
71
+
72
+ subject.duration(start_time, end_time)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,117 @@
1
+ describe QuizApiClient do
2
+ it 'has a version number' do
3
+ expect(QuizApiClient::VERSION).not_to be nil
4
+ end
5
+
6
+ context 'service helper methods' do
7
+ let(:host) { 'api.host' }
8
+ let(:shared_secret) { 'secret' }
9
+ let(:protocol) { 'http' }
10
+ let(:consumer_key) { 'some consumer_key' }
11
+ let(:consumer_request_id) { 'guid_value' }
12
+ let(:subject) do
13
+ QuizApiClient::Client.new(
14
+ consumer_key: consumer_key,
15
+ consumer_request_id: consumer_request_id,
16
+ host: host,
17
+ shared_secret: shared_secret,
18
+ protocol: protocol
19
+ )
20
+ end
21
+
22
+ describe '#config' do
23
+ it 'should populate the values' do
24
+ config = subject.config
25
+ expect(config.consumer_key).to eq consumer_key
26
+ expect(config.consumer_request_id).to eq consumer_request_id
27
+ expect(config.host).to eq host
28
+ expect(config.shared_secret).to eq shared_secret
29
+ expect(config.protocol).to eq protocol
30
+ end
31
+
32
+ it 'should yield the config on instantiation' do
33
+ client = QuizApiClient::Client.new(
34
+ consumer_key: consumer_key,
35
+ consumer_request_id: consumer_request_id,
36
+ host: host,
37
+ shared_secret: shared_secret,
38
+ protocol: protocol
39
+ ) do |config|
40
+ config.host = 'my.hosting'
41
+ end
42
+
43
+ expect(client.config.host).to eq 'my.hosting'
44
+ end
45
+ end
46
+
47
+ describe '#jwt_service' do
48
+ it 'should create the service' do
49
+ expect(QuizApiClient::Services::JwtService).to receive(:new).with(subject.config)
50
+ subject.jwt_service
51
+ end
52
+ end
53
+
54
+ describe '#quizzes_service' do
55
+ it 'should create the service' do
56
+ expect(QuizApiClient::Services::QuizzesService).to receive(:new).with(subject.config)
57
+ subject.quizzes_service
58
+ end
59
+ end
60
+
61
+ describe '#quiz_session_service' do
62
+ it 'should create the service' do
63
+ expect(QuizApiClient::Services::QuizSessionService).to receive(:new).with(subject.config)
64
+ subject.quiz_session_service
65
+ end
66
+ end
67
+
68
+ describe '#quiz_sessions_service' do
69
+ it 'should create the service' do
70
+ expect(QuizApiClient::Services::QuizSessionsService).to receive(:new).with(subject.config)
71
+ subject.quiz_sessions_service
72
+ end
73
+ end
74
+
75
+ describe '#qti_imports_service' do
76
+ it 'should create the service' do
77
+ expect(QuizApiClient::Services::QtiImportsService).to receive(:new).with(subject.config)
78
+ subject.qti_imports_service
79
+ end
80
+ end
81
+
82
+ describe '#item_analyses_service' do
83
+ it 'should create the service' do
84
+ expect(QuizApiClient::Services::ItemAnalysesService).to receive(:new).with(subject.config)
85
+ subject.item_analyses_service
86
+ end
87
+ end
88
+
89
+ describe '#quiz_analyses_service' do
90
+ it 'should create the service' do
91
+ expect(QuizApiClient::Services::QuizAnalysesService).to receive(:new).with(subject.config)
92
+ subject.quiz_analyses_service
93
+ end
94
+ end
95
+
96
+ describe '#quiz_session_events_service' do
97
+ it 'should create the service' do
98
+ expect(QuizApiClient::Services::QuizSessionEventsService).to receive(:new).with(subject.config)
99
+ subject.quiz_session_events_service
100
+ end
101
+ end
102
+
103
+ describe '#items_service' do
104
+ it 'should create the service' do
105
+ expect(QuizApiClient::Services::ItemsService).to receive(:new).with(subject.config)
106
+ subject.items_service
107
+ end
108
+ end
109
+
110
+ describe '#interaction_types_service' do
111
+ it 'should create the service' do
112
+ expect(QuizApiClient::Services::InteractionTypesService).to receive(:new).with(subject.config)
113
+ subject.interaction_types_service
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,50 @@
1
+ describe QuizApiClient::Services::BaseApiService do
2
+ let(:consumer_key) { 'consumer key' }
3
+ let(:host) { 'api.host' }
4
+ let(:shared_secret) { 'secret' }
5
+ let(:protocol) { 'http' }
6
+ let(:service_class) do
7
+ Class.new(QuizApiClient::Services::BaseApiService) do
8
+ def test_uri
9
+ uri
10
+ end
11
+ end
12
+ end
13
+ let(:service) do
14
+ service_class.new(QuizApiClient::Config.new do |c|
15
+ c.consumer_key = consumer_key
16
+ c.host = host
17
+ c.shared_secret = shared_secret
18
+ c.protocol = protocol
19
+ end)
20
+ end
21
+
22
+ def decode_token(token, secret)
23
+ JWT.decode(
24
+ token, secret, true,
25
+ algorithm: QuizApiClient::Services::JwtService::HASHING_ALGORITHM
26
+ )
27
+ end
28
+
29
+ describe '#uri' do
30
+ it 'returns full URI' do
31
+ expect(service.test_uri).to eq 'http://api.host'
32
+ end
33
+
34
+ context 'with https protocol' do
35
+ let(:protocol) { 'https' }
36
+
37
+ it 'uses specified protocol' do
38
+ expect(service.test_uri).to eq 'https://api.host'
39
+ end
40
+ end
41
+
42
+ context 'with port specified in host' do
43
+ let(:host) { 'api.host:8443' }
44
+
45
+ it 'includes port in URI' do
46
+ expect(service.test_uri).to eq 'http://api.host:8443'
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,25 @@
1
+ describe QuizApiClient::Services::InteractionTypesService do
2
+ let(:host) { 'api.host' }
3
+ let(:config) { QuizApiClient::Config.new { |c| c.host = host } }
4
+ let(:subject) { described_class.new(config) }
5
+
6
+ describe '#list' do
7
+ let(:status_code) { 200 }
8
+ let(:expected_url) { "https://#{host}/api/interaction_types" }
9
+ let(:stubbed_response) { [{ 'name' => 'Type One', 'id' => 1 }] }
10
+
11
+ before do
12
+ stub_request(:get, expected_url)
13
+ .to_return(
14
+ status: status_code,
15
+ body: stubbed_response.to_json,
16
+ headers: { 'Content-Type' => 'application/json' }
17
+ )
18
+ end
19
+
20
+ it 'gets from quiz_api/api/interaction_types and returns the response' do
21
+ result = subject.list(token: 'token')
22
+ expect(result.parsed_response).to eql(stubbed_response)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,76 @@
1
+ describe QuizApiClient::Services::ItemAnalysesService do
2
+ let(:host) { 'api.host' }
3
+ let(:config) { QuizApiClient::Config.new { |c| c.host = host } }
4
+ let(:subject) { described_class.new(config) }
5
+
6
+ describe '#list' do
7
+ let(:params) { { quiz_id: 1, id: 7 } }
8
+ let(:stubbed_response) { { 'id' => '1' } }
9
+ let(:expected_url) do
10
+ "https://#{host}/api/quizzes/#{params[:quiz_id]}/stats/item_analyses"
11
+ end
12
+ let(:expected_body) { '' }
13
+ let(:status_code) { 200 }
14
+
15
+ before do
16
+ stub_request(:get, expected_url)
17
+ .with(body: expected_body)
18
+ .to_return(
19
+ status: status_code,
20
+ body: stubbed_response.to_json,
21
+ headers: { 'Content-Type' => 'application/json' }
22
+ )
23
+ end
24
+
25
+ it 'posts to /api/quizzes/1/stats/item_analyses and returns the response' do
26
+ result = subject.list(params: params, token: 'token')
27
+ expect(result.parsed_response).to eql(stubbed_response)
28
+ end
29
+
30
+ it 'raises an error when no quiz_id is specified' do
31
+ params = {}
32
+ expect do
33
+ subject.list(params: params, token: 'token')
34
+ end.to raise_error 'Quiz Id Required'
35
+ end
36
+ end
37
+
38
+ describe '#get' do
39
+ let(:params) { { quiz_id: 1, id: 7 } }
40
+ let(:stubbed_response) { { 'id' => '1' } }
41
+ let(:expected_url) do
42
+ "https://#{host}/api/quizzes/#{params[:quiz_id]}/stats/item_analyses/#{params[:id]}"
43
+ end
44
+ let(:expected_body) { '' }
45
+ let(:status_code) { 200 }
46
+
47
+ before do
48
+ stub_request(:get, expected_url)
49
+ .with(body: expected_body)
50
+ .to_return(
51
+ status: status_code,
52
+ body: stubbed_response.to_json,
53
+ headers: { 'Content-Type' => 'application/json' }
54
+ )
55
+ end
56
+
57
+ it 'posts to /api/quizzes/1/stats/item_analyses/7 and returns the response' do
58
+ result = subject.get(params: params, token: 'token')
59
+ expect(result.parsed_response).to eql(stubbed_response)
60
+ end
61
+
62
+ it 'raises an error when no quiz_id is specified' do
63
+ params = {}
64
+ expect do
65
+ subject.list(params: params, token: 'token')
66
+ end.to raise_error 'Quiz Id Required'
67
+ end
68
+
69
+ context 'response' do
70
+ it 'returns the response with the correct status code' do
71
+ response = subject.get(params: params, token: 'token')
72
+ expect(response.code).to eq(status_code)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,56 @@
1
+ describe QuizApiClient::Services::ItemsService do
2
+ let(:host) { 'api.host' }
3
+ let(:config) { QuizApiClient::Config.new { |c| c.host = host } }
4
+ let(:subject) { described_class.new(config) }
5
+
6
+ describe '#list' do
7
+ let(:params) { { quiz_id: 1 } }
8
+ let(:stubbed_response) { { 'id' => 1, 'item' => { 'id' => 1 } } }
9
+ let(:expected_url) { "https://#{host}/api/quizzes/#{params[:quiz_id]}/items" }
10
+ let(:status_code) { 200 }
11
+
12
+ before do
13
+ stub_request(:get, expected_url)
14
+ .to_return(
15
+ status: status_code,
16
+ body: stubbed_response.to_json,
17
+ headers: { 'Content-Type' => 'application/json' }
18
+ )
19
+ end
20
+
21
+ it 'list from quiz_api/api/quizzes/{id}/items and returns the response' do
22
+ result = subject.list(params: params, token: 'token')
23
+ expect(result.parsed_response).to eql(stubbed_response)
24
+ end
25
+ end
26
+
27
+ describe '#create' do
28
+ let(:params) do
29
+ {
30
+ quiz_id: 1,
31
+ item_body: 'item body',
32
+ scoring_data: { value: 'value' },
33
+ scoring_algorithm: 'Equivalence',
34
+ interaction_type_id: 'choice',
35
+ user_response_type: 'response type'
36
+ }
37
+ end
38
+ let(:stubbed_response) { { 'id' => 1, 'item' => { 'id' => 1 } } }
39
+ let(:expected_url) { "https://#{host}/api/quizzes/#{params[:quiz_id]}/items" }
40
+ let(:status_code) { 201 }
41
+
42
+ before do
43
+ stub_request(:post, expected_url)
44
+ .to_return(
45
+ status: status_code,
46
+ body: stubbed_response.to_json,
47
+ headers: { 'Content-Type' => 'application/json' }
48
+ )
49
+ end
50
+
51
+ it 'posts to quiz_api/api/quizzes/{id}/items and returns the response' do
52
+ result = subject.create(params: params, token: 'token')
53
+ expect(result.parsed_response).to eql(stubbed_response)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ describe QuizApiClient::Services::JwtService do
2
+ let(:host) { 'test.host' }
3
+ let(:shared_secret) { 'secret' }
4
+ let(:consumer_key) { 'some consumer_key' }
5
+
6
+ let(:config) do
7
+ QuizApiClient::Config.new do |c|
8
+ c.shared_secret = shared_secret
9
+ c.host = host
10
+ c.consumer_key = consumer_key
11
+ end
12
+ end
13
+
14
+ subject do
15
+ QuizApiClient::Services::JwtService.new(config)
16
+ end
17
+
18
+ describe 'grant_permission' do
19
+ it 'generates a JWT using the shared_secret' do
20
+ jwt = subject.grant_permission(scope: 'quiz.create')
21
+
22
+ payload, header = JWT.decode(jwt, shared_secret, true, algorithm: 'HS512')
23
+
24
+ expect(payload['host']).to eq(host)
25
+ expect(payload['scope']).to eq('quiz.create')
26
+ expect(payload['consumer_key']).to eq(consumer_key)
27
+ expect(payload).not_to have_key('resource_id')
28
+ expect(payload).not_to have_key('user')
29
+ expect(payload).to have_key('exp')
30
+ expect(header['alg']).to eq('HS512')
31
+ end
32
+
33
+ it 'includes user uuid in payload when provided' do
34
+ jwt = subject.grant_permission(scope: 'quiz.create', uuid: '123')
35
+
36
+ payload = JWT.decode(jwt, shared_secret, true, algorithm: 'HS512').first
37
+
38
+ expect(payload['user']).to eq('uuid' => '123')
39
+ end
40
+
41
+ it 'merges in other provided user context' do
42
+ jwt = subject.grant_permission(scope: 'quiz.create', uuid: '123', user: { email: 'test@example.com' })
43
+
44
+ payload = JWT.decode(jwt, shared_secret, true, algorithm: 'HS512').first
45
+
46
+ expect(payload['user']).to eq('uuid' => '123', 'email' => 'test@example.com')
47
+ end
48
+
49
+ it 'allows arbitrary additional params' do
50
+ jwt = subject.grant_permission(scope: 'quiz.create', foo: 'bar', monkey: 'do')
51
+ payload = JWT.decode(jwt, shared_secret, true, algorithm: 'HS512').first
52
+ expect(payload['foo']).to eq 'bar'
53
+ expect(payload['monkey']).to eq 'do'
54
+ end
55
+
56
+ context 'with port in host' do
57
+ let(:host) { 'test.host:8443' }
58
+
59
+ it 'leaves port out of payload host' do
60
+ jwt = subject.grant_permission scope: 'quiz.build'
61
+ payload, _header = JWT.decode(jwt, shared_secret, true, algorithm: 'HS512')
62
+ expect(payload['host']).to eq 'test.host'
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,114 @@
1
+ describe QuizApiClient::Services::QtiImportsService do
2
+ let(:host) { 'api.host' }
3
+ let(:config) { QuizApiClient::Config.new { |c| c.host = host } }
4
+ let(:subject) { described_class.new(config) }
5
+
6
+ describe '#create' do
7
+ let(:params) { { url: 'https://fake-s3-url.com', quiz_id: 1 } }
8
+ let(:stubbed_response) { { 'id' => '1', 'status' => 'created' } }
9
+ let(:expected_url) { "https://#{host}/api/quizzes/1/qti_imports" }
10
+ let(:expected_body) { {} }
11
+ let(:status_code) { 200 }
12
+
13
+ before do
14
+ stub_request(:post, expected_url)
15
+ .with(body: expected_body)
16
+ .to_return(
17
+ status: 200,
18
+ body: stubbed_response.to_json,
19
+ headers: { 'Content-Type' => 'application/json' }
20
+ )
21
+ end
22
+
23
+ it 'posts to quiz_api/api/qti_imports and returns the response' do
24
+ result = subject.create(params: params, token: 'token')
25
+ expect(result.parsed_response).to eql(stubbed_response)
26
+ end
27
+
28
+ context 'response' do
29
+ it 'returns the response with the correct code' do
30
+ response = subject.create(params: params, token: 'token')
31
+ expect(response.code).to eq(status_code)
32
+ end
33
+ end
34
+
35
+ context 'no quiz id provided' do
36
+ let(:params) {}
37
+ it 'raises Quiz Id Required' do
38
+ expect do
39
+ subject.create(params: params, token: 'token')
40
+ end.to raise_error('Quiz Id Required')
41
+ end
42
+ end
43
+ end
44
+
45
+ describe '#banks' do
46
+ let(:params) { { everything: false, overwrite: false, assessment_question_banks: ['abc123'] } }
47
+ let(:stubbed_response) { { 'id' => '1', 'status' => 'created' } }
48
+ let(:expected_url) { "https://#{host}/api/qti_imports/banks" }
49
+ let(:expected_body) { {} }
50
+ let(:status_code) { 200 }
51
+
52
+ before do
53
+ stub_request(:post, expected_url)
54
+ .with(body: expected_body)
55
+ .to_return(
56
+ status: 200,
57
+ body: stubbed_response.to_json,
58
+ headers: { 'Content-Type' => 'application/json' }
59
+ )
60
+ end
61
+
62
+ it 'posts to quiz_api/api/qti_imports and returns the response' do
63
+ result = subject.banks(params: params, token: 'token')
64
+ expect(result.parsed_response).to eql(stubbed_response)
65
+ end
66
+
67
+ context 'response' do
68
+ it 'returns the response with the correct code' do
69
+ response = subject.banks(params: params, token: 'token')
70
+ expect(response.code).to eq(status_code)
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#get_imported_consumer_settings' do
76
+ let(:params) { { quiz_id: 1 } }
77
+ let(:stubbed_response) { { 'score_to_keep' => 'highest', 'max_attempts' => '10' } }
78
+ let(:expected_url) { "https://#{host}/api/quizzes/1/qti_imports/imported_consumer_settings" }
79
+
80
+ let(:expected_body) { {} }
81
+ let(:status_code) { 200 }
82
+
83
+ before do
84
+ stub_request(:get, expected_url)
85
+ .with(body: expected_body)
86
+ .to_return(
87
+ status: 200,
88
+ body: stubbed_response.to_json,
89
+ headers: { 'Content-Type' => 'application/json' }
90
+ )
91
+ end
92
+
93
+ it 'posts to quiz_api/api/qti_imports and returns the response' do
94
+ result = subject.get_imported_consumer_settings(params: params, token: 'token')
95
+ expect(result.parsed_response).to eql(stubbed_response)
96
+ end
97
+
98
+ context 'response' do
99
+ it 'returns the response with the correct code' do
100
+ response = subject.get_imported_consumer_settings(params: params, token: 'token')
101
+ expect(response.code).to eq(status_code)
102
+ end
103
+ end
104
+
105
+ context 'no quiz id provided' do
106
+ let(:params) {}
107
+ it 'raises Quiz Id Required' do
108
+ expect do
109
+ subject.create(params: params, token: 'token')
110
+ end.to raise_error('Quiz Id Required')
111
+ end
112
+ end
113
+ end
114
+ end