Add optional zstd mbox archives
This commit is contained in:
parent
21844688ac
commit
e612896e06
14 changed files with 620 additions and 39 deletions
120
backend/06-mbox_test.go
Normal file
120
backend/06-mbox_test.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
package backend
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
)
|
||||
|
||||
func TestZstdMboxRoundTripAndIndexRead(t *testing.T) {
|
||||
oldCfg, oldDB := Cfg, DB
|
||||
root := t.TempDir()
|
||||
t.Cleanup(func() {
|
||||
if DB != nil {
|
||||
_ = DB.Close()
|
||||
}
|
||||
Cfg, DB = oldCfg, oldDB
|
||||
})
|
||||
|
||||
Cfg = Config{
|
||||
DBPath: filepath.Join(root, "mail-graveyard.db"),
|
||||
MboxRoot: filepath.Join(root, "backup"),
|
||||
MboxCompression: "zstd",
|
||||
}
|
||||
if err := ConnectDB(true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := SaveArchiveMailbox("archive"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := SaveAccount(Account{
|
||||
Name: "test-account",
|
||||
SrcHost: "source.example",
|
||||
SrcPort: 993,
|
||||
SrcSecurity: "tls",
|
||||
SrcUser: "source@example.com",
|
||||
SrcPass: "x",
|
||||
DstHost: "target.example",
|
||||
DstPort: 993,
|
||||
DstSecurity: "tls",
|
||||
DstUser: "target@example.com",
|
||||
DstPass: "x",
|
||||
MboxDir: "archive",
|
||||
Active: true,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
account, err := GetAccount("test-account")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
msg := RawMessage{
|
||||
MessageID: "zstd-test@example.com",
|
||||
InternalDate: time.Date(2026, 7, 14, 10, 11, 12, 0, time.UTC),
|
||||
Body: []byte("Message-ID: <zstd-test@example.com>\r\n" +
|
||||
"From: Sender <sender@example.com>\r\n" +
|
||||
"Subject: Zstd Test\r\n" +
|
||||
"Date: Tue, 14 Jul 2026 10:11:12 +0000\r\n" +
|
||||
"\r\n" +
|
||||
"Hello\r\nFrom inside body\r\n"),
|
||||
}
|
||||
writer, err := NewMboxWriter(filepath.Join(Cfg.MboxRoot, "archive"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
info, err := writer.Append("INBOX", msg)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := SaveMboxIndex(MboxIndexEntry{
|
||||
AccountID: account.ID,
|
||||
Folder: "INBOX",
|
||||
MessageID: msg.MessageID,
|
||||
Subject: info.Subject,
|
||||
From: info.From,
|
||||
Date: info.Date,
|
||||
FileOffset: info.FileOffset,
|
||||
FrameLen: info.FrameLen,
|
||||
InnerOffset: info.InnerOffset,
|
||||
InnerLen: info.InnerLen,
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
compressed, err := os.ReadFile(info.Path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dec, err := zstd.NewReader(nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer dec.Close()
|
||||
plain, err := dec.DecodeAll(compressed, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want := mboxRecord(msg); !bytes.Equal(plain, want) {
|
||||
t.Fatalf("zstd round-trip differs: got %d bytes, want %d", len(plain), len(want))
|
||||
}
|
||||
|
||||
entries, err := ReadMboxList(info.Path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(entries) != 1 || entries[0].Index != 0 || entries[0].Subject != "Zstd Test" {
|
||||
t.Fatalf("unexpected index list: %#v", entries)
|
||||
}
|
||||
raw, err := ReadMboxMessage(info.Path, entries[0].Index)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Contains(raw, []byte("Hello")) || bytes.Contains(raw, []byte(">From inside body")) {
|
||||
t.Fatalf("unexpected message body: %q", raw)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue