diff --git a/app/controllers/rescan_controller.rb b/app/controllers/rescan_controller.rb deleted file mode 100644 index ba9909c2..00000000 --- a/app/controllers/rescan_controller.rb +++ /dev/null @@ -1,19 +0,0 @@ -class RescanController < ApplicationController - before_action :set_rescan - - def show - render json: @rescan - end - - def start - @rescan.schedule - render json: @rescan - end - - private - - def set_rescan - @rescan = RescanRunner.instance - authorize @rescan - end -end diff --git a/app/controllers/rescans_controller.rb b/app/controllers/rescans_controller.rb new file mode 100644 index 00000000..2e40ac23 --- /dev/null +++ b/app/controllers/rescans_controller.rb @@ -0,0 +1,31 @@ +class RescansController < ApplicationController + before_action :set_rescan, only: %i[show start] + + def index + authorize RescanRunner + @rescans = policy_scope(RescanRunner).paginate(page: params[:page], per_page: params[:per_page]) + add_pagination_headers(@rescans) + render json: @rescans + end + + def show + render json: @rescan + end + + def start + @rescan.schedule + render json: @rescan + end + + def start_all + authorize RescanRunner + RescanRunner.schedule_all + end + + private + + def set_rescan + @rescan = RescanRunner.find(params[:id]) + authorize @rescan + end +end diff --git a/app/models/location.rb b/app/models/location.rb index 198e8b05..7801205e 100644 --- a/app/models/location.rb +++ b/app/models/location.rb @@ -8,6 +8,9 @@ class Location < ApplicationRecord has_many :audio_files, dependent: :destroy + has_one :rescan_runner, dependent: :destroy + + after_create :create_runner validates :path, presence: true, uniqueness: true validate :cant_be_parent_or_subdir_of_other_location @@ -24,4 +27,9 @@ def cant_be_parent_or_subdir_of_other_location errors.add(:path, 'path-is-parent') if l.expanded_path.fnmatch?(File.join(expanded_path, '**')) end end + + def create_runner + runner = RescanRunner.create(location: self) + runner.schedule + end end diff --git a/app/models/rescan_runner.rb b/app/models/rescan_runner.rb index 37aac863..aefb0877 100644 --- a/app/models/rescan_runner.rb +++ b/app/models/rescan_runner.rb @@ -8,17 +8,24 @@ # processed :integer default(0), not null # running :boolean default(FALSE), not null # warning_text :text +# location_id :bigint not null # class RescanRunner < ApplicationRecord - def self.instance - RescanRunner.first || RescanRunner.create - end + belongs_to :location def schedule + return if running? + delay(queue: :rescans).run end + def self.schedule_all + find_each(&:schedule) + end + + private + def run # rubocop:disable Rails/SkipsModelValidations # RescanRunner doesn't have validations, and we need to use update_all to use it's atomicity @@ -30,21 +37,14 @@ def run reload begin - unless Location.count.positive? - update(error_text: 'No locations defined') - return - end - unless Codec.count.positive? update(error_text: 'No codecs defined') return end - Location.all.find_each do |l| - process_all_files(l, l.path) - end + process_all_files(location.path) - AudioFile.find_each do |af| + location.audio_files.find_each do |af| update(warning_text: "#{warning_text}File #{af.full_path} doesn't exist anymore.\n") unless af.check_self end rescue StandardError => e @@ -55,9 +55,7 @@ def run end end - private - - def process_all_files(location, path) + def process_all_files(path) unless File.directory?(path) update(error_text: "#{error_text}#{path} (in #{location.path} is not a directory\n") return @@ -65,13 +63,13 @@ def process_all_files(location, path) Dir.each_child(path) do |child| if File.directory?(File.join(path, child)) - process_all_files(location, File.join(path, child)) + process_all_files(File.join(path, child)) else Codec.all.find_each do |c| next unless File.extname(child)[1..]&.downcase == c.extension.downcase.to_s begin - process_file(location, c, File.join(path, child)) + process_file(c, File.join(path, child)) update(processed: processed + 1) rescue StandardError => e backtrace = Rails.env.production? ? e.backtrace.first(5).join("\n") : e.backtrace.join("\n") @@ -82,7 +80,7 @@ def process_all_files(location, path) end end - def process_file(location, codec, path) + def process_file(codec, path) relative_path = Pathname.new(path).relative_path_from(Pathname.new(location.path)) return if AudioFile.exists?(location: location, filename: relative_path.to_s) diff --git a/app/policies/rescan_runner_policy.rb b/app/policies/rescan_runner_policy.rb index 645cb33a..13bf74f5 100644 --- a/app/policies/rescan_runner_policy.rb +++ b/app/policies/rescan_runner_policy.rb @@ -1,4 +1,14 @@ class RescanRunnerPolicy < ApplicationPolicy + class Scope < Scope + def resolve + scope.all if user&.moderator? + end + end + + def index? + user&.moderator? + end + def show? user&.moderator? end @@ -6,4 +16,8 @@ def show? def start? user&.moderator? end + + def start_all? + user&.moderator? + end end diff --git a/app/serializers/rescan_runner_serializer.rb b/app/serializers/rescan_runner_serializer.rb index 4ba30ea9..e2a507bf 100644 --- a/app/serializers/rescan_runner_serializer.rb +++ b/app/serializers/rescan_runner_serializer.rb @@ -8,7 +8,8 @@ # processed :integer default(0), not null # running :boolean default(FALSE), not null # warning_text :text +# location_id :bigint not null # class RescanRunnerSerializer < ActiveModel::Serializer - attributes :error_text, :warning_text, :processed, :running, :finished_at + attributes :id, :error_text, :warning_text, :processed, :running, :finished_at, :location_id end diff --git a/config/routes.rb b/config/routes.rb index fde59a21..9b7c3318 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -65,7 +65,8 @@ # POST /api/locations(.:format) locations#create # location GET /api/locations/:id(.:format) locations#show # DELETE /api/locations/:id(.:format) locations#destroy -# plays POST /api/plays(.:format) plays#create +# plays GET /api/plays(.:format) plays#index +# POST /api/plays(.:format) plays#create # destroy_empty_tracks POST /api/tracks/destroy_empty(.:format) tracks#destroy_empty # audio_track GET /api/tracks/:id/audio(.:format) tracks#audio # merge_track POST /api/tracks/:id/merge(.:format) tracks#merge @@ -81,8 +82,10 @@ # PATCH /api/users/:id(.:format) users#update # PUT /api/users/:id(.:format) users#update # DELETE /api/users/:id(.:format) users#destroy -# rescan GET /api/rescan(.:format) rescan#show -# POST /api/rescan(.:format) rescan#start +# rescans GET /api/rescans(.:format) rescans#index +# rescan GET /api/rescans/:id(.:format) rescans#show +# POST /api/rescans/:id(.:format) rescans#start +# POST /api/rescans(.:format) rescans#start_all # rails_service_blob GET /rails/active_storage/blobs/redirect/:signed_id/*filename(.:format) active_storage/blobs/redirect#show # rails_service_blob_proxy GET /rails/active_storage/blobs/proxy/:signed_id/*filename(.:format) active_storage/blobs/proxy#show # GET /rails/active_storage/blobs/:signed_id/*filename(.:format) active_storage/blobs/redirect#show @@ -144,8 +147,8 @@ end end resources :users - - get '/rescan' => 'rescan#show' - post '/rescan' => 'rescan#start' + resources :rescans, only: %i[index show] + post 'rescans/:id', to: 'rescans#start' + post 'rescans', to: 'rescans#start_all' end end diff --git a/db/migrate/20210905090539_add_location_to_rescan_runner.rb b/db/migrate/20210905090539_add_location_to_rescan_runner.rb new file mode 100644 index 00000000..b253b7c2 --- /dev/null +++ b/db/migrate/20210905090539_add_location_to_rescan_runner.rb @@ -0,0 +1,11 @@ +class AddLocationToRescanRunner < ActiveRecord::Migration[6.1] + def change + RescanRunner.destroy_all + + add_reference :rescan_runners, :location, null: false, foreign_key: true + + Location.find_each do |l| + RescanRunner.create(finished_at: Date.new, location: l) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index ab4f0d9a..9346fe6b 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: 2021_01_04_190247) do +ActiveRecord::Schema.define(version: 2021_09_05_090539) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -208,6 +208,8 @@ t.integer "processed", default: 0, null: false t.boolean "running", default: false, null: false t.datetime "finished_at", default: -> { "CURRENT_TIMESTAMP" }, null: false + t.bigint "location_id", null: false + t.index ["location_id"], name: "index_rescan_runners_on_location_id" end create_table "track_artists", force: :cascade do |t| @@ -276,6 +278,7 @@ add_foreign_key "images", "image_types" add_foreign_key "plays", "tracks" add_foreign_key "plays", "users" + add_foreign_key "rescan_runners", "locations" add_foreign_key "track_artists", "artists" add_foreign_key "track_artists", "tracks" add_foreign_key "tracks", "albums" diff --git a/db/seeds.rb b/db/seeds.rb index 0fd20856..53749394 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -21,10 +21,10 @@ if File.directory? File.expand_path('~/music') Location.create(path: File.expand_path('~/music')) - RescanRunner.create.run elsif File.directory? File.expand_path('~/Music') Location.create(path: File.expand_path('~/Music')) - RescanRunner.create.run end +RescanRunner.schedule_all + Delayed::Job.enqueue(TranscodeCacheCleanJob.new, cron: '0 4 * * *') diff --git a/lib/tasks/rescan.rake b/lib/tasks/rescan.rake index 2a3794f0..c951b8a6 100644 --- a/lib/tasks/rescan.rake +++ b/lib/tasks/rescan.rake @@ -1,5 +1,5 @@ namespace :rescan do task start: :environment do - RescanRunner.instance.schedule + RescanRunner.schedule_all end end diff --git a/test/controllers/rescan_controller_test.rb b/test/controllers/rescan_controller_test.rb deleted file mode 100644 index 7fd35028..00000000 --- a/test/controllers/rescan_controller_test.rb +++ /dev/null @@ -1,46 +0,0 @@ -require 'test_helper' - -class RescanControllerTest < ActionDispatch::IntegrationTest - setup do - @moderator = create(:moderator) - @user = create(:user) - sign_in_as(@user) - end - - test 'should get not show for user' do - get rescan_url - assert_response :forbidden - end - - test 'should not create new runner if one exists' do - RescanRunner.create - sign_in_as(@moderator) - assert_difference('RescanRunner.count', 0) do - get rescan_url - end - assert_response :success - end - - test 'should create RescanRunner if none exists for moderator' do - sign_in_as(@moderator) - assert_difference('RescanRunner.count', 1) do - get rescan_url - end - assert_response :success - end - - test 'should not start rescan for user' do - post rescan_url - assert_response :forbidden - end - - test 'should start rescan' do - @runner = RescanRunner.create - sign_in_as(@moderator) - prev = @runner.finished_at - post rescan_url - assert_response :success - @runner.reload - assert_not_equal prev, @runner.finished_at - end -end diff --git a/test/controllers/rescans_controller_test.rb b/test/controllers/rescans_controller_test.rb new file mode 100644 index 00000000..df53a5cb --- /dev/null +++ b/test/controllers/rescans_controller_test.rb @@ -0,0 +1,60 @@ +require 'test_helper' + +class RescansControllerTest < ActionDispatch::IntegrationTest + setup do + @runner = create(:rescan_runner) + @moderator = create(:moderator) + @user = create(:user) + sign_in_as(@user) + end + + test 'should get not index for user' do + get rescans_url + assert_response :forbidden + end + + test 'should get index for moderator' do + sign_in_as(@moderator) + get rescans_url + assert_response :success + end + + test 'should get not show for user' do + get rescan_url(@runner) + assert_response :forbidden + end + + test 'should get show for moderator' do + sign_in_as(@moderator) + get rescan_url(@runner) + assert_response :success + end + + test 'should not start rescan for user' do + post rescan_url(@runner) + assert_response :forbidden + end + + test 'should start rescan' do + sign_in_as(@moderator) + prev = @runner.finished_at + post rescan_url(@runner) + assert_response :success + @runner.reload + assert_not_equal prev, @runner.finished_at + end + + test 'should not start all rescans for user' do + post rescans_url + assert_response :forbidden + end + + test 'should start all rescans' do + sign_in_as(@moderator) + prev = @runner.finished_at + post rescans_url + assert_response :success + @runner.reload + assert_not_equal prev, @runner.finished_at + end +end diff --git a/test/factories/rescan_runners.rb b/test/factories/rescan_runners.rb new file mode 100644 index 00000000..2d8a93e0 --- /dev/null +++ b/test/factories/rescan_runners.rb @@ -0,0 +1,17 @@ +# == Schema Information +# +# Table name: rescan_runners +# +# id :bigint not null, primary key +# error_text :text +# finished_at :datetime not null +# processed :integer default(0), not null +# running :boolean default(FALSE), not null +# warning_text :text +# location_id :bigint not null +# +FactoryBot.define do + factory :rescan_runner do + location + end +end diff --git a/test/models/location_test.rb b/test/models/location_test.rb index ccd90df6..5343a061 100644 --- a/test/models/location_test.rb +++ b/test/models/location_test.rb @@ -36,4 +36,10 @@ class LocationTest < ActiveSupport::TestCase child = build(:location, path: '/Var/parent/Music') assert child.valid? end + + test 'should create a rescan runner' do + assert_difference('RescanRunner.count', 1) do + create(:location) + end + end end diff --git a/test/models/rescan_runner_test.rb b/test/models/rescan_runner_test.rb index c9938e0d..b94975ca 100644 --- a/test/models/rescan_runner_test.rb +++ b/test/models/rescan_runner_test.rb @@ -8,12 +8,15 @@ # processed :integer default(0), not null # running :boolean default(FALSE), not null # warning_text :text +# location_id :bigint not null # require 'test_helper' class RescanRunnerTest < ActiveSupport::TestCase setup do - @runner = RescanRunner.create + @runner = create(:rescan_runner) + # We need to reload to get the default values set in the database + @runner.reload Codec.create(extension: 'flac', mimetype: 'audio/flac') Codec.create(extension: 'mp3', mimetype: 'audio/mpeg') ImageType.create(extension: 'png', mimetype: 'image/png') @@ -21,20 +24,37 @@ class RescanRunnerTest < ActiveSupport::TestCase CoverFilename.create(filename: 'image') end - test 'should complain when there are no locations' do - @runner.run + test 'schedule should create a job' do + prev = @runner.finished_at + @runner.schedule @runner.reload + assert_not_equal prev, @runner.finished_at + end - assert_not @runner.error_text.empty? - assert_equal 0, @runner.processed - assert_not @runner.running + test 'should not schedule a second job if running' do + @runner.update(running: true) + prev = @runner.finished_at + @runner.schedule + @runner.reload + assert_equal prev, @runner.finished_at + end + + test 'schedule_all should create one task per runner' do + 3.times { create(:rescan_runner) } + + prev = RescanRunner.all.map(&:finished_at) + RescanRunner.schedule_all + after = RescanRunner.all.map(&:finished_at) + prev.each_with_index do |time, i| + assert_not_equal time, after[i] + end end test 'should complain when there are no codecs' do - Location.create(path: Rails.root.join('test/files/success-one-file')) + @runner.location.update(path: Rails.root.join('test/files/success-one-file')) Codec.destroy_all - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? @@ -43,9 +63,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test "should complain when location doesn't exist" do - Location.create(path: Rails.root.join('test/files/doesnt-exist')) + @runner.location.update(path: Rails.root.join('test/files/doesnt-exist')) - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? @@ -54,9 +74,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should complain when location is a file' do - Location.create(path: Rails.root.join('test/files/base.flac')) + @runner.location.update(path: Rails.root.join('test/files/base.flac')) - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? @@ -65,9 +85,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should be able to read a file successfully' do - Location.create(path: Rails.root.join('test/files/success-one-file')) + @runner.location.update(path: Rails.root.join('test/files/success-one-file')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text @@ -90,9 +110,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should reuse artist if artist and composer are equal' do - Location.create(path: Rails.root.join('test/files/success-same-artist-composer')) + @runner.location.update(path: Rails.root.join('test/files/success-same-artist-composer')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text @@ -115,9 +135,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should be able to read a file from a nested directory successfully' do - Location.create(path: Rails.root.join('test/files/success-nested')) + @runner.location.update(path: Rails.root.join('test/files/success-nested')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text assert_equal '', @runner.warning_text @@ -139,9 +159,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should be able to recover from catastrophic error' do - Location.stubs(:all).raises('Catastrophic error') + Codec.stubs(:all).raises('Catastrophic error') - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? assert_includes @runner.error_text, 'Catastrophic error' @@ -150,11 +170,11 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should be able to recover from bad files and proceed' do - Location.create(path: Rails.root.join('test/files/failure-bad-file')) + @runner.location.update(path: Rails.root.join('test/files/failure-bad-file')) # Make sure bad file is processed before good file Dir.stubs(:each_child).multiple_yields('empty.flac', 'all-tags.mp3') - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? @@ -164,10 +184,10 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should not do anything if runner is already running' do - Location.create(path: Rails.root.join('test/files/success-one-file')) + @runner.location.update(path: Rails.root.join('test/files/success-one-file')) @runner.update(running: true, warning_text: 'unchanged', error_text: 'unchanged', processed: 64) - @runner.run + @runner.send(:run) @runner.reload assert_equal 'unchanged', @runner.error_text assert_equal 'unchanged', @runner.warning_text @@ -176,9 +196,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'artist tag should be required' do - Location.create(path: Rails.root.join('test/files/failure-no-artist')) + @runner.location.update(path: Rails.root.join('test/files/failure-no-artist')) - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? assert_includes @runner.error_text, 'no-artist.mp3' @@ -187,9 +207,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'album tag should be required' do - Location.create(path: Rails.root.join('test/files/failure-no-album')) + @runner.location.update(path: Rails.root.join('test/files/failure-no-album')) - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? assert_includes @runner.error_text, 'no-album.mp3' @@ -198,9 +218,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'title tag should be required' do - Location.create(path: Rails.root.join('test/files/failure-no-title')) + @runner.location.update(path: Rails.root.join('test/files/failure-no-title')) - @runner.run + @runner.send(:run) @runner.reload assert_not @runner.error_text.empty? assert_includes @runner.error_text, 'no-title.mp3' @@ -209,9 +229,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'album artists should be empty if albumartist tag is Various Artists' do - Location.create(path: Rails.root.join('test/files/success-various-artists')) + @runner.location.update(path: Rails.root.join('test/files/success-various-artists')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text @@ -234,9 +254,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'album artists should fall back to artist if albumartist tag is empty string' do - Location.create(path: Rails.root.join('test/files/success-empty-albumartist')) + @runner.location.update(path: Rails.root.join('test/files/success-empty-albumartist')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text @@ -259,9 +279,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'only one artist should be created if artist and album_artist are equal' do - Location.create(path: Rails.root.join('test/files/success-equal-artists')) + @runner.location.update(path: Rails.root.join('test/files/success-equal-artists')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text @@ -285,11 +305,11 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'artist should not be created if they already exist' do - Location.create(path: Rails.root.join('test/files/success-equal-artists')) + @runner.location.update(path: Rails.root.join('test/files/success-equal-artists')) Artist.create(name: 'artist') assert_difference('Artist.count', 0) do - @runner.run + @runner.send(:run) @runner.reload end @@ -314,11 +334,11 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'album should not be created if it already exists' do - Location.create(path: Rails.root.join('test/files/success-equal-artists')) + @runner.location.update(path: Rails.root.join('test/files/success-equal-artists')) Album.create(title: 'album', release: Date.new(1970, 1, 1)) assert_difference('Album.count', 0) do - @runner.run + @runner.send(:run) @runner.reload end @@ -339,11 +359,11 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'composer should not be created if they already exist' do - Location.create(path: Rails.root.join('test/files/success-one-file')) + @runner.location.update(path: Rails.root.join('test/files/success-one-file')) Artist.create(name: 'composer') assert_difference('Artist.count', 2) do - @runner.run + @runner.send(:run) @runner.reload end @@ -366,12 +386,12 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'genre should not be created if it already exists' do - Location.create(path: Rails.root.join('test/files/success-one-file')) + @runner.location.update(path: Rails.root.join('test/files/success-one-file')) # Also checks that normalized version is used for matching Genre.create(name: 'Genré') assert_difference('Genre.count', 0) do - @runner.run + @runner.send(:run) @runner.reload end @@ -393,10 +413,10 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should create album with cover if applicable' do - Location.create(path: Rails.root.join('test/files/success-with-cover')) + @runner.location.update(path: Rails.root.join('test/files/success-with-cover')) assert_difference('Image.count', 1) do - @runner.run + @runner.send(:run) end @runner.reload @@ -421,10 +441,10 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should remove non-existent audio file afterwards' do - Location.create(path: Rails.root.join('test/files/success-one-file')) + @runner.location.update(path: Rails.root.join('test/files/success-one-file')) af = AudioFile.create(bitrate: 1, filename: 'non-existent.flac', length: 1, codec: Codec.first, location: Location.first, sample_rate: 44_100, bit_depth: 16) - @runner.run + @runner.send(:run) @runner.reload assert_nil AudioFile.find_by(id: af.id) @@ -450,9 +470,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should get full date from id3v2 tag' do - Location.create(path: Rails.root.join('test/files/success-full-date')) + @runner.location.update(path: Rails.root.join('test/files/success-full-date')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text @@ -474,9 +494,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should be able to ignore time in id3v2 date tage' do - Location.create(path: Rails.root.join('test/files/success-ignore-time')) + @runner.location.update(path: Rails.root.join('test/files/success-ignore-time')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text @@ -498,9 +518,9 @@ class RescanRunnerTest < ActiveSupport::TestCase end test 'should not fail if date has incorrect format' do - Location.create(path: Rails.root.join('test/files/success-invalid-date')) + @runner.location.update(path: Rails.root.join('test/files/success-invalid-date')) - @runner.run + @runner.send(:run) @runner.reload assert_equal '', @runner.error_text