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