Skip to content

Commit a2606c3

Browse files
committed
BRD-20938 - updates following first pass peer review
1 parent b875758 commit a2606c3

File tree

7 files changed

+153
-103
lines changed

7 files changed

+153
-103
lines changed

lib/scorm_engine/api/endpoints/courses.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,25 @@ def get_courses(options = {})
3535
result = Enumerator.new do |enum|
3636
if single_course_id
3737
# Single course endpoint returns course data directly
38-
enum << ScormEngine::Models::Course.new_from_api(response.raw_response.body) if response.success? && response.raw_response.body.is_a?(Hash)
38+
if response.success? && response.raw_response.body.is_a?(Hash)
39+
enum << ScormEngine::Models::Course.new_from_api(response.raw_response.body)
40+
end
3941
else
4042
# Multiple courses endpoint returns array in "courses" key
4143
loop do
42-
response.success? && response.raw_response.body["courses"].each do |course|
44+
break unless response.success? && response.raw_response.body.is_a?(Hash)
45+
46+
courses = response.raw_response.body["courses"]
47+
break unless courses.is_a?(Array)
48+
49+
courses.each do |course|
4350
enum << ScormEngine::Models::Course.new_from_api(course)
4451
end
45-
break if !response.success? || response.raw_response.body["more"].nil?
46-
response = get(response.raw_response.body["more"])
52+
53+
more_url = response.raw_response.body["more"]
54+
break if more_url.nil?
55+
56+
response = get(more_url)
4757
end
4858
end
4959
end

lib/scorm_engine/models/course_import.rb

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,23 +27,31 @@ def self.new_from_api(options = {})
2727
this.options = options.dup
2828

2929
if options.key?("importResult")
30-
# API v2 response structure: status is at top level, importResult contains nested data
31-
# Also handles legacy test format with "result" and "importResult"
32-
this.id = options["jobId"] || options["result"]
33-
this.status = options["status"]&.upcase || options.fetch("importResult", {})["status"]&.upcase
34-
this.parser_warnings = options.fetch("importResult", {})["parserWarnings"]
35-
# Course data is nested in importResult for API v2
36-
this.course = Course.new_from_api(options.fetch("importResult", {})["course"]) if options.fetch("importResult", {}).key?("course")
37-
elsif options.key?("result") && options.size == 1
38-
# Initial import response format: {"result": "job-id"}
39-
this.id = options["result"]
40-
this.status = "RUNNING" # Assume running for initial response
30+
# API v2 response
31+
import_result = options["importResult"] || {}
32+
33+
this.id = options["jobId"] || options["result"]
34+
this.status = (options["status"] || import_result["status"])&.upcase
35+
this.parser_warnings = import_result["parserWarnings"]
36+
37+
if import_result.key?("course")
38+
this.course = Course.new_from_api(import_result["course"])
39+
end
40+
41+
elsif options.keys == ["result"]
42+
# Initial import response (legacy format: {"result" => "job-id"})
43+
this.id = options["result"]
44+
this.status = "RUNNING"
45+
4146
else
42-
# API v1 response structure or full status response
43-
this.id = options["jobId"]
44-
this.status = options["status"]&.upcase
45-
this.parser_warnings = options["parserWarnings"] # Handle API v1 parserWarnings at top level
46-
this.course = Course.new_from_api(options["course"]) if options.key?("course") # unavailable in error states
47+
# API v1 or full status response
48+
this.id = options["jobId"]
49+
this.status = options["status"]&.upcase
50+
this.parser_warnings = options["parserWarnings"]
51+
52+
if options.key?("course")
53+
this.course = Course.new_from_api(options["course"])
54+
end
4755
end
4856

4957
this

spec/scorm_engine/api/endpoints/courses/import_spec.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,21 @@
2323
end
2424

2525
describe "arguments posted to the api" do
26-
it "works in the general case" do
26+
before do
2727
allow(client).to receive(:post)
28+
end
29+
30+
it "works in the general case" do
2831
client.post_course_import(course_id: "id123", url: "http://path.to/scorm.zip")
2932
expect(client).to have_received(:post).with("courses/importJobs", { courseId: "id123", mayCreateNewVersion: false }, { url: "http://path.to/scorm.zip" })
3033
end
3134

3235
it "allows creating a new version" do
33-
allow(client).to receive(:post)
3436
client.post_course_import(course_id: "id123", url: "http://path.to/scorm.zip", may_create_new_version: true)
3537
expect(client).to have_received(:post).with("courses/importJobs", { courseId: "id123", mayCreateNewVersion: true }, { url: "http://path.to/scorm.zip" })
3638
end
3739

3840
it "allows overriding course name" do
39-
allow(client).to receive(:post)
4041
client.post_course_import(course_id: "id123", url: "http://path.to/scorm.zip", name: "the name")
4142
expect(client).to have_received(:post).with("courses/importJobs", { courseId: "id123", mayCreateNewVersion: false }, { url: "http://path.to/scorm.zip" })
4243
end

spec/scorm_engine/client_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,25 @@
1313
it "defaults to API version 2" do
1414
expect(client.current_api_version).to eq(2)
1515
end
16+
17+
context "with tenant_creator provided" do
18+
let(:tenant_creator) { instance_spy("TenantCreator") }
19+
let(:client_with_creator) { described_class.new(tenant: tenant, tenant_creator: tenant_creator) }
20+
21+
it "sets the tenant_creator" do
22+
expect(client_with_creator.tenant_creator).to eq(tenant_creator)
23+
end
24+
25+
it "stores the tenant_creator for later use" do
26+
expect(client_with_creator.instance_variable_get(:@tenant_creator)).to eq(tenant_creator)
27+
end
28+
end
29+
30+
context "without tenant_creator" do
31+
it "sets tenant_creator to nil" do
32+
expect(client.tenant_creator).to be_nil
33+
end
34+
end
1635
end
1736

1837
describe "#current_api_version" do

spec/scorm_engine/faraday/connection_spec.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
describe "#connection" do
3030
describe "version" do
31-
it "passes on to #base_uri" do
31+
it "passes on to #base_uri with version 2" do
3232
client = ScormEngine::Client.new(tenant: "test")
3333

3434
allow(client).to receive(:base_uri).and_call_original
@@ -37,6 +37,16 @@
3737

3838
expect(client).to have_received(:base_uri).with(version: 2)
3939
end
40+
41+
it "passes on to #base_uri with version 1" do
42+
client = ScormEngine::Client.new(tenant: "test")
43+
44+
allow(client).to receive(:base_uri).and_call_original
45+
46+
client.send(:connection, version: 1)
47+
48+
expect(client).to have_received(:base_uri).with(version: 1)
49+
end
4050
end
4151
end
4252
end

spec/scorm_engine/faraday/request_spec.rb

Lines changed: 48 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -197,135 +197,75 @@ def base_uri
197197
end
198198

199199
describe "with tenant_creator provided" do
200-
it "creates tenant and retries request successfully" do
201-
tenant_creator = instance_spy("TenantCreator")
202-
client = tenant_mock_class.new("test-tenant", tenant_creator: tenant_creator)
203-
mock_connection = instance_spy(Faraday::Connection)
204-
mock_request = instance_spy(Faraday::Request)
205-
206-
tenant_not_found_response = instance_double(
200+
let(:tenant_creator) { instance_spy("TenantCreator") }
201+
let(:client) { tenant_mock_class.new("test-tenant", tenant_creator: tenant_creator) }
202+
let(:mock_connection) { instance_spy(Faraday::Connection) }
203+
let(:mock_request) { instance_spy(Faraday::Request) }
204+
let(:tenant_not_found_response) do
205+
instance_double(
207206
Faraday::Response,
208207
status: 400,
209208
success?: false,
210209
body: { "message" => "test-tenant is not a valid tenant name" }
211210
)
211+
end
212212

213+
before do
213214
allow(mock_connection).to receive(:get).and_yield(mock_request).and_return(tenant_not_found_response)
214215
allow(mock_request).to receive(:headers).and_return({})
215216
allow(mock_request).to receive(:url)
216217
allow(client).to receive(:connection).and_return(mock_connection)
218+
end
217219

220+
it "creates tenant and retries request successfully" do
218221
response = client.send(:request, :get, "courses", {})
219222
expect(response.raw_response.status).to eq(400)
220223
end
221224

222225
it "calls the connection when retrying" do
223-
tenant_creator = instance_spy("TenantCreator")
224-
client = tenant_mock_class.new("test-tenant", tenant_creator: tenant_creator)
225-
mock_connection = instance_spy(Faraday::Connection)
226-
mock_request = instance_spy(Faraday::Request)
227-
228-
tenant_not_found_response = instance_double(
229-
Faraday::Response,
230-
status: 400,
231-
success?: false,
232-
body: { "message" => "test-tenant is not a valid tenant name" }
233-
)
234-
235-
allow(mock_connection).to receive(:get).and_yield(mock_request).and_return(tenant_not_found_response)
236-
allow(mock_request).to receive(:headers).and_return({})
237-
allow(mock_request).to receive(:url)
238-
allow(client).to receive(:connection).and_return(mock_connection)
239-
240226
client.send(:request, :get, "courses", {})
241227
expect(mock_connection).to have_received(:get).at_least(:once)
242228
end
243229

244230
it "calls tenant_creator with the tenant name" do
245-
tenant_creator = instance_spy("TenantCreator")
246-
client = tenant_mock_class.new("test-tenant", tenant_creator: tenant_creator)
247-
mock_connection = instance_spy(Faraday::Connection)
248-
mock_request = instance_spy(Faraday::Request)
249-
250-
tenant_not_found_response = instance_double(
251-
Faraday::Response,
252-
status: 400,
253-
success?: false,
254-
body: { "message" => "test-tenant is not a valid tenant name" }
255-
)
256-
257-
allow(mock_connection).to receive(:get).and_yield(mock_request).and_return(tenant_not_found_response)
258-
allow(mock_request).to receive(:headers).and_return({})
259-
allow(mock_request).to receive(:url)
260-
allow(client).to receive(:connection).and_return(mock_connection)
261-
262231
client.send(:request, :get, "courses", {})
263232
expect(tenant_creator).to have_received(:call).with("test-tenant")
264233
end
265234

266235
it "returns original error if tenant creation fails" do
267-
tenant_creator = instance_spy("TenantCreator")
268-
client = tenant_mock_class.new("test-tenant", tenant_creator: tenant_creator)
269-
mock_connection = instance_spy(Faraday::Connection)
270-
mock_request = instance_spy(Faraday::Request)
271-
272-
tenant_not_found_response = instance_double(
273-
Faraday::Response,
274-
status: 400,
275-
success?: false,
276-
body: { "message" => "test-tenant is not a valid tenant name" }
277-
)
278-
279236
allow(tenant_creator).to receive(:call).and_raise(StandardError, "Creation failed")
280-
allow(mock_connection).to receive(:get).and_yield(mock_request).and_return(tenant_not_found_response)
281-
allow(mock_request).to receive(:headers).and_return({})
282-
allow(mock_request).to receive(:url)
283-
allow(client).to receive(:connection).and_return(mock_connection)
284237

285238
response = client.send(:request, :get, "courses", {})
286239
expect(response.raw_response.status).to eq(400)
287240
end
288241
end
289242

290243
describe "without tenant_creator" do
291-
it "returns the original error" do
292-
client = tenant_mock_class.new("test-tenant")
293-
mock_connection = instance_spy(Faraday::Connection)
294-
mock_request = instance_spy(Faraday::Request)
295-
296-
tenant_not_found_response = instance_double(
244+
let(:client) { tenant_mock_class.new("test-tenant") }
245+
let(:mock_connection) { instance_spy(Faraday::Connection) }
246+
let(:mock_request) { instance_spy(Faraday::Request) }
247+
let(:tenant_not_found_response) do
248+
instance_double(
297249
Faraday::Response,
298250
status: 400,
299251
success?: false,
300252
body: { "message" => "test-tenant is not a valid tenant name" }
301253
)
254+
end
302255

256+
before do
303257
allow(mock_connection).to receive(:get).and_yield(mock_request).and_return(tenant_not_found_response)
304258
allow(mock_request).to receive(:headers).and_return({})
305259
allow(mock_request).to receive(:url)
306260
allow(client).to receive(:connection).and_return(mock_connection)
261+
end
307262

263+
it "returns the original error" do
308264
response = client.send(:request, :get, "courses", {})
309265
expect(response.raw_response.status).to eq(400)
310266
end
311267

312268
it "calls the connection" do
313-
client = tenant_mock_class.new("test-tenant")
314-
mock_connection = instance_spy(Faraday::Connection)
315-
mock_request = instance_spy(Faraday::Request)
316-
317-
tenant_not_found_response = instance_double(
318-
Faraday::Response,
319-
status: 400,
320-
success?: false,
321-
body: { "message" => "test-tenant is not a valid tenant name" }
322-
)
323-
324-
allow(mock_connection).to receive(:get).and_yield(mock_request).and_return(tenant_not_found_response)
325-
allow(mock_request).to receive(:headers).and_return({})
326-
allow(mock_request).to receive(:url)
327-
allow(client).to receive(:connection).and_return(mock_connection)
328-
329269
client.send(:request, :get, "courses", {})
330270
expect(mock_connection).to have_received(:get)
331271
end
@@ -352,6 +292,35 @@ def base_uri
352292
wrapped_response = ScormEngine::Response.new(raw_response: server_error)
353293
expect(client.send(:should_retry_with_tenant_creation?, wrapped_response)).to be false
354294
end
295+
296+
context "when response_or_error responds to :response" do
297+
it "returns true for tenant not found error in wrapped response" do
298+
client = tenant_mock_class.new("test-tenant")
299+
tenant_error = instance_double(Faraday::Response, status: 400, body: "test-tenant is not a valid tenant name")
300+
error_with_response = instance_double("ErrorObject", response: tenant_error)
301+
expect(client.send(:should_retry_with_tenant_creation?, error_with_response)).to be true
302+
end
303+
304+
it "returns false for other 400 errors in wrapped response" do
305+
client = tenant_mock_class.new("test-tenant")
306+
other_error = instance_double(Faraday::Response, status: 400, body: "Some other validation error")
307+
error_with_response = instance_double("ErrorObject", response: other_error)
308+
expect(client.send(:should_retry_with_tenant_creation?, error_with_response)).to be false
309+
end
310+
311+
it "returns false for non-400 errors in wrapped response" do
312+
client = tenant_mock_class.new("test-tenant")
313+
server_error = instance_double(Faraday::Response, status: 500, body: "Internal server error")
314+
error_with_response = instance_double("ErrorObject", response: server_error)
315+
expect(client.send(:should_retry_with_tenant_creation?, error_with_response)).to be false
316+
end
317+
318+
it "handles nil response gracefully" do
319+
client = tenant_mock_class.new("test-tenant")
320+
error_with_nil_response = instance_double("ErrorObject", response: nil)
321+
expect(client.send(:should_retry_with_tenant_creation?, error_with_nil_response)).to be false
322+
end
323+
end
355324
end
356325
end
357326
end

spec/scorm_engine/models/course_configuration_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,39 @@
3535
end
3636
end
3737

38+
context "with valid setting items (alternative key)" do
39+
let(:valid_setting_items_response) do
40+
{
41+
"settingItems" => [
42+
{
43+
"id" => "setting_one",
44+
"value" => "value_one"
45+
},
46+
{
47+
"id" => "setting_two",
48+
"value" => "value_two"
49+
},
50+
{
51+
"id" => "boolean_setting",
52+
"value" => "true"
53+
},
54+
]
55+
}
56+
end
57+
58+
it "converts setting items to hash correctly type" do
59+
settings = described_class.get_settings_from_api(valid_setting_items_response)
60+
61+
expect(settings).to be_a(Hash)
62+
end
63+
64+
it "converts setting items to hash correctly value" do
65+
settings = described_class.get_settings_from_api(valid_setting_items_response)
66+
67+
expect(settings).to eq({ "setting_one" => "value_one", "setting_two" => "value_two", "boolean_setting" => "true" })
68+
end
69+
end
70+
3871
context "with empty configuration items" do
3972
let(:empty_response) do
4073
{

0 commit comments

Comments
 (0)