imap-backup 5.0.0 → 6.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/bin/imap-backup +4 -0
- data/docs/development.md +10 -4
- data/imap-backup.gemspec +2 -7
- data/lib/cli_coverage.rb +18 -0
- data/lib/imap/backup/account/connection.rb +7 -11
- data/lib/imap/backup/account/folder.rb +0 -16
- data/lib/imap/backup/account.rb +31 -11
- data/lib/imap/backup/cli/folders.rb +3 -3
- data/lib/imap/backup/cli/migrate.rb +3 -3
- data/lib/imap/backup/cli/restore.rb +20 -4
- data/lib/imap/backup/cli/utils.rb +2 -2
- data/lib/imap/backup/cli.rb +6 -7
- data/lib/imap/backup/configuration.rb +1 -11
- data/lib/imap/backup/downloader.rb +13 -9
- data/lib/imap/backup/serializer/directory.rb +37 -0
- data/lib/imap/backup/serializer/imap.rb +120 -0
- data/lib/imap/backup/serializer/mbox.rb +23 -94
- data/lib/imap/backup/serializer/mbox_enumerator.rb +2 -0
- data/lib/imap/backup/serializer.rb +180 -3
- data/lib/imap/backup/setup/account.rb +52 -29
- data/lib/imap/backup/setup/helpers.rb +1 -1
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +1 -1
- data/lib/imap/backup/version.rb +2 -2
- data/lib/imap/backup.rb +0 -1
- data/spec/features/backup_spec.rb +22 -29
- data/spec/features/restore_spec.rb +8 -6
- data/spec/features/support/aruba.rb +12 -3
- data/spec/features/support/backup_directory.rb +0 -4
- data/spec/features/support/email_server.rb +0 -1
- data/spec/spec_helper.rb +4 -9
- data/spec/unit/imap/backup/account/connection_spec.rb +36 -8
- data/spec/unit/imap/backup/account/folder_spec.rb +18 -16
- data/spec/unit/imap/backup/account_spec.rb +246 -0
- data/spec/unit/imap/backup/cli/accounts_spec.rb +12 -1
- data/spec/unit/imap/backup/cli/backup_spec.rb +19 -0
- data/spec/unit/imap/backup/cli/folders_spec.rb +39 -0
- data/spec/unit/imap/backup/cli/local_spec.rb +26 -7
- data/spec/unit/imap/backup/cli/migrate_spec.rb +80 -0
- data/spec/unit/imap/backup/cli/restore_spec.rb +67 -0
- data/spec/unit/imap/backup/cli/setup_spec.rb +17 -0
- data/spec/unit/imap/backup/cli/utils_spec.rb +68 -5
- data/spec/unit/imap/backup/cli_spec.rb +93 -0
- data/spec/unit/imap/backup/client/apple_mail_spec.rb +9 -0
- data/spec/unit/imap/backup/configuration_spec.rb +2 -2
- data/spec/unit/imap/backup/downloader_spec.rb +60 -8
- data/spec/unit/imap/backup/logger_spec.rb +1 -1
- data/spec/unit/imap/backup/migrator_spec.rb +1 -1
- data/spec/unit/imap/backup/sanitizer_spec.rb +42 -0
- data/spec/unit/imap/backup/serializer/directory_spec.rb +37 -0
- data/spec/unit/imap/backup/serializer/imap_spec.rb +218 -0
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +62 -183
- data/spec/unit/imap/backup/serializer_spec.rb +296 -0
- data/spec/unit/imap/backup/setup/account_spec.rb +120 -25
- data/spec/unit/imap/backup/setup/helpers_spec.rb +15 -0
- data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +116 -0
- data/spec/unit/imap/backup/uploader_spec.rb +1 -1
- data/spec/unit/retry_on_error_spec.rb +34 -0
- metadata +44 -37
- data/lib/imap/backup/serializer/mbox_store.rb +0 -217
- data/lib/thunderbird/install.rb +0 -16
- data/lib/thunderbird/local_folder.rb +0 -65
- data/lib/thunderbird/profile.rb +0 -30
- data/lib/thunderbird/profiles.rb +0 -71
- data/lib/thunderbird/subdirectory.rb +0 -93
- data/lib/thunderbird/subdirectory_placeholder.rb +0 -21
- data/lib/thunderbird.rb +0 -14
- data/spec/gather_rspec_coverage.rb +0 -1
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +0 -329
@@ -1,28 +1,28 @@
|
|
1
1
|
require "features/helper"
|
2
2
|
|
3
|
-
RSpec.describe "backup", type: :
|
3
|
+
RSpec.describe "backup", type: :aruba, docker: true do
|
4
4
|
include_context "imap-backup connection"
|
5
5
|
include_context "message-fixtures"
|
6
6
|
|
7
|
+
let(:local_backup_path) { File.expand_path("~/backup") }
|
8
|
+
let(:backup_folders) { [{name: folder}] }
|
9
|
+
let(:folder) { "my-stuff" }
|
7
10
|
let(:messages_as_mbox) do
|
8
11
|
message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
|
9
12
|
end
|
10
|
-
|
11
|
-
let(:email1) { send_email folder, msg1 }
|
12
|
-
let(:email2) { send_email folder, msg2 }
|
13
|
+
|
13
14
|
let!(:pre) {}
|
14
15
|
let!(:setup) do
|
15
16
|
server_create_folder folder
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
send_email folder, msg1
|
18
|
+
send_email folder, msg2
|
19
|
+
create_config(accounts: [account.to_h])
|
20
|
+
|
21
|
+
run_command_and_stop("imap-backup backup")
|
19
22
|
end
|
20
23
|
|
21
24
|
after do
|
22
|
-
FileUtils.rm_rf local_backup_path
|
23
|
-
delete_emails folder
|
24
25
|
server_delete_folder folder
|
25
|
-
connection.disconnect
|
26
26
|
end
|
27
27
|
|
28
28
|
it "downloads messages" do
|
@@ -51,17 +51,16 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
51
51
|
|
52
52
|
context "when uid_validity does not match" do
|
53
53
|
let(:new_name) { "NEWNAME" }
|
54
|
-
let(:email3) { send_email folder, msg3 }
|
55
54
|
let(:original_folder_uid_validity) { server_uid_validity(folder) }
|
56
55
|
let!(:pre) do
|
57
56
|
server_create_folder folder
|
58
|
-
|
57
|
+
send_email folder, msg3
|
59
58
|
original_folder_uid_validity
|
60
59
|
connection.run_backup
|
61
60
|
connection.disconnect
|
62
61
|
server_rename_folder folder, new_name
|
63
62
|
end
|
64
|
-
let(:renamed_folder) { "#{folder}
|
63
|
+
let(:renamed_folder) { "#{folder}-#{original_folder_uid_validity}" }
|
65
64
|
|
66
65
|
after do
|
67
66
|
server_delete_folder new_name
|
@@ -71,37 +70,31 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
71
70
|
expect(mbox_content(renamed_folder)).to eq(message_as_mbox_entry(msg3))
|
72
71
|
end
|
73
72
|
|
73
|
+
it "renames the old metadata file" do
|
74
|
+
expect(imap_parsed(renamed_folder)).to be_a Hash
|
75
|
+
end
|
76
|
+
|
74
77
|
it "downloads messages" do
|
75
78
|
expect(mbox_content(folder)).to eq(messages_as_mbox)
|
76
79
|
end
|
77
80
|
|
81
|
+
it "creates a metadata file" do
|
82
|
+
expect(imap_parsed(folder)).to be_a Hash
|
83
|
+
end
|
84
|
+
|
78
85
|
context "when a renamed local backup exists" do
|
79
86
|
let!(:pre) do
|
80
87
|
super()
|
88
|
+
create_directory local_backup_path
|
81
89
|
File.write(imap_path(renamed_folder), "existing imap")
|
82
90
|
File.write(mbox_path(renamed_folder), "existing mbox")
|
83
91
|
end
|
84
92
|
|
85
93
|
it "moves the old backup to a uniquely named directory" do
|
86
|
-
renamed = "#{folder}
|
94
|
+
renamed = "#{folder}-#{original_folder_uid_validity}-1"
|
87
95
|
expect(mbox_content(renamed)).to eq(message_as_mbox_entry(msg3))
|
88
96
|
end
|
89
97
|
end
|
90
98
|
end
|
91
|
-
|
92
|
-
context "when an unversioned .imap file is found" do
|
93
|
-
let!(:pre) do
|
94
|
-
File.open(imap_path(folder), "w") { |f| f.write "old format imap" }
|
95
|
-
File.open(mbox_path(folder), "w") { |f| f.write "old format emails" }
|
96
|
-
end
|
97
|
-
|
98
|
-
it "replaces the .imap file with a versioned JSON file" do
|
99
|
-
expect(imap_metadata[:uids]).to eq(folder_uids)
|
100
|
-
end
|
101
|
-
|
102
|
-
it "does the download" do
|
103
|
-
expect(mbox_content(folder)).to eq(messages_as_mbox)
|
104
|
-
end
|
105
|
-
end
|
106
99
|
end
|
107
100
|
end
|
@@ -1,9 +1,11 @@
|
|
1
1
|
require "features/helper"
|
2
2
|
|
3
|
-
RSpec.describe "restore", type: :
|
3
|
+
RSpec.describe "restore", type: :aruba, docker: true do
|
4
4
|
include_context "imap-backup connection"
|
5
5
|
include_context "message-fixtures"
|
6
6
|
|
7
|
+
let(:local_backup_path) { File.expand_path("~/backup") }
|
8
|
+
let(:folder) { "my-stuff" }
|
7
9
|
let(:messages_as_mbox) do
|
8
10
|
message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
|
9
11
|
end
|
@@ -12,19 +14,19 @@ RSpec.describe "restore", type: :feature, docker: true do
|
|
12
14
|
end
|
13
15
|
let(:message_uids) { [msg1[:uid], msg2[:uid]] }
|
14
16
|
let(:existing_imap_content) { imap_data(uid_validity, message_uids).to_json }
|
15
|
-
let(:folder) { "my-stuff" }
|
16
17
|
let(:uid_validity) { 1234 }
|
17
18
|
|
18
19
|
let!(:pre) {}
|
19
20
|
let!(:setup) do
|
21
|
+
create_directory local_backup_path
|
20
22
|
File.write(imap_path(folder), existing_imap_content)
|
21
23
|
File.write(mbox_path(folder), messages_as_mbox)
|
22
|
-
|
24
|
+
create_config(accounts: [account.to_h])
|
25
|
+
|
26
|
+
run_command_and_stop("imap-backup restore #{account.username}")
|
23
27
|
end
|
24
28
|
let(:cleanup) do
|
25
|
-
FileUtils.rm_rf local_backup_path
|
26
29
|
server_delete_folder folder
|
27
|
-
connection.disconnect
|
28
30
|
end
|
29
31
|
|
30
32
|
after { cleanup }
|
@@ -92,7 +94,7 @@ RSpec.describe "restore", type: :feature, docker: true do
|
|
92
94
|
end
|
93
95
|
|
94
96
|
context "when the folder has content" do
|
95
|
-
let(:new_folder) { "#{folder}
|
97
|
+
let(:new_folder) { "#{folder}-#{uid_validity}" }
|
96
98
|
let(:cleanup) do
|
97
99
|
server_delete_folder new_folder
|
98
100
|
super()
|
@@ -1,7 +1,11 @@
|
|
1
1
|
require "aruba/rspec"
|
2
2
|
|
3
|
+
require_relative "backup_directory"
|
4
|
+
require "imap/backup/serializer/mbox"
|
5
|
+
|
3
6
|
Aruba.configure do |config|
|
4
7
|
config.home_directory = File.expand_path("./tmp/home")
|
8
|
+
config.allow_absolute_paths = true
|
5
9
|
end
|
6
10
|
|
7
11
|
module ConfigurationHelpers
|
@@ -33,10 +37,10 @@ module StoreHelpers
|
|
33
37
|
account = config.accounts.find { |a| a.username == email }
|
34
38
|
raise "Account not found" if !account
|
35
39
|
FileUtils.mkdir_p account.local_path
|
36
|
-
|
37
|
-
|
40
|
+
serializer = Imap::Backup::Serializer.new(account.local_path, folder)
|
41
|
+
serializer.force_uid_validity("42") if !serializer.uid_validity
|
38
42
|
serialized = to_serialized(from: from, subject: subject, body: body)
|
39
|
-
|
43
|
+
serializer.append uid, serialized
|
40
44
|
end
|
41
45
|
|
42
46
|
def to_serialized(from:, subject:, body:)
|
@@ -58,11 +62,16 @@ end
|
|
58
62
|
RSpec.configure do |config|
|
59
63
|
config.include ConfigurationHelpers, type: :aruba
|
60
64
|
config.include StoreHelpers, type: :aruba
|
65
|
+
config.include BackupDirectoryHelpers, type: :aruba
|
61
66
|
|
62
67
|
config.before(:suite) do
|
63
68
|
FileUtils.rm_rf "./tmp/home"
|
64
69
|
end
|
65
70
|
|
71
|
+
config.before(:example, type: :aruba) do
|
72
|
+
set_environment_variable("COVERAGE", "aruba")
|
73
|
+
end
|
74
|
+
|
66
75
|
config.after do
|
67
76
|
FileUtils.rm_rf "./tmp/home"
|
68
77
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,18 +1,13 @@
|
|
1
|
-
require "codeclimate-test-reporter"
|
2
1
|
require "rspec"
|
3
2
|
|
4
|
-
|
3
|
+
$LOAD_PATH << File.expand_path("../lib", __dir__)
|
5
4
|
|
6
|
-
|
7
|
-
$LOAD_PATH << File.expand_path("../lib", spec_path)
|
8
|
-
|
9
|
-
support_glob = File.join(spec_path, "support", "**", "*.rb")
|
5
|
+
support_glob = File.join(__dir__, "support", "**", "*.rb")
|
10
6
|
Dir[support_glob].sort.each { |f| require f }
|
11
7
|
|
12
8
|
require "simplecov"
|
13
|
-
|
14
|
-
|
15
|
-
end
|
9
|
+
|
10
|
+
SimpleCov.command_name "RSpec tests"
|
16
11
|
|
17
12
|
require "imap/backup"
|
18
13
|
require "imap/backup/cli"
|
@@ -24,21 +24,24 @@ describe Imap::Backup::Account::Connection do
|
|
24
24
|
let(:account) do
|
25
25
|
instance_double(
|
26
26
|
Imap::Backup::Account,
|
27
|
-
username:
|
27
|
+
username: username,
|
28
28
|
password: PASSWORD,
|
29
29
|
local_path: LOCAL_PATH,
|
30
30
|
folders: config_folders,
|
31
|
+
multi_fetch_size: multi_fetch_size,
|
31
32
|
server: server,
|
32
33
|
connection_options: nil
|
33
34
|
)
|
34
35
|
end
|
36
|
+
let(:username) { USERNAME }
|
35
37
|
let(:config_folders) { [FOLDER_CONFIG] }
|
38
|
+
let(:multi_fetch_size) { 1 }
|
36
39
|
let(:root_info) do
|
37
40
|
instance_double(Net::IMAP::MailboxList, name: ROOT_NAME)
|
38
41
|
end
|
39
42
|
let(:serializer) do
|
40
43
|
instance_double(
|
41
|
-
Imap::Backup::Serializer
|
44
|
+
Imap::Backup::Serializer,
|
42
45
|
folder: serialized_folder,
|
43
46
|
force_uid_validity: nil,
|
44
47
|
apply_uid_validity: new_uid_validity,
|
@@ -87,6 +90,23 @@ describe Imap::Backup::Account::Connection do
|
|
87
90
|
end
|
88
91
|
end
|
89
92
|
|
93
|
+
context "when the provider is Apple" do
|
94
|
+
let(:username) { "user@mac.com" }
|
95
|
+
let(:apple_client) do
|
96
|
+
instance_double(
|
97
|
+
Imap::Backup::Client::AppleMail, login: nil
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
before do
|
102
|
+
allow(Imap::Backup::Client::AppleMail).to receive(:new) { apple_client }
|
103
|
+
end
|
104
|
+
|
105
|
+
it "returns the Apple client" do
|
106
|
+
expect(result).to eq(apple_client)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
90
110
|
context "when run" do
|
91
111
|
before { subject.client }
|
92
112
|
|
@@ -116,7 +136,7 @@ describe Imap::Backup::Account::Connection do
|
|
116
136
|
|
117
137
|
before do
|
118
138
|
allow(Imap::Backup::Account::Folder).to receive(:new) { folder }
|
119
|
-
allow(Imap::Backup::Serializer
|
139
|
+
allow(Imap::Backup::Serializer).to receive(:new) { serializer }
|
120
140
|
end
|
121
141
|
|
122
142
|
it "creates the path" do
|
@@ -150,16 +170,24 @@ describe Imap::Backup::Account::Connection do
|
|
150
170
|
let(:exists) { true }
|
151
171
|
let(:uid_validity) { 123 }
|
152
172
|
let(:downloader) { instance_double(Imap::Backup::Downloader, run: nil) }
|
173
|
+
let(:multi_fetch_size) { 10 }
|
153
174
|
|
154
175
|
before do
|
155
176
|
allow(Imap::Backup::Downloader).
|
156
177
|
to receive(:new).with(folder, serializer, anything) { downloader }
|
157
178
|
allow(Imap::Backup::Account::Folder).to receive(:new).
|
158
179
|
with(subject, BACKUP_FOLDER) { folder }
|
159
|
-
allow(Imap::Backup::Serializer
|
180
|
+
allow(Imap::Backup::Serializer).to receive(:new).
|
160
181
|
with(LOCAL_PATH, IMAP_FOLDER) { serializer }
|
161
182
|
end
|
162
183
|
|
184
|
+
it "passes the multi_fetch_size" do
|
185
|
+
subject.run_backup
|
186
|
+
|
187
|
+
expect(Imap::Backup::Downloader).to have_received(:new).
|
188
|
+
with(anything, anything, {multi_fetch_size: 10})
|
189
|
+
end
|
190
|
+
|
163
191
|
context "with supplied config_folders" do
|
164
192
|
it "runs the downloader" do
|
165
193
|
expect(downloader).to receive(:run)
|
@@ -184,7 +212,7 @@ describe Imap::Backup::Account::Connection do
|
|
184
212
|
before do
|
185
213
|
allow(Imap::Backup::Account::Folder).to receive(:new).
|
186
214
|
with(subject, ROOT_NAME) { folder }
|
187
|
-
allow(Imap::Backup::Serializer
|
215
|
+
allow(Imap::Backup::Serializer).to receive(:new).
|
188
216
|
with(LOCAL_PATH, ROOT_NAME) { serializer }
|
189
217
|
end
|
190
218
|
|
@@ -273,18 +301,18 @@ describe Imap::Backup::Account::Connection do
|
|
273
301
|
end
|
274
302
|
let(:updated_serializer) do
|
275
303
|
instance_double(
|
276
|
-
Imap::Backup::Serializer
|
304
|
+
Imap::Backup::Serializer, force_uid_validity: nil
|
277
305
|
)
|
278
306
|
end
|
279
307
|
|
280
308
|
before do
|
281
309
|
allow(Imap::Backup::Account::Folder).to receive(:new).
|
282
310
|
with(subject, FOLDER_NAME) { folder }
|
283
|
-
allow(Imap::Backup::Serializer
|
311
|
+
allow(Imap::Backup::Serializer).to receive(:new).
|
284
312
|
with(anything, FOLDER_NAME) { serializer }
|
285
313
|
allow(Imap::Backup::Account::Folder).to receive(:new).
|
286
314
|
with(subject, "new name") { updated_folder }
|
287
|
-
allow(Imap::Backup::Serializer
|
315
|
+
allow(Imap::Backup::Serializer).to receive(:new).
|
288
316
|
with(anything, "new name") { updated_serializer }
|
289
317
|
allow(Imap::Backup::Uploader).to receive(:new).
|
290
318
|
with(folder, serializer) { uploader }
|
@@ -64,26 +64,36 @@ describe Imap::Backup::Account::Folder do
|
|
64
64
|
expect(subject.uids).to eq([])
|
65
65
|
end
|
66
66
|
end
|
67
|
+
|
68
|
+
context "when the UID search fails" do
|
69
|
+
before do
|
70
|
+
allow(client).to receive(:uid_search).and_raise(NoMethodError)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns an empty array" do
|
74
|
+
expect(subject.uids).to eq([])
|
75
|
+
end
|
76
|
+
end
|
67
77
|
end
|
68
78
|
|
69
|
-
describe "#
|
79
|
+
describe "#fetch_multi" do
|
70
80
|
let(:message_body) { instance_double(String, force_encoding: nil) }
|
71
|
-
let(:attributes) { {"BODY[]" => message_body, "other" => "xxx"} }
|
81
|
+
let(:attributes) { {"UID" => "uid", "BODY[]" => message_body, "other" => "xxx"} }
|
72
82
|
let(:fetch_data_item) do
|
73
83
|
instance_double(Net::IMAP::FetchData, attr: attributes)
|
74
84
|
end
|
75
85
|
|
76
86
|
before { allow(client).to receive(:uid_fetch) { [fetch_data_item] } }
|
77
87
|
|
78
|
-
it "returns the message" do
|
79
|
-
expect(subject.
|
88
|
+
it "returns the uid and message" do
|
89
|
+
expect(subject.fetch_multi([123])).to eq([{uid: "uid", body: message_body}])
|
80
90
|
end
|
81
91
|
|
82
92
|
context "when the server responds with nothing" do
|
83
93
|
before { allow(client).to receive(:uid_fetch) { nil } }
|
84
94
|
|
85
95
|
it "is nil" do
|
86
|
-
expect(subject.
|
96
|
+
expect(subject.fetch_multi([123])).to be_nil
|
87
97
|
end
|
88
98
|
end
|
89
99
|
|
@@ -94,15 +104,7 @@ describe Imap::Backup::Account::Folder do
|
|
94
104
|
end
|
95
105
|
|
96
106
|
it "is nil" do
|
97
|
-
expect(subject.
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
context "when the response doesn't include 'BODY[]'" do
|
102
|
-
let(:attributes) { {} }
|
103
|
-
|
104
|
-
it "is nil" do
|
105
|
-
expect(subject.fetch(123)).to be_nil
|
107
|
+
expect(subject.fetch_multi([123])).to be_nil
|
106
108
|
end
|
107
109
|
end
|
108
110
|
|
@@ -113,13 +115,13 @@ describe Imap::Backup::Account::Folder do
|
|
113
115
|
end
|
114
116
|
|
115
117
|
it "retries" do
|
116
|
-
subject.
|
118
|
+
subject.fetch_multi([123])
|
117
119
|
|
118
120
|
expect(client).to have_received(:uid_fetch).twice
|
119
121
|
end
|
120
122
|
|
121
123
|
it "succeeds" do
|
122
|
-
subject.
|
124
|
+
subject.fetch_multi([123])
|
123
125
|
end
|
124
126
|
end
|
125
127
|
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
describe Account do
|
3
|
+
subject { described_class.new(options) }
|
4
|
+
|
5
|
+
let(:options) { {username: "user", password: "pwd"} }
|
6
|
+
|
7
|
+
describe "#changes" do
|
8
|
+
it "lists changes" do
|
9
|
+
subject.username = "new"
|
10
|
+
|
11
|
+
expect(subject.changes).to eq(username: {from: "user", to: "new"})
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when more than one change is made" do
|
15
|
+
it "retains the last one" do
|
16
|
+
subject.username = "first"
|
17
|
+
subject.username = "second"
|
18
|
+
|
19
|
+
expect(subject.changes).to eq(username: {from: "user", to: "second"})
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "when a value is reset to its original value" do
|
24
|
+
it "removes the change" do
|
25
|
+
subject.username = "changed"
|
26
|
+
subject.username = "user"
|
27
|
+
|
28
|
+
expect(subject.changes).to eq({})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "#connection" do
|
34
|
+
it "returns a Connection for the Account" do
|
35
|
+
result = subject.connection
|
36
|
+
|
37
|
+
expect(result).to be_a(Account::Connection)
|
38
|
+
expect(result.account).to be subject
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "#valid?" do
|
43
|
+
context "with username and password" do
|
44
|
+
it "is true" do
|
45
|
+
expect(subject.valid?).to be true
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "without a username" do
|
50
|
+
let(:options) { {password: "pwd"} }
|
51
|
+
|
52
|
+
it "is false" do
|
53
|
+
expect(subject.valid?).to be false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "without a password" do
|
58
|
+
let(:options) { {username: "user"} }
|
59
|
+
|
60
|
+
it "is false" do
|
61
|
+
expect(subject.valid?).to be false
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#modified?" do
|
67
|
+
context "with changes" do
|
68
|
+
it "is true" do
|
69
|
+
subject.username = "new"
|
70
|
+
|
71
|
+
expect(subject.modified?).to be true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "without changes" do
|
76
|
+
it "is false" do
|
77
|
+
expect(subject.modified?).to be false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#clear_changes" do
|
83
|
+
it "clears changes" do
|
84
|
+
subject.username = "new"
|
85
|
+
subject.clear_changes
|
86
|
+
|
87
|
+
expect(subject.changes).to eq({})
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#mark_for_deletion" do
|
92
|
+
it "sets marked_for_deletion" do
|
93
|
+
subject.mark_for_deletion
|
94
|
+
|
95
|
+
expect(subject.marked_for_deletion?).to be true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#marked_for_deletion?" do
|
100
|
+
it "defaults to false" do
|
101
|
+
expect(subject.marked_for_deletion?).to be false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#to_h" do
|
106
|
+
it "returns a Hash representation" do
|
107
|
+
expect(subject.to_h).to eq({username: "user", password: "pwd"})
|
108
|
+
end
|
109
|
+
|
110
|
+
context "when local_path is set" do
|
111
|
+
let(:options) { {username: "user", password: "pwd", local_path: "local_path"} }
|
112
|
+
|
113
|
+
it "includes local_path" do
|
114
|
+
expect(subject.to_h).to include({local_path: "local_path"})
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when folders is set" do
|
119
|
+
let(:options) { {username: "user", password: "pwd", folders: ["folder"]} }
|
120
|
+
|
121
|
+
it "includes folders" do
|
122
|
+
expect(subject.to_h).to include({folders: ["folder"]})
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "when multi_fetch_size is set" do
|
127
|
+
let(:options) { {username: "user", password: "pwd", multi_fetch_size: "3"} }
|
128
|
+
|
129
|
+
it "includes multi_fetch_size" do
|
130
|
+
expect(subject.to_h).to include({multi_fetch_size: 3})
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "when server is set" do
|
135
|
+
let(:options) { {username: "user", password: "pwd", server: "server"} }
|
136
|
+
|
137
|
+
it "includes server" do
|
138
|
+
expect(subject.to_h).to include({server: "server"})
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "when connection_options is set" do
|
143
|
+
let(:options) { {username: "user", password: "pwd", connection_options: "foo"} }
|
144
|
+
|
145
|
+
it "includes connection_options" do
|
146
|
+
expect(subject.to_h).to include({connection_options: "foo"})
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "#multi_fetch_size" do
|
152
|
+
let(:options) { {username: "user", password: "pwd", multi_fetch_size: multi_fetch_size} }
|
153
|
+
let(:multi_fetch_size) { 10 }
|
154
|
+
|
155
|
+
it "returns the initialized value" do
|
156
|
+
expect(subject.multi_fetch_size).to eq(10)
|
157
|
+
end
|
158
|
+
|
159
|
+
context "when the initialized value is a string representation of a positive number" do
|
160
|
+
let(:multi_fetch_size) { "10" }
|
161
|
+
|
162
|
+
it "returns one" do
|
163
|
+
expect(subject.multi_fetch_size).to eq(10)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context "when the initialized value is not a number" do
|
168
|
+
let(:multi_fetch_size) { "ciao" }
|
169
|
+
|
170
|
+
it "returns one" do
|
171
|
+
expect(subject.multi_fetch_size).to eq(1)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "when the initialized value is not a positive number" do
|
176
|
+
let(:multi_fetch_size) { "-99" }
|
177
|
+
|
178
|
+
it "returns one" do
|
179
|
+
expect(subject.multi_fetch_size).to eq(1)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
[
|
185
|
+
[:username, "username", "username"],
|
186
|
+
[:password, "password", "password"],
|
187
|
+
[:local_path, "local_path", "local_path"],
|
188
|
+
[:multi_fetch_size, "2", 2],
|
189
|
+
[:server, "server", "server"],
|
190
|
+
[:folders, ["folder"], ["folder"]],
|
191
|
+
[:connection_options, '{"some": "option"}', {"some" => "option"}]
|
192
|
+
].each do |attribute, value, expected|
|
193
|
+
describe "##{attribute}=" do
|
194
|
+
let(:options) { {} }
|
195
|
+
|
196
|
+
before { subject.send(:"#{attribute}=", value) }
|
197
|
+
|
198
|
+
it "sets the #{attribute}" do
|
199
|
+
expect(subject.send(attribute)).to eq(expected)
|
200
|
+
end
|
201
|
+
|
202
|
+
it "adds a change" do
|
203
|
+
expect(subject.changes).to eq(attribute => {from: nil, to: expected})
|
204
|
+
end
|
205
|
+
|
206
|
+
if attribute == :folders
|
207
|
+
context "when the supplied value is not an Array" do
|
208
|
+
it "fails" do
|
209
|
+
expect do
|
210
|
+
subject.folders = "aaa"
|
211
|
+
end.to raise_error(RuntimeError, /must be an Array/)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if attribute == :multi_fetch_size
|
217
|
+
context "when the supplied value is not a number" do
|
218
|
+
before { subject.multi_fetch_size = "ciao" }
|
219
|
+
|
220
|
+
it "sets multi_fetch_size to one" do
|
221
|
+
expect(subject.multi_fetch_size).to eq(1)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
context "when the supplied value is not a positive number" do
|
226
|
+
before { subject.multi_fetch_size = "-1" }
|
227
|
+
|
228
|
+
it "sets multi_fetch_size to one" do
|
229
|
+
expect(subject.multi_fetch_size).to eq(1)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
if attribute == :connection_options
|
235
|
+
context "when the supplied value is not valid JSON" do
|
236
|
+
it "fails" do
|
237
|
+
expect do
|
238
|
+
subject.connection_options = "NOT JSON"
|
239
|
+
end.to raise_error(JSON::ParserError)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|