Merge pull request #121 from openHPI/error-info

Error info
This commit is contained in:
rteusner
2017-10-25 10:44:24 +02:00
committed by GitHub
47 changed files with 753 additions and 3 deletions

View File

@ -0,0 +1,18 @@
$(function() {
if ($.isController('error_templates')) {
$('#add-attribute').find('button').on('click', function () {
$.ajax(location + '/attribute.json', {
method: 'POST',
data: {
_method: 'PUT',
dataType: 'json',
error_template_attribute_id: $('#add-attribute').find('select').val()
}
}).success(function () {
location.reload();
}).error(function (error) {
$.flash.danger({text: error.statusText});
});
});
}
});

View File

@ -0,0 +1,9 @@
#add-attribute {
display: flex;
max-width: 400px;
margin-top: 30px;
button {
margin-left: 10px;
}
}

View File

@ -9,6 +9,14 @@ module SubmissionScoring
assessment = assessor.assess(output)
passed = ((assessment[:passed] == assessment[:count]) and (assessment[:score] > 0))
testrun_output = passed ? nil : 'message: ' + output[:message].to_s + "\n stdout: " + output[:stdout].to_s + "\n stderr: " + output[:stderr].to_s
if !testrun_output.blank?
submission.exercise.execution_environment.error_templates.each do |template|
pattern = Regexp.new(template.signature).freeze
if pattern.match(testrun_output)
StructuredError.create_from_template(template, testrun_output)
end
end
end
Testrun.new(submission: submission, cause: 'assess', file: file, passed: passed, output: testrun_output).save
output.merge!(assessment)
output.merge!(filename: file.name_with_extension, message: feedback_message(file, output[:score]), weight: file.weight)

View File

@ -0,0 +1,86 @@
class ErrorTemplateAttributesController < ApplicationController
before_action :set_error_template_attribute, only: [:show, :edit, :update, :destroy]
def authorize!
authorize(@error_template_attributes || @error_template_attribute)
end
private :authorize!
# GET /error_template_attributes
# GET /error_template_attributes.json
def index
@error_template_attributes = ErrorTemplateAttribute.all.order('important DESC', :key, :id).paginate(page: params[:page])
authorize!
end
# GET /error_template_attributes/1
# GET /error_template_attributes/1.json
def show
authorize!
end
# GET /error_template_attributes/new
def new
@error_template_attribute = ErrorTemplateAttribute.new
authorize!
end
# GET /error_template_attributes/1/edit
def edit
authorize!
end
# POST /error_template_attributes
# POST /error_template_attributes.json
def create
@error_template_attribute = ErrorTemplateAttribute.new(error_template_attribute_params)
authorize!
respond_to do |format|
if @error_template_attribute.save
format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully created.' }
format.json { render :show, status: :created, location: @error_template_attribute }
else
format.html { render :new }
format.json { render json: @error_template_attribute.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /error_template_attributes/1
# PATCH/PUT /error_template_attributes/1.json
def update
authorize!
respond_to do |format|
if @error_template_attribute.update(error_template_attribute_params)
format.html { redirect_to @error_template_attribute, notice: 'Error template attribute was successfully updated.' }
format.json { render :show, status: :ok, location: @error_template_attribute }
else
format.html { render :edit }
format.json { render json: @error_template_attribute.errors, status: :unprocessable_entity }
end
end
end
# DELETE /error_template_attributes/1
# DELETE /error_template_attributes/1.json
def destroy
authorize!
@error_template_attribute.destroy
respond_to do |format|
format.html { redirect_to error_template_attributes_url, notice: 'Error template attribute was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_error_template_attribute
@error_template_attribute = ErrorTemplateAttribute.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def error_template_attribute_params
params[:error_template_attribute].permit(:key, :description, :regex, :important)
end
end

View File

@ -0,0 +1,104 @@
class ErrorTemplatesController < ApplicationController
before_action :set_error_template, only: [:show, :edit, :update, :destroy, :add_attribute, :remove_attribute]
def authorize!
authorize(@error_templates || @error_template)
end
private :authorize!
# GET /error_templates
# GET /error_templates.json
def index
@error_templates = ErrorTemplate.all.order(:execution_environment_id, :name).paginate(page: params[:page])
authorize!
end
# GET /error_templates/1
# GET /error_templates/1.json
def show
authorize!
end
# GET /error_templates/new
def new
@error_template = ErrorTemplate.new
authorize!
end
# GET /error_templates/1/edit
def edit
authorize!
end
# POST /error_templates
# POST /error_templates.json
def create
@error_template = ErrorTemplate.new(error_template_params)
authorize!
respond_to do |format|
if @error_template.save
format.html { redirect_to @error_template, notice: 'Error template was successfully created.' }
format.json { render :show, status: :created, location: @error_template }
else
format.html { render :new }
format.json { render json: @error_template.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /error_templates/1
# PATCH/PUT /error_templates/1.json
def update
authorize!
respond_to do |format|
if @error_template.update(error_template_params)
format.html { redirect_to @error_template, notice: 'Error template was successfully updated.' }
format.json { render :show, status: :ok, location: @error_template }
else
format.html { render :edit }
format.json { render json: @error_template.errors, status: :unprocessable_entity }
end
end
end
# DELETE /error_templates/1
# DELETE /error_templates/1.json
def destroy
authorize!
@error_template.destroy
respond_to do |format|
format.html { redirect_to error_templates_url, notice: 'Error template was successfully destroyed.' }
format.json { head :no_content }
end
end
def add_attribute
authorize!
@error_template.error_template_attributes << ErrorTemplateAttribute.find(params['error_template_attribute_id'])
respond_to do |format|
format.html { redirect_to @error_template }
format.json { head :no_content }
end
end
def remove_attribute
authorize!
@error_template.error_template_attributes.delete(ErrorTemplateAttribute.find(params['error_template_attribute_id']))
respond_to do |format|
format.html { redirect_to @error_template }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_error_template
@error_template = ErrorTemplate.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def error_template_params
params[:error_template].permit(:name, :execution_environment_id, :signature, :description, :hint)
end
end

View File

@ -6,7 +6,7 @@ class SubmissionsController < ApplicationController
include SubmissionScoring
include Tubesock::Hijack
before_action :set_submission, only: [:download, :download_file, :render_file, :run, :score, :show, :statistics, :stop, :test]
before_action :set_submission, only: [:download, :download_file, :render_file, :run, :score, :extract_errors, :show, :statistics, :stop, :test]
before_action :set_docker_client, only: [:run, :test]
before_action :set_files, only: [:download, :download_file, :render_file, :show]
before_action :set_file, only: [:download_file, :render_file]
@ -191,6 +191,9 @@ class SubmissionsController < ApplicationController
end
def kill_socket(tubesock)
# search for errors and save them as StructuredError (for scoring runs see submission_scoring.rb)
extract_errors
# save the output of this "run" as a "testrun" (scoring runs are saved in submission_scoring.rb)
save_run_output
@ -199,6 +202,17 @@ class SubmissionsController < ApplicationController
tubesock.close
end
def extract_errors
if !@message_buffer.blank?
@submission.exercise.execution_environment.error_templates.each do |template|
pattern = Regexp.new(template.signature).freeze
if pattern.match(@message_buffer)
StructuredError.create_from_template(template, @message_buffer)
end
end
end
end
def handle_message(message, tubesock, container)
@run_output ||= ""
# Handle special commands first

View File

@ -0,0 +1,2 @@
module ErrorTemplateAttributesHelper
end

View File

@ -0,0 +1,2 @@
module ErrorTemplatesHelper
end

View File

@ -0,0 +1,8 @@
class ErrorTemplate < ActiveRecord::Base
belongs_to :execution_environment
has_and_belongs_to_many :error_template_attributes
def to_s
"#{id} [#{name}]"
end
end

View File

@ -0,0 +1,7 @@
class ErrorTemplateAttribute < ActiveRecord::Base
has_and_belongs_to_many :error_template
def to_s
"#{id} [#{key}]"
end
end

View File

@ -11,6 +11,7 @@ class ExecutionEnvironment < ActiveRecord::Base
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)') }

View File

@ -0,0 +1,12 @@
class StructuredError < ActiveRecord::Base
belongs_to :error_template
belongs_to :file, class_name: 'CodeOcean::File'
def self.create_from_template(template, message_buffer)
instance = self.create(error_template: template)
template.error_template_attributes.each do |attribute|
StructuredErrorAttribute.create_from_template(attribute, instance, message_buffer)
end
instance
end
end

View File

@ -0,0 +1,17 @@
class StructuredErrorAttribute < ActiveRecord::Base
belongs_to :structured_error
belongs_to :error_template_attribute
def self.create_from_template(attribute, structured_error, message_buffer)
match = false
value = nil
result = message_buffer.match(attribute.regex)
if result != nil
match = true
if result.captures.size > 0
value = result.captures[0]
end
end
self.create(structured_error: structured_error, error_template_attribute: attribute, value: value, match: match)
end
end

View File

@ -0,0 +1,3 @@
class ErrorTemplateAttributePolicy < AdminOnlyPolicy
end

View File

@ -0,0 +1,9 @@
class ErrorTemplatePolicy < AdminOnlyPolicy
def add_attribute?
admin?
end
def remove_attribute?
admin?
end
end

View File

@ -8,7 +8,8 @@
- if current_user.admin?
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path)
li.divider
- models = [ExecutionEnvironment, Exercise, ExerciseCollection, ProxyExercise, Tag, Consumer, CodeHarborLink, ExternalUser, FileType, FileTemplate, InternalUser].sort_by { |model| model.model_name.human(count: 2) }
- models = [ExecutionEnvironment, Exercise, ExerciseCollection, ProxyExercise, Tag, Consumer, CodeHarborLink,
ErrorTemplate, ErrorTemplateAttribute, ExternalUser, FileType, FileTemplate, InternalUser].sort_by {|model| model.model_name.human(count: 2) }
- models.each do |model|
- if policy(model).index?
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))

View File

@ -0,0 +1,16 @@
= form_for(@error_template_attribute) do |f|
= render('shared/form_errors', object: @error_template_attribute)
.form-group
= f.label(:key)
= f.text_field(:key, class: 'form-control', required: true)
.form-group
= f.label(:description)
= f.text_field(:description, class: 'form-control')
.form-group
= f.label(:regex)
= f.text_field(:regex, class: 'form-control', required: true)
.help-block == t('error_templates.hints.signature')
.form-group
= f.check_box(:important)
= t('activerecord.attributes.error_template_attribute.important')
.actions = render('shared/submit_button', f: f, object: @error_template_attribute)

View File

@ -0,0 +1,3 @@
h1 = @error_template_attribute
= render('form')

View File

@ -0,0 +1,28 @@
h1 = ErrorTemplateAttribute.model_name.human(count: 2)
.table-responsive
table.sortable.table
thead
tr
th
th = t('activerecord.attributes.error_template_attribute.key')
th = t('activerecord.attributes.error_template_attribute.description')
th = t('activerecord.attributes.error_template_attribute.regex')
th colspan=5 = t('shared.actions')
tbody
- @error_template_attributes.each do |error_template_attribute|
tr
td
- if error_template_attribute.important
span class="fa fa-star" aria-hidden="true"
- else
span class="fa fa-star-o" aria-hidden="true"
td = error_template_attribute.key
td = error_template_attribute.description
td = error_template_attribute.regex
td = link_to(t('shared.show'), error_template_attribute)
td = link_to(t('shared.edit'), edit_error_template_attribute_path(error_template_attribute))
td = link_to(t('shared.destroy'), error_template_attribute, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
= render('shared/pagination', collection: @error_template_attributes)
p = render('shared/new_button', model: ErrorTemplateAttribute)

View File

@ -0,0 +1,3 @@
h1 = t('shared.new_model', model: ErrorTemplateAttribute.model_name.human)
= render('form')

View File

@ -0,0 +1,8 @@
h1
= @error_template_attribute
= render('shared/edit_button', object: @error_template_attribute)
- [:key, :description, :regex, :important].each do |attribute|
= row(label: "error_template_attribute.#{attribute}", value: @error_template_attribute.send(attribute))
// todo: used by

View File

@ -0,0 +1,19 @@
= form_for(@error_template) do |f|
= render('shared/form_errors', object: @error_template)
.form-group
= f.label(:name)
= f.text_field(:name, class: 'form-control', required: true)
.form-group
= f.label(:execution_environment_id)
= f.collection_select(:execution_environment_id, ExecutionEnvironment.all.order(:name), :id, :name, {include_blank: false}, class: 'form-control')
.form-group
= f.label(:signature)
= f.text_field(:signature, class: 'form-control')
.help-block == t('error_templates.hints.signature')
.form-group
= f.label(:description)
= f.text_field(:description, class: 'form-control')
.form-group
= f.label(:hint)
= f.text_field(:hint, class: 'form-control')
.actions = render('shared/submit_button', f: f, object: @error_template)

View File

@ -0,0 +1,3 @@
h1 = @error_template
= render('form')

View File

@ -0,0 +1,22 @@
h1 = ErrorTemplate.model_name.human(count: 2)
.table-responsive
table.sortable.table
thead
tr
th = t('activerecord.attributes.error_template.name')
th = t('activerecord.attributes.error_template.description')
th = t('activerecord.attributes.exercise.execution_environment')
th colspan=3 = t('shared.actions')
tbody
- @error_templates.each do |error_template|
tr
td = error_template.name
td = error_template.description
td = link_to(error_template.execution_environment)
td = link_to(t('shared.show'), error_template)
td = link_to(t('shared.edit'), edit_error_template_path(error_template))
td = link_to(t('shared.destroy'), error_template, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
= render('shared/pagination', collection: @error_templates)
p = render('shared/new_button', model: ErrorTemplate)

View File

@ -0,0 +1,3 @@
h1 = t('shared.new_model', model: ErrorTemplate.model_name.human)
= render('form')

View File

@ -0,0 +1,40 @@
h1
= @error_template
= render('shared/edit_button', object: @error_template)
= row(label: 'error_template.name', value: @error_template.name)
= row(label: 'exercise.execution_environment', value: link_to(@error_template.execution_environment))
- [:signature, :description, :hint].each do |attribute|
= row(label: "error_template.#{attribute}", value: @error_template.send(attribute))
h3
= t 'error_templates.attributes'
.table-responsive
table.sortable.table
thead
tr
th
th = t('activerecord.attributes.error_template_attribute.key')
th = t('activerecord.attributes.error_template_attribute.description')
th = t('activerecord.attributes.error_template_attribute.regex')
th colspan=3 = t('shared.actions')
tbody
- @error_template.error_template_attributes.order('important DESC', :key).each do |attribute|
tr
td
- if attribute.important
span class="fa fa-star" aria-hidden="true"
- else
span class="fa fa-star-o" aria-hidden="true"
td = attribute.key
td = attribute.description
td = attribute.regex
td = link_to(t('shared.show'), attribute)
td = link_to(t('shared.destroy'), attribute_error_template_url(:error_template_attribute_id => attribute.id), :method => :delete)
#add-attribute
= collection_select({}, :error_template_attribute_id,
ErrorTemplateAttribute.where.not(id: @error_template.error_template_attributes.select(:id).to_a).order('important DESC', :key),
:id, :key, {include_blank: false}, class: '')
button.btn.btn-default = t('error_templates.add_attribute')

View File

@ -111,6 +111,16 @@ de:
name: "Name"
file_type: "Dateityp"
content: "Code"
error_template:
name: Name
signature: Regulärer Ausdruck
description: Beschreibung
hint: Hinweis
error_template_attribute:
important: "Wichtig"
key: "Name"
description: "Beschreibung"
regex: "Regulärer Ausdruck"
exercise_collections:
id: "ID"
name: "Name"
@ -126,6 +136,12 @@ de:
error:
one: Fehler
other: Fehler
error_template:
one: Fehlertemplate
other: Fehlertemplates
error_template_attribute:
one: Fehlertemplatettribut
other: Fehlertemplatettribute
execution_environment:
one: Ausführungsumgebung
other: Ausführungsumgebungen
@ -642,10 +658,14 @@ de:
estimated_time_20_to_30: "zwischen 20 und 30 Minuten"
estimated_time_more_30: "mehr als 30 Minuten"
working_time: "Geschätze Bearbeitungszeit für diese Aufgabe:"
error_templates:
hints:
signature: "Ein regulärer Ausdruck in Ruby-Syntax und ohne führende und schließende \"/\""
attributes: "Attribute"
add_attribute: "Attribut hinzufügen"
comments:
deleted: "Gelöscht"
save_update: "Speichern"
subscriptions:
successfully_unsubscribed: "Ihr Abonnement für weitere Kommentare auf dieser Kommentaranfrage wurde erfolgreich beendet."
subscription_not_existent: "Das Abonnement, von dem Sie sich abmelden wollen, existiert nicht."

View File

@ -111,6 +111,16 @@ en:
name: "Name"
file_type: "File Type"
content: "Content"
error_template:
name: Name
signature: Signature Regular Expression
description: Description
hint: Hint
error_template_attribute:
important: "Important"
key: "Identifier"
description: "Description"
regex: "Regular Expression"
exercise_collections:
id: "ID"
name: "Name"
@ -126,6 +136,12 @@ en:
error:
one: Error
other: Errors
error_template:
one: Error Template
other: Error Templates
error_template_attribute:
one: Error Template Attribute
other: Error Template Attributes
execution_environment:
one: Execution Environment
other: Execution Environments
@ -642,6 +658,11 @@ en:
estimated_time_20_to_30: "between 20 and 30 minutes"
estimated_time_more_30: "more than 30 minutes"
working_time: "Estimated time working on this exercise:"
error_templates:
hints:
signature: "A regular expression in Ruby syntax without leading and trailing \"/\""
attributes: "Attributes"
add_attribute: "Add attribute"
comments:
deleted: "Deleted"
save_update: "Save"

View File

@ -1,6 +1,13 @@
FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP)
Rails.application.routes.draw do
resources :error_template_attributes
resources :error_templates do
member do
put 'attribute', to: 'error_templates#add_attribute'
delete 'attribute', to: 'error_templates#remove_attribute'
end
end
resources :file_templates do
collection do
get 'by_file_type/:file_type_id', as: :by_file_type, action: :by_file_type

View File

@ -0,0 +1,11 @@
class CreateErrorTemplates < ActiveRecord::Migration
def change
create_table :error_templates do |t|
t.belongs_to :execution_environment
t.string :name
t.string :signature
t.timestamps null: false
end
end
end

View File

@ -0,0 +1,11 @@
class CreateErrorTemplateAttributes < ActiveRecord::Migration
def change
create_table :error_template_attributes do |t|
t.belongs_to :error_template
t.string :key
t.string :regex
t.timestamps null: false
end
end
end

View File

@ -0,0 +1,10 @@
class CreateStructuredErrors < ActiveRecord::Migration
def change
create_table :structured_errors do |t|
t.references :error_template
t.belongs_to :file
t.timestamps null: false
end
end
end

View File

@ -0,0 +1,11 @@
class CreateStructuredErrorAttributes < ActiveRecord::Migration
def change
create_table :structured_error_attributes do |t|
t.belongs_to :structured_error
t.references :error_template_attribute
t.string :value
t.timestamps null: false
end
end
end

View File

@ -0,0 +1,9 @@
class AddDescriptionAndHintToErrorTemplate < ActiveRecord::Migration
def change
add_column :error_templates, :description, :text
add_column :error_templates, :hint, :text
add_column :error_template_attributes, :description, :text
add_column :error_template_attributes, :important, :boolean
end
end

View File

@ -0,0 +1,6 @@
class ChangeErrorTemplateAttributeRelationshipToNToM < ActiveRecord::Migration
def change
remove_belongs_to :error_template_attributes, :error_template
create_join_table :error_templates, :error_template_attributes
end
end

View File

@ -0,0 +1,5 @@
class AddMatchToStructuredErrorAttribute < ActiveRecord::Migration
def change
add_column :structured_error_attributes, :match, :boolean
end
end

View File

@ -47,6 +47,30 @@ ActiveRecord::Schema.define(version: 20170920145852) do
t.string "oauth_secret", limit: 255
end
create_table "error_template_attributes", force: :cascade do |t|
t.string "key"
t.string "regex"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "description"
t.boolean "important"
end
create_table "error_template_attributes_templates", id: false, force: :cascade do |t|
t.integer "error_template_id", null: false
t.integer "error_template_attribute_id", null: false
end
create_table "error_templates", force: :cascade do |t|
t.integer "execution_environment_id"
t.string "name"
t.string "signature"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.text "description"
t.text "hint"
end
create_table "errors", force: :cascade do |t|
t.integer "execution_environment_id"
t.text "message"
@ -268,6 +292,22 @@ ActiveRecord::Schema.define(version: 20170920145852) do
t.datetime "updated_at"
end
create_table "structured_error_attributes", force: :cascade do |t|
t.integer "structured_error_id"
t.integer "error_template_attribute_id"
t.string "value"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "match"
end
create_table "structured_errors", force: :cascade do |t|
t.integer "error_template_id"
t.integer "file_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "submissions", force: :cascade do |t|
t.integer "exercise_id"
t.float "score"

View File

@ -0,0 +1,49 @@
require 'test_helper'
class ErrorTemplateAttributesControllerTest < ActionController::TestCase
setup do
@error_template_attribute = error_template_attributes(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:error_template_attributes)
end
test "should get new" do
get :new
assert_response :success
end
test "should create error_template_attribute" do
assert_difference('ErrorTemplateAttribute.count') do
post :create, error_template_attribute: { }
end
assert_redirected_to error_template_attribute_path(assigns(:error_template_attribute))
end
test "should show error_template_attribute" do
get :show, id: @error_template_attribute
assert_response :success
end
test "should get edit" do
get :edit, id: @error_template_attribute
assert_response :success
end
test "should update error_template_attribute" do
patch :update, id: @error_template_attribute, error_template_attribute: { }
assert_redirected_to error_template_attribute_path(assigns(:error_template_attribute))
end
test "should destroy error_template_attribute" do
assert_difference('ErrorTemplateAttribute.count', -1) do
delete :destroy, id: @error_template_attribute
end
assert_redirected_to error_template_attributes_path
end
end

View File

@ -0,0 +1,49 @@
require 'test_helper'
class ErrorTemplatesControllerTest < ActionController::TestCase
setup do
@error_template = error_templates(:one)
end
test "should get index" do
get :index
assert_response :success
assert_not_nil assigns(:error_templates)
end
test "should get new" do
get :new
assert_response :success
end
test "should create error_template" do
assert_difference('ErrorTemplate.count') do
post :create, error_template: { }
end
assert_redirected_to error_template_path(assigns(:error_template))
end
test "should show error_template" do
get :show, id: @error_template
assert_response :success
end
test "should get edit" do
get :edit, id: @error_template
assert_response :success
end
test "should update error_template" do
patch :update, id: @error_template, error_template: { }
assert_redirected_to error_template_path(assigns(:error_template))
end
test "should destroy error_template" do
assert_difference('ErrorTemplate.count', -1) do
delete :destroy, id: @error_template
end
assert_redirected_to error_templates_path
end
end

View File

@ -0,0 +1,7 @@
FactoryGirl.define do
factory :error_template_attribute do
error_template nil
key "MyString"
regex "MyString"
end
end

View File

@ -0,0 +1,7 @@
FactoryGirl.define do
factory :error_template do
execution_environment nil
name "MyString"
signature "MyString"
end
end

View File

@ -0,0 +1,7 @@
FactoryGirl.define do
factory :structured_error_attribute do
structured_error nil
error_template_attribute nil
value "MyString"
end
end

View File

@ -0,0 +1,6 @@
FactoryGirl.define do
factory :structured_error do
error_template nil
file nil
end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class ErrorTemplateAttributeTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class ErrorTemplateTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class StructuredErrorAttributeTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end

View File

@ -0,0 +1,7 @@
require 'test_helper'
class StructuredErrorTest < ActiveSupport::TestCase
# test "the truth" do
# assert true
# end
end