Skip to content

Commit f82fbae

Browse files
committed
Merge branch 'fix/leagcy-mailbox-migration'
2 parents 724f19b + bf7ee01 commit f82fbae

File tree

5 files changed

+106
-7
lines changed

5 files changed

+106
-7
lines changed

lib/imap/backup/serializer.rb

+42-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,41 @@ def self.folder_path_for(path:, folder:)
2828
extend Forwardable
2929

3030
def_delegator :mbox, :pathname, :mbox_pathname
31-
def_delegators :imap, :get, :messages, :uid_validity, :uids, :update_uid
31+
32+
# Get message metadata
33+
# @param uid [Integer] a message UID
34+
# @return [Serializer::Message]
35+
def get(uid)
36+
validate!
37+
imap.get(uid)
38+
end
39+
40+
# @return [Array<Hash>]
41+
def messages
42+
validate!
43+
imap.messages
44+
end
45+
46+
# @return [Integer] the UID validity for the folder
47+
def uid_validity
48+
validate!
49+
imap.uid_validity
50+
end
51+
52+
# @return [Array<Integer>] The uids of all messages
53+
def uids
54+
validate!
55+
imap.uids
56+
end
57+
58+
# Update a message's metadata, replacing its UID
59+
# @param old [Integer] the existing message UID
60+
# @param new [Integer] the new UID to apply to the message
61+
# @return [void]
62+
def update_uid(old, new)
63+
validate!
64+
imap.update_uid(old, new)
65+
end
3266

3367
# @return [String] a folder name
3468
attr_reader :folder
@@ -62,10 +96,14 @@ def validate!
6296

6397
optionally_migrate2to3
6498

65-
if imap.valid? && mbox.valid?
99+
imap_valid = imap.valid?
100+
mbox_valid = mbox.valid?
101+
if imap_valid && mbox_valid
66102
@validated = true
67103
return true
68104
end
105+
Logger.logger.info("Metadata file '#{imap.pathname}' is invalid") if !imap_valid
106+
Logger.logger.info("Mailbox '#{mbox.pathname}' is invalid") if !mbox_valid
69107

70108
delete
71109

@@ -247,6 +285,8 @@ def optionally_migrate2to3
247285
MESSAGE
248286

249287
migrator.run
288+
# Ensure new metadata gets loaded
289+
@imap = nil
250290
end
251291

252292
def ensure_containing_directory

lib/imap/backup/serializer/imap.rb

+1
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ def get(uid)
108108
def delete
109109
return if !exist?
110110

111+
Logger.logger.info("Deleting metadata file '#{pathname}'")
111112
FileUtils.rm(pathname)
112113
@loaded = false
113114
@messages = nil

lib/imap/backup/serializer/mbox.rb

+1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ def read(offset, length)
7070
def delete
7171
return if !exist?
7272

73+
Logger.logger.info("Deleting mailbox '#{pathname}'")
7374
FileUtils.rm(pathname)
7475
end
7576

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require "features/helper"
2+
3+
RSpec.describe "imap-backup migrate: avoid regression in migrating legacy backups",
4+
:container, type: :aruba do
5+
def overwrite_metadata_with_old_version(email, folder)
6+
content = imap_parsed(email, folder)
7+
uids = content[:messages].map { |m| m[:uid] }
8+
uid_validity = content[:uid_validity]
9+
old_metadata = {version: 2, uids: uids, uid_validity: uid_validity}
10+
path = imap_path(email, folder)
11+
File.open(path, "w") { |f| f.write(JSON.pretty_generate(old_metadata)) }
12+
end
13+
14+
let(:email) { "me@example.com" }
15+
let(:folder) { "migrate-folder" }
16+
let(:source_account) do
17+
{
18+
username: email,
19+
local_path: File.join(config_path, email.gsub("@", "_"))
20+
}
21+
end
22+
let(:destination_account) { test_server_connection_parameters }
23+
let(:destination_server) { test_server }
24+
let(:config_options) { {accounts: [source_account, destination_account]} }
25+
26+
let!(:setup) do
27+
test_server.warn_about_non_default_folders
28+
create_config(**config_options)
29+
append_local(
30+
email: email, folder: folder, subject: "Ciao", flags: [:Draft, :$CUSTOM]
31+
)
32+
overwrite_metadata_with_old_version(email, folder)
33+
end
34+
35+
after do
36+
destination_server.delete_folder folder
37+
destination_server.disconnect
38+
end
39+
40+
it "copies emails to the destination account" do
41+
run_command_and_stop "imap-backup migrate #{email} #{destination_account[:username]}"
42+
43+
messages = test_server.folder_messages(folder)
44+
expected = <<~MESSAGE.gsub("\n", "\r\n")
45+
From: sender@example.com
46+
Subject: Ciao
47+
48+
body
49+
50+
MESSAGE
51+
expect(messages[0]["BODY[]"]).to eq(expected)
52+
end
53+
end

spec/unit/serializer_spec.rb

+9-5
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@
2121
let(:mbox_valid) { true }
2222

2323
before do
24+
allow(imap).to receive(:pathname) { "imap pathname" }
2425
allow(imap).to receive(:valid?) { imap_valid }
2526
allow(imap).to receive(:delete)
27+
allow(mbox).to receive(:pathname) { "mbox pathname" }
2628
allow(mbox).to receive(:valid?) { mbox_valid }
2729
allow(mbox).to receive(:delete)
2830

@@ -33,23 +35,23 @@
3335
let(:imap_valid) { false }
3436

3537
it "deletes the imap file" do
36-
expect(imap).to have_received(:delete)
38+
expect(imap).to have_received(:delete).at_least(:once)
3739
end
3840

3941
it "deletes the mbox file" do
40-
expect(mbox).to have_received(:delete)
42+
expect(mbox).to have_received(:delete).at_least(:once)
4143
end
4244
end
4345

4446
context "when the mbox file is not valid" do
4547
let(:mbox_valid) { false }
4648

4749
it "deletes the imap file" do
48-
expect(imap).to have_received(:delete)
50+
expect(imap).to have_received(:delete).at_least(:once)
4951
end
5052

5153
it "deletes the mbox file" do
52-
expect(mbox).to have_received(:delete)
54+
expect(mbox).to have_received(:delete).at_least(:once)
5355
end
5456
end
5557
end
@@ -307,6 +309,7 @@ module Imap::Backup
307309
Serializer::Imap, "Old Imap",
308310
uid_validity: 1,
309311
uids: [1],
312+
valid?: true,
310313
get: message,
311314
delete: nil,
312315
folder_path: "existing/imap"
@@ -316,7 +319,8 @@ module Imap::Backup
316319
instance_double(
317320
Serializer::Mbox, "Old Mbox",
318321
delete: nil,
319-
folder_path: "existing/mbox"
322+
folder_path: "existing/mbox",
323+
valid?: true
320324
)
321325
end
322326
let(:imap) do

0 commit comments

Comments
 (0)