tessa 1.2.3 → 6.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -0
  3. data/README.md +76 -3
  4. data/app/assets/javascripts/tessa.esm.js +212 -173
  5. data/app/assets/javascripts/tessa.js +206 -167
  6. data/app/javascript/activestorage/file_checksum.ts +76 -0
  7. data/app/javascript/tessa/index.ts +264 -0
  8. data/app/javascript/tessa/types.ts +34 -0
  9. data/config/routes.rb +0 -1
  10. data/docs/tessa-activestorage-sequence-diagram.drawio.png +0 -0
  11. data/lib/tessa/simple_form/asset_input.rb +24 -25
  12. data/lib/tessa/version.rb +1 -1
  13. data/lib/tessa.rb +0 -80
  14. data/package.json +7 -2
  15. data/rollup.config.js +5 -5
  16. data/spec/dummy/app/models/single_asset_model.rb +1 -1
  17. data/spec/dummy/app/models/single_asset_model_form.rb +3 -5
  18. data/spec/dummy/bin/rails +2 -2
  19. data/spec/dummy/bin/rake +2 -2
  20. data/spec/dummy/bin/setup +14 -6
  21. data/spec/dummy/bin/yarn +9 -3
  22. data/spec/dummy/config/application.rb +11 -9
  23. data/spec/dummy/config/boot.rb +1 -1
  24. data/spec/dummy/config/environment.rb +1 -1
  25. data/spec/dummy/config/environments/development.rb +34 -5
  26. data/spec/dummy/config/environments/production.rb +49 -10
  27. data/spec/dummy/config/environments/test.rb +28 -12
  28. data/spec/dummy/config/initializers/backtrace_silencers.rb +4 -3
  29. data/spec/dummy/config/initializers/content_security_policy.rb +5 -0
  30. data/spec/dummy/config/initializers/filter_parameter_logging.rb +3 -1
  31. data/spec/dummy/config/initializers/new_framework_defaults_6_1.rb +67 -0
  32. data/spec/dummy/config/initializers/permissions_policy.rb +11 -0
  33. data/spec/dummy/config/initializers/wrap_parameters.rb +5 -0
  34. data/spec/dummy/config/locales/en.yml +1 -1
  35. data/spec/dummy/config/routes.rb +1 -1
  36. data/spec/dummy/config/storage.yml +31 -0
  37. data/spec/dummy/config.ru +2 -1
  38. data/spec/dummy/db/migrate/20230406194400_add_service_name_to_active_storage_blobs.active_storage.rb +22 -0
  39. data/spec/dummy/db/migrate/20230406194401_create_active_storage_variant_records.active_storage.rb +27 -0
  40. data/spec/dummy/db/schema.rb +15 -7
  41. data/tessa.gemspec +4 -6
  42. data/tsconfig.json +10 -0
  43. data/yarn.lock +74 -7
  44. metadata +37 -85
  45. data/app/javascript/activestorage/file_checksum.js +0 -53
  46. data/app/javascript/tessa/index.js.coffee +0 -183
  47. data/lib/tasks/tessa.rake +0 -7
  48. data/lib/tessa/active_storage/asset_wrapper.rb +0 -32
  49. data/lib/tessa/asset/failure.rb +0 -37
  50. data/lib/tessa/asset.rb +0 -47
  51. data/lib/tessa/asset_change.rb +0 -49
  52. data/lib/tessa/asset_change_set.rb +0 -49
  53. data/lib/tessa/config.rb +0 -24
  54. data/lib/tessa/controller_helpers.rb +0 -16
  55. data/lib/tessa/jobs/migrate_assets_job.rb +0 -225
  56. data/lib/tessa/model/dynamic_extensions.rb +0 -162
  57. data/lib/tessa/model/field.rb +0 -77
  58. data/lib/tessa/model.rb +0 -104
  59. data/lib/tessa/rack_upload_proxy.rb +0 -41
  60. data/lib/tessa/response_factory.rb +0 -15
  61. data/lib/tessa/upload/uploads_file.rb +0 -20
  62. data/lib/tessa/upload.rb +0 -31
  63. data/lib/tessa/view_helpers.rb +0 -23
  64. data/spec/dummy/app/models/multiple_asset_model.rb +0 -8
  65. data/spec/support/remote_call_macro.rb +0 -45
  66. data/spec/tessa/asset/failure_spec.rb +0 -48
  67. data/spec/tessa/asset_change_set_spec.rb +0 -198
  68. data/spec/tessa/asset_change_spec.rb +0 -86
  69. data/spec/tessa/asset_spec.rb +0 -196
  70. data/spec/tessa/config_spec.rb +0 -112
  71. data/spec/tessa/controller_helpers_spec.rb +0 -55
  72. data/spec/tessa/jobs/migrate_assets_job_spec.rb +0 -247
  73. data/spec/tessa/model_field_spec.rb +0 -72
  74. data/spec/tessa/model_spec.rb +0 -578
  75. data/spec/tessa/rack_upload_proxy_spec.rb +0 -83
  76. data/spec/tessa/upload/uploads_file_spec.rb +0 -72
  77. data/spec/tessa/upload_spec.rb +0 -125
@@ -1,578 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Tessa::Model do
4
- subject(:described_module) { described_class }
5
- let(:model) {
6
- SingleAssetModel
7
- }
8
-
9
- it { is_expected.to be_a(Module) }
10
-
11
- describe "::asset" do
12
- it "creates ModelField and sets it by name to @tessa_fields" do
13
- expect(model.tessa_fields[:avatar]).to be_a(Tessa::Model::Field)
14
- end
15
-
16
- context "with a field named :avatar" do
17
- subject(:instance) { model.new }
18
-
19
- it "creates an #avatar method" do
20
- expect(instance).to respond_to(:avatar)
21
- end
22
-
23
- it "creates an #avatar= method" do
24
- expect(instance).to respond_to(:avatar=)
25
- end
26
-
27
- it "allows overriding and calling super" do
28
- model.class_eval do
29
- def avatar
30
- @override_test = true
31
- super
32
- end
33
- end
34
-
35
- instance.avatar
36
- expect(instance.instance_variable_get(:@override_test)).to eq(true)
37
- end
38
- end
39
-
40
- context "with customized field" do
41
- let(:model) {
42
- MultipleAssetModel
43
- }
44
- let(:instance) { model.new(another_place: []) }
45
-
46
- it "sets all attributes on ModelField properly" do
47
- field = model.tessa_fields[:multiple_field]
48
- expect(field.name).to eq("multiple_field")
49
- expect(field.multiple).to eq(true)
50
- expect(field.id_field).to eq("another_place")
51
- end
52
- end
53
-
54
- context "with inheritance hierarchy" do
55
- let(:submodel) {
56
- Class.new(model) do
57
- asset :field2
58
- end
59
- }
60
-
61
- it "submodel has its own list of fields" do
62
- expect(submodel.tessa_fields.keys).to eq([:avatar, :field2])
63
- end
64
-
65
- it "does not alter parent class fields" do
66
- expect(model.tessa_fields.keys).to eq([:avatar])
67
- end
68
- end
69
-
70
- context "on a form object" do
71
- let(:model) {
72
- SingleAssetModelForm
73
- }
74
- subject(:instance) { model.new }
75
-
76
- it "creates an #avatar method" do
77
- expect(instance).to respond_to(:avatar)
78
- end
79
-
80
- it "creates an #avatar= method" do
81
- expect(instance).to respond_to(:avatar=)
82
- end
83
- end
84
- end
85
-
86
- describe "#asset_getter" do
87
- let(:instance) { model.new }
88
-
89
- context "with a multiple typed field" do
90
- let(:model) {
91
- MultipleAssetModel
92
- }
93
- let(:instance) { model.new(another_place: []) }
94
- subject(:getter) { instance.multiple_field }
95
-
96
- it "calls find for each of the file_ids and returns result" do
97
- instance.another_place = [1, 2, 3]
98
- expect(Tessa::Asset).to receive(:find).with([1, 2, 3]).and_return([:a1, :a2, :a3])
99
- expect(getter).to eq([:a1, :a2, :a3])
100
- end
101
-
102
- it "caches the result" do
103
- instance.another_place = [1]
104
- expect(Tessa::Asset).to receive(:find).and_return(:val).once
105
- instance.multiple_field
106
- instance.multiple_field
107
- end
108
-
109
- context "with no values" do
110
- it "does not call find" do
111
- instance.another_place = []
112
- expect(Tessa::Asset).not_to receive(:find)
113
- expect(instance.multiple_field).to eq([])
114
- end
115
- end
116
- end
117
-
118
- context "with a singular typed field" do
119
- let(:model) {
120
- SingleAssetModel
121
- }
122
- subject(:getter) { instance.avatar }
123
-
124
- it "calls find for file_id and returns result" do
125
- instance.avatar_id = 1
126
- expect(Tessa::Asset).to receive(:find).with(1).and_return(:a1)
127
- expect(getter).to eq(:a1)
128
- end
129
-
130
- it "caches the result" do
131
- instance.avatar_id = 1
132
- expect(Tessa::Asset).to receive(:find).and_return(:val).once
133
- instance.avatar
134
- instance.avatar
135
- end
136
-
137
- it "wraps ActiveStorage uploads with AssetWrapper" do
138
- file = Rack::Test::UploadedFile.new("README.md")
139
- instance.avatar = file
140
-
141
- asset = instance.avatar
142
- expect(asset).to be_a(Tessa::ActiveStorage::AssetWrapper)
143
- # This goes to the blobs URL, which then redirects to the backend service URL
144
- expect(asset.public_url).to start_with('https://www.example.com/rails/active_storage/blobs/')
145
- # This is a direct download to the service URL (in test mode that is "disk")
146
- expect(asset.private_url).to start_with('https://www.example.com/rails/active_storage/disk/')
147
- expect(asset.private_download_url).to start_with('https://www.example.com/rails/active_storage/disk/')
148
- expect(asset.private_download_url).to include('&disposition=attachment')
149
- end
150
-
151
- context "with nil value" do
152
- it "does not call find" do
153
- instance.avatar_id = nil
154
- expect(Tessa::Asset).not_to receive(:find)
155
- expect(instance.avatar).to be_nil
156
- end
157
- end
158
- end
159
-
160
- context "on a form object" do
161
- let(:model) {
162
- SingleAssetModelForm
163
- }
164
- subject(:getter) { instance.avatar }
165
-
166
- it 'returns nil when empty' do
167
- expect(getter).to be_nil
168
- end
169
-
170
- it 'returns assigned upload object' do
171
- file = Rack::Test::UploadedFile.new("README.md")
172
- instance.avatar = file
173
- expect(getter).to eq(file)
174
- end
175
- end
176
- end
177
-
178
- describe "#asset_setter" do
179
- let(:instance) { model.new }
180
-
181
- context "with a singular typed field" do
182
- let(:model) {
183
- SingleAssetModel
184
- }
185
- subject(:getter) { instance.avatar }
186
- let(:file) {
187
- Rack::Test::UploadedFile.new("README.md")
188
- }
189
-
190
- it 'attaches uploaded file' do
191
- instance.avatar = file
192
-
193
- expect(getter.name).to eq('avatar')
194
- expect(getter.filename).to eq('README.md')
195
- expect(getter.content_type).to eq('text/plain')
196
- expect(getter.service_url)
197
- .to start_with('https://www.example.com/rails/active_storage/disk/')
198
- end
199
-
200
- it 'sets the ID to be the ActiveStorage key' do
201
- instance.avatar = file
202
-
203
- expect(instance.avatar_id).to eq(instance.avatar_attachment.key)
204
- end
205
-
206
- it 'sets the ID in the attributes' do
207
- instance.avatar = file
208
-
209
- expect(instance.attributes['avatar_id']).to eq(instance.avatar_attachment.key)
210
- end
211
-
212
- it 'attaches signed ID from Tessa::AssetChangeSet' do
213
- blob = ::ActiveStorage::Blob.create_before_direct_upload!({
214
- filename: 'README.md',
215
- byte_size: file.size,
216
- content_type: file.content_type,
217
- checksum: '1234'
218
- })
219
-
220
- changeset = Tessa::AssetChangeSet.new(
221
- changes: [{ 'id' => blob.signed_id, 'action' => 'add' }]
222
- )
223
- instance.avatar = changeset
224
-
225
- expect(instance.avatar_id).to eq(instance.avatar_attachment.key)
226
- end
227
-
228
- it 'does nothing when "add"ing an existing blob' do
229
- # Before this HTTP POST, we've previously uploaded this file
230
- instance.avatar = file
231
-
232
- # In this HTTP POST, we re-upload the 'add' action with the same ID
233
- changeset = Tessa::AssetChangeSet.new(
234
- changes: [{ 'id' => instance.avatar_attachment.key, 'action' => 'add' }]
235
- )
236
-
237
- # We expect that we're not going to detatch the existing attachment
238
- expect(instance.avatar_attachment).to_not receive(:destroy)
239
-
240
- # act
241
- instance.avatar = changeset
242
-
243
- expect(instance.avatar_id).to eq(instance.avatar_attachment.key)
244
- end
245
- end
246
-
247
- context "with a multiple typed field" do
248
- let(:model) {
249
- MultipleAssetModel
250
- }
251
- let(:instance) { model.new(another_place: []) }
252
- let(:file) {
253
- Rack::Test::UploadedFile.new("README.md")
254
- }
255
- let(:file2) {
256
- Rack::Test::UploadedFile.new("LICENSE.txt")
257
- }
258
-
259
- it 'attaches uploaded files' do
260
- instance.multiple_field = [file, file2]
261
-
262
- expect(instance.multiple_field[0].name).to eq('multiple_field')
263
- expect(instance.multiple_field[0].filename).to eq('README.md')
264
- expect(instance.multiple_field[0].content_type).to eq('text/plain')
265
- expect(instance.multiple_field[0].service_url)
266
- .to start_with('https://www.example.com/rails/active_storage/disk/')
267
- expect(instance.multiple_field[1].name).to eq('multiple_field')
268
- expect(instance.multiple_field[1].filename).to eq('LICENSE.txt')
269
- expect(instance.multiple_field[1].content_type).to eq('text/plain')
270
- expect(instance.multiple_field[1].service_url)
271
- .to start_with('https://www.example.com/rails/active_storage/disk/')
272
- end
273
-
274
- it 'sets the ID to be the ActiveStorage key' do
275
- instance.multiple_field = [file, file2]
276
-
277
- expect(instance.another_place).to eq(instance.multiple_field_attachments.map(&:key))
278
- end
279
-
280
- it 'sets the ID in the attributes' do
281
- instance.multiple_field = [file, file2]
282
-
283
- expect(instance.attributes['another_place']).to eq(instance.multiple_field_attachments.map(&:key))
284
- end
285
-
286
- it 'attaches signed ID from Tessa::AssetChangeSet' do
287
- blob = ::ActiveStorage::Blob.create_before_direct_upload!({
288
- filename: 'README.md',
289
- byte_size: file.size,
290
- content_type: file.content_type,
291
- checksum: '1234'
292
- })
293
- blob2 = ::ActiveStorage::Blob.create_before_direct_upload!({
294
- filename: "LICENSE.txt",
295
- byte_size: file2.size,
296
- content_type: file2.content_type,
297
- checksum: '5678'
298
- })
299
-
300
- changeset = Tessa::AssetChangeSet.new(
301
- changes: [
302
- { 'id' => blob.signed_id, 'action' => 'add' },
303
- { 'id' => blob2.signed_id, 'action' => 'add' },
304
- ]
305
- )
306
- instance.multiple_field = changeset
307
-
308
- expect(instance.another_place).to eq([
309
- blob.key,
310
- blob2.key
311
- ])
312
- end
313
-
314
- it 'does nothing when "add"ing an existing blob' do
315
- # Before this HTTP POST, we've previously uploaded these files
316
- instance.multiple_field = [file, file2]
317
- keys = instance.multiple_field_attachments.map(&:key)
318
-
319
- # In this HTTP POST, we re-upload the 'add' action with the same ID
320
- changeset = Tessa::AssetChangeSet.new(
321
- changes: [
322
- { 'id' => keys[0], 'action' => 'add' },
323
- { 'id' => keys[1], 'action' => 'add' },
324
- ]
325
- )
326
-
327
- # We expect that we're not going to detatch the existing attachment
328
- instance.multiple_field_attachments.each do |a|
329
- expect(a).to_not receive(:destroy)
330
- end
331
-
332
- # act
333
- instance.multiple_field = changeset
334
-
335
- expect(instance.another_place).to eq(keys)
336
- end
337
-
338
- it 'replaces Tessa assets with ActiveStorage assets' do
339
- # Before deploying this code, we previously had DB records with Tessa IDs
340
- instance.update!(another_place: [1, 2, 3])
341
-
342
- # In this HTTP POST, we removed one of the tessa assets and uploaded a
343
- # new ActiveStorage asset
344
- blob = ::ActiveStorage::Blob.create_before_direct_upload!({
345
- filename: 'README.md',
346
- byte_size: file.size,
347
- content_type: file.content_type,
348
- checksum: '1234'
349
- })
350
- changeset = Tessa::AssetChangeSet.new(
351
- changes: [
352
- { 'id' => 1, 'action' => 'add' },
353
- { 'id' => 2, 'action' => 'remove' },
354
- { 'id' => 3, 'action' => 'add' },
355
- { 'id' => blob.signed_id, 'action' => 'add' },
356
- ]
357
- )
358
-
359
- # We'll download these assets when we access #multiple_field
360
- allow(Tessa.config.connection).to receive(:get)
361
- .with("/assets/1,3")
362
- .and_return(double("response",
363
- success?: true,
364
- body: [
365
- { 'id' => 1, 'public_url' => 'test1' },
366
- { 'id' => 2, 'public_url' => 'test2' }
367
- ].to_json))
368
-
369
- blob.upload(file)
370
-
371
- # act
372
- instance.multiple_field = changeset
373
-
374
- expect(instance.another_place).to eq([
375
- blob.key, 1, 3
376
- ])
377
-
378
- assets = instance.multiple_field
379
- expect(assets[0].key).to eq(blob.key)
380
- expect(assets[0].service_url)
381
- .to start_with('https://www.example.com/rails/active_storage/disk/')
382
-
383
- expect(assets[1].id).to eq(1)
384
- expect(assets[1].public_url).to eq('test1')
385
- expect(assets[2].id).to eq(2)
386
- expect(assets[2].public_url).to eq('test2')
387
- end
388
- end
389
- end
390
-
391
- describe "#apply_tessa_change_sets" do
392
- let(:instance) { model.new }
393
- let(:sets) { [ instance_spy(Tessa::AssetChangeSet) ] }
394
-
395
- before do
396
- instance.instance_variable_set(
397
- :@pending_tessa_change_sets,
398
- {
399
- avatar: sets[0],
400
- }
401
- )
402
- end
403
-
404
- it "iterates over all pending changesets calling apply" do
405
- instance.apply_tessa_change_sets
406
- expect(sets[0]).to have_received(:apply)
407
- end
408
-
409
- it "removes all changesets from list" do
410
- instance.apply_tessa_change_sets
411
- expect(instance.pending_tessa_change_sets).to be_empty
412
- end
413
-
414
- context "no @pending_tessa_change_sets ivar" do
415
- before do
416
- instance.instance_variable_set(
417
- :@pending_tessa_change_sets,
418
- nil
419
- )
420
- end
421
-
422
- it "doesn't raise error" do
423
- expect { instance.apply_tessa_change_sets }.to_not raise_error
424
- end
425
- end
426
- end
427
-
428
- describe "#fetch_tessa_remote_assets" do
429
- subject(:result) { model.new.fetch_tessa_remote_assets(arg) }
430
-
431
- context "argument is `nil`" do
432
- let(:arg) { nil }
433
-
434
- it "returns nil" do
435
- expect(result).to be_nil
436
- end
437
- end
438
-
439
- context "argument is `[]`" do
440
- let(:arg) { [] }
441
-
442
- it "returns []" do
443
- expect(result).to be_a(Array)
444
- expect(result).to be_empty
445
- end
446
- end
447
-
448
- context "when argument is not blank" do
449
- let(:id) { rand(100) }
450
- let(:arg) { id }
451
-
452
- it "calls Tessa::Asset.find with arguments" do
453
- expect(Tessa::Asset).to receive(:find).with(arg)
454
- result
455
- end
456
-
457
- context "when Tessa::Asset.find raises RequestFailed exception" do
458
- let(:error) {
459
- Tessa::RequestFailed.new("test exception", double(status: '500'))
460
- }
461
-
462
- before do
463
- allow(Tessa::Asset).to receive(:find).and_raise(error)
464
- end
465
-
466
- context "argument is single id" do
467
- let(:arg) { id }
468
-
469
- it "returns Failure" do
470
- expect(result).to be_a(Tessa::Asset::Failure)
471
- end
472
-
473
- it "returns asset with proper data" do
474
- expect(result.id).to eq(arg)
475
- end
476
- end
477
-
478
- context "argument is array" do
479
- let(:arg) { [ id, id * 2 ] }
480
-
481
- it "returns array" do
482
- expect(result).to be_a(Array)
483
- end
484
-
485
- it "returns instances of Failure" do
486
- expect(result).to all( be_a(Tessa::Asset::Failure) )
487
- end
488
-
489
- it "returns array with an asset for each id passed" do
490
- arg.zip(result) do |a, r|
491
- expect(r.id).to eq(a)
492
- end
493
- end
494
- end
495
- end
496
- end
497
- end
498
-
499
- describe "#remove_all_tessa_assets" do
500
- let(:instance) { model.new }
501
-
502
- context "with a single typed field" do
503
- let(:model) {
504
- SingleAssetModel
505
- }
506
-
507
- before do
508
- instance.avatar_id = 1
509
- end
510
-
511
- it "adds pending change sets for each field removing all current assets" do
512
- instance.remove_all_tessa_assets
513
- changes = instance.pending_tessa_change_sets.values
514
- .reduce(Tessa::AssetChangeSet.new, :+)
515
- .changes
516
- .map { |change| [change.id, change.action.to_sym] }
517
- expect(changes).to eq([
518
- [1, :remove]
519
- ])
520
- end
521
- end
522
-
523
-
524
- context "with a multiple typed field" do
525
- let(:model) {
526
- MultipleAssetModel
527
- }
528
- let(:instance) { model.new(another_place: []) }
529
-
530
- before do
531
- instance.another_place = [2, 3]
532
- end
533
-
534
- it "adds pending change sets for each field removing all current assets" do
535
- instance.remove_all_tessa_assets
536
- changes = instance.pending_tessa_change_sets.values
537
- .reduce(Tessa::AssetChangeSet.new, :+)
538
- .changes
539
- .map { |change| [change.id, change.action.to_sym] }
540
- expect(changes).to eq([
541
- [2, :remove],
542
- [3, :remove],
543
- ])
544
- end
545
- end
546
- end
547
-
548
- describe "adds callbacks" do
549
- context "model responds to after_commit" do
550
- let(:model) {
551
- Class.new do
552
- def self.after_commit(arg=nil)
553
- @after_commit ||= arg
554
- end
555
- end.tap { |c| c.send(:include, described_module) }
556
- }
557
-
558
- it "calls it with :apply_tessa_change_sets" do
559
- expect(model.after_commit).to eq(:apply_tessa_change_sets)
560
- end
561
- end
562
-
563
- context "model responds to before_destroy" do
564
- let(:model) {
565
- Class.new do
566
- def self.before_destroy(arg=nil)
567
- @before_destroy ||= arg
568
- end
569
- end.tap { |c| c.send(:include, described_module) }
570
- }
571
-
572
- it "calls it with :remove_all_tessa_assets" do
573
- expect(model.before_destroy).to eq(:remove_all_tessa_assets)
574
- end
575
- end
576
- end
577
-
578
- end
@@ -1,83 +0,0 @@
1
- require 'spec_helper'
2
-
3
- RSpec.describe Tessa::RackUploadProxy do
4
- Result = Struct.new(:status, :headers, :body) do
5
- def json
6
- JSON.parse body.first
7
- end
8
- end
9
-
10
- subject(:result) { Result.new *described_class.call(env) }
11
- let(:env) {
12
- {
13
- "rack.request.form_hash" => params,
14
- "rack.session" => session,
15
- }
16
- }
17
- let(:params) { {} }
18
- let(:session) { {} }
19
-
20
- let(:blob) { ActiveStorage::Blob.last }
21
-
22
- before do
23
- allow(ActiveStorage.verifier).to receive(:generate)
24
- .and_return('some-consistent-token')
25
- end
26
-
27
- shared_examples_for "proper json return" do
28
- it "returns asset_id" do
29
- expect(result.json['asset_id']).to eq(blob.signed_id)
30
- end
31
-
32
- it "returns upload_url" do
33
- expect(result.json['upload_url']).to eq('https://www.example.com/rails/active_storage/disk/some-consistent-token')
34
- end
35
-
36
- it "returns upload_method" do
37
- expect(result.json['upload_method']).to eq('PUT')
38
- end
39
-
40
- it "returns upload_method" do
41
- expect(result.json['upload_headers']).to eq(blob.service_headers_for_direct_upload)
42
- end
43
-
44
- it "returns a 200 response" do
45
- expect(result.status).to eq(200)
46
- end
47
-
48
- it "sets the mime type to application/json" do
49
- expect(result.headers).to include("Content-Type" => "application/json")
50
- end
51
- end
52
-
53
- context "with no params and no session" do
54
- it "raises a bad request error" do
55
- expect(result.status).to eq(400)
56
- end
57
- end
58
-
59
- context "with params" do
60
- let(:params) {
61
- {
62
- "name" => "my-name",
63
- "size" => 456,
64
- "mime_type" => "plain/text",
65
- "checksum" => '1234'
66
- }
67
- }
68
-
69
- it "creates the ActiveStorage blob" do
70
- expect {
71
- described_class.call(env)
72
- }.to change { ActiveStorage::Blob.count }.by(1)
73
-
74
- expect(blob.filename).to eq('my-name')
75
- expect(blob.byte_size).to eq(456)
76
- expect(blob.content_type).to eq('plain/text')
77
- expect(blob.checksum).to eq('1234')
78
- end
79
-
80
- it_behaves_like "proper json return"
81
- end
82
-
83
- end