resizing 1.1.0.pre → 1.2.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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +17 -2
  3. data/Gemfile +6 -2
  4. data/README.md +59 -32
  5. data/lib/resizing/active_storage/service/resizing_service.rb +6 -2
  6. data/lib/resizing/active_storage/service.rb +9 -0
  7. data/lib/resizing/active_storage.rb +7 -0
  8. data/lib/resizing/carrier_wave/storage/file.rb +35 -16
  9. data/lib/resizing/carrier_wave/storage/remote.rb +7 -3
  10. data/lib/resizing/carrier_wave.rb +10 -11
  11. data/lib/resizing/client.rb +40 -24
  12. data/lib/resizing/configurable.rb +1 -1
  13. data/lib/resizing/configuration.rb +6 -16
  14. data/lib/resizing/http_clientable.rb +3 -3
  15. data/lib/resizing/mock_client.rb +6 -5
  16. data/lib/resizing/public_id.rb +5 -4
  17. data/lib/resizing/version.rb +1 -1
  18. data/lib/resizing.rb +15 -12
  19. data/resizing.gemspec +5 -8
  20. data/test/resizing/active_storage_service_test.rb +98 -0
  21. data/test/resizing/carrier_wave/storage/file_test.rb +149 -8
  22. data/test/resizing/carrier_wave/storage/remote_test.rb +75 -0
  23. data/test/resizing/carrier_wave_test.rb +99 -37
  24. data/test/resizing/client_test.rb +96 -11
  25. data/test/resizing/configurable_test.rb +82 -0
  26. data/test/resizing/configuration_test.rb +118 -2
  27. data/test/resizing/constants_test.rb +25 -0
  28. data/test/resizing/error_test.rb +73 -0
  29. data/test/resizing/http_clientable_test.rb +84 -0
  30. data/test/resizing/mock_client_test.rb +75 -0
  31. data/test/resizing/public_id_test.rb +1 -1
  32. data/test/resizing_module_test.rb +206 -0
  33. data/test/test_helper.rb +89 -9
  34. metadata +33 -47
  35. data/lib/resizing/video/client.rb +0 -116
  36. data/lib/resizing/video.rb +0 -8
  37. data/test/resizing/video/client_test.rb +0 -158
  38. data/test/vcr/video/metadata/success.yml +0 -47
  39. data/test/vcr/video/prepare/success.yml +0 -47
  40. data/test/vcr/video/upload_completed/success.yml +0 -47
  41. /data/{exe → bin}/console +0 -0
  42. /data/{exe → bin}/generate-changelog +0 -0
  43. /data/{exe → bin}/setup +0 -0
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Resizing
6
+ class HttpClientableTest < Minitest::Test
7
+ class TestClient
8
+ include HttpClientable
9
+
10
+ def config
11
+ @config ||= Configuration.new(
12
+ image_host: 'https://image.example.com',
13
+ project_id: 'test_project',
14
+ secret_token: 'test_token'
15
+ )
16
+ end
17
+ end
18
+
19
+ def setup
20
+ @client = TestClient.new
21
+ end
22
+
23
+ def teardown
24
+ # NOP
25
+ end
26
+
27
+ def test_http_client_initialization
28
+ http_client = @client.http_client
29
+
30
+ assert_instance_of Faraday::Connection, http_client
31
+ end
32
+
33
+ def test_http_client_has_open_timeout
34
+ http_client = @client.http_client
35
+
36
+ assert_equal @client.config.open_timeout, http_client.options[:open_timeout]
37
+ end
38
+
39
+ def test_http_client_has_response_timeout
40
+ http_client = @client.http_client
41
+
42
+ assert_equal @client.config.response_timeout, http_client.options[:timeout]
43
+ end
44
+
45
+ def test_http_client_is_cached
46
+ http_client1 = @client.http_client
47
+ http_client2 = @client.http_client
48
+
49
+ assert_equal http_client1.object_id, http_client2.object_id
50
+ end
51
+
52
+ def test_handle_faraday_error_yields_block
53
+ result = @client.handle_faraday_error { 'test_result' }
54
+
55
+ assert_equal 'test_result', result
56
+ end
57
+
58
+ def test_handle_faraday_error_catches_timeout_error
59
+ assert_raises Resizing::APIError do
60
+ @client.handle_faraday_error do
61
+ raise Faraday::TimeoutError, 'timeout'
62
+ end
63
+ end
64
+ end
65
+
66
+ def test_handle_timeout_error_raises_api_error
67
+ error = Faraday::TimeoutError.new('test timeout')
68
+
69
+ assert_raises Resizing::APIError do
70
+ @client.handle_timeout_error(error)
71
+ end
72
+ end
73
+
74
+ def test_handle_timeout_error_message_includes_error_info
75
+ error = Faraday::TimeoutError.new('test timeout')
76
+
77
+ begin
78
+ @client.handle_timeout_error(error)
79
+ rescue Resizing::APIError => e
80
+ assert_includes e.message, 'TimeoutError'
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ module Resizing
6
+ class MockClientTest < Minitest::Test
7
+ def setup
8
+ @client = Resizing::MockClient.new
9
+ end
10
+
11
+ def teardown
12
+ # NOP
13
+ end
14
+
15
+ def test_post_returns_parsed_json
16
+ VCR.use_cassette('client/post', record: :none) do
17
+ result = @client.post(nil)
18
+
19
+ assert_instance_of Hash, result
20
+ assert result.key?('id')
21
+ assert result.key?('public_id')
22
+ assert result.key?('latest_version_id')
23
+ assert result.key?('latest_etag')
24
+ end
25
+ end
26
+
27
+ def test_put_returns_parsed_json_with_modified_name
28
+ VCR.use_cassette('client/put', record: :none) do
29
+ name = 'test-image-123'
30
+ result = @client.put(name, nil, {})
31
+
32
+ assert_instance_of Hash, result
33
+ assert_equal name, result['id']
34
+ assert_includes result['public_id'], name
35
+ assert result.key?('latest_version_id')
36
+ assert result.key?('latest_etag')
37
+ end
38
+ end
39
+
40
+ def test_delete_returns_parsed_json_with_modified_name
41
+ VCR.use_cassette('client/delete', record: :none) do
42
+ name = 'delete-test-image'
43
+ result = @client.delete(name)
44
+
45
+ assert_instance_of Hash, result
46
+ assert_equal name, result['id']
47
+ assert_includes result['public_id'], name
48
+ end
49
+ end
50
+
51
+ def test_metadata_returns_parsed_json_with_modified_name
52
+ VCR.use_cassette('client/metadata', record: :none) do
53
+ name = 'metadata-test-image'
54
+ result = @client.metadata(name)
55
+
56
+ assert_instance_of Hash, result
57
+ assert_equal name, result['id']
58
+ # The cassette contains a fixed public_id, so we just check it exists
59
+ assert result.key?('public_id')
60
+ end
61
+ end
62
+
63
+ def test_post_response_contains_expected_fields
64
+ VCR.use_cassette('client/post', record: :none) do
65
+ result = @client.post(nil)
66
+
67
+ # Verify response structure
68
+ assert result['id'].is_a?(String)
69
+ assert result['public_id'].is_a?(String)
70
+ assert result['latest_version_id'].is_a?(String)
71
+ assert result['latest_etag'].is_a?(String)
72
+ end
73
+ end
74
+ end
75
+ end
@@ -30,7 +30,7 @@ module Resizing
30
30
 
31
31
  def test_expect_equal_identifier
32
32
  public_id = Resizing::PublicId.new @public_id_as_string
33
- assert_equal @public_id_as_string.gsub(/\/v.*$/, ''), public_id.identifier
33
+ assert_equal @public_id_as_string.gsub(%r{/v.*$}, ''), public_id.identifier
34
34
  end
35
35
 
36
36
  def test_expect_equal_public_id
@@ -0,0 +1,206 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_helper'
4
+
5
+ class ResizingModuleTest < Minitest::Test
6
+ def setup
7
+ # Reset configure before each test to ensure clean state
8
+ return unless Resizing.instance_variable_defined?(:@configure)
9
+
10
+ Resizing.remove_instance_variable(:@configure)
11
+ end
12
+
13
+ def teardown
14
+ # Reset configure after each test
15
+ return unless Resizing.instance_variable_defined?(:@configure)
16
+
17
+ Resizing.remove_instance_variable(:@configure)
18
+ end
19
+
20
+ def test_configure_raises_error_when_not_initialized
21
+ assert_raises Resizing::ConfigurationError do
22
+ Resizing.configure
23
+ end
24
+ end
25
+
26
+ def test_configure_returns_duplicate_of_configuration
27
+ config = Resizing::Configuration.new(
28
+ image_host: 'https://test.example.com',
29
+ project_id: 'test_id',
30
+ secret_token: 'test_token'
31
+ )
32
+ Resizing.configure = config
33
+
34
+ result = Resizing.configure
35
+
36
+ assert_instance_of Resizing::Configuration, result
37
+ assert_equal config.image_host, result.image_host
38
+ assert_equal config.project_id, result.project_id
39
+ refute_equal config.object_id, result.object_id # Should be a duplicate
40
+ end
41
+
42
+ def test_configure_setter_accepts_configuration_object
43
+ config = Resizing::Configuration.new(
44
+ image_host: 'https://test.example.com',
45
+ project_id: 'test_id',
46
+ secret_token: 'test_token'
47
+ )
48
+
49
+ Resizing.configure = config
50
+
51
+ assert_equal config, Resizing.instance_variable_get(:@configure)
52
+ end
53
+
54
+ def test_configure_setter_converts_hash_to_configuration
55
+ config_hash = {
56
+ image_host: 'https://hash.example.com',
57
+ project_id: 'hash_id',
58
+ secret_token: 'hash_token'
59
+ }
60
+
61
+ Resizing.configure = config_hash
62
+
63
+ result = Resizing.instance_variable_get(:@configure)
64
+ assert_instance_of Resizing::Configuration, result
65
+ assert_equal 'https://hash.example.com', result.image_host
66
+ assert_equal 'hash_id', result.project_id
67
+ end
68
+
69
+ def test_get_raises_not_implemented_error
70
+ assert_raises NotImplementedError do
71
+ Resizing.get('test')
72
+ end
73
+ end
74
+
75
+ def test_url_from_image_id_returns_url_without_version_and_transforms
76
+ Resizing.configure = {
77
+ image_host: 'https://img.example.com',
78
+ project_id: 'project123',
79
+ secret_token: 'token123'
80
+ }
81
+
82
+ url = Resizing.url_from_image_id('image456')
83
+
84
+ assert_equal 'https://img.example.com/projects/project123/upload/images/image456', url
85
+ end
86
+
87
+ def test_url_from_image_id_returns_url_with_version
88
+ Resizing.configure = {
89
+ image_host: 'https://img.example.com',
90
+ project_id: 'project123',
91
+ secret_token: 'token123'
92
+ }
93
+
94
+ url = Resizing.url_from_image_id('image456', '789')
95
+
96
+ assert_equal 'https://img.example.com/projects/project123/upload/images/image456/v789', url
97
+ end
98
+
99
+ def test_url_from_image_id_returns_url_with_transformations
100
+ Resizing.configure = {
101
+ image_host: 'https://img.example.com',
102
+ project_id: 'project123',
103
+ secret_token: 'token123'
104
+ }
105
+
106
+ url = Resizing.url_from_image_id('image456', nil, [{ w: 100, h: 200 }])
107
+
108
+ assert_includes url, 'https://img.example.com/projects/project123/upload/images/image456/'
109
+ assert_includes url, 'w_100,h_200'
110
+ end
111
+
112
+ def test_url_from_image_id_returns_url_with_version_and_transformations
113
+ Resizing.configure = {
114
+ image_host: 'https://img.example.com',
115
+ project_id: 'project123',
116
+ secret_token: 'token123'
117
+ }
118
+
119
+ url = Resizing.url_from_image_id('image456', '789', [{ w: 100 }])
120
+
121
+ assert_includes url, 'image456/v789/'
122
+ assert_includes url, 'w_100'
123
+ end
124
+
125
+ def test_generate_identifier_returns_identifier_string
126
+ Resizing.configure = {
127
+ image_host: 'https://img.example.com',
128
+ project_id: 'project123',
129
+ secret_token: 'token123'
130
+ }
131
+
132
+ identifier = Resizing.generate_identifier
133
+
134
+ assert_instance_of String, identifier
135
+ assert_includes identifier, 'project123'
136
+ assert_match %r{/projects/project123/upload/images/}, identifier
137
+ end
138
+
139
+ def test_client_returns_mock_client_when_enable_mock_is_true
140
+ Resizing.configure = {
141
+ image_host: 'https://img.example.com',
142
+ project_id: 'project123',
143
+ secret_token: 'token123',
144
+ enable_mock: true
145
+ }
146
+
147
+ client = Resizing.client
148
+
149
+ assert_instance_of Resizing::MockClient, client
150
+ end
151
+
152
+ def test_client_returns_real_client_when_enable_mock_is_false
153
+ Resizing.configure = {
154
+ image_host: 'https://img.example.com',
155
+ project_id: 'project123',
156
+ secret_token: 'token123',
157
+ enable_mock: false
158
+ }
159
+
160
+ client = Resizing.client
161
+
162
+ assert_instance_of Resizing::Client, client
163
+ end
164
+
165
+ def test_put_delegates_to_client
166
+ Resizing.configure = {
167
+ image_host: 'https://img.example.com',
168
+ project_id: 'project123',
169
+ secret_token: 'token123',
170
+ enable_mock: true
171
+ }
172
+
173
+ result = Resizing.put('image_id', 'dummy', content_type: 'image/jpeg')
174
+
175
+ assert_instance_of Hash, result
176
+ assert_equal 'image_id', result['id']
177
+ end
178
+
179
+ def test_delete_delegates_to_client
180
+ Resizing.configure = {
181
+ image_host: 'https://img.example.com',
182
+ project_id: 'project123',
183
+ secret_token: 'token123',
184
+ enable_mock: true
185
+ }
186
+
187
+ result = Resizing.delete('image_id')
188
+
189
+ assert_instance_of Hash, result
190
+ assert_equal 'image_id', result['id']
191
+ end
192
+
193
+ def test_metadata_delegates_to_client
194
+ Resizing.configure = {
195
+ image_host: 'https://img.example.com',
196
+ project_id: 'project123',
197
+ secret_token: 'token123',
198
+ enable_mock: true
199
+ }
200
+
201
+ result = Resizing.metadata('image_id', {})
202
+
203
+ assert_instance_of Hash, result
204
+ assert_equal 'image_id', result['id']
205
+ end
206
+ end
data/test/test_helper.rb CHANGED
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  require 'simplecov'
3
4
  require 'simplecov-cobertura'
4
5
 
5
6
  SimpleCov.start do
6
- add_filter "/test/"
7
+ add_filter '/test/'
7
8
 
8
9
  if ENV['CI']
9
10
  formatter SimpleCov::Formatter::CoberturaFormatter
@@ -22,15 +23,18 @@ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
22
23
  require 'time'
23
24
  require 'timecop'
24
25
  require 'vcr'
26
+ require 'logger'
25
27
 
26
28
  require 'rails'
27
29
  require 'active_record'
28
30
  require 'fog-aws'
29
31
  require 'carrierwave'
32
+ require 'carrierwave/orm/activerecord'
30
33
  require 'resizing'
31
34
  require 'pry-byebug'
32
35
 
33
36
  require 'minitest/autorun'
37
+ require 'minitest/mock'
34
38
 
35
39
  VCR.configure do |c|
36
40
  c.cassette_library_dir = 'test/vcr'
@@ -38,11 +42,93 @@ VCR.configure do |c|
38
42
  c.allow_http_connections_when_no_cassette = false
39
43
 
40
44
  # raise Faraday::TimeoutError, when project_id is timeout_project_id
41
- c.before_http_request(lambda {|r| URI(r.uri).path.match? %r(/projects/timeout_project_id) } ) do
45
+ c.before_http_request(->(r) { URI(r.uri).path.match? %r{/projects/timeout_project_id} }) do
42
46
  raise Faraday::TimeoutError
43
47
  end
44
48
  end
45
49
 
50
+ # VCRカセットのリクエストが実際に使用されたかを検証するヘルパー
51
+ module VCRRequestAssertions
52
+ # VCRカセット内でブロックを実行し、カセットのインタラクションがすべて使用されたことを確認
53
+ #
54
+ # @param cassette_name [String] VCRカセット名
55
+ # @param options [Hash] VCR.use_cassetteに渡すオプション
56
+ # @yield 実行するブロック
57
+ # @return [void]
58
+ #
59
+ # @example
60
+ # assert_vcr_requests_made 'carrier_wave_test/remove_resizing_picture' do
61
+ # model.remove_resizing_picture!
62
+ # model.save!
63
+ # end
64
+ def assert_vcr_requests_made(cassette_name, options = {}, &block)
65
+ options = { record: :none }.merge(options)
66
+
67
+ VCR.use_cassette(cassette_name, options) do |cassette|
68
+ interaction_list = cassette.http_interactions
69
+ initial_count = interaction_list.remaining_unused_interaction_count
70
+
71
+ assert initial_count.positive?,
72
+ "Cassette '#{cassette_name}' should have at least 1 interaction"
73
+
74
+ yield cassette if block_given?
75
+
76
+ remaining_count = interaction_list.remaining_unused_interaction_count
77
+ used_count = initial_count - remaining_count
78
+
79
+ assert_equal 0, remaining_count,
80
+ "Expected all #{initial_count} cassette interactions to be used, " \
81
+ "but #{remaining_count} remain unused (#{used_count} were used)"
82
+ end
83
+ end
84
+
85
+ # VCRカセット内でブロックを実行し、指定した数のインタラクションが使用されたことを確認
86
+ #
87
+ # @param cassette_name [String] VCRカセット名
88
+ # @param expected_count [Integer] 使用されるべきインタラクション数
89
+ # @param options [Hash] VCR.use_cassetteに渡すオプション
90
+ # @yield 実行するブロック
91
+ # @return [void]
92
+ #
93
+ # @example
94
+ # assert_vcr_requests_count 'client/post', 1 do
95
+ # Resizing.post(file)
96
+ # end
97
+ def assert_vcr_requests_count(cassette_name, expected_count, options = {}, &block)
98
+ options = { record: :none }.merge(options)
99
+
100
+ VCR.use_cassette(cassette_name, options) do |cassette|
101
+ interaction_list = cassette.http_interactions
102
+ initial_count = interaction_list.remaining_unused_interaction_count
103
+
104
+ yield cassette if block_given?
105
+
106
+ remaining_count = interaction_list.remaining_unused_interaction_count
107
+ used_count = initial_count - remaining_count
108
+
109
+ assert_equal expected_count, used_count,
110
+ "Expected #{expected_count} cassette interactions to be used, " \
111
+ "but #{used_count} were used"
112
+ end
113
+ end
114
+
115
+ # VCRカセット内でブロックを実行し、リクエストが発行されないことを確認
116
+ #
117
+ # @param cassette_name [String] VCRカセット名
118
+ # @param options [Hash] VCR.use_cassetteに渡すオプション
119
+ # @yield 実行するブロック
120
+ # @return [void]
121
+ #
122
+ # @example
123
+ # assert_vcr_no_requests 'carrier_wave_test/remove_resizing_picture' do
124
+ # model.remove_resizing_picture = true
125
+ # # save!を呼ばないのでリクエストは発行されない
126
+ # end
127
+ def assert_vcr_no_requests(cassette_name, options = {}, &block)
128
+ assert_vcr_requests_count(cassette_name, 0, options, &block)
129
+ end
130
+ end
131
+
46
132
  ActiveRecord::Base.establish_connection(
47
133
  adapter: 'mysql2',
48
134
  host: '127.0.0.1',
@@ -56,7 +142,7 @@ ActiveRecord::Base.establish_connection(
56
142
  ActiveRecord::Schema.define do
57
143
  self.verbose = false
58
144
 
59
- %i(test_models test_jpg_models test_model_with_default_urls).each do |model_name|
145
+ %i[test_models test_jpg_models test_model_with_default_urls].each do |model_name|
60
146
  connection.execute "drop table if exists #{model_name}"
61
147
 
62
148
  create_table model_name do |t|
@@ -101,19 +187,13 @@ class ResizingUploaderWithDefaultURL < CarrierWave::Uploader::Base
101
187
  end
102
188
 
103
189
  class TestModel < ::ActiveRecord::Base
104
- extend CarrierWave::Mount
105
-
106
190
  mount_uploader :resizing_picture, ResizingUploader
107
191
  end
108
192
 
109
193
  class TestJPGModel < ::ActiveRecord::Base
110
- extend CarrierWave::Mount
111
-
112
194
  mount_uploader :resizing_picture, ResizingJPGUploader
113
195
  end
114
196
 
115
197
  class TestModelWithDefaultURL < ::ActiveRecord::Base
116
- extend CarrierWave::Mount
117
-
118
198
  mount_uploader :resizing_picture, ResizingUploaderWithDefaultURL
119
199
  end