activestorage_legacy 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.babelrc +5 -0
- data/.codeclimate.yml +7 -0
- data/.eslintrc +19 -0
- data/.github/workflows/gem-push.yml +29 -0
- data/.github/workflows/ruby-tests.yml +37 -0
- data/.gitignore +9 -0
- data/.rubocop.yml +125 -0
- data/.travis.yml +25 -0
- data/Gemfile +33 -0
- data/Gemfile.lock +271 -0
- data/MIT-LICENSE +20 -0
- data/README.md +160 -0
- data/Rakefile +12 -0
- data/activestorage.gemspec +27 -0
- data/app/assets/javascripts/activestorage.js +1 -0
- data/app/controllers/active_storage/blobs_controller.rb +22 -0
- data/app/controllers/active_storage/direct_uploads_controller.rb +21 -0
- data/app/controllers/active_storage/disk_controller.rb +52 -0
- data/app/controllers/active_storage/variants_controller.rb +28 -0
- data/app/helpers/active_storage/file_field_with_direct_upload_helper.rb +18 -0
- data/app/javascript/activestorage/blob_record.js +54 -0
- data/app/javascript/activestorage/blob_upload.js +34 -0
- data/app/javascript/activestorage/direct_upload.js +42 -0
- data/app/javascript/activestorage/direct_upload_controller.js +67 -0
- data/app/javascript/activestorage/direct_uploads_controller.js +50 -0
- data/app/javascript/activestorage/file_checksum.js +53 -0
- data/app/javascript/activestorage/helpers.js +42 -0
- data/app/javascript/activestorage/index.js +11 -0
- data/app/javascript/activestorage/ujs.js +74 -0
- data/app/jobs/active_storage/purge_attachment_worker.rb +9 -0
- data/app/jobs/active_storage/purge_blob_worker.rb +9 -0
- data/app/models/active_storage/attachment.rb +33 -0
- data/app/models/active_storage/blob.rb +198 -0
- data/app/models/active_storage/filename.rb +49 -0
- data/app/models/active_storage/variant.rb +82 -0
- data/app/models/active_storage/variation.rb +53 -0
- data/config/routes.rb +9 -0
- data/config/storage_services.yml +34 -0
- data/lib/active_storage/attached/macros.rb +86 -0
- data/lib/active_storage/attached/many.rb +51 -0
- data/lib/active_storage/attached/one.rb +56 -0
- data/lib/active_storage/attached.rb +38 -0
- data/lib/active_storage/engine.rb +81 -0
- data/lib/active_storage/gem_version.rb +15 -0
- data/lib/active_storage/log_subscriber.rb +48 -0
- data/lib/active_storage/messages_metadata.rb +64 -0
- data/lib/active_storage/migration.rb +27 -0
- data/lib/active_storage/patches/active_record.rb +19 -0
- data/lib/active_storage/patches/delegation.rb +98 -0
- data/lib/active_storage/patches/secure_random.rb +26 -0
- data/lib/active_storage/patches.rb +4 -0
- data/lib/active_storage/service/azure_service.rb +115 -0
- data/lib/active_storage/service/configurator.rb +28 -0
- data/lib/active_storage/service/disk_service.rb +124 -0
- data/lib/active_storage/service/gcs_service.rb +79 -0
- data/lib/active_storage/service/mirror_service.rb +46 -0
- data/lib/active_storage/service/s3_service.rb +96 -0
- data/lib/active_storage/service.rb +113 -0
- data/lib/active_storage/verifier.rb +113 -0
- data/lib/active_storage/version.rb +8 -0
- data/lib/active_storage.rb +34 -0
- data/lib/tasks/activestorage.rake +20 -0
- data/package.json +33 -0
- data/test/controllers/direct_uploads_controller_test.rb +123 -0
- data/test/controllers/disk_controller_test.rb +57 -0
- data/test/controllers/variants_controller_test.rb +21 -0
- data/test/database/create_users_migration.rb +7 -0
- data/test/database/setup.rb +6 -0
- data/test/dummy/Rakefile +3 -0
- data/test/dummy/app/assets/config/manifest.js +5 -0
- data/test/dummy/app/assets/images/.keep +0 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/concerns/.keep +0 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/jobs/application_job.rb +2 -0
- data/test/dummy/app/models/application_record.rb +3 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/yarn +11 -0
- data/test/dummy/config/application.rb +22 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +49 -0
- data/test/dummy/config/environments/production.rb +82 -0
- data/test/dummy/config/environments/test.rb +33 -0
- data/test/dummy/config/initializers/application_controller_renderer.rb +6 -0
- data/test/dummy/config/initializers/assets.rb +14 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/secret_key.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/config/secrets.yml +32 -0
- data/test/dummy/config/spring.rb +6 -0
- data/test/dummy/config/storage_services.yml +3 -0
- data/test/dummy/config.ru +5 -0
- data/test/dummy/db/.keep +0 -0
- data/test/dummy/lib/assets/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/package.json +5 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/test/dummy/public/apple-touch-icon.png +0 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/filename_test.rb +36 -0
- data/test/fixtures/files/racecar.jpg +0 -0
- data/test/models/attachments_test.rb +122 -0
- data/test/models/blob_test.rb +47 -0
- data/test/models/variant_test.rb +27 -0
- data/test/service/.gitignore +1 -0
- data/test/service/azure_service_test.rb +14 -0
- data/test/service/configurations-example.yml +31 -0
- data/test/service/configurator_test.rb +14 -0
- data/test/service/disk_service_test.rb +12 -0
- data/test/service/gcs_service_test.rb +42 -0
- data/test/service/mirror_service_test.rb +62 -0
- data/test/service/s3_service_test.rb +52 -0
- data/test/service/shared_service_tests.rb +66 -0
- data/test/sidekiq/minitest_support.rb +6 -0
- data/test/support/assertions.rb +20 -0
- data/test/test_helper.rb +69 -0
- data/webpack.config.js +27 -0
- data/yarn.lock +3164 -0
- metadata +330 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "database/setup"
|
3
|
+
|
4
|
+
class ActiveStorage::BlobTest < ActiveSupport::TestCase
|
5
|
+
test "create after upload sets byte size and checksum" do
|
6
|
+
data = "Hello world!"
|
7
|
+
blob = create_blob data: data
|
8
|
+
|
9
|
+
assert_equal data, blob.download
|
10
|
+
assert_equal data.length, blob.byte_size
|
11
|
+
assert_equal Digest::MD5.base64digest(data), blob.checksum
|
12
|
+
end
|
13
|
+
|
14
|
+
test "text?" do
|
15
|
+
blob = create_blob data: "Hello world!"
|
16
|
+
assert blob.text?
|
17
|
+
assert_not blob.audio?
|
18
|
+
end
|
19
|
+
|
20
|
+
test "download yields chunks" do
|
21
|
+
blob = create_blob data: "a" * 75.kilobytes
|
22
|
+
chunks = []
|
23
|
+
|
24
|
+
blob.download do |chunk|
|
25
|
+
chunks << chunk
|
26
|
+
end
|
27
|
+
|
28
|
+
assert_equal 2, chunks.size
|
29
|
+
assert_equal "a" * 64.kilobytes, chunks.first
|
30
|
+
assert_equal "a" * 11.kilobytes, chunks.second
|
31
|
+
end
|
32
|
+
|
33
|
+
test "urls expiring in 5 minutes" do
|
34
|
+
blob = create_blob
|
35
|
+
|
36
|
+
freeze_time do
|
37
|
+
assert_equal expected_url_for(blob), blob.service_url
|
38
|
+
assert_equal expected_url_for(blob, disposition: :attachment), blob.service_url(disposition: :attachment)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def expected_url_for(blob, disposition: :inline)
|
44
|
+
query_string = { content_type: blob.content_type, disposition: disposition }.to_param
|
45
|
+
"/rails/active_storage/disk/#{ActiveStorage.verifier.generate(blob.key, expires_in: 5.minutes, purpose: :blob_key)}/#{blob.filename}?#{query_string}"
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "database/setup"
|
3
|
+
|
4
|
+
class ActiveStorage::VariantTest < ActiveSupport::TestCase
|
5
|
+
setup do
|
6
|
+
@blob = create_image_blob filename: "racecar.jpg"
|
7
|
+
end
|
8
|
+
|
9
|
+
test "resized variation" do
|
10
|
+
variant = @blob.variant(resize: "100x100").processed
|
11
|
+
assert_match /racecar.jpg/, variant.service_url
|
12
|
+
|
13
|
+
image = read_image_variant(variant)
|
14
|
+
assert_equal 100, image.width
|
15
|
+
assert_equal 67, image.height
|
16
|
+
end
|
17
|
+
|
18
|
+
test "resized and monochrome variation" do
|
19
|
+
variant = @blob.variant(resize: "100x100", monochrome: true).processed
|
20
|
+
assert_match /racecar.jpg/, variant.service_url
|
21
|
+
|
22
|
+
image = read_image_variant(variant)
|
23
|
+
assert_equal 100, image.width
|
24
|
+
assert_equal 67, image.height
|
25
|
+
assert_match /Gray/, image.colorspace
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
configurations.yml
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
require "httparty"
|
3
|
+
require "uri"
|
4
|
+
|
5
|
+
if SERVICE_CONFIGURATIONS[:azure]
|
6
|
+
class ActiveStorage::Service::AzureServiceTest < ActiveSupport::TestCase
|
7
|
+
SERVICE = ActiveStorage::Service.configure(:azure, SERVICE_CONFIGURATIONS)
|
8
|
+
|
9
|
+
include ActiveStorage::Service::SharedServiceTests
|
10
|
+
end
|
11
|
+
|
12
|
+
else
|
13
|
+
puts "Skipping Azure Storage Service tests because no Azure configuration was supplied"
|
14
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copy this file to configurations.yml and edit the credentials to match your IAM test account and bucket
|
2
|
+
s3:
|
3
|
+
service: S3
|
4
|
+
access_key_id:
|
5
|
+
secret_access_key:
|
6
|
+
region:
|
7
|
+
bucket:
|
8
|
+
|
9
|
+
gcs:
|
10
|
+
service: GCS
|
11
|
+
keyfile: {
|
12
|
+
type: "service_account",
|
13
|
+
project_id: "",
|
14
|
+
private_key_id: "",
|
15
|
+
private_key: "",
|
16
|
+
client_email: "",
|
17
|
+
client_id: "",
|
18
|
+
auth_uri: "https://accounts.google.com/o/oauth2/auth",
|
19
|
+
token_uri: "https://accounts.google.com/o/oauth2/token",
|
20
|
+
auth_provider_x509_cert_url: "https://www.googleapis.com/oauth2/v1/certs",
|
21
|
+
client_x509_cert_url: ""
|
22
|
+
}
|
23
|
+
project:
|
24
|
+
bucket:
|
25
|
+
|
26
|
+
azure:
|
27
|
+
service: Azure
|
28
|
+
path: ""
|
29
|
+
storage_account_name: ""
|
30
|
+
storage_access_key: ""
|
31
|
+
container: ""
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
|
3
|
+
class ActiveStorage::Service::ConfiguratorTest < ActiveSupport::TestCase
|
4
|
+
test "builds correct service instance based on service name" do
|
5
|
+
service = ActiveStorage::Service::Configurator.build(:foo, foo: { service: "Disk", root: "path" })
|
6
|
+
assert_instance_of ActiveStorage::Service::DiskService, service
|
7
|
+
end
|
8
|
+
|
9
|
+
test "raises error when passing non-existent service name" do
|
10
|
+
assert_raise RuntimeError do
|
11
|
+
ActiveStorage::Service::Configurator.build(:bigfoot, {})
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
|
3
|
+
class ActiveStorage::Service::DiskServiceTest < ActiveSupport::TestCase
|
4
|
+
SERVICE = ActiveStorage::Service::DiskService.new(root: File.join(Dir.tmpdir, "active_storage"))
|
5
|
+
|
6
|
+
include ActiveStorage::Service::SharedServiceTests
|
7
|
+
|
8
|
+
test "url generation" do
|
9
|
+
assert_match /rails\/active_storage\/disk\/.*\/avatar\.png\?.+disposition=inline/,
|
10
|
+
@service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: :inline, filename: "avatar.png", content_type: "image/png")
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
require "httparty"
|
3
|
+
|
4
|
+
if SERVICE_CONFIGURATIONS[:gcs]
|
5
|
+
class ActiveStorage::Service::GCSServiceTest < ActiveSupport::TestCase
|
6
|
+
SERVICE = ActiveStorage::Service.configure(:gcs, SERVICE_CONFIGURATIONS)
|
7
|
+
|
8
|
+
include ActiveStorage::Service::SharedServiceTests
|
9
|
+
|
10
|
+
test "direct upload" do
|
11
|
+
begin
|
12
|
+
key = SecureRandom.base58(24)
|
13
|
+
data = "Something else entirely!"
|
14
|
+
checksum = Digest::MD5.base64digest(data)
|
15
|
+
url = @service.url_for_direct_upload(key, expires_in: 5.minutes, content_type: "text/plain", content_length: data.size, checksum: checksum)
|
16
|
+
|
17
|
+
HTTParty.put(
|
18
|
+
url,
|
19
|
+
body: data,
|
20
|
+
headers: { "Content-Type" => "text/plain", "Content-MD5" => checksum },
|
21
|
+
debug_output: STDOUT
|
22
|
+
)
|
23
|
+
|
24
|
+
assert_equal data, @service.download(key)
|
25
|
+
ensure
|
26
|
+
@service.delete key
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
test "signed URL generation" do
|
31
|
+
freeze_time do
|
32
|
+
url = SERVICE.bucket.signed_url(FIXTURE_KEY, expires: 120) +
|
33
|
+
"&response-content-disposition=inline%3B+filename%3D%22test.txt%22" +
|
34
|
+
"&response-content-type=text%2Fplain"
|
35
|
+
|
36
|
+
assert_equal url, @service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
else
|
41
|
+
puts "Skipping GCS Service tests because no GCS configuration was supplied"
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
|
3
|
+
class ActiveStorage::Service::MirrorServiceTest < ActiveSupport::TestCase
|
4
|
+
mirror_config = (1..3).map do |i|
|
5
|
+
[ "mirror_#{i}",
|
6
|
+
service: "Disk",
|
7
|
+
root: Dir.mktmpdir("active_storage_tests_mirror_#{i}") ]
|
8
|
+
end.to_h
|
9
|
+
|
10
|
+
config = mirror_config.merge \
|
11
|
+
mirror: { service: "Mirror", primary: "primary", mirrors: mirror_config.keys },
|
12
|
+
primary: { service: "Disk", root: Dir.mktmpdir("active_storage_tests_primary") }
|
13
|
+
|
14
|
+
SERVICE = ActiveStorage::Service.configure :mirror, config
|
15
|
+
|
16
|
+
include ActiveStorage::Service::SharedServiceTests
|
17
|
+
|
18
|
+
test "uploading to all services" do
|
19
|
+
begin
|
20
|
+
data = "Something else entirely!"
|
21
|
+
key = upload(data, to: @service)
|
22
|
+
|
23
|
+
assert_equal data, SERVICE.primary.download(key)
|
24
|
+
SERVICE.mirrors.each do |mirror|
|
25
|
+
assert_equal data, mirror.download(key)
|
26
|
+
end
|
27
|
+
ensure
|
28
|
+
@service.delete key
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
test "downloading from primary service" do
|
33
|
+
data = "Something else entirely!"
|
34
|
+
key = upload(data, to: SERVICE.primary)
|
35
|
+
|
36
|
+
assert_equal data, @service.download(key)
|
37
|
+
end
|
38
|
+
|
39
|
+
test "deleting from all services" do
|
40
|
+
@service.delete FIXTURE_KEY
|
41
|
+
assert_not SERVICE.primary.exist?(FIXTURE_KEY)
|
42
|
+
SERVICE.mirrors.each do |mirror|
|
43
|
+
assert_not mirror.exist?(FIXTURE_KEY)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
test "URL generation in primary service" do
|
48
|
+
freeze_time do
|
49
|
+
assert_equal SERVICE.primary.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain"),
|
50
|
+
@service.url(FIXTURE_KEY, expires_in: 2.minutes, disposition: :inline, filename: "test.txt", content_type: "text/plain")
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def upload(data, to:)
|
56
|
+
SecureRandom.base58(24).tap do |key|
|
57
|
+
io = StringIO.new(data).tap(&:read)
|
58
|
+
@service.upload key, io, checksum: Digest::MD5.base64digest(data)
|
59
|
+
assert io.eof?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "service/shared_service_tests"
|
2
|
+
require "httparty"
|
3
|
+
|
4
|
+
if SERVICE_CONFIGURATIONS[:s3]
|
5
|
+
class ActiveStorage::Service::S3ServiceTest < ActiveSupport::TestCase
|
6
|
+
SERVICE = ActiveStorage::Service.configure(:s3, SERVICE_CONFIGURATIONS)
|
7
|
+
|
8
|
+
include ActiveStorage::Service::SharedServiceTests
|
9
|
+
|
10
|
+
test "direct upload" do
|
11
|
+
begin
|
12
|
+
key = SecureRandom.base58(24)
|
13
|
+
data = "Something else entirely!"
|
14
|
+
checksum = Digest::MD5.base64digest(data)
|
15
|
+
url = @service.url_for_direct_upload(key, expires_in: 5.minutes, content_type: "text/plain", content_length: data.size, checksum: checksum)
|
16
|
+
|
17
|
+
HTTParty.put(
|
18
|
+
url,
|
19
|
+
body: data,
|
20
|
+
headers: { "Content-Type" => "text/plain", "Content-MD5" => checksum },
|
21
|
+
debug_output: STDOUT
|
22
|
+
)
|
23
|
+
|
24
|
+
assert_equal data, @service.download(key)
|
25
|
+
ensure
|
26
|
+
@service.delete key
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
test "signed URL generation" do
|
31
|
+
assert_match /#{SERVICE_CONFIGURATIONS[:s3][:bucket]}\.s3.(\S+)?amazonaws.com.*response-content-disposition=inline.*avatar\.png.*response-content-type=image%2Fpng/,
|
32
|
+
@service.url(FIXTURE_KEY, expires_in: 5.minutes, disposition: :inline, filename: "avatar.png", content_type: "image/png")
|
33
|
+
end
|
34
|
+
|
35
|
+
test "uploading with server-side encryption" do
|
36
|
+
config = SERVICE_CONFIGURATIONS.deep_merge(s3: { upload: { server_side_encryption: "AES256" }})
|
37
|
+
service = ActiveStorage::Service.configure(:s3, config)
|
38
|
+
|
39
|
+
begin
|
40
|
+
key = SecureRandom.base58(24)
|
41
|
+
data = "Something else entirely!"
|
42
|
+
service.upload key, StringIO.new(data), checksum: Digest::MD5.base64digest(data)
|
43
|
+
|
44
|
+
assert_equal "AES256", service.bucket.object(key).server_side_encryption
|
45
|
+
ensure
|
46
|
+
service.delete key
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
else
|
51
|
+
puts "Skipping S3 Service tests because no S3 configuration was supplied"
|
52
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
module ActiveStorage::Service::SharedServiceTests
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
FIXTURE_KEY = SecureRandom.base58(24)
|
7
|
+
FIXTURE_DATA = "\211PNG\r\n\032\n\000\000\000\rIHDR\000\000\000\020\000\000\000\020\001\003\000\000\000%=m\"\000\000\000\006PLTE\000\000\000\377\377\377\245\331\237\335\000\000\0003IDATx\234c\370\377\237\341\377_\206\377\237\031\016\2603\334?\314p\1772\303\315\315\f7\215\031\356\024\203\320\275\317\f\367\201R\314\f\017\300\350\377\177\000Q\206\027(\316]\233P\000\000\000\000IEND\256B`\202".force_encoding(Encoding::BINARY)
|
8
|
+
|
9
|
+
included do
|
10
|
+
setup do
|
11
|
+
@service = self.class.const_get(:SERVICE)
|
12
|
+
@service.upload FIXTURE_KEY, StringIO.new(FIXTURE_DATA)
|
13
|
+
end
|
14
|
+
|
15
|
+
teardown do
|
16
|
+
@service.delete FIXTURE_KEY
|
17
|
+
end
|
18
|
+
|
19
|
+
test "uploading with integrity" do
|
20
|
+
begin
|
21
|
+
key = SecureRandom.base58(24)
|
22
|
+
data = "Something else entirely!"
|
23
|
+
@service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest(data))
|
24
|
+
|
25
|
+
assert_equal data, @service.download(key)
|
26
|
+
ensure
|
27
|
+
@service.delete key
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
test "uploading without integrity" do
|
32
|
+
begin
|
33
|
+
key = SecureRandom.base58(24)
|
34
|
+
data = "Something else entirely!"
|
35
|
+
|
36
|
+
assert_raises(ActiveStorage::IntegrityError) do
|
37
|
+
@service.upload(key, StringIO.new(data), checksum: Digest::MD5.base64digest("bad data"))
|
38
|
+
end
|
39
|
+
|
40
|
+
assert_not @service.exist?(key)
|
41
|
+
ensure
|
42
|
+
@service.delete key
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
test "downloading" do
|
47
|
+
assert_equal FIXTURE_DATA, @service.download(FIXTURE_KEY)
|
48
|
+
end
|
49
|
+
|
50
|
+
test "existing" do
|
51
|
+
assert @service.exist?(FIXTURE_KEY)
|
52
|
+
assert_not @service.exist?(FIXTURE_KEY + "nonsense")
|
53
|
+
end
|
54
|
+
|
55
|
+
test "deleting" do
|
56
|
+
@service.delete FIXTURE_KEY
|
57
|
+
assert_not @service.exist?(FIXTURE_KEY)
|
58
|
+
end
|
59
|
+
|
60
|
+
test "deleting nonexistent key" do
|
61
|
+
assert_nothing_raised do
|
62
|
+
@service.delete SecureRandom.base58(24)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'minitest/mock'
|
2
|
+
|
3
|
+
module Assertions
|
4
|
+
def assert_not(object, message = nil)
|
5
|
+
message ||= "Expected #{object} to be nil or false"
|
6
|
+
assert !object, message
|
7
|
+
end
|
8
|
+
|
9
|
+
def freeze_time(&block)
|
10
|
+
if block
|
11
|
+
reference_date = Time.now
|
12
|
+
|
13
|
+
Time.stub :now, reference_date do
|
14
|
+
block.call
|
15
|
+
end
|
16
|
+
else
|
17
|
+
block.call
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter %r{^/test/}
|
4
|
+
end
|
5
|
+
|
6
|
+
require File.expand_path("../../test/dummy/config/environment.rb", __FILE__)
|
7
|
+
|
8
|
+
require "bundler/setup"
|
9
|
+
|
10
|
+
require "test/unit/active_support"
|
11
|
+
|
12
|
+
require "active_support"
|
13
|
+
require "active_support/test_case"
|
14
|
+
|
15
|
+
require "byebug"
|
16
|
+
require "sidekiq"
|
17
|
+
require 'sidekiq/testing'
|
18
|
+
require "sidekiq/minitest_support"
|
19
|
+
|
20
|
+
require "active_storage"
|
21
|
+
|
22
|
+
require "yaml"
|
23
|
+
SERVICE_CONFIGURATIONS = begin
|
24
|
+
YAML.load_file(File.expand_path("../service/configurations.yml", __FILE__)).deep_symbolize_keys
|
25
|
+
rescue Errno::ENOENT
|
26
|
+
puts "Missing service configuration file in test/service/configurations.yml"
|
27
|
+
{}
|
28
|
+
end
|
29
|
+
|
30
|
+
require "tmpdir"
|
31
|
+
require 'active_storage/service/disk_service'
|
32
|
+
require 'support/assertions'
|
33
|
+
|
34
|
+
ActiveStorage::Blob.service = ActiveStorage::Service::DiskService.new(root: Dir.mktmpdir("active_storage_tests"))
|
35
|
+
ActiveStorage::Service.logger = ::Logger.new(STDOUT)
|
36
|
+
|
37
|
+
ActiveStorage.verifier = ActiveStorage::Verifier.new('Testing')
|
38
|
+
|
39
|
+
class ActiveSupport::TestCase
|
40
|
+
include Assertions
|
41
|
+
include SidekiqMinitestSupport
|
42
|
+
|
43
|
+
private
|
44
|
+
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
|
45
|
+
ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_image_blob(filename: "racecar.jpg", content_type: "image/jpeg")
|
49
|
+
ActiveStorage::Blob.create_after_upload! \
|
50
|
+
io: file_fixture(filename).open,
|
51
|
+
filename: filename, content_type: content_type
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_blob_before_direct_upload(filename: "hello.txt", byte_size:, checksum:, content_type: "text/plain")
|
55
|
+
ActiveStorage::Blob.create_before_direct_upload! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type
|
56
|
+
end
|
57
|
+
|
58
|
+
def read_image_variant(variant)
|
59
|
+
MiniMagick::Image.open variant.service.send(:path_for, variant.key)
|
60
|
+
end
|
61
|
+
|
62
|
+
def file_fixture(filename)
|
63
|
+
Pathname(File.join(file_fixture_path, filename))
|
64
|
+
end
|
65
|
+
|
66
|
+
def file_fixture_path
|
67
|
+
File.expand_path("../fixtures/files", __FILE__)
|
68
|
+
end
|
69
|
+
end
|
data/webpack.config.js
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
const webpack = require("webpack")
|
2
|
+
const path = require("path")
|
3
|
+
|
4
|
+
module.exports = {
|
5
|
+
entry: {
|
6
|
+
"activestorage": path.resolve(__dirname, "app/javascript/activestorage/index.js"),
|
7
|
+
},
|
8
|
+
|
9
|
+
output: {
|
10
|
+
filename: "[name].js",
|
11
|
+
path: path.resolve(__dirname, "app/assets/javascripts"),
|
12
|
+
library: "ActiveStorage",
|
13
|
+
libraryTarget: "umd"
|
14
|
+
},
|
15
|
+
|
16
|
+
module: {
|
17
|
+
rules: [
|
18
|
+
{
|
19
|
+
test: /\.js$/,
|
20
|
+
exclude: /node_modules/,
|
21
|
+
use: {
|
22
|
+
loader: "babel-loader"
|
23
|
+
}
|
24
|
+
}
|
25
|
+
]
|
26
|
+
}
|
27
|
+
}
|