middleman-s3_sync 4.6.4 → 4.7.0
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/.github/workflows/ci.yml +29 -0
- data/.github/workflows/release.yml +53 -0
- data/Changelog.md +12 -0
- data/README.md +88 -0
- data/WARP.md +5 -1
- data/lib/middleman/s3_sync/cloudfront.rb +50 -26
- data/lib/middleman/s3_sync/options.rb +12 -1
- data/lib/middleman/s3_sync/resource.rb +72 -25
- data/lib/middleman/s3_sync/version.rb +1 -1
- data/lib/middleman/s3_sync.rb +163 -13
- data/lib/middleman-s3_sync/commands.rb +3 -1
- data/lib/middleman-s3_sync/extension.rb +22 -5
- data/middleman-s3_sync.gemspec +22 -18
- data/spec/aws_sdk_parameters_spec.rb +70 -6
- data/spec/cloudfront_spec.rb +2 -0
- data/spec/indifferent_hash_spec.rb +278 -0
- data/spec/resource_spec.rb +206 -0
- data/spec/s3_sync_integration_spec.rb +362 -7
- metadata +80 -64
|
@@ -15,11 +15,21 @@ describe 'S3Sync CloudFront Integration' do
|
|
|
15
15
|
dry_run: false,
|
|
16
16
|
verbose: false,
|
|
17
17
|
delete: true,
|
|
18
|
-
bucket: 'test-bucket'
|
|
18
|
+
bucket: 'test-bucket',
|
|
19
|
+
after_s3_sync: nil
|
|
19
20
|
)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
before do
|
|
24
|
+
# Reset cached app to avoid double leakage between tests
|
|
25
|
+
Middleman::S3Sync.instance_variable_set(:@app, nil)
|
|
26
|
+
|
|
27
|
+
# Mock sitemap for ensure_resource_list_updated! call
|
|
28
|
+
sitemap = double('sitemap')
|
|
29
|
+
allow(sitemap).to receive(:ensure_resource_list_updated!)
|
|
30
|
+
allow(app).to receive(:respond_to?).with(:sitemap).and_return(true)
|
|
31
|
+
allow(app).to receive(:sitemap).and_return(sitemap)
|
|
32
|
+
|
|
23
33
|
allow(::Middleman::Application).to receive(:new).and_return(app)
|
|
24
34
|
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(s3_sync_options)
|
|
25
35
|
allow(Middleman::S3Sync).to receive(:say_status)
|
|
@@ -36,7 +46,7 @@ describe 'S3Sync CloudFront Integration' do
|
|
|
36
46
|
describe 'CloudFront invalidation integration' do
|
|
37
47
|
it 'calls CloudFront invalidation after sync operations' do
|
|
38
48
|
# Reset invalidation paths for this test
|
|
39
|
-
Middleman::S3Sync.instance_variable_set(:@invalidation_paths,
|
|
49
|
+
Middleman::S3Sync.instance_variable_set(:@invalidation_paths, Set.new)
|
|
40
50
|
|
|
41
51
|
expect(Middleman::S3Sync::CloudFront).to receive(:invalidate).with(
|
|
42
52
|
[], # Initially empty, gets populated during resource operations
|
|
@@ -70,7 +80,8 @@ describe 'S3Sync CloudFront Integration' do
|
|
|
70
80
|
cloudfront_invalidate: true,
|
|
71
81
|
cloudfront_distribution_id: 'E1234567890123',
|
|
72
82
|
cloudfront_invalidate_all: true,
|
|
73
|
-
bucket: 'test-bucket'
|
|
83
|
+
bucket: 'test-bucket',
|
|
84
|
+
after_s3_sync: nil
|
|
74
85
|
)
|
|
75
86
|
end
|
|
76
87
|
|
|
@@ -89,7 +100,8 @@ describe 'S3Sync CloudFront Integration' do
|
|
|
89
100
|
let(:s3_sync_options) do
|
|
90
101
|
double(
|
|
91
102
|
cloudfront_invalidate: false,
|
|
92
|
-
bucket: 'test-bucket'
|
|
103
|
+
bucket: 'test-bucket',
|
|
104
|
+
after_s3_sync: nil
|
|
93
105
|
)
|
|
94
106
|
end
|
|
95
107
|
|
|
@@ -101,10 +113,32 @@ describe 'S3Sync CloudFront Integration' do
|
|
|
101
113
|
end
|
|
102
114
|
end
|
|
103
115
|
|
|
116
|
+
describe 'sitemap population' do
|
|
117
|
+
it 'calls ensure_resource_list_updated! before processing resources' do
|
|
118
|
+
sitemap = double('sitemap')
|
|
119
|
+
expect(sitemap).to receive(:ensure_resource_list_updated!)
|
|
120
|
+
allow(sitemap).to receive(:respond_to?).with(:ensure_resource_list_updated!).and_return(true)
|
|
121
|
+
allow(app).to receive(:sitemap).and_return(sitemap)
|
|
122
|
+
|
|
123
|
+
Middleman::S3Sync.sync
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it 'handles apps without sitemap gracefully' do
|
|
127
|
+
Middleman::S3Sync.instance_variable_set(:@app, nil)
|
|
128
|
+
|
|
129
|
+
app_without_sitemap = double('app_without_sitemap')
|
|
130
|
+
allow(app_without_sitemap).to receive(:respond_to?).with(:sitemap).and_return(false)
|
|
131
|
+
allow(::Middleman::Application).to receive(:new).and_return(app_without_sitemap)
|
|
132
|
+
|
|
133
|
+
# Should not raise an error
|
|
134
|
+
expect { Middleman::S3Sync.sync }.not_to raise_error
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
104
138
|
describe 'path tracking during resource operations' do
|
|
105
139
|
before do
|
|
106
140
|
# Reset invalidation paths before each path tracking test
|
|
107
|
-
Middleman::S3Sync.instance_variable_set(:@invalidation_paths,
|
|
141
|
+
Middleman::S3Sync.instance_variable_set(:@invalidation_paths, Set.new)
|
|
108
142
|
end
|
|
109
143
|
|
|
110
144
|
it 'adds paths to invalidation list when resources are processed' do
|
|
@@ -126,7 +160,328 @@ describe 'S3Sync CloudFront Integration' do
|
|
|
126
160
|
Middleman::S3Sync.add_invalidation_path('/same/path.html')
|
|
127
161
|
Middleman::S3Sync.add_invalidation_path('/same/path.html')
|
|
128
162
|
|
|
129
|
-
|
|
163
|
+
# Set automatically handles uniqueness - verify it contains exactly one occurrence
|
|
164
|
+
expect(Middleman::S3Sync.invalidation_paths.to_a.count('/same/path.html')).to eq(1)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe 'orphan file discovery (scan_build_dir)' do
|
|
169
|
+
let(:build_dir) { Dir.mktmpdir }
|
|
170
|
+
|
|
171
|
+
after do
|
|
172
|
+
FileUtils.remove_entry(build_dir) if File.directory?(build_dir)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
before do
|
|
176
|
+
# Reset state
|
|
177
|
+
Middleman::S3Sync.instance_variable_set(:@s3_sync_resources, {})
|
|
178
|
+
Middleman::S3Sync.instance_variable_set(:@bucket_files, {})
|
|
179
|
+
|
|
180
|
+
allow(Middleman::S3Sync).to receive(:say_status)
|
|
181
|
+
allow(Middleman::S3Sync).to receive(:build_dir).and_return(build_dir)
|
|
182
|
+
allow(Middleman::S3Sync).to receive(:remote_resource_for_path).and_return(nil)
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
context 'when scan_build_dir is enabled' do
|
|
186
|
+
let(:s3_sync_options) do
|
|
187
|
+
double(
|
|
188
|
+
scan_build_dir: true,
|
|
189
|
+
build_dir: build_dir,
|
|
190
|
+
bucket: 'test-bucket',
|
|
191
|
+
prefix: nil,
|
|
192
|
+
delete: false,
|
|
193
|
+
verbose: false,
|
|
194
|
+
ignore_paths: [],
|
|
195
|
+
prefer_gzip: false,
|
|
196
|
+
force: false,
|
|
197
|
+
acl: 'public-read',
|
|
198
|
+
after_s3_sync: nil
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
it 'discovers files not in sitemap' do
|
|
203
|
+
# Create orphan files in build directory
|
|
204
|
+
FileUtils.mkdir_p(File.join(build_dir, 'images'))
|
|
205
|
+
File.write(File.join(build_dir, 'orphan.txt'), 'orphan content')
|
|
206
|
+
File.write(File.join(build_dir, 'images', 'optimized.webp'), 'image data')
|
|
207
|
+
|
|
208
|
+
# Mock Resource creation to avoid S3 calls
|
|
209
|
+
allow(Middleman::S3Sync::Resource).to receive(:new) do |resource, remote, path: nil|
|
|
210
|
+
mock_resource = double('resource', status: :new, path: path)
|
|
211
|
+
allow(mock_resource).to receive(:tap).and_yield(mock_resource).and_return(mock_resource)
|
|
212
|
+
mock_resource
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
Middleman::S3Sync.send(:discover_orphan_files)
|
|
216
|
+
|
|
217
|
+
resources = Middleman::S3Sync.send(:s3_sync_resources)
|
|
218
|
+
expect(resources.keys).to include('orphan.txt')
|
|
219
|
+
expect(resources.keys).to include('images/optimized.webp')
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
it 'skips files already in sitemap' do
|
|
223
|
+
File.write(File.join(build_dir, 'existing.html'), 'content')
|
|
224
|
+
|
|
225
|
+
# Pre-populate sitemap resource
|
|
226
|
+
Middleman::S3Sync.send(:s3_sync_resources)['existing.html'] = double('resource')
|
|
227
|
+
|
|
228
|
+
# Count how many times Resource.new is called
|
|
229
|
+
call_count = 0
|
|
230
|
+
allow(Middleman::S3Sync::Resource).to receive(:new) do |resource, remote, path: nil|
|
|
231
|
+
call_count += 1
|
|
232
|
+
mock_resource = double('resource', status: :new, path: path)
|
|
233
|
+
allow(mock_resource).to receive(:tap).and_yield(mock_resource).and_return(mock_resource)
|
|
234
|
+
mock_resource
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
Middleman::S3Sync.send(:discover_orphan_files)
|
|
238
|
+
|
|
239
|
+
# Should not have created a new resource for existing.html
|
|
240
|
+
expect(call_count).to eq(0)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
it 'skips directories' do
|
|
244
|
+
FileUtils.mkdir_p(File.join(build_dir, 'subdir', 'nested'))
|
|
245
|
+
File.write(File.join(build_dir, 'subdir', 'file.txt'), 'content')
|
|
246
|
+
|
|
247
|
+
created_paths = []
|
|
248
|
+
allow(Middleman::S3Sync::Resource).to receive(:new) do |resource, remote, path: nil|
|
|
249
|
+
created_paths << path
|
|
250
|
+
mock_resource = double('resource', status: :new, path: path)
|
|
251
|
+
allow(mock_resource).to receive(:tap).and_yield(mock_resource).and_return(mock_resource)
|
|
252
|
+
mock_resource
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
Middleman::S3Sync.send(:discover_orphan_files)
|
|
256
|
+
|
|
257
|
+
expect(created_paths).to include('subdir/file.txt')
|
|
258
|
+
expect(created_paths).not_to include('subdir')
|
|
259
|
+
expect(created_paths).not_to include('subdir/nested')
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
context 'when scan_build_dir is disabled' do
|
|
264
|
+
let(:s3_sync_options) do
|
|
265
|
+
double(
|
|
266
|
+
scan_build_dir: false,
|
|
267
|
+
build_dir: build_dir,
|
|
268
|
+
bucket: 'test-bucket',
|
|
269
|
+
after_s3_sync: nil
|
|
270
|
+
)
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
it 'does not scan for orphan files' do
|
|
274
|
+
File.write(File.join(build_dir, 'orphan.txt'), 'content')
|
|
275
|
+
|
|
276
|
+
expect(Middleman::S3Sync).not_to receive(:discover_orphan_files)
|
|
277
|
+
|
|
278
|
+
# Call work_to_be_done? but mock the heavy parts
|
|
279
|
+
allow(Middleman::S3Sync).to receive(:mm_resources).and_return([])
|
|
280
|
+
allow(Middleman::S3Sync).to receive(:remote_only_paths).and_return([])
|
|
281
|
+
allow(Middleman::S3Sync).to receive(:files_to_create).and_return([])
|
|
282
|
+
allow(Middleman::S3Sync).to receive(:files_to_update).and_return([])
|
|
283
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([])
|
|
284
|
+
|
|
285
|
+
Middleman::S3Sync.send(:work_to_be_done?)
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
describe 'batch delete operations' do
|
|
291
|
+
let(:bucket) { double('bucket') }
|
|
292
|
+
let(:resource1) { double('resource1', path: 'file1.html', remote_path: 'file1.html') }
|
|
293
|
+
let(:resource2) { double('resource2', path: 'file2.html', remote_path: 'file2.html') }
|
|
294
|
+
let(:resource3) { double('resource3', path: 'file3.html', remote_path: 'file3.html') }
|
|
295
|
+
|
|
296
|
+
before do
|
|
297
|
+
# Remove the stub for delete_resources so we test the actual implementation
|
|
298
|
+
allow(Middleman::S3Sync).to receive(:delete_resources).and_call_original
|
|
299
|
+
allow(Middleman::S3Sync).to receive(:say_status)
|
|
300
|
+
allow(Middleman::S3Sync).to receive(:bucket).and_return(bucket)
|
|
301
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(s3_sync_options)
|
|
302
|
+
Middleman::S3Sync.instance_variable_set(:@invalidation_paths, Set.new)
|
|
303
|
+
Middleman::S3Sync.instance_variable_set(:@categorized_resources, nil)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
it 'uses batch delete_objects API instead of individual deletes' do
|
|
307
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([resource1, resource2, resource3])
|
|
308
|
+
|
|
309
|
+
expect(bucket).to receive(:delete_objects).with(
|
|
310
|
+
delete: {
|
|
311
|
+
objects: [
|
|
312
|
+
{ key: 'file1.html' },
|
|
313
|
+
{ key: 'file2.html' },
|
|
314
|
+
{ key: 'file3.html' }
|
|
315
|
+
]
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
Middleman::S3Sync.send(:delete_resources)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
it 'adds invalidation paths for all deleted resources' do
|
|
323
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([resource1, resource2])
|
|
324
|
+
allow(bucket).to receive(:delete_objects)
|
|
325
|
+
|
|
326
|
+
Middleman::S3Sync.send(:delete_resources)
|
|
327
|
+
|
|
328
|
+
expect(Middleman::S3Sync.invalidation_paths).to include('/file1.html')
|
|
329
|
+
expect(Middleman::S3Sync.invalidation_paths).to include('/file2.html')
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
it 'does not call delete_objects during dry run' do
|
|
333
|
+
dry_run_options = double(
|
|
334
|
+
dry_run: true,
|
|
335
|
+
delete: true,
|
|
336
|
+
bucket: 'test-bucket',
|
|
337
|
+
after_s3_sync: nil
|
|
338
|
+
)
|
|
339
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(dry_run_options)
|
|
340
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([resource1])
|
|
341
|
+
|
|
342
|
+
expect(bucket).not_to receive(:delete_objects)
|
|
343
|
+
|
|
344
|
+
Middleman::S3Sync.send(:delete_resources)
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
it 'does nothing when there are no files to delete' do
|
|
348
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([])
|
|
349
|
+
|
|
350
|
+
expect(bucket).not_to receive(:delete_objects)
|
|
351
|
+
|
|
352
|
+
Middleman::S3Sync.send(:delete_resources)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
describe 'after_s3_sync callback' do
|
|
357
|
+
before do
|
|
358
|
+
Middleman::S3Sync.instance_variable_set(:@app, nil)
|
|
359
|
+
|
|
360
|
+
sitemap = double('sitemap')
|
|
361
|
+
allow(sitemap).to receive(:ensure_resource_list_updated!)
|
|
362
|
+
allow(app).to receive(:respond_to?).with(:sitemap).and_return(true)
|
|
363
|
+
allow(app).to receive(:sitemap).and_return(sitemap)
|
|
364
|
+
|
|
365
|
+
allow(::Middleman::Application).to receive(:new).and_return(app)
|
|
366
|
+
allow(Middleman::S3Sync).to receive(:say_status)
|
|
367
|
+
allow(Middleman::S3Sync).to receive(:work_to_be_done?).and_return(true)
|
|
368
|
+
allow(Middleman::S3Sync).to receive(:update_bucket_versioning)
|
|
369
|
+
allow(Middleman::S3Sync).to receive(:update_bucket_website)
|
|
370
|
+
allow(Middleman::S3Sync).to receive(:ignore_resources)
|
|
371
|
+
allow(Middleman::S3Sync).to receive(:create_resources)
|
|
372
|
+
allow(Middleman::S3Sync).to receive(:update_resources)
|
|
373
|
+
allow(Middleman::S3Sync).to receive(:delete_resources)
|
|
374
|
+
allow(Middleman::S3Sync::CloudFront).to receive(:invalidate)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
context 'when after_s3_sync callback is provided' do
|
|
378
|
+
it 'executes the callback after sync completes' do
|
|
379
|
+
callback_executed = false
|
|
380
|
+
callback_options = double(
|
|
381
|
+
cloudfront_invalidate: false,
|
|
382
|
+
bucket: 'test-bucket',
|
|
383
|
+
after_s3_sync: -> { callback_executed = true }
|
|
384
|
+
)
|
|
385
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(callback_options)
|
|
386
|
+
allow(Middleman::S3Sync).to receive(:files_to_create).and_return([])
|
|
387
|
+
allow(Middleman::S3Sync).to receive(:files_to_update).and_return([])
|
|
388
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([])
|
|
389
|
+
|
|
390
|
+
Middleman::S3Sync.sync
|
|
391
|
+
|
|
392
|
+
expect(callback_executed).to be true
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
it 'passes sync results to the callback' do
|
|
396
|
+
received_results = nil
|
|
397
|
+
callback_options = double(
|
|
398
|
+
cloudfront_invalidate: false,
|
|
399
|
+
bucket: 'test-bucket',
|
|
400
|
+
after_s3_sync: ->(results) { received_results = results }
|
|
401
|
+
)
|
|
402
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(callback_options)
|
|
403
|
+
allow(Middleman::S3Sync).to receive(:files_to_create).and_return(['file1.html'])
|
|
404
|
+
allow(Middleman::S3Sync).to receive(:files_to_update).and_return(['file2.html', 'file3.html'])
|
|
405
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([])
|
|
406
|
+
|
|
407
|
+
Middleman::S3Sync.sync
|
|
408
|
+
|
|
409
|
+
expect(received_results).to be_a(Hash)
|
|
410
|
+
expect(received_results[:created]).to eq(1)
|
|
411
|
+
expect(received_results[:updated]).to eq(2)
|
|
412
|
+
expect(received_results[:deleted]).to eq(0)
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
it 'logs callback execution status' do
|
|
416
|
+
callback_options = double(
|
|
417
|
+
cloudfront_invalidate: false,
|
|
418
|
+
bucket: 'test-bucket',
|
|
419
|
+
after_s3_sync: -> { 'done' }
|
|
420
|
+
)
|
|
421
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(callback_options)
|
|
422
|
+
allow(Middleman::S3Sync).to receive(:files_to_create).and_return([])
|
|
423
|
+
allow(Middleman::S3Sync).to receive(:files_to_update).and_return([])
|
|
424
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([])
|
|
425
|
+
|
|
426
|
+
expect(Middleman::S3Sync).to receive(:say_status).with('callback', /Running after_s3_sync/)
|
|
427
|
+
expect(Middleman::S3Sync).to receive(:say_status).with('callback', /after_s3_sync completed/)
|
|
428
|
+
|
|
429
|
+
Middleman::S3Sync.sync
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
it 'handles callback errors gracefully' do
|
|
433
|
+
error_callback_options = double(
|
|
434
|
+
cloudfront_invalidate: false,
|
|
435
|
+
bucket: 'test-bucket',
|
|
436
|
+
after_s3_sync: ->(_results) { raise 'Callback error!' }
|
|
437
|
+
)
|
|
438
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(error_callback_options)
|
|
439
|
+
allow(Middleman::S3Sync).to receive(:files_to_create).and_return([])
|
|
440
|
+
allow(Middleman::S3Sync).to receive(:files_to_update).and_return([])
|
|
441
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([])
|
|
442
|
+
|
|
443
|
+
expect(Middleman::S3Sync).to receive(:say_status).with('error', /Callback error!/)
|
|
444
|
+
expect { Middleman::S3Sync.sync }.not_to raise_error
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
context 'when after_s3_sync callback is nil' do
|
|
449
|
+
it 'does not attempt to execute a callback' do
|
|
450
|
+
nil_callback_options = double(
|
|
451
|
+
cloudfront_invalidate: false,
|
|
452
|
+
bucket: 'test-bucket',
|
|
453
|
+
after_s3_sync: nil
|
|
454
|
+
)
|
|
455
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(nil_callback_options)
|
|
456
|
+
|
|
457
|
+
# Should not log callback execution
|
|
458
|
+
expect(Middleman::S3Sync).not_to receive(:say_status).with('callback', anything)
|
|
459
|
+
|
|
460
|
+
Middleman::S3Sync.sync
|
|
461
|
+
end
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
context 'when after_s3_sync callback is a method name' do
|
|
465
|
+
it 'executes the method on the app' do
|
|
466
|
+
method_executed = false
|
|
467
|
+
allow(app).to receive(:my_callback) { method_executed = true }
|
|
468
|
+
allow(app).to receive(:respond_to?).with(:my_callback).and_return(true)
|
|
469
|
+
allow(app).to receive(:method).with(:my_callback).and_return(double(arity: 0))
|
|
470
|
+
|
|
471
|
+
method_callback_options = double(
|
|
472
|
+
cloudfront_invalidate: false,
|
|
473
|
+
bucket: 'test-bucket',
|
|
474
|
+
after_s3_sync: :my_callback
|
|
475
|
+
)
|
|
476
|
+
allow(Middleman::S3Sync).to receive(:s3_sync_options).and_return(method_callback_options)
|
|
477
|
+
allow(Middleman::S3Sync).to receive(:files_to_create).and_return([])
|
|
478
|
+
allow(Middleman::S3Sync).to receive(:files_to_update).and_return([])
|
|
479
|
+
allow(Middleman::S3Sync).to receive(:files_to_delete).and_return([])
|
|
480
|
+
|
|
481
|
+
Middleman::S3Sync.sync
|
|
482
|
+
|
|
483
|
+
expect(method_executed).to be true
|
|
484
|
+
end
|
|
130
485
|
end
|
|
131
486
|
end
|
|
132
|
-
end
|
|
487
|
+
end
|