diff --git a/spec/controllers/course/statistics/assessment_controller_spec.rb b/spec/controllers/course/statistics/assessment_controller_spec.rb index 200fdfdce47..e8ccb27c33b 100644 --- a/spec/controllers/course/statistics/assessment_controller_spec.rb +++ b/spec/controllers/course/statistics/assessment_controller_spec.rb @@ -163,6 +163,118 @@ end end + describe '#live_feedback_statistics' do + render_views + + let(:question1) { create(:course_assessment_question_programming, assessment: assessment).acting_as } + let(:question2) { create(:course_assessment_question_multiple_response, assessment: assessment).acting_as } + let!(:course_student) { students[0] } + + before do + create_list(:course_assessment_live_feedback, 3, + assessment: assessment, + question: question1, + creator: course_student.user, + with_comment: true) + create(:course_assessment_live_feedback, assessment: assessment, question: question2, + creator: course_student.user) + end + + subject do + get :live_feedback_statistics, as: :json, params: { course_id: course, id: assessment.id } + end + + context 'when the Normal User tries to get live feedback statistics' do + let(:user) { create(:user) } + before { controller_sign_in(controller, user) } + + it { expect { subject }.to raise_exception(CanCan::AccessDenied) } + end + + context 'when the Course Student tries to get live feedback statistics' do + let(:user) { create(:course_student, course: course).user } + before { controller_sign_in(controller, user) } + + it { expect { subject }.to raise_exception(CanCan::AccessDenied) } + end + + context 'when the Course Manager gets live feedback statistics' do + let(:user) { create(:course_manager, course: course).user } + before { controller_sign_in(controller, user) } + + it 'returns OK with the correct live feedback statistics' do + expect(subject).to have_http_status(:success) + json_result = JSON.parse(response.body) + + first_result = json_result.first + + # Check the general structure + expect(first_result).to have_key('courseUser') + expect(first_result['courseUser']).to have_key('id') + expect(first_result['courseUser']).to have_key('name') + expect(first_result['courseUser']).to have_key('role') + expect(first_result['courseUser']).to have_key('isPhantom') + + expect(first_result).to have_key('workflowState') + expect(first_result).to have_key('groups') + expect(first_result).to have_key('liveFeedbackCount') + expect(first_result).to have_key('questionIds') + + # Ensure that the feedback count is correct for the specific questions + question_index = first_result['questionIds'].index(question1.id) + if first_result['courseUser']['id'] == course_student.id + expect(first_result['liveFeedbackCount'][question_index]).to eq(3) + else + expect(first_result['liveFeedbackCount'][question_index]).to eq(0) + end + + # No feedback for the second question, since there is no comment + question_index = first_result['questionIds'].index(question2.id) + if first_result['courseUser']['id'] == course_student.id + expect(first_result['liveFeedbackCount'][question_index]).to eq(0) + end + end + end + + context 'when the Administrator gets live feedback statistics' do + let(:administrator) { create(:administrator) } + before { controller_sign_in(controller, administrator) } + + it 'returns OK with the correct live feedback statistics' do + expect(subject).to have_http_status(:success) + json_result = JSON.parse(response.body) + + first_result = json_result.first + + # Check the general structure + expect(first_result).to have_key('courseUser') + expect(first_result['courseUser']).to have_key('id') + expect(first_result['courseUser']).to have_key('name') + expect(first_result['courseUser']).to have_key('role') + expect(first_result['courseUser']).to have_key('isPhantom') + + expect(first_result).to have_key('workflowState') + expect(first_result).to have_key('groups') + expect(first_result).to have_key('liveFeedbackCount') + expect(first_result).to have_key('questionIds') + + # Ensure that the feedback count is correct for the specific questions + question_index = first_result['questionIds'].index(question1.id) + if first_result['courseUser']['id'] == course_student.id + expect(first_result['liveFeedbackCount'][question_index]).to eq(3) + else + expect(first_result['liveFeedbackCount'][question_index]).to eq(0) + end + + # No feedback for the second question, since there is no comment + question_index = first_result['questionIds'].index(question2.id) + if first_result['courseUser']['id'] == course_student.id + expect(first_result['liveFeedbackCount'][question_index]).to eq(0) + end + end + end + end + describe '#live_feedback_history' do let(:question) do create(:course_assessment_question_programming, assessment: assessment).acting_as @@ -173,13 +285,10 @@ let!(:live_feedback) do create(:course_assessment_live_feedback, assessment: assessment, question: question, - creator: course_student) - end - let!(:code) { create(:course_assessment_live_feedback_code, feedback: live_feedback) } - let!(:comment) do - create(:course_assessment_live_feedback_comment, code: code, line_number: 1, - comment: 'This is a test comment') + creator: user, + with_comment: true) end + render_views subject do get :live_feedback_history, as: :json, diff --git a/spec/factories/course_assessment_live_feedback_code.rb b/spec/factories/course_assessment_live_feedback_code.rb index 8469cf30648..ed879b2bcb3 100644 --- a/spec/factories/course_assessment_live_feedback_code.rb +++ b/spec/factories/course_assessment_live_feedback_code.rb @@ -4,5 +4,13 @@ feedback { association(:course_assessment_live_feedback) } filename { 'test_code.rb' } content { 'puts "Hello, World!"' } + + transient do + with_comment { false } + end + + after(:create) do |live_feedback_code, evaluator| + create(:course_assessment_live_feedback_comment, code: live_feedback_code) if evaluator.with_comment + end end end diff --git a/spec/factories/course_assessment_live_feedbacks.rb b/spec/factories/course_assessment_live_feedbacks.rb index 3133b5dc9a9..c934b9867e6 100644 --- a/spec/factories/course_assessment_live_feedbacks.rb +++ b/spec/factories/course_assessment_live_feedbacks.rb @@ -2,7 +2,14 @@ FactoryBot.define do factory :course_assessment_live_feedback, class: Course::Assessment::LiveFeedback do assessment - question { association(:course_assessment_question_programming, assessment: assessment) } - creator { association(:course_user, course: assessment.course) } + question { association(:course_assessment_question, assessment: assessment) } + + transient do + with_comment { false } + end + + after(:create) do |live_feedback, evaluator| + create(:course_assessment_live_feedback_code, feedback: live_feedback, with_comment: evaluator.with_comment) + end end end diff --git a/spec/models/course/assessment/live_feedback_spec.rb b/spec/models/course/assessment/live_feedback_spec.rb new file mode 100644 index 00000000000..0895dc9db30 --- /dev/null +++ b/spec/models/course/assessment/live_feedback_spec.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true +require 'rails_helper' + +RSpec.describe Course::Assessment::LiveFeedback do + # Associations + it { is_expected.to belong_to(:assessment).class_name('Course::Assessment').inverse_of(:live_feedbacks).required } + it { + is_expected.to belong_to(:question).class_name('Course::Assessment::Question').inverse_of(:live_feedbacks).required + } + it { + is_expected.to have_many(:code). + class_name('Course::Assessment::LiveFeedbackCode'). + inverse_of(:feedback). + dependent(:destroy) + } + + let(:instance) { Instance.default } + with_tenant(:instance) do + let(:assessment) { create(:assessment) } + let(:question) { create(:course_assessment_question_programming, assessment: assessment) } + let(:user) { create(:course_user, course: assessment.course).user } + let(:files) do + [ + Struct.new(:filename, :content).new('test1.rb', 'Hello World'), + Struct.new(:filename, :content).new('test2.rb', 'Goodbye World') + ] + end + + describe '.create_with_codes' do + context 'when the live feedback is successfully created' do + it 'creates a live feedback with associated codes' do + feedback = Course::Assessment::LiveFeedback.create_with_codes( + assessment.id, question.id, user, nil, files + ) + + expect(feedback).to be_persisted + expect(feedback.code.size).to eq(files.size) + expect(feedback.code.map(&:filename)).to match_array(files.map(&:filename)) + expect(feedback.code.map(&:content)).to match_array(files.map(&:content)) + end + end + + context 'when the live feedback fails to save' do + it 'returns nil and logs an error' do + allow_any_instance_of(Course::Assessment::LiveFeedback).to receive(:save).and_return(false) + + expect(Rails.logger).to receive(:error).with(/Failed to save live_feedback/) + feedback = Course::Assessment::LiveFeedback.create_with_codes( + assessment.id, question.id, user, nil, files + ) + + expect(feedback).to be_nil + end + end + + context 'when a live feedback code fails to save' do + it 'logs an error and continues to create the live feedback' do + allow_any_instance_of(Course::Assessment::LiveFeedbackCode).to receive(:save).and_return(false) + + expect(Rails.logger).to receive(:error).with(/Failed to save live_feedback_code/).twice + feedback = Course::Assessment::LiveFeedback.create_with_codes( + assessment.id, question.id, user, nil, files + ) + + expect(feedback).to be_persisted + expect(feedback.code.size).to eq(0) # No codes should be saved + end + end + end + end +end