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.
- checksums.yaml +4 -4
- data/.devcontainer/Dockerfile +73 -0
- data/.devcontainer/compose.yaml +58 -0
- data/.devcontainer/devcontainer-lock.json +9 -0
- data/.devcontainer/devcontainer.json +32 -0
- data/.devcontainer/post-create.sh +41 -0
- data/.github/copilot-instructions.md +1 -0
- data/.github/labeler.yml +28 -0
- data/.github/release.yml +35 -0
- data/.github/workflows/labeler.yml +20 -0
- data/.github/workflows/lint.yml +37 -0
- data/.github/workflows/test.yml +16 -9
- data/.gitignore +6 -0
- data/AGENTS.md +1 -0
- data/CHANGELOG.md +3 -0
- data/CLAUDE.md +68 -0
- data/Gemfile +9 -3
- data/README.md +114 -2
- data/Rakefile +5 -7
- data/bin/setup +3 -0
- data/git-hooks/pre-push +6 -0
- data/git-hooks/pre-push-ruby.sh +48 -0
- data/lib/resizing/carrier_wave/callback_file.rb +88 -0
- data/lib/resizing/carrier_wave/storage/file.rb +120 -47
- data/lib/resizing/carrier_wave/storage/remote.rb +7 -3
- data/lib/resizing/carrier_wave.rb +51 -7
- data/lib/resizing/client.rb +0 -1
- data/lib/resizing/configurable.rb +1 -1
- data/lib/resizing/configuration.rb +40 -10
- data/lib/resizing/mock_client.rb +23 -7
- data/lib/resizing/public_id.rb +3 -2
- data/lib/resizing/version.rb +1 -1
- data/lib/resizing.rb +0 -1
- data/resizing.gemspec +3 -3
- data/test/resizing/carrier_wave/storage/file_test.rb +455 -9
- data/test/resizing/carrier_wave/storage/remote_test.rb +91 -9
- data/test/resizing/carrier_wave_cache_callbacks_test.rb +224 -0
- data/test/resizing/carrier_wave_callback_file_test.rb +77 -0
- data/test/resizing/carrier_wave_test.rb +477 -23
- data/test/resizing/carrier_wave_uploader_test.rb +211 -0
- data/test/resizing/client_test.rb +324 -25
- data/test/resizing/configuration_test.rb +235 -10
- data/test/resizing/http_clientable_test.rb +27 -0
- data/test/resizing/mock_client_test.rb +111 -1
- data/test/resizing/public_id_test.rb +96 -0
- data/test/resizing_module_test.rb +110 -0
- data/test/resizing_test.rb +8 -0
- data/test/test_helper.rb +358 -11
- data/test/vcr/carrier_wave_test/update_image.yml +63 -0
- metadata +35 -10
data/lib/resizing/mock_client.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'securerandom'
|
|
4
|
+
|
|
3
5
|
module Resizing
|
|
4
6
|
class MockClient
|
|
5
7
|
def post(_file_or_binary, _options = {})
|
|
@@ -10,33 +12,47 @@ module Resizing
|
|
|
10
12
|
def put(name, _file_or_binary, _options)
|
|
11
13
|
r = load_yaml('test/vcr/client/put.yml')
|
|
12
14
|
result = JSON.parse(r['string'])
|
|
13
|
-
#
|
|
15
|
+
# カセット内の値に依存せず、name と新しい version で id / public_id を組み立て直す
|
|
16
|
+
version = generate_version
|
|
14
17
|
result['id'] = name
|
|
15
|
-
result['
|
|
16
|
-
result['
|
|
18
|
+
result['version'] = version
|
|
19
|
+
result['latest_version_id'] = version
|
|
20
|
+
result['public_id'] = build_public_id(result['public_id'], image_id: name, version: version)
|
|
17
21
|
result
|
|
18
22
|
end
|
|
19
23
|
|
|
20
24
|
def delete(name)
|
|
21
25
|
r = load_yaml('test/vcr/client/delete.yml')
|
|
22
26
|
result = JSON.parse(r['string'])
|
|
23
|
-
#
|
|
27
|
+
# カセット内の値に依存せず、name で id / public_id を組み立て直す
|
|
24
28
|
result['id'] = name
|
|
25
|
-
result['public_id']
|
|
29
|
+
result['public_id'] = build_public_id(result['public_id'], image_id: name)
|
|
26
30
|
result
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def metadata(name, _options = {})
|
|
30
34
|
r = load_yaml('test/vcr/client/metadata.yml')
|
|
31
35
|
result = JSON.parse(r['string'])
|
|
32
|
-
#
|
|
36
|
+
# カセット内の値に依存せず、name で id / public_id を組み立て直す
|
|
33
37
|
result['id'] = name
|
|
34
|
-
result['public_id']
|
|
38
|
+
result['public_id'] = build_public_id(result['public_id'], image_id: name)
|
|
35
39
|
result
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
private
|
|
39
43
|
|
|
44
|
+
# カセットの public_id を PublicId で分解し、image_id / version を差し替えて組み立て直す
|
|
45
|
+
def build_public_id(public_id, image_id:, version: nil)
|
|
46
|
+
parsed = Resizing::PublicId.new(public_id)
|
|
47
|
+
version ||= parsed.version
|
|
48
|
+
identifier = "/projects/#{parsed.project_id}/upload/images/#{image_id}"
|
|
49
|
+
version.nil? ? identifier : "#{identifier}/v#{version}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def generate_version
|
|
53
|
+
SecureRandom.alphanumeric(32)
|
|
54
|
+
end
|
|
55
|
+
|
|
40
56
|
def load_yaml(filename)
|
|
41
57
|
path = "#{library_root}/#{filename}"
|
|
42
58
|
YAML.load_file(path)['http_interactions'].first['response']['body']
|
data/lib/resizing/public_id.rb
CHANGED
|
@@ -7,8 +7,9 @@ module Resizing
|
|
|
7
7
|
parsed
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
+
# nil / 空文字 / 空白のみの文字列はいずれも「値なし」として扱う
|
|
10
11
|
def empty?
|
|
11
|
-
@public_id.to_s.empty?
|
|
12
|
+
@public_id.to_s.strip.empty?
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def image_id
|
|
@@ -39,7 +40,7 @@ module Resizing
|
|
|
39
40
|
private
|
|
40
41
|
|
|
41
42
|
def parsed
|
|
42
|
-
return nil if
|
|
43
|
+
return nil if empty?
|
|
43
44
|
|
|
44
45
|
unless defined? @parsed
|
|
45
46
|
@parsed = Resizing.separate_public_id(@public_id)
|
data/lib/resizing/version.rb
CHANGED
data/lib/resizing.rb
CHANGED
|
@@ -14,7 +14,6 @@ module Resizing
|
|
|
14
14
|
autoload :Configuration, 'resizing/configuration'
|
|
15
15
|
autoload :CarrierWave, 'resizing/carrier_wave'
|
|
16
16
|
autoload :PublicId, 'resizing/public_id'
|
|
17
|
-
autoload :Video, 'resizing/video'
|
|
18
17
|
autoload :ActiveStorage, 'resizing/active_storage'
|
|
19
18
|
|
|
20
19
|
class Error < StandardError; end
|
data/resizing.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = 'Client and utilities for Resizing '
|
|
13
13
|
spec.homepage = 'https://github.com/jksy/resizing-gem'
|
|
14
14
|
spec.license = 'MIT'
|
|
15
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 3.
|
|
15
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.1.0')
|
|
16
16
|
|
|
17
17
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
18
18
|
|
|
@@ -26,8 +26,8 @@ Gem::Specification.new do |spec|
|
|
|
26
26
|
`git ls-files -z`.split("\x0")
|
|
27
27
|
end
|
|
28
28
|
spec.require_paths = ['lib']
|
|
29
|
-
spec.add_runtime_dependency 'faraday', '
|
|
30
|
-
spec.add_runtime_dependency 'faraday-multipart'
|
|
29
|
+
spec.add_runtime_dependency 'faraday', '>= 1.0', '< 3'
|
|
30
|
+
spec.add_runtime_dependency 'faraday-multipart', '>= 1.0'
|
|
31
31
|
spec.add_development_dependency 'carrierwave', '~> 2.2.5'
|
|
32
32
|
spec.add_development_dependency 'fog-aws'
|
|
33
33
|
spec.add_development_dependency 'minitest', '~> 5.16'
|
|
@@ -1,21 +1,18 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'test_helper'
|
|
4
|
+
require 'tempfile'
|
|
4
5
|
|
|
5
6
|
module Resizing
|
|
6
7
|
module CarrierWave
|
|
7
8
|
module Storage
|
|
8
9
|
class FileTest < Minitest::Test
|
|
10
|
+
include VCRRequestAssertions
|
|
11
|
+
include FakeApiClientAssertions
|
|
12
|
+
include ResizingTestConfiguration
|
|
13
|
+
|
|
9
14
|
def setup
|
|
10
|
-
|
|
11
|
-
image_host: 'http://192.168.56.101:5000',
|
|
12
|
-
video_host: 'http://192.168.56.101:5000',
|
|
13
|
-
project_id: 'e06e710d-f026-4dcf-b2c0-eab0de8bb83f',
|
|
14
|
-
secret_token: 'ewbym2r1pk49x1d2lxdbiiavnqp25j2kh00hsg3koy0ppm620x5mhlmgl3rq5ci8',
|
|
15
|
-
open_timeout: 10,
|
|
16
|
-
response_timeout: 20
|
|
17
|
-
}
|
|
18
|
-
Resizing.configure = @configuration_template
|
|
15
|
+
configure_resizing
|
|
19
16
|
end
|
|
20
17
|
|
|
21
18
|
def teardown
|
|
@@ -165,6 +162,455 @@ module Resizing
|
|
|
165
162
|
file.delete
|
|
166
163
|
end
|
|
167
164
|
end
|
|
165
|
+
|
|
166
|
+
# ============================================================
|
|
167
|
+
# delete の詳細な挙動 (Resizing::Client を偽クライアントに差し替えて検証)
|
|
168
|
+
# ============================================================
|
|
169
|
+
|
|
170
|
+
IDENTIFIER = '/projects/e06e710d-f026-4dcf-b2c0-eab0de8bb83f/upload/images/14ea7aac-a194-4330-931f-6b562aec413d/v_8c5lEhDB5RT3PZp1Fn5PYGm9YVx_x0e'
|
|
171
|
+
IMAGE_ID = '14ea7aac-a194-4330-931f-6b562aec413d'
|
|
172
|
+
OTHER_IDENTIFIER = '/projects/e06e710d-f026-4dcf-b2c0-eab0de8bb83f/upload/images/new-image-id-2222-2222-222222222222/v2'
|
|
173
|
+
NOT_FOUND_RESPONSE = {
|
|
174
|
+
'error' => 'ActiveRecord::RecordNotFound',
|
|
175
|
+
'message' => "Upload::Image##{IMAGE_ID} not found(no record)"
|
|
176
|
+
}.freeze
|
|
177
|
+
|
|
178
|
+
def model_with_column(value)
|
|
179
|
+
model = TestModel.new
|
|
180
|
+
model.send(:write_attribute, :resizing_picture, value)
|
|
181
|
+
model
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def test_delete_clears_column_when_deleted_image_is_current
|
|
185
|
+
model = model_with_column(IDENTIFIER)
|
|
186
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
187
|
+
|
|
188
|
+
with_fake_api_client({ 'id' => IMAGE_ID }) do |fake|
|
|
189
|
+
file.delete
|
|
190
|
+
assert_equal [IMAGE_ID], fake.deleted_ids
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
assert_nil model.read_attribute(:resizing_picture)
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def test_delete_keeps_column_when_deleting_previous_image_after_update
|
|
197
|
+
# 画像更新時: カラムは新しい画像を指しているが、削除対象は古い画像なのでカラムは消さない
|
|
198
|
+
model = model_with_column(OTHER_IDENTIFIER)
|
|
199
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
200
|
+
|
|
201
|
+
with_fake_api_client({ 'id' => IMAGE_ID }) do |fake|
|
|
202
|
+
file.delete
|
|
203
|
+
assert_equal [IMAGE_ID], fake.deleted_ids
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
assert_equal OTHER_IDENTIFIER, model.read_attribute(:resizing_picture)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def test_delete_treats_not_found_as_success_and_clears_column
|
|
210
|
+
model = model_with_column(IDENTIFIER)
|
|
211
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
212
|
+
|
|
213
|
+
with_fake_api_client(NOT_FOUND_RESPONSE) do
|
|
214
|
+
assert_nil file.delete
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
assert_nil model.read_attribute(:resizing_picture)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def test_delete_treats_not_found_of_previous_image_without_clearing_column
|
|
221
|
+
model = model_with_column(OTHER_IDENTIFIER)
|
|
222
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
223
|
+
|
|
224
|
+
with_fake_api_client(NOT_FOUND_RESPONSE) { file.delete }
|
|
225
|
+
|
|
226
|
+
assert_equal OTHER_IDENTIFIER, model.read_attribute(:resizing_picture)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def test_delete_raises_api_error_when_response_id_does_not_match
|
|
230
|
+
model = model_with_column(IDENTIFIER)
|
|
231
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
232
|
+
|
|
233
|
+
with_fake_api_client({ 'id' => 'someone-else' }) do
|
|
234
|
+
assert_raises(Resizing::APIError) { file.delete }
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# 失敗時はカラムを変更しない
|
|
238
|
+
assert_equal IDENTIFIER, model.read_attribute(:resizing_picture)
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def test_delete_uses_column_value_when_constructed_without_identifier
|
|
242
|
+
# remove! の経路では識別子なしで File が生成され、モデルのカラム値から削除対象を決める
|
|
243
|
+
model = model_with_column(IDENTIFIER)
|
|
244
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture)
|
|
245
|
+
|
|
246
|
+
with_fake_api_client({ 'id' => IMAGE_ID }) do |fake|
|
|
247
|
+
file.delete
|
|
248
|
+
assert_equal [IMAGE_ID], fake.deleted_ids
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
assert_nil model.read_attribute(:resizing_picture)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def test_delete_uses_previous_column_value_when_column_has_unsaved_change
|
|
255
|
+
# attribute_was: カラムが変更済み (保存前) の場合は変更前の値の画像を削除する
|
|
256
|
+
model = model_with_column(IDENTIFIER)
|
|
257
|
+
model.save!
|
|
258
|
+
model.send(:write_attribute, :resizing_picture, OTHER_IDENTIFIER)
|
|
259
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture)
|
|
260
|
+
|
|
261
|
+
with_fake_api_client({ 'id' => IMAGE_ID }) do |fake|
|
|
262
|
+
file.delete
|
|
263
|
+
assert_equal [IMAGE_ID], fake.deleted_ids
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
# 削除したのは古い画像なので、新しい値はそのまま残る
|
|
267
|
+
assert_equal OTHER_IDENTIFIER, model.read_attribute(:resizing_picture)
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def test_delete_does_not_write_column_of_destroyed_model
|
|
271
|
+
model = model_with_column(IDENTIFIER)
|
|
272
|
+
model.save!
|
|
273
|
+
|
|
274
|
+
with_fake_api_client({ 'id' => IMAGE_ID }) do |fake|
|
|
275
|
+
ActiveRecord::Base.transaction do
|
|
276
|
+
model.destroy!
|
|
277
|
+
end
|
|
278
|
+
assert model.destroyed?
|
|
279
|
+
assert_equal [IMAGE_ID], fake.deleted_ids
|
|
280
|
+
|
|
281
|
+
# destroy 済み (frozen) のモデルに対して delete しても FrozenError にならない
|
|
282
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
283
|
+
file.delete
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# ============================================================
|
|
288
|
+
# store の詳細な挙動 (Resizing.post を差し替えて送信内容を検証)
|
|
289
|
+
# ============================================================
|
|
290
|
+
|
|
291
|
+
POST_RESPONSE = {
|
|
292
|
+
'id' => IMAGE_ID,
|
|
293
|
+
'public_id' => IDENTIFIER,
|
|
294
|
+
'content_type' => 'image/jpeg'
|
|
295
|
+
}.freeze
|
|
296
|
+
|
|
297
|
+
def capture_post(response = POST_RESPONSE, &block)
|
|
298
|
+
captured = {}
|
|
299
|
+
responder = lambda do |content, options|
|
|
300
|
+
captured[:content] = content
|
|
301
|
+
captured[:options] = options
|
|
302
|
+
response.dup
|
|
303
|
+
end
|
|
304
|
+
Resizing.stub(:post, responder, &block)
|
|
305
|
+
captured
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
def new_storage_file(model = TestModel.new)
|
|
309
|
+
Resizing::CarrierWave::Storage::File.new(model.resizing_picture)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def test_store_posts_uploaded_file_with_its_content_type_and_original_filename
|
|
313
|
+
file = new_storage_file
|
|
314
|
+
uploaded_file = ActionDispatch::Http::UploadedFile.new(
|
|
315
|
+
filename: 'my photo.jpg',
|
|
316
|
+
type: 'image/jpeg',
|
|
317
|
+
tempfile: ::File.open('test/data/images/sample1.jpg', 'r')
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
captured = capture_post { file.store(uploaded_file) }
|
|
321
|
+
|
|
322
|
+
assert_equal 'image/jpeg', captured[:options][:content_type]
|
|
323
|
+
assert_equal 'my photo.jpg', captured[:options][:filename]
|
|
324
|
+
assert captured[:content].respond_to?(:read)
|
|
325
|
+
assert_equal 0, captured[:content].pos, 'IO should be rewound before upload'
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def test_store_guesses_content_type_from_extension_for_plain_file
|
|
329
|
+
file = new_storage_file
|
|
330
|
+
source_file = ::File.open('test/data/images/sample1.jpg', 'r')
|
|
331
|
+
source_file.read(10) # 読み進めた状態でも巻き戻して送る
|
|
332
|
+
|
|
333
|
+
captured = capture_post { file.store(source_file) }
|
|
334
|
+
|
|
335
|
+
assert_equal 'image/jpeg', captured[:options][:content_type]
|
|
336
|
+
assert_equal 'sample1.jpg', captured[:options][:filename]
|
|
337
|
+
assert_equal 0, captured[:content].pos
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Issue #88: MIME::Types に登録のない拡張子でも例外にならず、
|
|
341
|
+
# application/octet-stream にフォールバックすること
|
|
342
|
+
def test_store_falls_back_to_octet_stream_for_unknown_extension
|
|
343
|
+
# 前提: この拡張子は MIME::Types に登録されていない
|
|
344
|
+
assert_empty MIME::Types.type_for('bookmark.url')
|
|
345
|
+
|
|
346
|
+
file = new_storage_file
|
|
347
|
+
tempfile = Tempfile.new(['bookmark', '.url'])
|
|
348
|
+
begin
|
|
349
|
+
tempfile.write("[InternetShortcut]\nURL=https://example.com/\n")
|
|
350
|
+
tempfile.rewind
|
|
351
|
+
|
|
352
|
+
captured = capture_post { file.store(tempfile) }
|
|
353
|
+
|
|
354
|
+
assert_equal 'application/octet-stream', captured[:options][:content_type]
|
|
355
|
+
assert_equal ::File.basename(tempfile.path), captured[:options][:filename]
|
|
356
|
+
ensure
|
|
357
|
+
tempfile.close!
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def test_guess_content_type_falls_back_for_unknown_or_missing_path
|
|
362
|
+
file = new_storage_file
|
|
363
|
+
|
|
364
|
+
assert_equal 'image/jpeg', file.send(:guess_content_type, 'sample1.jpg')
|
|
365
|
+
assert_equal 'application/octet-stream', file.send(:guess_content_type, 'bookmark.url')
|
|
366
|
+
assert_equal 'application/octet-stream', file.send(:guess_content_type, 'no_extension')
|
|
367
|
+
assert_equal 'application/octet-stream', file.send(:guess_content_type, nil)
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def test_store_sends_basename_only_as_filename
|
|
371
|
+
file = new_storage_file
|
|
372
|
+
uploaded_file = ActionDispatch::Http::UploadedFile.new(
|
|
373
|
+
filename: '/some/dir/photo.jpg',
|
|
374
|
+
type: 'image/jpeg',
|
|
375
|
+
tempfile: ::File.open('test/data/images/sample1.jpg', 'r')
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
captured = capture_post { file.store(uploaded_file) }
|
|
379
|
+
|
|
380
|
+
assert_equal 'photo.jpg', captured[:options][:filename]
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def test_store_writes_public_id_into_model_column
|
|
384
|
+
model = TestModel.new
|
|
385
|
+
file = new_storage_file(model)
|
|
386
|
+
|
|
387
|
+
capture_post { file.store(::File.open('test/data/images/sample1.jpg', 'r')) }
|
|
388
|
+
|
|
389
|
+
assert_equal IDENTIFIER, model.read_attribute(:resizing_picture)
|
|
390
|
+
assert_equal IDENTIFIER, file.public_id.to_s
|
|
391
|
+
assert_equal IDENTIFIER, file.current_path
|
|
392
|
+
assert_equal IMAGE_ID, file.public_id.image_id
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
def test_store_uses_content_type_from_response
|
|
396
|
+
file = new_storage_file
|
|
397
|
+
response = POST_RESPONSE.merge('content_type' => 'image/webp')
|
|
398
|
+
|
|
399
|
+
capture_post(response) { file.store(::File.open('test/data/images/sample1.jpg', 'r')) }
|
|
400
|
+
|
|
401
|
+
assert_equal 'image/webp', file.content_type
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
def test_store_returns_true
|
|
405
|
+
file = new_storage_file
|
|
406
|
+
result = nil
|
|
407
|
+
|
|
408
|
+
capture_post { result = file.store(::File.open('test/data/images/sample1.jpg', 'r')) }
|
|
409
|
+
|
|
410
|
+
assert_equal true, result
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
def test_store_raises_for_storage_file_instance
|
|
414
|
+
model = TestModel.new
|
|
415
|
+
file = new_storage_file(model)
|
|
416
|
+
other = new_storage_file(model)
|
|
417
|
+
|
|
418
|
+
assert_raises(NotImplementedError) { file.store(other) }
|
|
419
|
+
end
|
|
420
|
+
|
|
421
|
+
# ============================================================
|
|
422
|
+
# current_path / name / public_id
|
|
423
|
+
# ============================================================
|
|
424
|
+
|
|
425
|
+
def test_current_path_falls_back_to_model_column
|
|
426
|
+
model = model_with_column(IDENTIFIER)
|
|
427
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture)
|
|
428
|
+
|
|
429
|
+
assert_equal IDENTIFIER, file.current_path
|
|
430
|
+
assert_equal IDENTIFIER, file.path
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
def test_current_path_prefers_identifier_over_model_column
|
|
434
|
+
model = model_with_column(OTHER_IDENTIFIER)
|
|
435
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
436
|
+
|
|
437
|
+
assert_equal IDENTIFIER, file.current_path
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
def test_name_reads_from_model_column_not_identifier
|
|
441
|
+
model = model_with_column(OTHER_IDENTIFIER)
|
|
442
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
443
|
+
|
|
444
|
+
assert_equal 'new-image-id-2222-2222-222222222222', file.name
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def test_public_id_exposes_parsed_parts
|
|
448
|
+
model = TestModel.new
|
|
449
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
450
|
+
|
|
451
|
+
assert_equal IMAGE_ID, file.public_id.image_id
|
|
452
|
+
assert_equal 'e06e710d-f026-4dcf-b2c0-eab0de8bb83f', file.public_id.project_id
|
|
453
|
+
assert_equal '_8c5lEhDB5RT3PZp1Fn5PYGm9YVx_x0e', file.public_id.version
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
def test_retrieve_replaces_public_id
|
|
457
|
+
model = TestModel.new
|
|
458
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
459
|
+
|
|
460
|
+
file.retrieve(OTHER_IDENTIFIER)
|
|
461
|
+
|
|
462
|
+
assert_equal OTHER_IDENTIFIER, file.public_id.to_s
|
|
463
|
+
assert_equal OTHER_IDENTIFIER, file.current_path
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
# ============================================================
|
|
467
|
+
# Issue #91: 未定義の `file` を参照していたメソッド群
|
|
468
|
+
# (size / content_type / read / exists? / attributes)
|
|
469
|
+
# ============================================================
|
|
470
|
+
|
|
471
|
+
# test/vcr/client/metadata.yml のカセットに対応する identifier
|
|
472
|
+
METADATA_IDENTIFIER = '/projects/e06e710d-f026-4dcf-b2c0-eab0de8bb83f/upload/images/87263920-2081-498e-a107-9625f4fde01b/vHg9VFvdI6HRzLFbV495VdwVmHIspLRCo'
|
|
473
|
+
METADATA_SIZE = 848_590
|
|
474
|
+
|
|
475
|
+
def storage_file_for(identifier)
|
|
476
|
+
Resizing::CarrierWave::Storage::File.new(TestModel.new.resizing_picture, identifier)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def test_size_content_type_exists_and_attributes_come_from_metadata_api
|
|
480
|
+
file = storage_file_for(METADATA_IDENTIFIER)
|
|
481
|
+
|
|
482
|
+
# メタデータは1回だけ取得され、以降はメモ化される
|
|
483
|
+
assert_vcr_requests_made 'client/metadata' do
|
|
484
|
+
assert_equal METADATA_SIZE, file.size
|
|
485
|
+
assert_equal 'image/jpeg', file.content_type
|
|
486
|
+
assert file.exists?
|
|
487
|
+
assert_equal 'sample1.jpg', file.attributes['filename']
|
|
488
|
+
end
|
|
489
|
+
end
|
|
490
|
+
|
|
491
|
+
def test_metadata_backed_methods_do_not_raise_name_error_without_public_id
|
|
492
|
+
file = new_storage_file
|
|
493
|
+
|
|
494
|
+
# public_id がないのでリクエストは発行されず、既定値を返す
|
|
495
|
+
assert_vcr_no_requests 'client/metadata' do
|
|
496
|
+
assert_equal 0, file.size
|
|
497
|
+
assert_nil file.content_type
|
|
498
|
+
refute file.exists?
|
|
499
|
+
assert_nil file.attributes
|
|
500
|
+
end
|
|
501
|
+
end
|
|
502
|
+
|
|
503
|
+
def test_metadata_backed_methods_do_not_raise_when_api_fails
|
|
504
|
+
file = storage_file_for(IDENTIFIER)
|
|
505
|
+
|
|
506
|
+
Resizing.stub(:metadata, ->(*) { raise Resizing::APIError, 'boom' }) do
|
|
507
|
+
assert_equal 0, file.size
|
|
508
|
+
assert_nil file.content_type
|
|
509
|
+
refute file.exists?
|
|
510
|
+
assert_nil file.attributes
|
|
511
|
+
end
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
def test_metadata_not_found_response_is_treated_as_missing
|
|
515
|
+
file = storage_file_for(IDENTIFIER)
|
|
516
|
+
not_found = { 'error' => 'ActiveRecord::RecordNotFound' }
|
|
517
|
+
|
|
518
|
+
Resizing.stub(:metadata, ->(*) { not_found }) do
|
|
519
|
+
refute file.exists?
|
|
520
|
+
assert_equal 0, file.size
|
|
521
|
+
assert_nil file.content_type
|
|
522
|
+
end
|
|
523
|
+
end
|
|
524
|
+
|
|
525
|
+
def test_content_type_prefers_locally_known_value_over_metadata_api
|
|
526
|
+
file = new_storage_file
|
|
527
|
+
|
|
528
|
+
# store 済みで content_type が分かっている場合はメタデータを取りに行かない
|
|
529
|
+
assert_vcr_no_requests 'client/metadata' do
|
|
530
|
+
capture_post { file.store(::File.open('test/data/images/sample1.jpg', 'r')) }
|
|
531
|
+
|
|
532
|
+
assert_equal 'image/jpeg', file.content_type
|
|
533
|
+
end
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def test_metadata_falls_back_to_model_column_when_built_without_identifier
|
|
537
|
+
model = model_with_column(METADATA_IDENTIFIER)
|
|
538
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture)
|
|
539
|
+
|
|
540
|
+
assert_vcr_requests_made 'client/metadata' do
|
|
541
|
+
assert_equal METADATA_SIZE, file.size
|
|
542
|
+
end
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def test_retrieve_drops_cached_metadata_only_when_identifier_changes
|
|
546
|
+
file = storage_file_for(METADATA_IDENTIFIER)
|
|
547
|
+
|
|
548
|
+
assert_vcr_requests_made 'client/metadata' do
|
|
549
|
+
assert_equal METADATA_SIZE, file.size
|
|
550
|
+
# 同じ identifier での retrieve ではメタデータを再取得しない
|
|
551
|
+
file.retrieve(METADATA_IDENTIFIER)
|
|
552
|
+
assert_equal METADATA_SIZE, file.size
|
|
553
|
+
end
|
|
554
|
+
|
|
555
|
+
file.retrieve(IDENTIFIER)
|
|
556
|
+
Resizing.stub(:metadata, ->(*) { { 'size' => 1 } }) do
|
|
557
|
+
assert_equal 1, file.size
|
|
558
|
+
end
|
|
559
|
+
end
|
|
560
|
+
|
|
561
|
+
def test_read_raises_not_implemented_error_instead_of_name_error
|
|
562
|
+
file = storage_file_for(METADATA_IDENTIFIER)
|
|
563
|
+
|
|
564
|
+
error = assert_raises(NotImplementedError) { file.read }
|
|
565
|
+
assert_match(/Resizing/, error.message)
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
# Issue #91 の再現手順: DB から読み直したモデルの size が NameError にならないこと
|
|
569
|
+
# (CarrierWave の Uploader::Proxy#size は file.try(:size) を呼ぶ)
|
|
570
|
+
def test_uploader_proxy_size_and_content_type_for_model_loaded_from_db
|
|
571
|
+
model = model_with_column(METADATA_IDENTIFIER)
|
|
572
|
+
model.save!
|
|
573
|
+
loaded = TestModel.find(model.id)
|
|
574
|
+
|
|
575
|
+
assert_vcr_requests_made 'client/metadata' do
|
|
576
|
+
assert_equal METADATA_SIZE, loaded.resizing_picture.size
|
|
577
|
+
assert_equal 'image/jpeg', loaded.resizing_picture.content_type
|
|
578
|
+
end
|
|
579
|
+
end
|
|
580
|
+
|
|
581
|
+
# Issue #94: カラムに空文字が入っているレコードでも delete で例外にならない
|
|
582
|
+
def test_delete_does_nothing_when_column_is_empty_string
|
|
583
|
+
model = model_with_column('')
|
|
584
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture)
|
|
585
|
+
|
|
586
|
+
with_fake_api_client({ 'id' => IMAGE_ID }) do |fake|
|
|
587
|
+
assert_nil file.delete
|
|
588
|
+
assert_empty fake.deleted_ids
|
|
589
|
+
end
|
|
590
|
+
|
|
591
|
+
assert_equal '', model.read_attribute(:resizing_picture)
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
def test_delete_does_nothing_when_identifier_is_empty_string
|
|
595
|
+
model = model_with_column('')
|
|
596
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, '')
|
|
597
|
+
|
|
598
|
+
with_fake_api_client({ 'id' => IMAGE_ID }) do |fake|
|
|
599
|
+
assert_nil file.delete
|
|
600
|
+
assert_empty fake.deleted_ids
|
|
601
|
+
end
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
def test_delete_with_valid_public_id_clears_model_column
|
|
605
|
+
VCR.use_cassette 'carrier_wave_test/remove_resizing_picture', record: :none do
|
|
606
|
+
model = model_with_column(IDENTIFIER)
|
|
607
|
+
file = Resizing::CarrierWave::Storage::File.new(model.resizing_picture, IDENTIFIER)
|
|
608
|
+
|
|
609
|
+
file.delete
|
|
610
|
+
|
|
611
|
+
assert_nil model.read_attribute(:resizing_picture)
|
|
612
|
+
end
|
|
613
|
+
end
|
|
168
614
|
end
|
|
169
615
|
end
|
|
170
616
|
end
|