Errno :: ENOENT: нет такого файла или каталога @ rb_file_s_mtime после тестирования возможностей - PullRequest
0 голосов
/ 20 января 2019

При запуске функции Rspecs я получаю следующую ошибку (полная трассировка внизу этого сообщения)

Пума выходная

Rack app error handling request { GET /rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDRG9JYTJWNVNTSm5kbUZ5YVdGdWRITXZSM0IxTkRGRFRWQlFaVXRNYVUxaVpuQjZXVmw1UTNBMUx6RXhNR1V3TVRka01UWTVNalZrWXpkak5UQTNNamhqT1dNeE5UUmhOREl3TURjNVlXRTJaVFZtWTJNME16VmtZak5sTm1VNU4ySXhNemd3WldObFl6Z0dPZ1pGVkRvUVpHbHpjRzl6YVhScGIyNUpJajFwYm14cGJtVTdJR1pwYkdWdVlXMWxQU0pwYldGblpTNXdibWNpT3lCbWFXeGxibUZ0WlNvOVZWUkdMVGduSjJsdFlXZGxMbkJ1WndZN0JsUTZFV052Ym5SbGJuUmZkSGx3WlVraURtbHRZV2RsTDNCdVp3WTdCbFE9IiwiZXhwIjoiMjAxOS0wMS0yNVQxMToxNjoyOS40NDBaIiwicHVyIjoiYmxvYl9rZXkifX0=--db9451afdc95b292aa6a77c40e00ab0ceb687766/image.png }
#<Errno::ENOENT: No such file or directory @ rb_file_s_mtime - /path/to/rails/app/tmp/storage/va/ri/variants/Gpu41CMPPeKLiMbfpzYYyCp5/110e017d16925dc7c50728c9c154a420079aa6e5fcc435db3e6e97b1380ecec8>
(followed by the full trace at the end of this message)

Rspec outpout

Failure/Error: last_modified = ::File.mtime(path).httpdate

Errno::ENOENT:
No such file or directory @ rb_file_s_mtime - /path/to/rails/app/tmp/storage/va/ri/variants/Gpu41CMPPeKLiMbfpzYYyCp5/110e017d16925dc7c50728c9c154a420079aa6e5fcc435db3e6e97b1380ecec8
(followed by the full trace at the end of this message)

Контекст

product.image является вложением ActiveStorage. Во время теста отображается фрагмент «products / _form», который отображает файл вложения (а именно изображение png) со следующим кодом: = image_tag url_for(product.image.variant(resize: "120x120")). Если я закомментирую эту строку, ошибка больше не возникает.

Сам тест каждый раз проходит успешно, похоже, ошибка появляется после теста. Я проверил все мои after блоки, похоже, это происходит после всех моих блоков после.

Трассировка указывает, что ActiveStorage::DiskController.serve_file вызывает ошибки. Неужели приложение ActiveStorage не закрывается изящно?

Я использую active_storage.service = :test конфигурацию, но :file выдает те же ошибки.

Изображение повреждено

Когда тест не пройден, в браузере с «дистанционным управлением» я вижу, что изображение не отображается и вместо него отображается значок с разбитым изображением.

Выполнение тестов

Существует группа из 4 избранных тестов, которые проходят рендеринг части "products / _form". Когда я запускаю 4 теста в случайном порядке, ошибка возникает случайно. Иногда я вижу ошибку 2 раза, иногда 3 раза.

когда я запускаю тесты по отдельности, ошибка никогда не появляется.

Как мне прикрепить изображение

Это помощник, который я использую, чтобы прикрепить файл к модели перед прохождением теста:

path = Rails.root.join 'spec', 'support', 'fixtures', 'img1.png'
raise 'Image does not exist' unless File.exist? path

product.image.attach io: File.open(path), filename: 'image.png'

Я также пытался использовать этот помощник:

path = Rails.root.join 'spec', 'support', 'fixtures', 'img1.png'
raise 'Image does not exist' unless File.exist? path

blob =  ActiveStorage::Blob.create_after_upload! io: path.open, \
  filename: 'img1.png', content_type: 'image/png'
product.images.attach blob

А этот помощник:

path = Rails.root.join 'spec', 'support', 'fixtures', 'img1.png'
raise 'Image does not exist' unless File.exist? path

byte_size = path.size
checksum = Digest::MD5.file(path).base64digest

blob = ActiveStorage::Blob.create_before_direct_upload!(filename: 'img1.png', \
   byte_size: byte_size, checksum: checksum, content_type: 'image/png').tap do |blob|
  ActiveStorage::Blob.service.upload(blob.key, path.open)
end

product.images.attach blob

Конфигурация системы

Версия Rails : 5.2.2

Версия Ruby : ruby ​​2.5.3p105 (версия 2018-10-18 65156) [x86_64-linux]

Версия Rbenv : rbenv 1.1.1-39-g59785f6

Rspec связанные версии : rspec 3.8.0 - capybara 3.12.0 - selenium-webdriver 3.141.0

Полный след

     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/file.rb:63:in `mtime'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/file.rb:63:in `serving'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activestorage-5.2.2/app/controllers/active_storage/disk_controller.rb:42:in `serve_file'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activestorage-5.2.2/app/controllers/active_storage/disk_controller.rb:12:in `show'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/basic_implicit_render.rb:6:in `send_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/base.rb:194:in `process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/rendering.rb:30:in `process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/callbacks.rb:42:in `block in process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/callbacks.rb:132:in `run_callbacks'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/callbacks.rb:41:in `process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/rescue.rb:22:in `process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/instrumentation.rb:34:in `block in process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/notifications.rb:168:in `block in instrument'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/notifications/instrumenter.rb:23:in `instrument'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/notifications.rb:168:in `instrument'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/instrumentation.rb:32:in `process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal/params_wrapper.rb:256:in `process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2/lib/active_record/railties/controller_runtime.rb:24:in `process_action'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/abstract_controller/base.rb:134:in `process'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionview-5.2.2/lib/action_view/rendering.rb:32:in `process'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal.rb:191:in `dispatch'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_controller/metal.rb:252:in `dispatch'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:52:in `dispatch'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:34:in `serve'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/journey/router.rb:52:in `block in serve'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/journey/router.rb:35:in `each'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/journey/router.rb:35:in `serve'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/routing/route_set.rb:840:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/tempfile_reaper.rb:15:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/etag.rb:25:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/conditional_get.rb:25:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/head.rb:12:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/http/content_security_policy.rb:18:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/session/abstract/id.rb:232:in `context'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/session/abstract/id.rb:226:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/cookies.rb:670:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/callbacks.rb:28:in `block in call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/callbacks.rb:98:in `run_callbacks'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/callbacks.rb:26:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/debug_exceptions.rb:61:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/show_exceptions.rb:33:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/railties-5.2.2/lib/rails/rack/logger.rb:38:in `call_app'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/railties-5.2.2/lib/rails/rack/logger.rb:26:in `block in call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/tagged_logging.rb:71:in `block in tagged'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/tagged_logging.rb:28:in `tagged'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/tagged_logging.rb:71:in `tagged'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/railties-5.2.2/lib/rails/rack/logger.rb:26:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/remote_ip.rb:81:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/request_id.rb:27:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/method_override.rb:22:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/runtime.rb:22:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/activesupport-5.2.2/lib/active_support/cache/strategy/local_cache_middleware.rb:29:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/executor.rb:14:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/actionpack-5.2.2/lib/action_dispatch/middleware/static.rb:127:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/sendfile.rb:111:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/railties-5.2.2/lib/rails/engine.rb:524:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/urlmap.rb:68:in `block in call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/urlmap.rb:53:in `each'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/rack-2.0.6/lib/rack/urlmap.rb:53:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/capybara-3.12.0/lib/capybara/server/middleware.rb:48:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/configuration.rb:225:in `call'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/server.rb:658:in `handle_request'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/server.rb:472:in `process_client'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/server.rb:332:in `block in run'
     # /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/puma-3.12.0/lib/puma/thread_pool.rb:133:in `block in spawn_thread'
     # 
     #   Showing full backtrace because every line was filtered out.
     #   See docs for RSpec::Configuration#backtrace_exclusion_patterns and
     #   RSpec::Configuration#backtrace_inclusion_patterns for more information.
     # ------------------
     # --- Caused by: ---
     # Capybara::CapybaraError:
     #   Your application server raised an error - It has been raised in your test code because Capybara.raise_server_errors == true
     #   /opt/rbenv/versions/2.5.3/lib/ruby/gems/2.5.0/gems/capybara-3.12.0/lib/capybara/session.rb:147:in `raise_server_error!'

Обновление

(Извините за молчание, мне нужно было срочно заняться)

Вот интересная информация. Я вставил после эту строку , этот код: puts "SERVING #{path} - Exist ? #{File.exist? path}"

А вот вывод (обратите внимание на имена файлов и логическое значение в конце каждой строки):

SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/Me/GK/MeGKkqgbb7Sg4RbPoCYWR6pZ - Exist ? true
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/sG/3v/sG3vd2viGWmaf69pNXSChZPa - Exist ? true
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/fy/2C/fy2C8PpaR96YgCzwt5WVBLrQ - Exist ? true
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/sG/3v/sG3vd2viGWmaf69pNXSChZPa - Exist ? true
. (green dot)
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/Me/GK/MeGKkqgbb7Sg4RbPoCYWR6pZ - Exist ? false
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/sG/3v/sG3vd2viGWmaf69pNXSChZPa - Exist ? false
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/Me/GK/MeGKkqgbb7Sg4RbPoCYWR6pZ - Exist ? false
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/fy/2C/fy2C8PpaR96YgCzwt5WVBLrQ - Exist ? false
F (red F)
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/Me/GK/MeGKkqgbb7Sg4RbPoCYWR6pZ - Exist ? false
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/sG/3v/sG3vd2viGWmaf69pNXSChZPa - Exist ? false
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/Me/GK/MeGKkqgbb7Sg4RbPoCYWR6pZ - Exist ? false
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/fy/2C/fy2C8PpaR96YgCzwt5WVBLrQ - Exist ? false
F (red F)
SERVING /tmp/active_storage_tests20190129-10797-1rebdf7/Tx/DL/TxDLPEk3ykf8ETkURY5YZPgY - Exist ? true

Как мы видим, путь к файлам одинаков, даже между тестами. Жесткая база данных очищается между каждым тестом. Таким образом, есть «где-то», где хранится контрольная сумма файла, чтобы activestorage мог повторно использовать одну и ту же запись большого двоичного объекта. В результате он повторно использует тот же путь к файлу, и именно поэтому у меня возникает ошибка FileNotFound.

Есть идеи? Обратите внимание, что в дополнение к DatabaseCleaner у меня есть блок после, определенный следующим образом:

config.after(:each) do
  ActiveStorage::Current.reset
  ActiveStorage::Attachment.all.each(&:delete)
  ActiveStorage::Blob.all.each(&:delete)
end

Я подозреваю, что ошибка

Заполнил PR заявкой на воспроизведение https://github.com/rails/rails/issues/34989

В ожидании ответа

Ответы [ 3 ]

0 голосов
/ 23 января 2019

Возможно, что-то не так с настройкой изображения, с которым вы тестируете.Возможно, вы захотите обновить его до того, как команда Rails будет обрабатывать его в своих собственных тестах:

module ActiveStorageHelpers
  # ported from https://github.com/rails/rails/blob/5-2-stable/activestorage/test/test_helper.rb#L57
  def create_file_blob(filename: "image.jpg", content_type: "image/jpeg", metadata: nil)
    ActiveStorage::Blob.create_after_upload! io: file_fixture(filename).open, filename: filename, content_type: content_type, metadata: metadata
  end
end

RSpec.configure do |config|
  config.include ActiveStorageHelpers
end

Затем поместите крошечный файл изображения в spec/fixtures/file/images.jpg (вот где метод file_fixture будет искать его).

Имея это в наличии, вы можете настроить изображение на вашей модели в вашем тесте функций следующим образом:

instance_of_model.images.attach(create_file_blob)
0 голосов
/ 24 января 2019

Тот факт, что тест пройден, означает, что тест не зависит от того, какое изображение найдено.Неработающая ссылка на изображение, как правило, не приводит к сбою теста, и на самом деле часто авторы тестов даже не удосуживаются предоставить имитированные изображения, потому что это (воспринимается как) слишком много работы и не имеет отношения к тестируемой вещи..

Смотрите, ребята, поэтому мы просим привести пример .Теперь, когда ОП опубликовал пример , мы можем видеть, что его проблема заключается в кэшировании в браузере, и будет решена заменой

product.images.attach io: File.open(path), filename: 'image.png'

на

product.images.attach io: File.open(path), filename: "image-#{Time.now.strftime("%s%L")}.png"

в помощнике, который делает приложение.Я бы не решил это без примера.

Пояснение

ActiveStorage обрабатывает сохранение и обслуживание файлов.OP сохранял и обслуживал изображения с этим, что определенно предназначалось для этого.Чтобы разные службы могли обслуживать файлы по-разному, ActiveStorage отделяет опубликованный URL-адрес от фактического URL-адреса обслуживания изображения.

Опубликованный URL-адрес является почти постоянной ссылкой: это кодированная версия ключа базы данных длявложение.ActiveStorage обрабатывает запрос URL-адреса путем поиска места хранения файла и отправки временного перенаправления 302 на URL-адрес, который можно использовать для доступа к файлу, который называется «служебный URL-адрес».При использовании AWS S3 для хранения файлов URL-адрес службы может представлять собой подписанный URL-адрес, срок действия которого быстро истекает, но, тем не менее, он подключает браузер напрямую к S3, а не через веб-сервер в качестве посредника.

По умолчанию URL-адрес службы действителен в течение 5 минут, и ActiveRecord явно устанавливает

Cache-Control: max-age=300, private

при перенаправлении 302.Браузер кэширует этот ответ на перенаправление, и в течение следующих 5 минут браузер даже не попытается использовать или проверить опубликованный URL-адрес, он немедленно заменит общедоступный URL-адрес на кэшированный URL-адрес службы.

К сожалению, хотя общедоступныйURL предсказуемо воссоздается, сервисный URL генерируется случайным образом, поэтому, когда браузер выполняет автоматическое перенаправление, ранее действительный сервисный URL больше не работает.Решение (или обходной путь, в зависимости от вашей точки зрения) состоит в том, чтобы различать общедоступные URL-адреса путем включения в имя файла отметки времени, чтобы тесты не использовали повторно общедоступные URL-адреса.

Sidenote

Кстати, вы не должны использовать url_for с image_tag.Измените свой ERB с

= image_tag url_for(product.image.variant(resize: "120x120"))

на

= image_tag(product.image.variant(resize: "120x120"))

Rails достаточно умен, чтобы справиться с любым случаем, но рекомендуется использовать последний способ.

0 голосов
/ 23 января 2019

Я не уверен, что вы имеете в виду, за исключением того, что происходит после теста?

Тест должен быть, как правило, атомарным и независимым, поэтому все созданные вами ресурсы должны быть удалены сразу после завершения теста.Кроме того, тесты выполняются в произвольном порядке, поэтому тесты, которые зависят друг от друга, иногда не будут выполняться, а иногда нет, в зависимости от порядка выполнения.

Можете ли вы поделиться результатами или тестами, которые не пройдены?

...