resizing 1.2.1 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +73 -0
  3. data/.devcontainer/compose.yaml +58 -0
  4. data/.devcontainer/devcontainer-lock.json +9 -0
  5. data/.devcontainer/devcontainer.json +32 -0
  6. data/.devcontainer/post-create.sh +41 -0
  7. data/.github/copilot-instructions.md +1 -0
  8. data/.github/labeler.yml +28 -0
  9. data/.github/release.yml +35 -0
  10. data/.github/workflows/labeler.yml +20 -0
  11. data/.github/workflows/lint.yml +37 -0
  12. data/.github/workflows/test.yml +16 -9
  13. data/.gitignore +6 -0
  14. data/AGENTS.md +1 -0
  15. data/CHANGELOG.md +3 -0
  16. data/CLAUDE.md +68 -0
  17. data/Gemfile +9 -3
  18. data/README.md +114 -2
  19. data/Rakefile +5 -7
  20. data/bin/setup +3 -0
  21. data/git-hooks/pre-push +6 -0
  22. data/git-hooks/pre-push-ruby.sh +48 -0
  23. data/lib/resizing/carrier_wave/callback_file.rb +88 -0
  24. data/lib/resizing/carrier_wave/storage/file.rb +120 -47
  25. data/lib/resizing/carrier_wave/storage/remote.rb +7 -3
  26. data/lib/resizing/carrier_wave.rb +51 -7
  27. data/lib/resizing/client.rb +0 -1
  28. data/lib/resizing/configurable.rb +1 -1
  29. data/lib/resizing/configuration.rb +40 -10
  30. data/lib/resizing/mock_client.rb +23 -7
  31. data/lib/resizing/public_id.rb +3 -2
  32. data/lib/resizing/version.rb +1 -1
  33. data/lib/resizing.rb +0 -1
  34. data/resizing.gemspec +3 -3
  35. data/test/resizing/carrier_wave/storage/file_test.rb +455 -9
  36. data/test/resizing/carrier_wave/storage/remote_test.rb +91 -9
  37. data/test/resizing/carrier_wave_cache_callbacks_test.rb +224 -0
  38. data/test/resizing/carrier_wave_callback_file_test.rb +77 -0
  39. data/test/resizing/carrier_wave_test.rb +477 -23
  40. data/test/resizing/carrier_wave_uploader_test.rb +211 -0
  41. data/test/resizing/client_test.rb +324 -25
  42. data/test/resizing/configuration_test.rb +235 -10
  43. data/test/resizing/http_clientable_test.rb +27 -0
  44. data/test/resizing/mock_client_test.rb +111 -1
  45. data/test/resizing/public_id_test.rb +96 -0
  46. data/test/resizing_module_test.rb +110 -0
  47. data/test/resizing_test.rb +8 -0
  48. data/test/test_helper.rb +358 -11
  49. data/test/vcr/carrier_wave_test/update_image.yml +63 -0
  50. metadata +35 -10
@@ -5,20 +5,13 @@ require 'test_helper'
5
5
  module Resizing
6
6
  class CarrierWaveTest < Minitest::Test
7
7
  include VCRRequestAssertions
8
+ include ResizingTestConfiguration
8
9
 
9
10
  def setup
10
11
  TestModel.delete_all
11
12
  TestJPGModel.delete_all
12
13
 
13
- @configuration_template = {
14
- image_host: 'http://192.168.56.101:5000',
15
- video_host: 'http://192.168.56.101:5000',
16
- project_id: 'e06e710d-f026-4dcf-b2c0-eab0de8bb83f',
17
- secret_token: 'ewbym2r1pk49x1d2lxdbiiavnqp25j2kh00hsg3koy0ppm620x5mhlmgl3rq5ci8',
18
- open_timeout: 10,
19
- response_timeout: 20
20
- }
21
- Resizing.configure = @configuration_template
14
+ configure_resizing
22
15
  end
23
16
 
24
17
  def teardown; end
@@ -163,25 +156,486 @@ module Resizing
163
156
  assert_instance_of Resizing::CarrierWave::Storage::File, file
164
157
  end
165
158
 
166
- def expect_url
167
- 'http://192.168.56.101:5000/projects/e06e710d-f026-4dcf-b2c0-eab0de8bb83f/' \
168
- 'upload/images/14ea7aac-a194-4330-931f-6b562aec413d/v_8c5lEhDB5RT3PZp1Fn5PYGm9YVx_x0e'
159
+ def prepare_model(model_class)
160
+ result = nil
161
+ VCR.use_cassette 'carrier_wave_test/save', record: :once do
162
+ result = model_class.new
163
+ attach_sample_image(result)
164
+ end
165
+ result
169
166
  end
170
167
 
171
- def prepare_model(model)
172
- result = nil
168
+ # TestModelWithCallbackTracking 用のヘルパーメソッド
169
+ def prepare_tracking_model
170
+ prepare_model(TestModelWithCallbackTracking)
171
+ end
172
+
173
+ # 既存モデルに新しい画像をアップロードするヘルパーメソッド
174
+ def upload_new_image(model)
173
175
  VCR.use_cassette 'carrier_wave_test/save', record: :once do
174
- result = model.new
175
- file = File.open('test/data/images/sample1.jpg', 'r')
176
- uploaded_file = ActionDispatch::Http::UploadedFile.new(
177
- filename: File.basename(file.path),
178
- type: 'image/jpeg',
179
- tempfile: file
180
- )
176
+ attach_sample_image(model)
177
+ end
178
+ end
179
+
180
+ # after_commit コールバックが正しく呼ばれることを確認するテスト
181
+ # 通常のテスト環境ではトランザクションが使われるため、after_commit が呼ばれない問題がある
182
+ # このテストでは、明示的にトランザクションをコミットして after_commit が呼ばれることを確認する
181
183
 
182
- result.resizing_picture = uploaded_file
184
+ def test_after_commit_callback_is_called_on_create
185
+ # after_commit on: :create が呼ばれることを確認
186
+ model = prepare_tracking_model
187
+
188
+ # 明示的なトランザクション内で save! することで after_commit が呼ばれる
189
+ ActiveRecord::Base.transaction do
190
+ model.save!
183
191
  end
184
- result
192
+
193
+ assert_includes model.callback_log, :create_commit,
194
+ 'after_commit on: :create should be called after transaction commit'
195
+ end
196
+
197
+ def test_after_commit_callback_is_called_on_update
198
+ # after_commit on: :update が呼ばれることを確認
199
+ model = prepare_tracking_model
200
+ model.save!
201
+ model.callback_log.clear
202
+
203
+ # 更新時の after_commit が呼ばれることを確認
204
+ upload_new_image(model)
205
+
206
+ ActiveRecord::Base.transaction do
207
+ model.save!
208
+ end
209
+
210
+ assert_includes model.callback_log, :update_commit,
211
+ 'after_commit on: :update should be called after transaction commit'
212
+ end
213
+
214
+ def test_after_commit_callback_is_called_on_destroy
215
+ # after_commit on: :destroy が呼ばれることを確認
216
+ model = prepare_tracking_model
217
+ model.save!
218
+ model.callback_log.clear
219
+
220
+ # destroy 時の after_commit が呼ばれることを確認
221
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
222
+ ActiveRecord::Base.transaction do
223
+ model.destroy!
224
+ end
225
+ end
226
+
227
+ assert_includes model.callback_log, :destroy_commit,
228
+ 'after_commit on: :destroy should be called after transaction commit'
229
+ end
230
+
231
+ def test_carrierwave_remove_previously_stored_is_called_on_update
232
+ # CarrierWave が登録する remove_previously_stored_#{column} が
233
+ # 画像の更新時に呼ばれることを確認
234
+ model = prepare_model TestModel
235
+ model.save!
236
+
237
+ model.resizing_picture_url
238
+
239
+ # 新しい画像をアップロードして更新
240
+ upload_new_image(model)
241
+
242
+ # 更新時には古い画像を削除するため DELETE リクエストが発行されるべき
243
+ # ただし、同じカセットを使用しているため同じ URL になる
244
+ # 実際の環境では異なる public_id が返されるため、古い画像が削除される
245
+ ActiveRecord::Base.transaction do
246
+ model.save!
247
+ end
248
+
249
+ # モデルが更新されたことを確認
250
+ refute_nil model.resizing_picture_url
251
+ end
252
+
253
+ def test_delete_is_called_when_image_is_updated
254
+ # 画像更新時に古い画像を削除する DELETE リクエストが発行されることを確認
255
+ # VCR カセット update_image.yml には以下が含まれる:
256
+ # 1. POST (古い画像のアップロード) -> old-image-id を返す
257
+ # 2. POST (新しい画像のアップロード) -> new-image-id を返す
258
+ # 3. DELETE (古い画像の削除) -> old-image-id を削除
259
+
260
+ assert_vcr_requests_made 'carrier_wave_test/update_image' do
261
+ model = TestModel.new
262
+ model.resizing_picture = File.open(File.expand_path('../data/images/sample1.jpg', __dir__))
263
+ model.save!
264
+
265
+ old_public_id = model.read_attribute(:resizing_picture)
266
+ assert_match(/old-image-id/, old_public_id)
267
+
268
+ # 新しい画像をアップロードして更新(同じカセット内で)
269
+ model.resizing_picture = File.open(File.expand_path('../data/images/sample1.jpg', __dir__))
270
+
271
+ ActiveRecord::Base.transaction do
272
+ model.save!
273
+ end
274
+
275
+ # 更新後は新しい画像の public_id が設定されている
276
+ new_public_id = model.read_attribute(:resizing_picture)
277
+ assert_match(/new-image-id/, new_public_id)
278
+ end
279
+ # カセット内の全リクエスト(POST x2, DELETE x1)が発行されたことを確認
280
+ end
281
+
282
+ # ============================================================
283
+ # before_save / after_save コールバックのテスト
284
+ # ============================================================
285
+
286
+ def test_before_save_write_identifier_is_called
287
+ # before_save :write_#{column}_identifier が呼ばれ、
288
+ # カラムに識別子が書き込まれることを確認
289
+ model = prepare_model TestModel
290
+
291
+ # 保存前は識別子がDBに書き込まれていない
292
+ assert model.new_record?
293
+
294
+ model.save!
295
+
296
+ # 保存後は識別子がDBに書き込まれている
297
+ assert_match %r{/projects/.+/upload/images/.+}, model.read_attribute(:resizing_picture)
298
+ end
299
+
300
+ def test_after_save_store_is_called
301
+ # after_save :store_#{column}! が呼ばれ、
302
+ # ファイルがストレージに保存されることを確認
303
+ model = prepare_model TestModel
304
+ model.save!
305
+
306
+ # ファイルが保存され、URLが取得できることを確認
307
+ refute_nil model.resizing_picture_url
308
+ refute model.resizing_picture.blank?
309
+ end
310
+
311
+ def test_after_save_store_previous_changes_is_called
312
+ # after_save :store_previous_changes_for_#{column} が呼ばれ、
313
+ # 前の変更が追跡されることを確認
314
+ model = prepare_model TestModel
315
+ model.save!
316
+
317
+ model.read_attribute(:resizing_picture)
318
+
319
+ # 新しい画像をアップロード
320
+ upload_new_image(model)
321
+
322
+ model.save!
323
+
324
+ # previous_changes に resizing_picture の変更が記録されていることを確認
325
+ # (CarrierWave が store_previous_changes_for_#{column} で追跡)
326
+ assert model.previous_changes.key?('resizing_picture'),
327
+ 'previous_changes should track resizing_picture changes'
328
+ end
329
+
330
+ # ============================================================
331
+ # after_commit :mark_remove_#{column}_false のテスト
332
+ # ============================================================
333
+
334
+ def test_mark_remove_column_false_is_called_after_update
335
+ # after_commit :mark_remove_#{column}_false, on: :update が呼ばれ、
336
+ # remove フラグがリセットされることを確認
337
+ model = prepare_model TestModel
338
+ model.save!
339
+
340
+ # remove フラグを設定
341
+ model.remove_resizing_picture = '1'
342
+
343
+ # この時点ではフラグが設定されている
344
+ assert_equal '1', model.remove_resizing_picture
345
+
346
+ # save 後に after_commit で mark_remove_resizing_picture_false が呼ばれる
347
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
348
+ ActiveRecord::Base.transaction do
349
+ model.save!
350
+ end
351
+ end
352
+
353
+ # after_commit 後はフラグがリセットされている
354
+ # CarrierWave が mark_remove_#{column}_false でフラグをリセット
355
+ refute model.remove_resizing_picture,
356
+ 'remove flag should be reset after commit'
357
+ end
358
+
359
+ # ============================================================
360
+ # after_commit :remove_#{column}!, on: :destroy のテスト
361
+ # ============================================================
362
+
363
+ def test_remove_column_is_called_on_destroy
364
+ # after_commit :remove_#{column}!, on: :destroy が呼ばれ、
365
+ # ファイルが削除されることを確認
366
+ model = prepare_model TestModel
367
+ model.save!
368
+
369
+ refute model.resizing_picture.blank?
370
+
371
+ # destroy 時に DELETE リクエストが発行されることを確認
372
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
373
+ ActiveRecord::Base.transaction do
374
+ model.destroy!
375
+ end
376
+ end
377
+
378
+ # モデルが削除されたことを確認
379
+ assert model.destroyed?
380
+ end
381
+
382
+ # ============================================================
383
+ # コールバック順序のテスト
384
+ # ============================================================
385
+
386
+ def test_callback_order_on_create
387
+ # create 時のコールバック順序を確認
388
+ # 1. before_save :write_#{column}_identifier
389
+ # 2. after_save :store_#{column}!
390
+ # 3. after_commit (on: :create)
391
+ model = prepare_tracking_model
392
+
393
+ ActiveRecord::Base.transaction do
394
+ model.save!
395
+ end
396
+
397
+ # コールバックが正しい順序で呼ばれたことを確認
398
+ assert_equal %i[before_save after_save create_commit], model.callback_log
399
+ end
400
+
401
+ def test_callback_order_on_update
402
+ # update 時のコールバック順序を確認
403
+ # 1. before_save :write_#{column}_identifier
404
+ # 2. after_save :store_#{column}!
405
+ # 3. after_save :store_previous_changes_for_#{column}
406
+ # 4. after_commit :mark_remove_#{column}_false
407
+ # 5. after_commit :remove_previously_stored_#{column}
408
+ model = prepare_tracking_model
409
+ model.save!
410
+ model.callback_log.clear
411
+
412
+ # 更新
413
+ upload_new_image(model)
414
+
415
+ ActiveRecord::Base.transaction do
416
+ model.save!
417
+ end
418
+
419
+ # コールバックが正しい順序で呼ばれたことを確認
420
+ assert_equal %i[before_save after_save update_commit], model.callback_log
421
+ end
422
+
423
+ def test_callback_order_on_destroy
424
+ # destroy 時のコールバック順序を確認
425
+ # 1. before_destroy
426
+ # 2. after_destroy
427
+ # 3. after_commit :remove_#{column}!, on: :destroy
428
+ model = prepare_tracking_model
429
+ model.save!
430
+ model.callback_log.clear
431
+
432
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
433
+ ActiveRecord::Base.transaction do
434
+ model.destroy!
435
+ end
436
+ end
437
+
438
+ # コールバックが正しい順序で呼ばれたことを確認
439
+ assert_equal %i[before_destroy after_destroy destroy_commit], model.callback_log
440
+ end
441
+
442
+ # ============================================================
443
+ # DB から再読込したモデルに対する操作 (retrieve_from_store! 経由)
444
+ # ============================================================
445
+
446
+ def test_url_of_freshly_loaded_model_matches_saved_url
447
+ model = prepare_model TestModel
448
+ model.save!
449
+
450
+ loaded = TestModel.find(model.id)
451
+
452
+ assert_equal "#{expect_url}/", loaded.resizing_picture_url
453
+ assert_equal "#{expect_url}/c_fill,w_40,h_40", loaded.resizing_picture_url(:small)
454
+ refute loaded.resizing_picture.blank?
455
+ end
456
+
457
+ def test_remove_on_freshly_loaded_model_deletes_remote_image_and_clears_column
458
+ model = prepare_model TestModel
459
+ model.save!
460
+
461
+ loaded = TestModel.find(model.id)
462
+
463
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
464
+ loaded.remove_resizing_picture!
465
+ loaded.save!
466
+ end
467
+
468
+ assert_nil loaded.read_attribute(:resizing_picture)
469
+ assert_nil loaded.resizing_picture_url
470
+ assert_nil TestModel.find(model.id).read_attribute(:resizing_picture)
471
+ end
472
+
473
+ def test_remove_flag_on_freshly_loaded_model_deletes_remote_image_and_clears_column
474
+ model = prepare_model TestModel
475
+ model.save!
476
+
477
+ loaded = TestModel.find(model.id)
478
+ loaded.remove_resizing_picture = true
479
+
480
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
481
+ loaded.save!
482
+ end
483
+
484
+ assert loaded.resizing_picture.blank?
485
+ assert_nil TestModel.find(model.id).read_attribute(:resizing_picture)
486
+ end
487
+
488
+ def test_destroy_on_freshly_loaded_model_deletes_remote_image
489
+ model = prepare_model TestModel
490
+ model.save!
491
+
492
+ loaded = TestModel.find(model.id)
493
+
494
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
495
+ ActiveRecord::Base.transaction do
496
+ loaded.destroy!
497
+ end
498
+ end
499
+
500
+ assert loaded.destroyed?
501
+ assert_nil TestModel.find_by(id: model.id)
502
+ end
503
+
504
+ def test_remove_persists_nil_to_database
505
+ model = prepare_model TestModel
506
+ model.save!
507
+
508
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
509
+ model.remove_resizing_picture!
510
+ model.save!
511
+ end
512
+
513
+ assert_nil TestModel.find(model.id).read_attribute(:resizing_picture)
514
+ end
515
+
516
+ # ============================================================
517
+ # 代入時の挙動
518
+ # ============================================================
519
+
520
+ def test_column_is_set_immediately_after_assignment
521
+ model = TestModel.new
522
+
523
+ assert_vcr_requests_made 'carrier_wave_test/save' do
524
+ attach_sample_image(model)
525
+ end
526
+
527
+ assert_equal expect_identifier, model.read_attribute(:resizing_picture)
528
+ assert_equal expect_identifier, model.resizing_picture.identifier
529
+ assert_equal expect_identifier, model.resizing_picture.file.public_id.to_s
530
+ assert_equal 'image/jpeg', model.resizing_picture.file.content_type
531
+ assert model.resizing_picture.present?
532
+ end
533
+
534
+ def test_save_after_assignment_does_not_upload_again
535
+ model = prepare_model TestModel
536
+
537
+ # 代入時に既にアップロード済みなので、save! で再度 POST は発行されない
538
+ assert_vcr_no_requests 'carrier_wave_test/save' do
539
+ model.save!
540
+ end
541
+
542
+ assert_equal expect_identifier, TestModel.find(model.id).read_attribute(:resizing_picture)
543
+ end
544
+
545
+ def test_assigning_nil_keeps_existing_image
546
+ model = prepare_model TestModel
547
+ model.save!
548
+
549
+ assert_vcr_no_requests 'carrier_wave_test/save' do
550
+ model.resizing_picture = nil
551
+ model.save!
552
+ end
553
+
554
+ assert_equal expect_identifier, model.read_attribute(:resizing_picture)
555
+ assert_equal "#{expect_url}/", model.resizing_picture_url
556
+ end
557
+
558
+ def test_assigning_blank_string_keeps_existing_image
559
+ model = prepare_model TestModel
560
+ model.save!
561
+
562
+ assert_vcr_no_requests 'carrier_wave_test/save' do
563
+ model.resizing_picture = ''
564
+ model.save!
565
+ end
566
+
567
+ assert_equal expect_identifier, model.read_attribute(:resizing_picture)
568
+ end
569
+
570
+ def test_assigning_plain_file_object_uploads_image
571
+ model = TestModel.new
572
+
573
+ assert_vcr_requests_made 'carrier_wave_test/save' do
574
+ model.resizing_picture = File.open('test/data/images/sample1.jpg', 'r')
575
+ end
576
+
577
+ assert_equal expect_identifier, model.read_attribute(:resizing_picture)
578
+ end
579
+
580
+ def test_default_url_is_returned_after_remove
581
+ TestModelWithDefaultURL.delete_all
582
+ model = prepare_model TestModelWithDefaultURL
583
+ model.save!
584
+ refute_equal 'http://example.com/test.jpg', model.resizing_picture_url
585
+
586
+ assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
587
+ model.remove_resizing_picture!
588
+ model.save!
589
+ end
590
+
591
+ assert_equal 'http://example.com/test.jpg', model.resizing_picture_url
592
+ end
593
+
594
+ # ============================================================
595
+ # 画像更新時の永続化
596
+ # ============================================================
597
+
598
+ def test_updated_image_is_persisted_and_old_image_deleted
599
+ # update_image カセット: POST(old) -> POST(new) -> DELETE(old)
600
+ model = nil
601
+ assert_vcr_requests_made 'carrier_wave_test/update_image' do
602
+ model = TestModel.new
603
+ model.resizing_picture = File.open('test/data/images/sample1.jpg', 'r')
604
+ model.save!
605
+
606
+ model.resizing_picture = File.open('test/data/images/sample1.jpg', 'r')
607
+ ActiveRecord::Base.transaction do
608
+ model.save!
609
+ end
610
+ end
611
+
612
+ loaded = TestModel.find(model.id)
613
+ assert_equal(
614
+ '/projects/e06e710d-f026-4dcf-b2c0-eab0de8bb83f/upload/images/new-image-id-2222-2222-222222222222/v2',
615
+ loaded.read_attribute(:resizing_picture)
616
+ )
617
+ assert_equal(
618
+ 'http://192.168.56.101:5000/projects/e06e710d-f026-4dcf-b2c0-eab0de8bb83f/upload/images/new-image-id-2222-2222-222222222222/v2/',
619
+ loaded.resizing_picture_url
620
+ )
621
+ end
622
+
623
+ def test_previous_changes_after_update_records_old_and_new_identifier
624
+ model = nil
625
+ assert_vcr_requests_made 'carrier_wave_test/update_image' do
626
+ model = TestModel.new
627
+ model.resizing_picture = File.open('test/data/images/sample1.jpg', 'r')
628
+ model.save!
629
+
630
+ model.resizing_picture = File.open('test/data/images/sample1.jpg', 'r')
631
+ ActiveRecord::Base.transaction do
632
+ model.save!
633
+ end
634
+ end
635
+
636
+ old_value, new_value = model.previous_changes['resizing_picture']
637
+ assert_match(/old-image-id/, old_value)
638
+ assert_match(/new-image-id/, new_value)
185
639
  end
186
640
  end
187
641
  end