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 acc, err := OpenAccount(pkglog, "mjl")
28 tcheck(t, err, "open account")
29 defer func() {
30 err := acc.Close()
31 log.Check(err, "closing account")
32 acc.CheckClosed()
33 }()
34 defer Switchboard()()
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 m := Message{Received: time.Now(), Size: int64(len(msg))}
45 err = acc.DeliverMailbox(pkglog, "Inbox", &m, msgFile)
46 tcheck(t, err, "deliver")
47
48 m = Message{Received: time.Now(), Size: int64(len(msg))}
49 err = acc.DeliverMailbox(pkglog, "Trash", &m, msgFile)
50 tcheck(t, err, "deliver")
51
52 var maildirZip, maildirTar, mboxZip, mboxTar bytes.Buffer
53
54 archive := func(archiver Archiver, maildir bool) {
55 t.Helper()
56 err = ExportMessages(ctxbg, log, acc.DB, acc.Dir, archiver, maildir, "", true)
57 tcheck(t, err, "export messages")
58 err = archiver.Close()
59 tcheck(t, err, "archiver close")
60 }
61
62 os.RemoveAll("../testdata/exportmaildir")
63 os.RemoveAll("../testdata/exportmbox")
64
65 archive(ZipArchiver{zip.NewWriter(&maildirZip)}, true)
66 archive(ZipArchiver{zip.NewWriter(&mboxZip)}, false)
67 archive(TarArchiver{tar.NewWriter(&maildirTar)}, true)
68 archive(TarArchiver{tar.NewWriter(&mboxTar)}, false)
69 archive(DirArchiver{filepath.FromSlash("../testdata/exportmaildir")}, true)
70 archive(DirArchiver{filepath.FromSlash("../testdata/exportmbox")}, false)
71
72 const defaultMailboxes = 6 // Inbox, Drafts, etc
73 if r, err := zip.NewReader(bytes.NewReader(maildirZip.Bytes()), int64(maildirZip.Len())); err != nil {
74 t.Fatalf("reading maildir zip: %v", err)
75 } else if len(r.File) != defaultMailboxes*3+2 {
76 t.Fatalf("maildir zip, expected %d*3 dirs, and 2 files, got %d files", defaultMailboxes, len(r.File))
77 }
78
79 if r, err := zip.NewReader(bytes.NewReader(mboxZip.Bytes()), int64(mboxZip.Len())); err != nil {
80 t.Fatalf("reading mbox zip: %v", err)
81 } else if len(r.File) != defaultMailboxes {
82 t.Fatalf("maildir zip, expected %d files, got %d files", defaultMailboxes, len(r.File))
83 }
84
85 checkTarFiles := func(r io.Reader, n int) {
86 t.Helper()
87 tr := tar.NewReader(r)
88 have := 0
89 for {
90 h, err := tr.Next()
91 if err == io.EOF {
92 break
93 }
94 have++
95 if h.Name == "errors.txt" {
96 t.Fatalf("got errors.txt")
97 }
98 _, err = io.Copy(io.Discard, tr)
99 tcheck(t, err, "copy")
100 }
101 if have != n {
102 t.Fatalf("got %d files, expected %d", have, n)
103 }
104 }
105
106 checkTarFiles(&maildirTar, defaultMailboxes*3+2)
107 checkTarFiles(&mboxTar, defaultMailboxes)
108
109 checkDirFiles := func(dir string, n int) {
110 t.Helper()
111 have := 0
112 err := filepath.WalkDir(dir, func(path string, d fs.DirEntry, err error) error {
113 if err == nil && !d.IsDir() {
114 have++
115 }
116 return nil
117 })
118 tcheck(t, err, "walkdir")
119 if n != have {
120 t.Fatalf("got %d files, expected %d", have, n)
121 }
122 }
123
124 checkDirFiles(filepath.FromSlash("../testdata/exportmaildir"), 2)
125 checkDirFiles(filepath.FromSlash("../testdata/exportmbox"), defaultMailboxes)
126}
127