archivmail-export nutzte noch die alte storage.New(path string)-Signatur statt storage.Config (fehlender Keyfile/Compress hätte Rohbytes statt Klartext-EML exportiert). Toten Self-Assignment-Code in storage.go entfernt. Testdateien (storage, audit, api, userstore, auth) an aktuelle Signaturen angeglichen; auth-Tests liefen bisher gegen einen SQLite-Pfad statt Postgres- DSN und wurden auf das TEST_DATABASE_URL-Schema-Isolationsmuster der übrigen Pakete umgestellt. api_test.go las den Login-Token noch aus dem JSON-Body statt aus dem httpOnly-Cookie (Auth-Contract-Drift). TestParseMissingDate an tatsächliches Verhalten angepasst: der Parser lässt das Datum bewusst als Zero-Value, der time.Now()-Fallback sitzt in der Storage-Schicht — damit bleibt nachvollziehbar ob ein Datum aus der Mail stammt oder vom Archiv gesetzt wurde (GoBD). Verifiziert auf 192.168.1.132: go build/vet/test ./... komplett grün, kein Skip (Postgres + Manticore erreichbar). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019j28kGcaJAhBnrYX34hGdt
128 lines
2.9 KiB
Go
128 lines
2.9 KiB
Go
package storage_test
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"archivmail/internal/storage"
|
|
)
|
|
|
|
func TestSaveAndLoad(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store, err := storage.New(storage.Config{Dir: dir})
|
|
if err != nil {
|
|
t.Fatalf("New: %v", err)
|
|
}
|
|
|
|
raw := []byte("From: alice@example.com\r\nSubject: Test\r\n\r\nHello World")
|
|
id, err := store.Save(context.Background(), raw, time.Now(), nil)
|
|
if err != nil {
|
|
t.Fatalf("Save: %v", err)
|
|
}
|
|
if len(id) != 64 {
|
|
t.Errorf("expected 64-char SHA256 hex, got %d chars", len(id))
|
|
}
|
|
|
|
got, err := store.Load(id)
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if !bytes.Equal(raw, got) {
|
|
t.Errorf("loaded content mismatch")
|
|
}
|
|
}
|
|
|
|
func TestDeduplication(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store, err := storage.New(storage.Config{Dir: dir})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
raw := []byte("From: alice@example.com\r\n\r\nDuplicate test")
|
|
id1, err := store.Save(context.Background(), raw, time.Now(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
id2, err := store.Save(context.Background(), raw, time.Now(), nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if id1 != id2 {
|
|
t.Errorf("duplicate mail produced different IDs: %s vs %s", id1, id2)
|
|
}
|
|
|
|
// Only one file should exist
|
|
count := 0
|
|
filepath.Walk(filepath.Join(dir, "store"), func(p string, info os.FileInfo, _ error) error {
|
|
if !info.IsDir() { count++ }
|
|
return nil
|
|
})
|
|
if count != 1 {
|
|
t.Errorf("expected 1 stored file after dedup, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestDelete(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store, err := storage.New(storage.Config{Dir: dir})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
raw := []byte("From: alice@example.com\r\n\r\nDelete me")
|
|
id, _ := store.Save(context.Background(), raw, time.Now(), nil)
|
|
|
|
if err := store.Delete(id); err != nil {
|
|
t.Fatalf("Delete: %v", err)
|
|
}
|
|
if _, err := store.Load(id); err == nil {
|
|
t.Error("Load after Delete should return error")
|
|
}
|
|
}
|
|
|
|
func TestStats(t *testing.T) {
|
|
dir := t.TempDir()
|
|
store, err := storage.New(storage.Config{Dir: dir})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
mails := [][]byte{
|
|
[]byte("From: a@x.com\r\n\r\nMail 1"),
|
|
[]byte("From: b@x.com\r\n\r\nMail 2"),
|
|
[]byte("From: c@x.com\r\n\r\nMail 3"),
|
|
}
|
|
for _, m := range mails {
|
|
store.Save(context.Background(), m, time.Now(), nil)
|
|
}
|
|
|
|
stats, err := store.Stats()
|
|
if err != nil {
|
|
t.Fatalf("Stats: %v", err)
|
|
}
|
|
if stats.TotalMails != 3 {
|
|
t.Errorf("expected 3 mails, got %d", stats.TotalMails)
|
|
}
|
|
if stats.TotalBytes <= 0 {
|
|
t.Error("expected positive TotalBytes")
|
|
}
|
|
}
|
|
|
|
func TestStorageDirectoryCreation(t *testing.T) {
|
|
dir := filepath.Join(t.TempDir(), "nested", "path")
|
|
_, err := storage.New(storage.Config{Dir: dir})
|
|
if err != nil {
|
|
t.Fatalf("New with nested path: %v", err)
|
|
}
|
|
for _, sub := range []string{"store", "attachments", "meta"} {
|
|
if _, err := os.Stat(filepath.Join(dir, sub)); os.IsNotExist(err) {
|
|
t.Errorf("expected subdirectory %s to be created", sub)
|
|
}
|
|
}
|
|
}
|