1package store
2
3import (
4 "archive/tar"
5 "archive/zip"
6 "bytes"
7 "io"
8 "io/fs"
9 "os"
10 "path/filepath"
11 "testing"
12 "time"
13
14 "github.com/mjl-/mox/mlog"
15 "github.com/mjl-/mox/mox-"
16)
17
18func TestExport(t *testing.T) {
19 // Set up an account, add 2 messages to different 2 mailboxes. export as tar/zip
20 // and maildir/mbox. check there are 2 files in the repo, no errors.txt.
21
22 log := mlog.New("export", nil)
23
24 os.RemoveAll("../testdata/store/data")
25 mox.ConfigStaticPath = filepath.FromSlash("../testdata/store/mox.conf")
26 mox.MustLoadConfig(true, false)
27 defer Switchboard()()
28 acc, err := OpenAccount(pkglog, "mjl", false)
29 tcheck(t, err, "open account")
30 defer func() {
31 err := acc.Close()
32 log.Check(err, "closing account")
33 acc.WaitClosed()
34 }()
35
36 msgFile, err := CreateMessageTemp(pkglog, "mox-test-export")
37 tcheck(t, err, "create temp")
38 defer os.Remove(msgFile.Name()) // To be sure.
39 defer msgFile.Close()
40 const msg = "test: test\r\n\r\ntest\r\n"
41 _, err = msgFile.Write([]byte(msg))
42 tcheck(t, err, "write message")
43
44 var m Message
45 acc.WithWLock(func() {
46 m = Message{Received: time.Now(), Size: int64(len(msg))}
47 err = acc.DeliverMailbox(pkglog, "Inbox", &m, msgFile)
48 tcheck(t, err, "deliver")
49
50 m = Message{Received: time.Now(), Size: int64(len(msg))}
51 err = acc.DeliverMailbox(pkglog, "Trash", &m, msgFile)
52 tcheck(t, err, "deliver")
53 })
54
55 var maildirZip, maildirTar, mboxZip, mboxTar bytes.Buffer
56
57 archive := func(archiver Archiver, mailbox string, messageIDs []int64, maildir bool) {
58 t.Helper()
59 err = ExportMessages(ctxbg, log, acc.DB, acc.Dir, archiver, maildir, mailbox, messageIDs, true)
60 tcheck(t, err, "export messages")
61 err = archiver.Close()
62 tcheck(t, err, "archiver close")
63 }
64
65 os.RemoveAll("../testdata/exportmaildir")
66 os.RemoveAll("../testdata/exportmbox")
67
68 archive(ZipArchiver{zip.NewWriter(&maildirZip)}, "", nil, true)
69 archive(ZipArchiver{zip.NewWriter(&mboxZip)}, "", nil, false)
70 archive(TarArchiver{tar.NewWriter(&maildirTar)}, "", nil, true)
71 archive(TarArchiver{tar.NewWriter(&mboxTar)}, "", nil, false)
72 archive(TarArchiver{tar.NewWriter(&mboxTar)}, "Inbox", nil, false)
73 archive(TarArchiver{tar.NewWriter(&mboxTar)}, "", []int64{m.ID}, false)
74 archive(DirArchiver{filepath.FromSlash("../testdata/exportmaildir")}, "", nil, true)
75 archive(DirArchiver{filepath.FromSlash("../testdata/exportmbox")}, "", nil, false)
76
77 const defaultMailboxes = 6 // Inbox, Drafts, etc
78 if r, err := zip.NewReader(bytes.NewReader(maildirZip.Bytes()), int64(maildirZip.Len())); err != nil {
79 t.Fatalf("reading maildir zip: %v", err)
80 } else if len(r.File) != defaultMailboxes*3+2 {
81 t.Fatalf("maildir zip, expected %d*3 dirs, and 2 files, got %d files", defaultMailboxes, len(r.File))
82 }
83
84 if r, err := zip.NewReader(bytes.NewReader(mboxZip.Bytes()), int64(mboxZip.Len())); err != nil {
85 t.Fatalf("reading mbox zip: %v", err)
86 } else if len(r.File) != defaultMailboxes {
87 t.Fatalf("maildir zip, expected %d files, got %d files", defaultMailboxes, len(r.File))
88 }
89
90 checkTarFiles := func(r io.Reader, n int) {
91 t.Helper()
92 tr := tar.NewReader(r)
93 have := 0
94 for {
95 h, err := tr.Next()
96 if err == io.EOF {
97 break
98 }
99 have++
100 if h.Name == "errors.txt" {
101 t.Fatalf("got errors.txt")
102 }
103 _, err = io.Copy(io.Discard, tr)
104 tcheck(t, err, "copy")
105 }
106 if have != n {
107 t.Fatalf("got %d files, expected %d", have, n)
108 }
109 }
110
111 checkTarFiles(&maildirTar, defaultMailboxes*3+2)
112 checkTarFiles(&mboxTar, defaultMailboxes)
113
114 checkDirFiles := func(dir string, n int) {
115 t.Helper()
116 have := 0
117 err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
118 if err == nil && !d.IsDir() {
119 have++
120 }
121 return nil
122 })
123 tcheck(t, err, "walkdir")
124 if n != have {
125 t.Fatalf("got %d files, expected %d", have, n)
126 }
127 }
128
129 checkDirFiles(filepath.FromSlash("../testdata/exportmaildir"), 2)
130 checkDirFiles(filepath.FromSlash("../testdata/exportmbox"), defaultMailboxes)
131}
132