activestorage_legacy 0.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 +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
|
+
}
|