browse-everything 0.15.1 → 0.16.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.
Files changed (76) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +61 -9
  3. data/.rubocop_todo.yml +2 -15
  4. data/.travis.yml +19 -19
  5. data/CONTRIBUTING.md +6 -6
  6. data/Gemfile +12 -8
  7. data/README.md +30 -0
  8. data/Rakefile +2 -1
  9. data/app/assets/javascripts/browse_everything/behavior.js.coffee +5 -0
  10. data/app/controllers/browse_everything_controller.rb +75 -23
  11. data/app/helpers/browse_everything_helper.rb +2 -8
  12. data/app/helpers/font_awesome_version_helper.rb +9 -8
  13. data/app/services/browse_everything_session.rb +10 -0
  14. data/app/services/browse_everything_session/provider_session.rb +42 -0
  15. data/app/services/browser_factory.rb +25 -0
  16. data/app/views/browse_everything/_files.html.erb +56 -6
  17. data/browse-everything.gemspec +29 -25
  18. data/config/routes.rb +7 -2
  19. data/lib/browse-everything.rb +2 -0
  20. data/lib/browse_everything.rb +45 -12
  21. data/lib/browse_everything/auth/google/credentials.rb +28 -0
  22. data/lib/browse_everything/auth/google/request_parameters.rb +61 -0
  23. data/lib/browse_everything/browser.rb +11 -4
  24. data/lib/browse_everything/driver/authentication_factory.rb +22 -0
  25. data/lib/browse_everything/driver/base.rb +72 -19
  26. data/lib/browse_everything/driver/box.rb +46 -17
  27. data/lib/browse_everything/driver/dropbox.rb +36 -10
  28. data/lib/browse_everything/driver/file_system.rb +14 -26
  29. data/lib/browse_everything/driver/google_drive.rb +187 -54
  30. data/lib/browse_everything/driver/s3.rb +81 -75
  31. data/lib/browse_everything/engine.rb +3 -2
  32. data/lib/browse_everything/file_entry.rb +3 -1
  33. data/lib/browse_everything/retriever.rb +103 -31
  34. data/lib/browse_everything/version.rb +3 -1
  35. data/lib/generators/browse_everything/assets_generator.rb +3 -2
  36. data/lib/generators/browse_everything/config_generator.rb +11 -9
  37. data/lib/generators/browse_everything/install_generator.rb +3 -2
  38. data/lib/generators/browse_everything/templates/browse_everything_providers.yml.example +12 -11
  39. data/spec/controllers/browse_everything_controller_spec.rb +80 -0
  40. data/spec/features/select_files_spec.rb +13 -13
  41. data/spec/features/test_compiling_stylesheets_spec.rb +2 -0
  42. data/spec/fixtures/vcr_cassettes/google_drive.yml +331 -0
  43. data/spec/fixtures/vcr_cassettes/retriever.yml +93 -0
  44. data/spec/helper/browse_everything_controller_helper_spec.rb +21 -7
  45. data/spec/javascripts/jasmine_spec.rb +2 -0
  46. data/spec/javascripts/support/jasmine_helper.rb +1 -0
  47. data/spec/lib/browse_everything/auth/google/credentials_spec.rb +41 -0
  48. data/spec/{unit → lib/browse_everything}/browse_everything_helper_spec.rb +2 -0
  49. data/spec/lib/browse_everything/browser_spec.rb +109 -0
  50. data/spec/{unit → lib/browse_everything/driver}/base_spec.rb +5 -4
  51. data/spec/{unit → lib/browse_everything/driver}/box_spec.rb +20 -5
  52. data/spec/{unit → lib/browse_everything/driver}/dropbox_spec.rb +15 -18
  53. data/spec/{unit → lib/browse_everything/driver}/file_system_spec.rb +32 -26
  54. data/spec/lib/browse_everything/driver/google_drive_spec.rb +171 -0
  55. data/spec/{unit → lib/browse_everything/driver}/s3_spec.rb +38 -21
  56. data/spec/lib/browse_everything/driver_spec.rb +38 -0
  57. data/spec/{unit → lib/browse_everything}/file_entry_spec.rb +4 -1
  58. data/spec/lib/browse_everything/retriever_spec.rb +200 -0
  59. data/spec/lib/browse_everything_spec.rb +67 -0
  60. data/spec/services/browse_everything_session/provider_session_spec.rb +50 -0
  61. data/spec/services/browser_factory_spec.rb +40 -0
  62. data/spec/spec_helper.rb +39 -18
  63. data/spec/support/app/controllers/file_handler_controller.rb +4 -4
  64. data/spec/support/app/views/file_handler/main.html.erb +1 -1
  65. data/spec/support/capybara.rb +17 -0
  66. data/spec/support/rake.rb +3 -1
  67. data/spec/support/wait_for_ajax.rb +14 -0
  68. data/spec/test_app_templates/Gemfile.extra +1 -0
  69. data/spec/test_app_templates/lib/generators/test_app_generator.rb +10 -4
  70. data/spec/views/browse_everything/{_file.html.erb_spec.rb → _files.html.erb_spec.rb} +24 -18
  71. data/tasks/ci.rake +2 -0
  72. metadata +159 -107
  73. data/app/views/browse_everything/_file.html.erb +0 -52
  74. data/app/views/browse_everything/resolve.html.erb +0 -1
  75. data/spec/unit/browser_spec.rb +0 -76
  76. data/spec/unit/retriever_spec.rb +0 -109
@@ -0,0 +1,171 @@
1
+ # frozen_string_literal: true
2
+
3
+ include BrowserConfigHelper
4
+
5
+ describe BrowseEverything::Driver::GoogleDrive, vcr: { cassette_name: 'google_drive', record: :none } do
6
+ let(:browser) { BrowseEverything::Browser.new(url_options) }
7
+ let(:provider) { browser.providers['google_drive'] }
8
+ let(:provider_yml) do
9
+ {
10
+ client_id: 'CLIENTID', client_secret: 'CLIENTSECRET',
11
+ url_options: { port: '3000', protocol: 'http://', host: 'example.com' }
12
+ }
13
+ end
14
+
15
+ before do
16
+ stub_configuration
17
+ end
18
+
19
+ after do
20
+ unstub_configuration
21
+ end
22
+
23
+ describe 'simple properties' do
24
+ subject { provider }
25
+
26
+ its(:name) { is_expected.to eq('Google Drive') }
27
+ its(:key) { is_expected.to eq('google_drive') }
28
+ its(:icon) { is_expected.to eq('google-plus-sign') }
29
+ end
30
+
31
+ describe '#validate_config' do
32
+ it 'raises and error with an incomplete configuration' do
33
+ expect { BrowseEverything::Driver::GoogleDrive.new({}) }.to raise_error(BrowseEverything::InitializationError)
34
+ end
35
+
36
+ it 'raises and error with a configuration without a client secret' do
37
+ expect { BrowseEverything::Driver::GoogleDrive.new(client_id: 'test-client-id') }.to raise_error(BrowseEverything::InitializationError)
38
+ end
39
+ end
40
+
41
+ context 'without valid credentials' do
42
+ let(:driver) { described_class.new(provider_yml) }
43
+
44
+ describe '#token=' do
45
+ let(:value) { 'test' }
46
+
47
+ it 'restores the credentials' do
48
+ allow(driver).to receive(:restore_credentials)
49
+ driver.token = value
50
+ expect(driver).to have_received(:restore_credentials).with('test')
51
+ end
52
+
53
+ context 'when set to a Hash' do
54
+ let(:value) { { 'access_token' => 'test' } }
55
+
56
+ before do
57
+ driver.token = value
58
+ end
59
+
60
+ it 'sets the access token value' do
61
+ expect(driver.token).to be_a String
62
+ expect(driver.token).to eq 'test'
63
+ end
64
+ end
65
+ end
66
+ end
67
+
68
+ context 'with a valid connection' do
69
+ let(:driver) { described_class.new(provider_yml) }
70
+
71
+ before do
72
+ driver.connect({ code: 'code' }, {})
73
+ end
74
+
75
+ describe '#authorized?' do
76
+ it 'is authorized' do
77
+ expect(driver.authorized?).to be true
78
+ end
79
+ end
80
+
81
+ describe '#contents' do
82
+ subject(:contents) { driver.contents.to_a }
83
+
84
+ let(:drive_service_class) { class_double(Google::Apis::DriveV3::DriveService).as_stubbed_const(transfer_nested_constants: true) }
85
+ let(:drive_service) { instance_double(Google::Apis::DriveV3::DriveService) }
86
+ let(:file_list) { instance_double(Google::Apis::DriveV3::FileList) }
87
+ let(:file1) { instance_double(Google::Apis::DriveV3::File) }
88
+ let(:file2) { instance_double(Google::Apis::DriveV3::File) }
89
+ let(:files) { [file1, file2] }
90
+
91
+ before do
92
+ allow(file1).to receive(:id).and_return('asset-id2')
93
+ allow(file2).to receive(:id).and_return('directory-id1')
94
+ allow(file1).to receive(:name).and_return('asset-name2.pdf')
95
+ allow(file2).to receive(:name).and_return('directory-name1')
96
+ allow(file1).to receive(:size).and_return('891764')
97
+ allow(file2).to receive(:size).and_return('0')
98
+ allow(file1).to receive(:modified_time).and_return(Time.current)
99
+ allow(file2).to receive(:modified_time).and_return(Time.current)
100
+ allow(file1).to receive(:mime_type).and_return('application/pdf')
101
+ allow(file2).to receive(:mime_type).and_return('application/vnd.google-apps.folder')
102
+ allow(file_list).to receive(:files).and_return(files)
103
+ allow(file_list).to receive(:next_page_token).and_return(nil)
104
+ allow(drive_service).to receive(:list_files).and_yield(file_list, nil)
105
+ allow(drive_service).to receive(:batch).and_yield(drive_service)
106
+ allow(drive_service).to receive(:authorization=)
107
+ allow(drive_service).to receive(:tap).and_yield(drive_service).and_return(drive_service)
108
+ allow(drive_service_class).to receive(:new).and_return(drive_service)
109
+ end
110
+
111
+ it 'retrieves files' do
112
+ expect(contents).not_to be_empty
113
+
114
+ expect(contents.first).to be_a BrowseEverything::FileEntry
115
+ expect(contents.first.location).to eq 'google_drive:directory-id1'
116
+ expect(contents.first.mtime).to be_a Time
117
+ expect(contents.first.name).to eq 'directory-name1'
118
+ expect(contents.first.size).to eq 0
119
+ expect(contents.first.type).to eq 'directory'
120
+
121
+ expect(contents.last).to be_a BrowseEverything::FileEntry
122
+ expect(contents.last.location).to eq 'google_drive:asset-id2'
123
+ expect(contents.last.mtime).to be_a Time
124
+ expect(contents.last.name).to eq 'asset-name2.pdf'
125
+ expect(contents.last.size).to eq 891764
126
+ expect(contents.last.type).to eq 'application/pdf'
127
+ end
128
+
129
+ context 'when an error is encountered while authenticating' do
130
+ before do
131
+ allow(drive_service).to receive(:list_files).and_yield(file_list, Google::Apis::Error.new('test error'))
132
+ end
133
+
134
+ it 'raises an exception' do
135
+ expect { driver.contents.to_a }.to raise_error(Google::Apis::Error, 'test error')
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#link_for' do
141
+ subject(:link) { driver.link_for('asset-id2') }
142
+
143
+ it 'generates the link for a Google Drive asset' do
144
+ expect(link).to be_an Array
145
+ expect(link.first).to eq 'https://www.googleapis.com/drive/v3/files/asset-id2?alt=media'
146
+ expect(link.last).to be_a Hash
147
+ expect(link.last).to include auth_header: { 'Authorization' => 'Bearer access-token' }
148
+ expect(link.last).to include :expires
149
+ expect(link.last).to include file_name: 'asset-name2.pdf'
150
+ expect(link.last).to include file_size: 0
151
+ end
152
+ end
153
+
154
+ describe '#auth_link' do
155
+ subject(:uri) { driver.auth_link }
156
+
157
+ it 'exposes the authorization endpoint URI' do
158
+ expect(uri).to be_a Addressable::URI
159
+ expect(uri.to_s).to eq 'https://accounts.google.com/o/oauth2/auth?access_type=offline&approval_prompt=force&client_id=CLIENTID&include_granted_scopes=true&redirect_uri=http://example.com:3000/browse/connect&response_type=code&scope=https://www.googleapis.com/auth/drive'
160
+ end
161
+ end
162
+
163
+ describe '#drive' do
164
+ subject(:drive) { driver.drive_service }
165
+
166
+ it 'exposes the Google Drive API client' do
167
+ expect(drive).to be_a Google::Apis::DriveV3::DriveService
168
+ end
169
+ end
170
+ end
171
+ end
@@ -1,11 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  include BrowserConfigHelper
2
4
 
3
- describe BrowseEverything::Driver::FileSystem do
4
- before(:all) { stub_configuration }
5
- after(:all) { unstub_configuration }
5
+ describe BrowseEverything::Driver::S3 do
6
+ subject { provider }
7
+
6
8
  let(:browser) { BrowseEverything::Browser.new(url_options) }
7
9
  let(:provider) { browser.providers['s3'] }
8
- subject { provider }
10
+
11
+ before do
12
+ stub_configuration
13
+ end
14
+
15
+ after do
16
+ unstub_configuration
17
+ end
9
18
 
10
19
  describe 'defaults' do
11
20
  its(:icon) { is_expected.to eq('amazon') }
@@ -14,34 +23,34 @@ describe BrowseEverything::Driver::FileSystem do
14
23
 
15
24
  describe 'configuration' do
16
25
  it '#validate_config' do
17
- expect { BrowseEverything::Driver::S3.new({}) }.to raise_error(BrowseEverything::InitializationError)
26
+ expect { described_class.new({}) }.to raise_error(BrowseEverything::InitializationError)
18
27
  end
19
28
 
20
29
  it 'rejects app_key if app_secret is missing' do
21
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', app_key: 'APP_KEY') }.to raise_error(BrowseEverything::InitializationError)
30
+ expect { described_class.new(bucket: 'bucket', app_key: 'APP_KEY') }.to raise_error(BrowseEverything::InitializationError)
22
31
  end
23
32
 
24
33
  it 'rejects app_secret if app_key is missing' do
25
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', app_secret: 'APP_SECRET') }.to raise_error(BrowseEverything::InitializationError)
34
+ expect { described_class.new(bucket: 'bucket', app_secret: 'APP_SECRET') }.to raise_error(BrowseEverything::InitializationError)
26
35
  end
27
36
 
28
37
  it 'accepts app_key and app_secret together' do
29
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', app_key: 'APP_KEY', app_secret: 'APP_SECRET') }.not_to raise_error
38
+ expect { described_class.new(bucket: 'bucket', app_key: 'APP_KEY', app_secret: 'APP_SECRET') }.not_to raise_error
30
39
  end
31
40
 
32
41
  it 'rejects an invalid response type' do
33
- expect { BrowseEverything::Driver::S3.new(bucket: 'bucket', response_type: :foo) }.to raise_error(BrowseEverything::InitializationError)
42
+ expect { described_class.new(bucket: 'bucket', response_type: :foo) }.to raise_error(BrowseEverything::InitializationError)
34
43
  end
35
44
 
36
45
  it 'deprecates :signed_url' do
37
- driver = BrowseEverything::Driver::S3.new(bucket: 'bucket', signed_url: false)
46
+ driver = described_class.new(bucket: 'bucket', signed_url: false)
38
47
  expect(driver.config).not_to have_key(:signed_url)
39
48
  expect(driver.config[:response_type]).to eq(:public_url)
40
49
  end
41
50
  end
42
51
 
43
52
  describe '#contents' do
44
- context 'root directory' do
53
+ context 'when in a root directory' do
45
54
  before do
46
55
  Aws.config[:s3] = {
47
56
  stub_responses: {
@@ -51,19 +60,22 @@ describe BrowseEverything::Driver::FileSystem do
51
60
  end
52
61
 
53
62
  let(:contents) { provider.contents('') }
54
- context '[0]' do
63
+
64
+ context 'with a single asset' do
55
65
  subject { contents[0] }
66
+
56
67
  its(:name) { is_expected.to eq('bar') }
57
68
  specify { is_expected.to be_container }
58
69
  end
59
- context '[1]' do
70
+ context 'with two assets' do
60
71
  subject { contents[1] }
72
+
61
73
  its(:name) { is_expected.to eq('foo') }
62
74
  specify { is_expected.to be_container }
63
75
  end
64
76
  end
65
77
 
66
- context 'subdirectory' do
78
+ context 'when in a subdirectory' do
67
79
  before do
68
80
  Aws.config[:s3] = {
69
81
  stub_responses: {
@@ -81,21 +93,25 @@ describe BrowseEverything::Driver::FileSystem do
81
93
  end
82
94
 
83
95
  let(:contents) { provider.contents('foo/') }
84
- context '[0]' do
96
+
97
+ context 'with a single directory' do
85
98
  subject { contents[0] }
99
+
86
100
  its(:name) { is_expected.to eq('..') }
87
101
  specify { is_expected.to be_container }
88
102
  end
89
- context '[1]' do
103
+ context 'with a JPEG asset' do
90
104
  subject { contents[1] }
105
+
91
106
  its(:name) { is_expected.to eq('baz.jpg') }
92
107
  its(:location) { is_expected.to eq('s3:foo/baz.jpg') }
93
108
  its(:type) { is_expected.to eq('image/jpeg') }
94
109
  its(:size) { is_expected.to eq(52645) }
95
110
  specify { is_expected.not_to be_container }
96
111
  end
97
- context '[2]' do
112
+ context 'with a PNG asset' do
98
113
  subject { contents[2] }
114
+
99
115
  its(:name) { is_expected.to eq('quux.png') }
100
116
  its(:location) { is_expected.to eq('s3:foo/quux.png') }
101
117
  its(:type) { is_expected.to eq('image/png') }
@@ -103,8 +119,9 @@ describe BrowseEverything::Driver::FileSystem do
103
119
  specify { is_expected.not_to be_container }
104
120
  end
105
121
 
106
- context '#link_for' do
122
+ context 'when retrieving the link for an asset' do
107
123
  subject { contents[2] }
124
+
108
125
  before do
109
126
  object = instance_double(Aws::S3::Object)
110
127
  allow(object).to receive(:presigned_url).and_return('https://s3.amazonaws.com/presigned_url')
@@ -116,17 +133,17 @@ describe BrowseEverything::Driver::FileSystem do
116
133
 
117
134
  it ':signed_url' do
118
135
  provider.config[:response_type] = :signed_url
119
- expect(provider.link_for('foo/quux.png')).to eq('https://s3.amazonaws.com/presigned_url')
136
+ expect(provider.link_for('foo/quux.png')).to eq ["https://s3.amazonaws.com/presigned_url", {:file_name=>"quux.png", :expires=>14400}]
120
137
  end
121
138
 
122
139
  it ':public_url' do
123
140
  provider.config[:response_type] = :public_url
124
- expect(provider.link_for('foo/quux.png')).to eq('https://s3.amazonaws.com/public_url')
141
+ expect(provider.link_for('foo/quux.png')).to eq ["https://s3.amazonaws.com/public_url", {:file_name=>"quux.png"}]
125
142
  end
126
143
 
127
144
  it ':s3_uri' do
128
145
  provider.config[:response_type] = :s3_uri
129
- expect(provider.link_for('foo/quux.png')).to eq('s3://s3.bucket/foo/quux.png')
146
+ expect(provider.link_for('foo/quux.png')).to eq ['s3://s3.bucket/foo/quux.png', {:file_name=>"quux.png"}]
130
147
  end
131
148
  end
132
149
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe BrowseEverything::Driver do
4
+
5
+ let(:my_driver) do
6
+ MyDriver.new
7
+ end
8
+
9
+ before do
10
+ class MyDriver
11
+ include BrowseEverything::Driver
12
+
13
+ def get_sorter
14
+ sorter
15
+ end
16
+ end
17
+ end
18
+
19
+ describe '#sorter' do
20
+ it 'defaults to nil' do
21
+ expect(BrowseEverything::Driver.sorter).to be nil
22
+ end
23
+ end
24
+
25
+ describe '#sorter=' do
26
+ let(:new_sorter) do
27
+ lambda { |files| }
28
+ end
29
+
30
+ before do
31
+ BrowseEverything::Driver.sorter = new_sorter
32
+ end
33
+
34
+ it 'mutates the sorter from the initializer' do
35
+ expect(my_driver.get_sorter).to eq new_sorter
36
+ end
37
+ end
38
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  describe BrowseEverything::FileEntry do
2
- let(:mtime) { Time.now }
4
+ let(:mtime) { Time.current }
3
5
 
4
6
  describe 'regular file' do
5
7
  subject do
@@ -42,6 +44,7 @@ describe BrowseEverything::FileEntry do
42
44
  '..', '', mtime, true
43
45
  )
44
46
  end
47
+
45
48
  it { is_expected.to be_container }
46
49
  it { is_expected.to be_relative_parent_path }
47
50
 
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe BrowseEverything::Retriever, vcr: { cassette_name: 'retriever', record: :none } do
4
+ let(:retriever) { described_class.new }
5
+ let(:datafile) { File.expand_path('../../fixtures/file_system/file_1.pdf', __dir__) }
6
+ let(:datafile_with_spaces) { File.expand_path('../../fixtures/file_system/file 1.pdf', __dir__) }
7
+ let(:data) { File.open(datafile, 'rb', &:read) }
8
+ let(:data_with_spaces) { File.open(datafile_with_spaces, 'rb', &:read) }
9
+ let(:size) { File.size(datafile) }
10
+
11
+ describe '#get_file_size' do
12
+ subject(:file_size) { retriever.file_size(options) }
13
+ let(:url) { URI.parse("file://#{datafile}") }
14
+ let(:headers) { [] }
15
+ let(:file_size) { 0 }
16
+ let(:options) do
17
+ {
18
+ url: url,
19
+ headers: headers,
20
+ file_size: file_size
21
+ }.with_indifferent_access
22
+ end
23
+
24
+ it 'calculates or retrieves the size of a file' do
25
+ retriever.retrieve(options) do |chunk, retrieved, total|
26
+ expect(total).to eq 2256
27
+ end
28
+ end
29
+
30
+ context "when retrieving a resource from a cloud storage provider" do
31
+ let(:url) { URI.parse("https://drive.google.com/uc?id=id&export=download") }
32
+
33
+ it 'calculates or retrieves the size of a file' do
34
+ retriever.retrieve(options) do |chunk, retrieved, total|
35
+ expect(total).to eq 1234
36
+ end
37
+ end
38
+ end
39
+
40
+ context "when retrieving a resource with an unsupported protocol" do
41
+ let(:url) { URI.parse("ftp://invalid") }
42
+
43
+ it "raises an error" do
44
+ expect { retriever.retrieve(options) {|c, r, t|} }.to raise_error(URI::BadURIError, "Unknown URI scheme: ftp")
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'with a non-URI' do
50
+ let(:spec) do
51
+ {
52
+ '0' => {
53
+ 'url' => '/some/dir/file.pdf',
54
+ 'file_name' => 'file.pdf',
55
+ 'file_size' => size.to_s
56
+ }
57
+ }
58
+ end
59
+
60
+ describe '#retrieve' do
61
+ it 'raises an error' do
62
+ expect { retriever.retrieve(spec['0']) }.to raise_error(URI::BadURIError)
63
+ end
64
+ end
65
+ end
66
+
67
+ context 'when retrieving using the HTTP' do
68
+ let(:expiry_time) { (Time.current + 3600).xmlschema }
69
+ let(:spec) do
70
+ {
71
+ '0' => {
72
+ 'url' => 'https://retrieve.cloud.example.com/some/dir/file.pdf',
73
+ 'auth_header' => { 'Authorization' => 'Bearer ya29.kQCEAHj1bwFXr2AuGQJmSGRWQXpacmmYZs4kzCiXns3d6H1ZpIDWmdM8' },
74
+ 'expires' => expiry_time,
75
+ 'file_name' => 'file.pdf',
76
+ 'file_size' => size.to_s
77
+ }
78
+ }
79
+ end
80
+
81
+ context 'when retrieving data using chunked-encoded streams' do
82
+ it 'content' do
83
+ content = +''
84
+ retriever.retrieve(spec['0']) { |chunk, _retrieved, _total| content << chunk }
85
+ expect(content).to eq(data)
86
+ end
87
+
88
+ it 'callbacks' do
89
+ expect { |block| retriever.retrieve(spec['0'], &block) }.to yield_with_args(data, data.length, data.length)
90
+ end
91
+ end
92
+
93
+ context 'when downloading content' do
94
+ it 'content' do
95
+ file = retriever.download(spec['0'])
96
+ expect(File.open(file, 'rb', &:read)).to eq(data)
97
+ end
98
+
99
+ it 'callbacks' do
100
+ expect { |block| retriever.download(spec['0'], &block) }.to yield_with_args(String, data.length, data.length)
101
+ end
102
+ end
103
+
104
+ context 'when downloading content and a server error occurs' do
105
+ let(:spec) do
106
+ {
107
+ '0' => {
108
+ 'url' => 'https://retrieve.cloud.example.com/some/dir/file_error.pdf',
109
+ 'auth_header' => { 'Authorization' => 'Bearer ya29.kQCEAHj1bwFXr2AuGQJmSGRWQXpacmmYZs4kzCiXns3d6H1ZpIDWmdM8' },
110
+ 'expires' => expiry_time,
111
+ 'file_name' => 'file.pdf',
112
+ 'file_size' => size.to_s
113
+ }
114
+ }
115
+ end
116
+ let(:download_options) { spec['0'] }
117
+
118
+ it 'raises an exception' do
119
+ expect { retriever.download(download_options) }.to raise_error(BrowseEverything::DownloadError, /BrowseEverything::Retriever: Failed to download/)
120
+ end
121
+ end
122
+ end
123
+
124
+ context 'when retrieving file content' do
125
+ let(:spec) do
126
+ {
127
+ '0' => {
128
+ 'url' => "file://#{datafile}",
129
+ 'file_name' => 'file.pdf',
130
+ 'file_size' => size.to_s
131
+ },
132
+ '1' => {
133
+ 'url' => "file://#{datafile_with_spaces}",
134
+ 'file_name' => 'file.pdf',
135
+ 'file_size' => size.to_s
136
+ }
137
+ }
138
+ end
139
+
140
+ context 'when retrieving data using chunked-encoded streams' do
141
+ it 'content' do
142
+ content = +''
143
+ retriever.retrieve(spec['0']) { |chunk, _retrieved, _total| content << chunk }
144
+ expect(content).to eq(data)
145
+ end
146
+
147
+ it 'content with spaces' do
148
+ content = +''
149
+ retriever.retrieve(spec['1']) { |chunk, _retrieved, _total| content << chunk }
150
+ expect(content).to eq(data_with_spaces)
151
+ end
152
+
153
+ it 'callbacks' do
154
+ expect { |block| retriever.retrieve(spec['0'], &block) }.to yield_with_args(data, data.length, data.length)
155
+ end
156
+ end
157
+
158
+ context 'when downloading content' do
159
+ it 'content' do
160
+ file = retriever.download(spec['0'])
161
+ expect(File.open(file, 'rb', &:read)).to eq(data)
162
+ end
163
+
164
+ it 'callbacks' do
165
+ expect { |block| retriever.download(spec['0'], &block) }.to yield_with_args(String, data.length, data.length)
166
+ end
167
+ end
168
+ end
169
+
170
+ context '.can_retrieve?' do
171
+ let(:expiry_time) { (Time.current + 3600).xmlschema }
172
+ let(:spec) do
173
+ {
174
+ '0' => {
175
+ 'url' => url,
176
+ 'auth_header' => { 'Authorization' => 'Bearer ya29.kQCEAHj1bwFXr2AuGQJmSGRWQXpacmmYZs4kzCiXns3d6H1ZpIDWmdM8' },
177
+ 'expires' => expiry_time,
178
+ 'file_name' => 'file.pdf',
179
+ 'file_size' => '64134'
180
+ }
181
+ }
182
+ end
183
+
184
+ context 'can retrieve' do
185
+ let(:url) { 'https://retrieve.cloud.example.com/some/dir/can_retrieve.pdf' }
186
+
187
+ it 'says it can' do
188
+ expect(described_class.can_retrieve?(url)).to be_truthy
189
+ end
190
+ end
191
+
192
+ context 'cannot retrieve' do
193
+ let(:url) { 'https://retrieve.cloud.example.com/some/dir/cannot_retrieve.pdf' }
194
+
195
+ it 'says it cannot' do
196
+ expect(described_class.can_retrieve?(url)).to be_falsey
197
+ end
198
+ end
199
+ end
200
+ end