imap-backup 5.0.0 → 6.0.0.rc2
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/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
|