diff --git a/app/assets/javascripts/editor/editor.js.erb b/app/assets/javascripts/editor/editor.js.erb index 4e26723a..c4cb46f9 100644 --- a/app/assets/javascripts/editor/editor.js.erb +++ b/app/assets/javascripts/editor/editor.js.erb @@ -436,7 +436,6 @@ configureEditors: function () { url: $('#editor').data('errors-url') }); jqxhr.always(this.hideSpinner); - jqxhr.done(this.renderHint); }, toggleButtonStates: function () { @@ -482,7 +481,6 @@ configureEditors: function () { resetOutputTab: function () { this.clearOutput(); - $('#hint').fadeOut(); $('#flowrHint').fadeOut(); this.clearHints(); this.showOutputBar(); diff --git a/app/assets/javascripts/editor/evaluation.js b/app/assets/javascripts/editor/evaluation.js index 79bd52a8..5e631b2b 100644 --- a/app/assets/javascripts/editor/evaluation.js +++ b/app/assets/javascripts/editor/evaluation.js @@ -57,14 +57,6 @@ CodeOceanEditorEvaluation = { } }, - renderHint: function (object) { - var hint = object.data || object.hint; - if (hint) { - $('#hint .card-body').text(hint); - $('#hint').fadeIn(); - } - }, - renderScore: function () { var score = parseFloat($('#score').data('score')); var maximum_score = parseFloat($('#score').data('maximum-score')); diff --git a/app/assets/stylesheets/editor.css.scss b/app/assets/stylesheets/editor.css.scss index dff00f6f..63888039 100644 --- a/app/assets/stylesheets/editor.css.scss +++ b/app/assets/stylesheets/editor.css.scss @@ -64,10 +64,6 @@ button i.fa-spin { overflow: auto; } -#hint { - display: none; -} - #outputInformation { #output { max-height: 500px; diff --git a/app/controllers/code_ocean/errors_controller.rb b/app/controllers/code_ocean/errors_controller.rb deleted file mode 100644 index 6b594dae..00000000 --- a/app/controllers/code_ocean/errors_controller.rb +++ /dev/null @@ -1,45 +0,0 @@ -module CodeOcean - class ErrorsController < ApplicationController - before_action :set_execution_environment - - def authorize! - authorize(@error || @errors) - end - private :authorize! - - def create - @error = CodeOcean::Error.new(error_params) - authorize! - hint = Whistleblower.new(execution_environment: @error.execution_environment).generate_hint(@error.message) - respond_to do |format| - format.json do - if hint - render(json: {hint: hint}) - else - head (@error.save ? :created : :unprocessable_entity) - end - end - end - end - - def error_params - params[:error].permit(:message, :submission_id).merge(execution_environment_id: @execution_environment.id) if params[:error].present? - end - private :error_params - - def index - @errors = CodeOcean::Error.for_execution_environment(@execution_environment).grouped_by_message.paginate(page: params[:page]) - authorize! - end - - def set_execution_environment - @execution_environment = ExecutionEnvironment.find(params[:execution_environment_id]) - end - private :set_execution_environment - - def show - @error = CodeOcean::Error.find(params[:id]) - authorize! - end - end -end \ No newline at end of file diff --git a/app/controllers/hints_controller.rb b/app/controllers/hints_controller.rb deleted file mode 100644 index 48b86b63..00000000 --- a/app/controllers/hints_controller.rb +++ /dev/null @@ -1,57 +0,0 @@ -class HintsController < ApplicationController - include CommonBehavior - - before_action :set_execution_environment - before_action :set_hint, only: MEMBER_ACTIONS - - def authorize! - authorize(@hint || @hints) - end - private :authorize! - - def create - @hint = Hint.new(hint_params) - authorize! - create_and_respond(object: @hint, path: proc { execution_environment_hint_path(@execution_environment, @hint) }) - end - - def destroy - destroy_and_respond(object: @hint, path: execution_environment_hints_path(@execution_environment)) - end - - def edit - end - - def hint_params - params[:hint].permit(:locale, :message, :name, :regular_expression).merge(execution_environment_id: @execution_environment.id) if params[:hint].present? - end - private :hint_params - - def index - @hints = @execution_environment.hints.order(:name).paginate(page: params[:page]) - authorize! - end - - def new - @hint = Hint.new - authorize! - end - - def set_execution_environment - @execution_environment = ExecutionEnvironment.find(params[:execution_environment_id]) - end - private :set_execution_environment - - def set_hint - @hint = Hint.find(params[:id]) - authorize! - end - private :set_hint - - def show - end - - def update - update_and_respond(object: @hint, params: hint_params, path: execution_environment_hint_path(@execution_environment, @hint)) - end -end diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb index 32d1e560..039efa02 100644 --- a/app/controllers/submissions_controller.rb +++ b/app/controllers/submissions_controller.rb @@ -126,14 +126,6 @@ class SubmissionsController < ApplicationController # server_sent_event.write({stdout: output[:stdout]}, event: 'output') if output[:stdout] # server_sent_event.write({stderr: output[:stderr]}, event: 'output') if output[:stderr] - - # unless output[:stderr].nil? - # if hint = Whistleblower.new(execution_environment: @submission.execution_environment).generate_hint(output[:stderr]) - # server_sent_event.write(hint, event: 'hint') - # else - # store_error(output[:stderr]) - # end - # end # end hijack do |tubesock| @@ -365,11 +357,6 @@ class SubmissionsController < ApplicationController head :ok end - def store_error(stderr) - CodeOcean::Error.create(submission_id: @submission.id, execution_environment_id: @submission.execution_environment.id, message: stderr) - end - private :store_error - def test hijack do |tubesock| Thread.new { EventMachine.run } unless EventMachine.reactor_running? && EventMachine.reactor_thread.alive? diff --git a/app/models/code_ocean/error.rb b/app/models/code_ocean/error.rb deleted file mode 100644 index 935962cf..00000000 --- a/app/models/code_ocean/error.rb +++ /dev/null @@ -1,19 +0,0 @@ -module CodeOcean - class Error < ApplicationRecord - belongs_to :execution_environment - - scope :for_execution_environment, ->(execution_environment) { where(execution_environment_id: execution_environment.id) } - scope :grouped_by_message, -> { select('MAX(created_at) AS created_at, MAX(id) AS id, message, COUNT(id) AS count').group(:message).order('count DESC') } - - validates :execution_environment_id, presence: true - validates :message, presence: true - - def self.nested_resource? - true - end - - def to_s - id.to_s - end - end -end diff --git a/app/models/execution_environment.rb b/app/models/execution_environment.rb index 613fb082..33d57868 100644 --- a/app/models/execution_environment.rb +++ b/app/models/execution_environment.rb @@ -10,7 +10,6 @@ class ExecutionEnvironment < ApplicationRecord has_many :exercises belongs_to :file_type - has_many :hints has_many :error_templates scope :with_exercises, -> { where('id IN (SELECT execution_environment_id FROM exercises)') } diff --git a/app/models/hint.rb b/app/models/hint.rb deleted file mode 100644 index b54ba8a2..00000000 --- a/app/models/hint.rb +++ /dev/null @@ -1,17 +0,0 @@ -class Hint < ApplicationRecord - belongs_to :execution_environment - - validates :execution_environment_id, presence: true - validates :locale, presence: true - validates :message, presence: true - validates :name, presence: true - validates :regular_expression, presence: true - - def self.nested_resource? - true - end - - def to_s - name - end -end diff --git a/app/policies/code_ocean/error_policy.rb b/app/policies/code_ocean/error_policy.rb deleted file mode 100644 index 376af8d8..00000000 --- a/app/policies/code_ocean/error_policy.rb +++ /dev/null @@ -1,7 +0,0 @@ -module CodeOcean - class ErrorPolicy < AdminOrAuthorPolicy - def author? - @user == @record.execution_environment.author - end - end -end diff --git a/app/policies/hint_policy.rb b/app/policies/hint_policy.rb deleted file mode 100644 index 4c0a08fd..00000000 --- a/app/policies/hint_policy.rb +++ /dev/null @@ -1,5 +0,0 @@ -class HintPolicy < AdminOrAuthorPolicy - def author? - @user == @record.execution_environment.author - end -end diff --git a/app/views/code_ocean/errors/index.html.slim b/app/views/code_ocean/errors/index.html.slim deleted file mode 100644 index dbfa2312..00000000 --- a/app/views/code_ocean/errors/index.html.slim +++ /dev/null @@ -1,19 +0,0 @@ -h1 = CodeOcean::Error.model_name.human(count: 2) - -.table-responsive - table.table - thead - tr - th = t('errors.index.count') - th = t('activerecord.attributes.error.message') - th = t('shared.created_at') - th = t('shared.actions') - tbody - - @errors.each do |error| - tr - td = error.count - td = error.message - td = l(error.created_at, format: :short) - td = link_to(t('shared.show'), execution_environment_error_path(params[:execution_environment_id], error)) - -p = render('shared/pagination', collection: @errors) diff --git a/app/views/code_ocean/errors/show.html.slim b/app/views/code_ocean/errors/show.html.slim deleted file mode 100644 index 1bb6623e..00000000 --- a/app/views/code_ocean/errors/show.html.slim +++ /dev/null @@ -1,4 +0,0 @@ -h1 = CodeOcean::Error.model_name.human - -= row(label: 'error.message', value: @error.message) -= row(label: 'shared.created_at', value: l(@error.created_at, format: :short)) diff --git a/app/views/execution_environments/index.html.slim b/app/views/execution_environments/index.html.slim index 20e99620..a749df7c 100644 --- a/app/views/execution_environments/index.html.slim +++ b/app/views/execution_environments/index.html.slim @@ -11,7 +11,6 @@ h1 = ExecutionEnvironment.model_name.human(count: 2) th = t('activerecord.attributes.execution_environment.network_enabled') th = t('activerecord.attributes.execution_environment.permitted_execution_time') th colspan=5 = t('shared.actions') - th colspan=2 = t('shared.resources') tbody - @execution_environments.each do |execution_environment| tr @@ -26,8 +25,6 @@ h1 = ExecutionEnvironment.model_name.human(count: 2) td = link_to(t('shared.destroy'), execution_environment, data: {confirm: t('shared.confirm_destroy')}, method: :delete) td = link_to(t('.shell'), shell_execution_environment_path(execution_environment)) td = link_to(t('shared.statistics'), statistics_execution_environment_path(execution_environment)) - td = link_to(t('activerecord.models.error.other'), execution_environment_errors_path(execution_environment.id)) - td = link_to(t('activerecord.models.hint.other'), execution_environment_hints_path(execution_environment.id)) = render('shared/pagination', collection: @execution_environments) p = render('shared/new_button', model: ExecutionEnvironment) diff --git a/app/views/exercises/_editor.html.slim b/app/views/exercises/_editor.html.slim index 44fc0aec..700a9855 100644 --- a/app/views/exercises/_editor.html.slim +++ b/app/views/exercises/_editor.html.slim @@ -4,7 +4,7 @@ - show_break_interventions = @show_break_interventions || "false" - show_rfc_interventions = @show_rfc_interventions || "false" - hide_rfc_button = @hide_rfc_button || false -#editor.row data-exercise-id=@exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-errors-url=execution_environment_errors_path(exercise.execution_environment) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise) +#editor.row data-exercise-id=@exercise.id data-message-depleted=t('exercises.editor.depleted') data-message-timeout=t('exercises.editor.timeout', permitted_execution_time: @exercise.execution_environment.permitted_execution_time) data-submissions-url=submissions_path data-user-id=@current_user.id data-user-external-id=external_user_external_id data-working-times-url=working_times_exercise_path(@exercise) data-intervention-save-url=intervention_exercise_path(@exercise) data-rfc-interventions=show_rfc_interventions data-break-interventions=show_break_interventions data-course_token=@course_token data-search-save-url=search_exercise_path(@exercise) div id="sidebar" class=(@exercise.hide_file_tree ? 'sidebar-col-collapsed' : 'sidebar-col') = render('editor_file_tree', exercise: @exercise, files: @files) div.editor-col.col.p-0 id='frames' #editor-buttons.btn-group.enforce-bottom-margin diff --git a/app/views/exercises/_editor_output.html.slim b/app/views/exercises/_editor_output.html.slim index 133b558e..dd6849da 100644 --- a/app/views/exercises/_editor_output.html.slim +++ b/app/views/exercises/_editor_output.html.slim @@ -37,11 +37,6 @@ div.h-100 id='output_sidebar_uncollapsed' class='d-none col-sm-12 enforce-bottom div.enforce-big-top-margin #turtlediv canvas#turtlecanvas.d-none width=400 height=400 - div.enforce-big-top-margin - #hint - .card.bg-warning.text-white - .card-header = t('exercises.implement.hint') - .card-body div.enforce-big-top-margin #prompt.input-group.d-none div.input-group-prepend diff --git a/app/views/hints/_form.html.slim b/app/views/hints/_form.html.slim deleted file mode 100644 index 21ab6bc1..00000000 --- a/app/views/hints/_form.html.slim +++ /dev/null @@ -1,17 +0,0 @@ -= form_for(@hint, url: execution_environment_hints_path(params[:execution_environment_id])) do |f| - = render('shared/form_errors', object: @hint) - .form-group - = f.label(:name) - = f.text_field(:name, class: 'form-control', required: true) - .form-group - = f.label(:locale) - = f.select(:locale, I18n.available_locales.map { |locale| [t("locales.#{locale}"), locale] }, {}, class: 'form-control') - .form-group - = f.label(:message) - = f.text_field(:message, class: 'form-control', placeholder: "'$2' has no method '$1'.", required: true) - .help-block.form-text = t('.hints.message') - .form-group - = f.label(:regular_expression) - = f.text_field(:regular_expression, class: 'form-control', placeholder: 'undefined method (\w+) for (\w+)', required: true) - .help-block.form-text = t('.hints.regular_expression') - .actions = render('shared/submit_button', f: f, object: @hint) diff --git a/app/views/hints/edit.html.slim b/app/views/hints/edit.html.slim deleted file mode 100644 index 0c1f4dac..00000000 --- a/app/views/hints/edit.html.slim +++ /dev/null @@ -1,3 +0,0 @@ -h1 = @hint - -= render('form') diff --git a/app/views/hints/index.html.slim b/app/views/hints/index.html.slim deleted file mode 100644 index 6a80bbdb..00000000 --- a/app/views/hints/index.html.slim +++ /dev/null @@ -1,20 +0,0 @@ -h1 = Hint.model_name.human(count: 2) - -.table-responsive - table.table - thead - tr - th = t('activerecord.attributes.hint.name') - th = t('activerecord.attributes.hint.locale') - th colspan=3 = t('shared.actions') - tbody - - @hints.each do |hint| - tr - td = hint.name - td = t("locales.#{hint.locale}") - td = link_to(t('shared.show'), execution_environment_hint_path(params[:execution_environment_id], hint.id)) - td = link_to(t('shared.edit'), edit_execution_environment_hint_path(params[:execution_environment_id], hint.id)) - td = link_to(t('shared.destroy'), execution_environment_hint_path(params[:execution_environment_id], hint.id), data: {confirm: t('shared.confirm_destroy')}, method: :delete) - -= render('shared/pagination', collection: @hints) -p = render('shared/new_button', model: Hint, path: new_execution_environment_hint_path(params[:execution_environment_id])) diff --git a/app/views/hints/new.html.slim b/app/views/hints/new.html.slim deleted file mode 100644 index a933bede..00000000 --- a/app/views/hints/new.html.slim +++ /dev/null @@ -1,3 +0,0 @@ -h1 = t('shared.new_model', model: Hint.model_name.human) - -= render('form') diff --git a/app/views/hints/show.html.slim b/app/views/hints/show.html.slim deleted file mode 100644 index 6db995f5..00000000 --- a/app/views/hints/show.html.slim +++ /dev/null @@ -1,8 +0,0 @@ -h1 - = @hint - = render('shared/edit_button', object: @hint, path: edit_execution_environment_hint_path(params[:execution_environment_id], @hint.id)) - -= row(label: 'hint.name', value: @hint.name) -= row(label: 'hint.locale', value: t("locales.#{@hint.locale}")) -= row(label: 'hint.message', value: @hint.message) -= row(label: 'hint.regular_expression', value: code_tag(@hint.regular_expression)) diff --git a/app/views/tags/index.html.slim b/app/views/tags/index.html.slim index 2d4916af..449a1165 100644 --- a/app/views/tags/index.html.slim +++ b/app/views/tags/index.html.slim @@ -5,8 +5,7 @@ h1 = Tag.model_name.human(count: 2) thead tr th = t('activerecord.attributes.hint.name') - /th = t('activerecord.attributes.hint.locale') - /th colspan=3 = t('shared.actions') + th colspan=3 = t('shared.actions') tbody - @tags.each do |tag| tr diff --git a/config/locales/de.yml b/config/locales/de.yml index fdaa2a18..4cbd120c 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -7,8 +7,6 @@ de: name: Name oauth_key: OAuth Key oauth_secret: OAuth Secret - error: - message: Nachricht execution_environment: docker_image: Docker-Image exposed_ports: Zugängliche Ports @@ -140,9 +138,6 @@ de: consumer: one: Konsument other: Konsumenten - error: - one: Fehler - other: Fehler error_template: one: Fehlertemplate other: Fehlertemplates @@ -229,10 +224,6 @@ de: consumers: show: link: Konsument - errors: - connection_refused: Verbindung abgelehnt - index: - count: Anzahl execution_environments: form: hints: diff --git a/config/locales/en.yml b/config/locales/en.yml index 5073d525..40922a02 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -7,8 +7,6 @@ en: name: Name oauth_key: OAuth Key oauth_secret: OAuth Secret - error: - message: Message execution_environment: docker_image: Docker Image exposed_ports: Exposed Ports @@ -140,9 +138,6 @@ en: consumer: one: Consumer other: Consumers - error: - one: Error - other: Errors error_template: one: Error Template other: Error Templates @@ -229,10 +224,6 @@ en: consumers: show: link: Consumer - errors: - connection_refused: Connection refused - index: - count: Count execution_environments: form: hints: diff --git a/config/routes.rb b/config/routes.rb index c146a69b..4b1a4719 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -64,9 +64,6 @@ Rails.application.routes.draw do post 'shell', as: :execute_command, action: :execute_command get :statistics end - - resources :errors, only: [:create, :index, :show], controller: 'code_ocean/errors' - resources :hints end post '/import_proforma_xml' => 'exercises#import_proforma_xml' diff --git a/db/migrate/20181127160857_drop_hints.rb b/db/migrate/20181127160857_drop_hints.rb new file mode 100644 index 00000000..83f0905e --- /dev/null +++ b/db/migrate/20181127160857_drop_hints.rb @@ -0,0 +1,5 @@ +class DropHints < ActiveRecord::Migration[5.2] + def change + drop_table :hints + end +end diff --git a/db/migrate/20181129093207_drop_errors.rb b/db/migrate/20181129093207_drop_errors.rb new file mode 100644 index 00000000..fde39f6d --- /dev/null +++ b/db/migrate/20181129093207_drop_errors.rb @@ -0,0 +1,46 @@ +class DropErrors < ActiveRecord::Migration[5.2] + + # define old CodeOcean::Error module so that the migration works + module CodeOcean + class Error < ApplicationRecord + belongs_to :execution_environment + + scope :for_execution_environment, ->(execution_environment) { where(execution_environment_id: execution_environment.id) } + scope :grouped_by_message, -> { select('MAX(created_at) AS created_at, MAX(id) AS id, message, COUNT(id) AS count').group(:message).order('count DESC') } + + validates :execution_environment_id, presence: true + validates :message, presence: true + + def self.nested_resource? + true + end + + def to_s + id.to_s + end + end + end + + + + def change + + puts 'Migrating CodeOcean::Errors to StructuredErrors using RegEx. This might take a (long) while but will return.' + submissions_controller = SubmissionsController.new + + # Iterate only over those Errors containing a message and submission_id + CodeOcean::Error.where.not(message: [nil, ""]).where.not(submission_id: [nil, ""]).each do |error| + raw_output = error.message + submission = Submission.find_by(id: error.submission_id) + + # Validate that we have everything we need: the output, the submission and the execution environment + next if submission.exercise.execution_environment.blank? + + submissions_controller.instance_variable_set(:@raw_output, raw_output) + submissions_controller.instance_variable_set(:@submission, submission) + submissions_controller.extract_errors + end + + drop_table :errors + end +end diff --git a/db/schema.rb b/db/schema.rb index f918e876..82ec19bf 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_11_26_163428) do +ActiveRecord::Schema.define(version: 2018_11_29_093207) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -81,15 +81,6 @@ ActiveRecord::Schema.define(version: 2018_11_26_163428) do t.text "hint" end - create_table "errors", force: :cascade do |t| - t.integer "execution_environment_id" - t.text "message" - t.datetime "created_at" - t.datetime "updated_at" - t.integer "submission_id" - t.index ["submission_id"], name: "index_errors_on_submission_id" - end - create_table "events", force: :cascade do |t| t.string "category" t.string "data" @@ -223,16 +214,6 @@ ActiveRecord::Schema.define(version: 2018_11_26_163428) do t.index ["context_id", "context_type"], name: "index_files_on_context_id_and_context_type" end - create_table "hints", force: :cascade do |t| - t.integer "execution_environment_id" - t.string "locale", limit: 255 - t.text "message" - t.string "name", limit: 255 - t.string "regular_expression", limit: 255 - t.datetime "created_at" - t.datetime "updated_at" - end - create_table "internal_users", force: :cascade do |t| t.integer "consumer_id" t.string "email", limit: 255 diff --git a/db/seeds/development.rb b/db/seeds/development.rb index fab9a989..b266a20b 100644 --- a/db/seeds/development.rb +++ b/db/seeds/development.rb @@ -10,17 +10,11 @@ Rails.application.routes.default_url_options = Rails.application.config.action_m # execution environments ExecutionEnvironment.create_factories -# errors -CodeOcean::Error.create_factories - # exercises @exercises = find_factories_by_class(Exercise).map(&:name).map { |factory_name| [factory_name, FactoryBot.create(factory_name)] }.to_h # file types FileType.create_factories -# hints -Hint.create_factories - # submissions FactoryBot.create(:submission, exercise: @exercises[:fibonacci]) diff --git a/db/seeds/production.rb b/db/seeds/production.rb index 5b63f57b..ad12ee2c 100644 --- a/db/seeds/production.rb +++ b/db/seeds/production.rb @@ -25,9 +25,6 @@ Exercise.create_factories # file types FileType.create_factories -# hints -Hint.create_factories - # change all resources' author [ExecutionEnvironment, Exercise, FileType].each do |model| model.update_all(user_id: InternalUser.first.id) diff --git a/lib/whistleblower.rb b/lib/whistleblower.rb deleted file mode 100644 index 43876494..00000000 --- a/lib/whistleblower.rb +++ /dev/null @@ -1,20 +0,0 @@ -class Whistleblower - PLACEHOLDER_REGEXP = /\$(\d)/ - - def find_hint(stderr) - @execution_environment.hints.detect do |hint| - @matches = Regexp.new(hint.regular_expression).match(stderr) - end - end - private :find_hint - - def generate_hint(stderr) - if hint = find_hint(stderr) - hint.message.gsub(PLACEHOLDER_REGEXP) { @matches[Regexp.last_match(1).to_i] } - end - end - - def initialize(options = {}) - @execution_environment = options[:execution_environment] - end -end diff --git a/spec/controllers/hints_controller_spec.rb b/spec/controllers/hints_controller_spec.rb deleted file mode 100644 index d95b7efe..00000000 --- a/spec/controllers/hints_controller_spec.rb +++ /dev/null @@ -1,103 +0,0 @@ -require 'rails_helper' - -describe HintsController do - let(:execution_environment) { FactoryBot.create(:ruby) } - let(:hint) { FactoryBot.create(:ruby_syntax_error) } - let(:user) { FactoryBot.create(:admin) } - before(:each) { allow(controller).to receive(:current_user).and_return(user) } - - describe 'POST #create' do - context 'with a valid hint' do - let(:perform_request) { proc { post :create, params: { execution_environment_id: execution_environment.id, hint: FactoryBot.attributes_for(:ruby_syntax_error) } } } - before(:each) { perform_request.call } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: Hint) - - it 'creates the hint' do - expect { perform_request.call }.to change(Hint, :count).by(1) - end - - expect_redirect(Hint.last) - end - - context 'with an invalid hint' do - before(:each) { post :create, params: { execution_environment_id: execution_environment.id, hint: {} } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: Hint) - expect_status(200) - expect_template(:new) - end - end - - describe 'DELETE #destroy' do - before(:each) { delete :destroy, params: { execution_environment_id: execution_environment.id, id: hint.id } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: Hint) - - it 'destroys the hint' do - hint = FactoryBot.create(:ruby_syntax_error) - expect { delete :destroy, params: { execution_environment_id: execution_environment.id, id: hint.id } }.to change(Hint, :count).by(-1) - end - - expect_redirect { execution_environment_hints_path(execution_environment) } - end - - describe 'GET #edit' do - before(:each) { get :edit, params: { execution_environment_id: execution_environment.id, id: hint.id } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: Hint) - expect_status(200) - expect_template(:edit) - end - - describe 'GET #index' do - before(:all) { FactoryBot.create_pair(:ruby_syntax_error) } - before(:each) { get :index, params: { execution_environment_id: execution_environment.id } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hints: Hint.all) - expect_status(200) - expect_template(:index) - end - - describe 'GET #new' do - before(:each) { get :new, params: { execution_environment_id: execution_environment.id } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: Hint) - expect_status(200) - expect_template(:new) - end - - describe 'GET #show' do - before(:each) { get :show, params: { execution_environment_id: execution_environment.id, id: hint.id } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: :hint) - expect_status(200) - expect_template(:show) - end - - describe 'PUT #update' do - context 'with a valid hint' do - before(:each) { put :update, params: { execution_environment_id: execution_environment.id, hint: FactoryBot.attributes_for(:ruby_syntax_error), id: hint.id } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: Hint) - expect_redirect { hint } - end - - context 'with an invalid hint' do - before(:each) { put :update, params: { execution_environment_id: execution_environment.id, hint: {name: ''}, id: hint.id } } - - expect_assigns(execution_environment: :execution_environment) - expect_assigns(hint: Hint) - expect_status(200) - expect_template(:edit) - end - end -end diff --git a/spec/controllers/submissions_controller_spec.rb b/spec/controllers/submissions_controller_spec.rb index 497318e0..6aaf8555 100644 --- a/spec/controllers/submissions_controller_spec.rb +++ b/spec/controllers/submissions_controller_spec.rb @@ -157,40 +157,6 @@ describe SubmissionsController do pending("todo") end - - context 'when an error occurs during execution' do - let(:stderr) { "undefined method `foo' for main:Object (NoMethodError)" } - - before(:each) do - expect_any_instance_of(DockerClient).to receive(:execute_run_command).with(submission, filename).and_yield(:stderr, stderr) - end - - after(:each) { perform_request } - - context 'when the error is covered by a hint' do - let(:hint) { "Your object 'main' of class 'Object' does not understand the method 'foo'." } - - before(:each) do - expect_any_instance_of(Whistleblower).to receive(:generate_hint).with(stderr).and_return(hint) - end - - it 'does not store the error' do - pending("no server sent events used right now") - expect(CodeOcean::Error).not_to receive(:create) - end - end - - context 'when the error is not covered by a hint' do - before(:each) do - expect_any_instance_of(Whistleblower).to receive(:generate_hint).with(stderr) - end - - it 'stores the error' do - pending("no server sent events used right now") - expect(CodeOcean::Error).to receive(:create).with(execution_environment_id: submission.exercise.execution_environment_id, message: stderr) - end - end - end end describe 'GET #show' do diff --git a/spec/factories/code_ocean/error.rb b/spec/factories/code_ocean/error.rb deleted file mode 100644 index 0bcae320..00000000 --- a/spec/factories/code_ocean/error.rb +++ /dev/null @@ -1,6 +0,0 @@ -FactoryBot.define do - factory :error, class: CodeOcean::Error do - association :execution_environment, factory: :ruby - message { "exercise.rb:4:in `
': undefined local variable or method `foo' for main:Object (NameError)" } - end -end diff --git a/spec/factories/hint.rb b/spec/factories/hint.rb deleted file mode 100644 index ec1aba9d..00000000 --- a/spec/factories/hint.rb +++ /dev/null @@ -1,101 +0,0 @@ -FactoryBot.define do - factory :node_js_invalid_assignment, class: Hint do - association :execution_environment, factory: :node_js - english - message { 'There was an error with an assignment. Maybe you have to use the equality operator here.' } - name { 'Invalid assignment' } - regular_expression { 'Invalid left-hand side in assignment' } - end - - factory :node_js_reference_error, class: Hint do - association :execution_environment, factory: :node_js - english - message { "'$1' is not defined." } - name { 'ReferenceError' } - regular_expression { 'ReferenceError: (\w+) is not defined' } - end - - factory :node_js_syntax_error, class: Hint do - association :execution_environment, factory: :node_js - english - message { 'You seem to have made a typo.' } - name { 'SyntaxError' } - regular_expression { 'SyntaxError: Unexpected token (\w+)' } - end - - factory :ruby_load_error, class: Hint do - association :execution_environment, factory: :ruby - english - message { "The file '$1' cannot be found." } - name { 'LoadError' } - regular_expression { 'cannot load such file -- (\w+) (LoadError)' } - end - - factory :ruby_name_error_constant, class: Hint do - association :execution_environment, factory: :ruby - english - message { "The constant '$1' is not defined." } - name { 'NameError (uninitialized constant)' } - regular_expression { 'uninitialized constant (\w+) \(NameError\)' } - end - - factory :ruby_name_error_variable, class: Hint do - association :execution_environment, factory: :ruby - english - message { "Your object '$2' of class '$3' does not know what '$1' is. Maybe you made a typo or still have to define '$1'." } - name { 'NameError (undefined local variable or method)' } - regular_expression { 'undefined local variable or method `(\w+)\' for (\w+):(\w+) \(NameError\)' } - end - - factory :ruby_no_method_error, class: Hint do - association :execution_environment, factory: :ruby - english - message { "Your object '$2' of class '$3' does not understand the method '$1'. Maybe you made a typo or still have to implement that method." } - name { 'NoMethodError' } - regular_expression { 'undefined method `([\w\!\?=\[\]]+)\' for (\w+):(\w+) \(NoMethodError\)' } - end - - factory :ruby_syntax_error, class: Hint do - association :execution_environment, factory: :ruby - english - message { 'You seem to have made a typo.' } - name { 'SyntaxError' } - regular_expression { 'syntax error' } - end - - factory :ruby_system_stack_error, class: Hint do - association :execution_environment, factory: :ruby - english - message { 'You seem to have built an infinite loop or recursion.' } - name { 'SystemStackError' } - regular_expression { 'stack level too deep \(SystemStackError\)' } - end - - factory :sqlite_no_such_column, class: Hint do - association :execution_environment, factory: :sqlite - english - message { "The column '$1' does not exist." } - name { 'No Such Column' } - regular_expression { 'no such column: (\w+)' } - end - - factory :sqlite_no_such_table, class: Hint do - association :execution_environment, factory: :sqlite - english - message { "The table '$1' does not exist." } - name { 'No Such Table' } - regular_expression { 'no such table: (\w+)' } - end - - factory :sqlite_syntax_error, class: Hint do - association :execution_environment, factory: :sqlite - english - message { "You seem to have made a typo near '$1'." } - name { 'SyntaxError' } - regular_expression { 'near "(\w+)": syntax error' } - end - - trait :english do - locale { 'en' } - end -end diff --git a/spec/lib/whistleblower_spec.rb b/spec/lib/whistleblower_spec.rb deleted file mode 100644 index e0a8a8d9..00000000 --- a/spec/lib/whistleblower_spec.rb +++ /dev/null @@ -1,28 +0,0 @@ -require 'rails_helper' - -describe Whistleblower do - let(:hint) { FactoryBot.create(:ruby_no_method_error) } - let(:stderr) { "undefined method `foo' for main:Object (NoMethodError)" } - let(:whistleblower) { described_class.new(execution_environment: hint.execution_environment) } - - describe '#find_hint' do - let(:find_hint) { whistleblower.send(:find_hint, stderr) } - - it 'finds the hint' do - expect(find_hint).to eq(hint) - end - - it 'stores the matches' do - find_hint - expect(whistleblower.instance_variable_get(:@matches)).to be_a(MatchData) - end - end - - describe '#generate_hint' do - it 'returns the customized hint message' do - message = whistleblower.generate_hint(stderr) - expect(message[0..9]).to eq(hint.message[0..9]) - expect(message[-10..-1]).to eq(hint.message[-10..-1]) - end - end -end diff --git a/spec/models/code_ocean/error_spec.rb b/spec/models/code_ocean/error_spec.rb deleted file mode 100644 index aa111b3c..00000000 --- a/spec/models/code_ocean/error_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'rails_helper' - -describe CodeOcean::Error do - let(:error) { described_class.create } - - it 'validates the presence of an execution environment' do - expect(error.errors[:execution_environment_id]).to be_present - end - - it 'validates the presence of a message' do - expect(error.errors[:message]).to be_present - end - - describe '.nested_resource?' do - it 'is true' do - expect(described_class.nested_resource?).to be true - end - end -end diff --git a/spec/models/hint_spec.rb b/spec/models/hint_spec.rb deleted file mode 100644 index 87e04b51..00000000 --- a/spec/models/hint_spec.rb +++ /dev/null @@ -1,37 +0,0 @@ -require 'rails_helper' - -describe Hint do - let(:hint) { described_class.create } - - it 'validates the presence of an execution environment' do - expect(hint.errors[:execution_environment_id]).to be_present - end - - it 'validates the presence of a locale' do - expect(hint.errors[:locale]).to be_present - end - - it 'validates the presence of a message' do - expect(hint.errors[:message]).to be_present - end - - it 'validates the presence of a name' do - expect(hint.errors[:name]).to be_present - end - - it 'validates the presence of a regular expression' do - expect(hint.errors[:regular_expression]).to be_present - end - - describe '.nested_resource?' do - it 'is true' do - expect(described_class.nested_resource?).to be true - end - end - - describe '#to_s' do - it "equals the hint's name" do - expect(hint.to_s).to eq(hint.name) - end - end -end diff --git a/spec/policies/code_ocean/error_policy_spec.rb b/spec/policies/code_ocean/error_policy_spec.rb deleted file mode 100644 index 71ff72d5..00000000 --- a/spec/policies/code_ocean/error_policy_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'rails_helper' - -describe CodeOcean::ErrorPolicy do - subject { described_class } - - let(:error) { FactoryBot.build(:error) } - - [:create?, :index?, :new?].each do |action| - permissions(action) do - it 'grants access to admins' do - expect(subject).to permit(FactoryBot.build(:admin), error) - end - - it 'grants access to teachers' do - expect(subject).to permit(FactoryBot.build(:teacher), error) - end - - it 'does not grant access to external users' do - expect(subject).not_to permit(FactoryBot.build(:external_user), error) - end - end - end - - [:destroy?, :edit?, :show?, :update?].each do |action| - permissions(action) do - it 'grants access to admins' do - expect(subject).to permit(FactoryBot.build(:admin), error) - end - - it 'grants access to authors' do - expect(subject).to permit(error.execution_environment.author, error) - end - - it 'does not grant access to all other users' do - [:external_user, :teacher].each do |factory_name| - expect(subject).not_to permit(FactoryBot.build(factory_name), error) - end - end - end - end -end diff --git a/spec/policies/hint_policy_spec.rb b/spec/policies/hint_policy_spec.rb deleted file mode 100644 index 2aa9d9e5..00000000 --- a/spec/policies/hint_policy_spec.rb +++ /dev/null @@ -1,41 +0,0 @@ -require 'rails_helper' - -describe HintPolicy do - subject { described_class } - - let(:hint) { FactoryBot.build(:ruby_no_method_error) } - - [:create?, :index?, :new?].each do |action| - permissions(action) do - it 'grants access to admins' do - expect(subject).to permit(FactoryBot.build(:admin), hint) - end - - it 'grants access to teachers' do - expect(subject).to permit(FactoryBot.build(:teacher), hint) - end - - it 'does not grant access to external users' do - expect(subject).not_to permit(FactoryBot.build(:external_user), hint) - end - end - end - - [:destroy?, :edit?, :show?, :update?].each do |action| - permissions(action) do - it 'grants access to admins' do - expect(subject).to permit(FactoryBot.build(:admin), hint) - end - - it 'grants access to authors' do - expect(subject).to permit(hint.execution_environment.author, hint) - end - - it 'does not grant access to all other users' do - [:external_user, :teacher].each do |factory_name| - expect(subject).not_to permit(FactoryBot.build(factory_name), hint) - end - end - end - end -end diff --git a/spec/views/exercises/implement.html.slim_spec.rb b/spec/views/exercises/implement.html.slim_spec.rb index 7c5e641a..fe9ab0d0 100644 --- a/spec/views/exercises/implement.html.slim_spec.rb +++ b/spec/views/exercises/implement.html.slim_spec.rb @@ -14,7 +14,6 @@ describe 'exercises/implement.html.slim' do end it 'contains the required editor data attributes' do - expect(rendered).to have_css("#editor[data-errors-url='#{execution_environment_errors_path(exercise.execution_environment)}']") expect(rendered).to have_css("#editor[data-exercise-id='#{exercise.id}']") expect(rendered).to have_css('#editor[data-message-timeout]') expect(rendered).to have_css("#editor[data-submissions-url='#{submissions_path}']")