143 lines
4.9 KiB
Go
143 lines
4.9 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestGenerateAndLoadRoundTrip(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "settings.ini")
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load (generate): %v", err)
|
|
}
|
|
if _, err := os.Stat(path); err != nil {
|
|
t.Fatalf("settings.ini was not written: %v", err)
|
|
}
|
|
if got := cfg.Section("Server").Key("SMTP_PORT").String(); got != "25" {
|
|
t.Errorf("SMTP_PORT = %q, want 25", got)
|
|
}
|
|
if got := cfg.Section("Attachments").Key("attachments_path").String(); got == "" {
|
|
t.Error("Attachments.attachments_path default is missing (the approved bug fix)")
|
|
}
|
|
|
|
// Load again against the now-existing file — must not regenerate/overwrite.
|
|
cfg2, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load (existing): %v", err)
|
|
}
|
|
if got := cfg2.Section("Server").Key("SMTP_PORT").String(); got != "25" {
|
|
t.Errorf("second Load: SMTP_PORT = %q, want 25", got)
|
|
}
|
|
}
|
|
|
|
func TestLoadBackfillsMissingKeysWithoutTouchingExistingValues(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "settings.ini")
|
|
|
|
// Simulate an older settings.ini written before [LetsEncryptHTTP] and [TLS]'s
|
|
// *_cert keys existed, with a deliberately non-default value on a key that IS
|
|
// already present — Load must never touch that.
|
|
old := "[Server]\nHOSTNAME = old.example.com\n\n[TLS]\ntls_cert_file = custom/path.crt\n"
|
|
if err := os.WriteFile(path, []byte(old), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if got := cfg.Section("Server").Key("HOSTNAME").String(); got != "old.example.com" {
|
|
t.Errorf("existing value clobbered: HOSTNAME = %q", got)
|
|
}
|
|
if got := cfg.Section("TLS").Key("tls_cert_file").String(); got != "custom/path.crt" {
|
|
t.Errorf("existing value clobbered: tls_cert_file = %q", got)
|
|
}
|
|
if got := cfg.Section("TLS").Key("smtp_tls_cert").String(); got != "custom" {
|
|
t.Errorf("smtp_tls_cert not backfilled: got %q, want default %q", got, "custom")
|
|
}
|
|
if !cfg.Section("LetsEncryptHTTP").HasKey("enabled") {
|
|
t.Error("[LetsEncryptHTTP] section was not backfilled")
|
|
}
|
|
|
|
// The backfill must be persisted to disk, not just held in memory.
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(raw), "smtp_tls_cert") {
|
|
t.Error("backfilled key was not saved back to settings.ini")
|
|
}
|
|
}
|
|
|
|
func TestLoadMigratesLegacyIMAPAndJMAPSectionsIntoServer(t *testing.T) {
|
|
dir := t.TempDir()
|
|
path := filepath.Join(dir, "settings.ini")
|
|
|
|
// Simulate a settings.ini from before IMAP_PORT/JMAP_* moved into [Server], with a
|
|
// non-default port the admin actually configured - Load must preserve that value,
|
|
// not silently reset it to default just because it's now read from a new section.
|
|
old := "[Server]\nHOSTNAME = old.example.com\n\n[IMAP]\nIMAP_PORT = 1143\nIMAP_TLS_PORT = 1993\n\n[JMAP]\nJMAP_ENABLE = false\nJMAP_PORT = 9443\n"
|
|
if err := os.WriteFile(path, []byte(old), 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
cfg, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("Load: %v", err)
|
|
}
|
|
if got := cfg.Section("Server").Key("IMAP_PORT").String(); got != "1143" {
|
|
t.Errorf("IMAP_PORT not migrated with its configured value: got %q, want 1143", got)
|
|
}
|
|
if got := cfg.Section("Server").Key("IMAP_TLS_PORT").String(); got != "1993" {
|
|
t.Errorf("IMAP_TLS_PORT not migrated with its configured value: got %q, want 1993", got)
|
|
}
|
|
if got := cfg.Section("Server").Key("JMAP_ENABLE").String(); got != "false" {
|
|
t.Errorf("JMAP_ENABLE not migrated with its configured value: got %q, want false", got)
|
|
}
|
|
if got := cfg.Section("Server").Key("JMAP_PORT").String(); got != "9443" {
|
|
t.Errorf("JMAP_PORT not migrated with its configured value: got %q, want 9443", got)
|
|
}
|
|
if cfg.HasSection("IMAP") || cfg.HasSection("JMAP") {
|
|
t.Error("legacy [IMAP]/[JMAP] sections were not removed")
|
|
}
|
|
|
|
// Migration must be persisted to disk, not just held in memory.
|
|
raw, err := os.ReadFile(path)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(raw), "[IMAP]") || strings.Contains(string(raw), "[JMAP]") {
|
|
t.Error("legacy sections still present in saved settings.ini")
|
|
}
|
|
if !strings.Contains(string(raw), "IMAP_PORT") {
|
|
t.Error("migrated key not saved back to settings.ini")
|
|
}
|
|
|
|
// Loading a second time (now already migrated) must be a stable no-op.
|
|
cfg2, err := Load(path)
|
|
if err != nil {
|
|
t.Fatalf("second Load: %v", err)
|
|
}
|
|
if got := cfg2.Section("Server").Key("IMAP_PORT").String(); got != "1143" {
|
|
t.Errorf("second Load: IMAP_PORT = %q, want 1143", got)
|
|
}
|
|
}
|
|
|
|
func TestAbsoluteSQLitePath(t *testing.T) {
|
|
cases := []struct{ url, root, want string }{
|
|
{"sqlite:///server_data/db.sqlite", "/app", "/app/server_data/db.sqlite"},
|
|
{"sqlite:////abs/db.sqlite", "/app", "/abs/db.sqlite"},
|
|
{"mysql://x", "/app", "mysql://x"},
|
|
}
|
|
for _, c := range cases {
|
|
if got := AbsoluteSQLitePath(c.url, c.root); got != c.want {
|
|
t.Errorf("AbsoluteSQLitePath(%q, %q) = %q, want %q", c.url, c.root, got, c.want)
|
|
}
|
|
}
|
|
}
|