Files
selfpost/internal/domain/opendkim_test.go
T
mix 8ce050ef7f release: 0.1.0: Phases 0-11: panel, Postfix/DKIM, milter, backup, Docker image
Initial feature-complete implementation of the v1.0 specification (phases 0-11 of `docs/implementation-plan.md`).
Added:
  - Panel (Go, single static binary) with SQLite persistence, one-time
  - Domain management with per-domain DKIM (RSA-2048, generated in pure Go) and
  - Application (sender identity) management: SASL credentials via `sasldb2`,
  - Full Postfix relay config generated from env at container start: SMTPS 465,
  - Journal milter (pure Go, `go-milter`) recording every send to `send_log`;
  … (6 more in CHANGELOG)

Full detail: CHANGELOG.md (0.1.0).
2026-09-22 15:24:51 +03:00

157 lines
4.3 KiB
Go

package domain
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestRenderTables(t *testing.T) {
keysDir := "/data/opendkim/keys"
// Deliberately out of order to exercise the deterministic sort.
domains := []SigningDomain{
{Name: "zeta.example", Selector: "selfpost"},
{Name: "alpha.example", Selector: "sel2"},
}
kt, st, err := renderTables(keysDir, domains)
if err != nil {
t.Fatalf("renderTables: %v", err)
}
wantKT := "alpha.example alpha.example:sel2:/data/opendkim/keys/alpha.example/sel2.private\n" +
"zeta.example zeta.example:selfpost:/data/opendkim/keys/zeta.example/selfpost.private\n"
if string(kt) != wantKT {
t.Errorf("KeyTable =\n%q\nwant\n%q", kt, wantKT)
}
wantST := "*@alpha.example alpha.example\n*@zeta.example zeta.example\n"
if string(st) != wantST {
t.Errorf("SigningTable =\n%q\nwant\n%q", st, wantST)
}
}
func TestRenderTablesEmpty(t *testing.T) {
kt, st, err := renderTables("/keys", nil)
if err != nil {
t.Fatalf("renderTables(nil): %v", err)
}
if len(kt) != 0 || len(st) != 0 {
t.Errorf("expected empty tables, got kt=%q st=%q", kt, st)
}
}
func TestAssertConfigSafeRejectsInjection(t *testing.T) {
bad := []struct{ name, sel string }{
{"exa mple.com", "selfpost"},
{"example.com\nInject yes", "selfpost"},
{"example.com", "sel:evil"},
{"../etc", "selfpost"},
{"", "selfpost"},
{"example.com", ""},
}
for _, b := range bad {
if err := assertConfigSafe(b.name, b.sel); err == nil {
t.Errorf("assertConfigSafe(%q,%q) = nil, want error", b.name, b.sel)
}
}
if err := assertConfigSafe("example.com", "selfpost"); err != nil {
t.Errorf("assertConfigSafe of a clean pair errored: %v", err)
}
}
// newTestOpenDKIM builds a manager rooted at a temp dir with reload stubbed out.
func newTestOpenDKIM(t *testing.T) (*OpenDKIM, *int) {
t.Helper()
dir := t.TempDir()
if err := os.MkdirAll(filepath.Join(dir, "keys"), 0o750); err != nil {
t.Fatal(err)
}
reloads := 0
o := NewOpenDKIM(dir)
o.reload = func() error { reloads++; return nil }
return o, &reloads
}
func TestEnsureKeyReusesExisting(t *testing.T) {
o, _ := newTestOpenDKIM(t)
created, err := o.EnsureKey("example.com", "selfpost")
if err != nil {
t.Fatalf("EnsureKey: %v", err)
}
if !created {
t.Fatal("expected a new key to be created")
}
first, err := os.ReadFile(o.keyPath("example.com", "selfpost"))
if err != nil {
t.Fatal(err)
}
created, err = o.EnsureKey("example.com", "selfpost")
if err != nil {
t.Fatalf("EnsureKey (second): %v", err)
}
if created {
t.Error("expected existing key to be reused, not regenerated")
}
second, _ := os.ReadFile(o.keyPath("example.com", "selfpost"))
if string(first) != string(second) {
t.Error("key file changed on reuse — published DNS record would break")
}
}
func TestRebuildWritesTablesAndReloads(t *testing.T) {
o, reloads := newTestOpenDKIM(t)
if _, err := o.EnsureKey("example.com", "selfpost"); err != nil {
t.Fatal(err)
}
if err := o.Rebuild([]SigningDomain{{Name: "example.com", Selector: "selfpost"}}); err != nil {
t.Fatalf("Rebuild: %v", err)
}
if *reloads != 1 {
t.Errorf("reload called %d times, want 1", *reloads)
}
kt, err := os.ReadFile(o.keyTablePath)
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(kt), "example.com:selfpost:") {
t.Errorf("KeyTable missing entry: %q", kt)
}
}
func TestRemoveKey(t *testing.T) {
o, _ := newTestOpenDKIM(t)
if _, err := o.EnsureKey("example.com", "selfpost"); err != nil {
t.Fatal(err)
}
if err := o.RemoveKey("example.com"); err != nil {
t.Fatalf("RemoveKey: %v", err)
}
if _, err := os.Stat(filepath.Join(o.keysDir, "example.com")); !os.IsNotExist(err) {
t.Error("key directory still present after RemoveKey")
}
// Removing a non-existent key is not an error.
if err := o.RemoveKey("example.com"); err != nil {
t.Errorf("RemoveKey on missing dir errored: %v", err)
}
}
func TestRecordFromWrittenKey(t *testing.T) {
o, _ := newTestOpenDKIM(t)
if _, err := o.EnsureKey("example.com", "selfpost"); err != nil {
t.Fatal(err)
}
rec, err := o.Record("example.com", "selfpost")
if err != nil {
t.Fatalf("Record: %v", err)
}
if rec.Name != "selfpost._domainkey.example.com" {
t.Errorf("record name = %q", rec.Name)
}
if !strings.HasPrefix(rec.Value, "v=DKIM1;") {
t.Errorf("record value = %q", rec.Value)
}
}