#18: implement SMTP Submission (587/465), SASL PLAIN/LOGIN auth and preflight diagnostics

This commit is contained in:
Lucas Rayan committed 2026-08-01 22:49:02 -03:00
1 parent 7249419efc
commit d56f29d084
70 files changed
+5163 -659

No files matched your search

@@ -0,0 +1,179 @@
package usecase
import (
"context"
"strings"
"testing"
"lambdamail/protocols/internal/domain/entity"
)
func conflictTestInput() SyncDnsRecordsInput {
return SyncDnsRecordsInput{
DomainName: "example.test",
MailHost: "mail.example.test",
ServerIPv4: "192.0.2.1",
RsaDkimPubKey: "rsaKey",
EdDkimPubKey: "edKey",
}
}
// PLAN.md section 7.5: pre-existing records that are not ours are never
// touched, only reported as conflicts. A third-party verification TXT sits at
// the zone apex under the same type and name as our SPF record, so a reconciler
// keyed only on type+name would overwrite it and break the other service.
func TestSyncDnsRecords_ForeignApexTxtIsPreserved(t *testing.T) {
dns := newFakeDnsProvider()
dns.seed(entity.DnsRecord{
Type: "TXT",
Name: "example.test",
Value: "google-site-verification=abc123",
})
uc := NewSyncDnsRecordsUseCase(dns, nil)
out, err := uc.Execute(context.Background(), conflictTestInput())
if err != nil {
t.Fatalf("Execute: %v", err)
}
apexTxt := dns.find("TXT", "example.test")
if len(apexTxt) != 2 {
t.Fatalf("expected the foreign TXT and our SPF to coexist, got %d records", len(apexTxt))
}
var foundForeign, foundSPF bool
for _, r := range apexTxt {
if r.Value == "google-site-verification=abc123" {
foundForeign = true
}
if strings.HasPrefix(r.Value, "v=spf1") {
foundSPF = true
}
}
if !foundForeign {
t.Error("the foreign verification TXT was destroyed")
}
if !foundSPF {
t.Error("the SPF record was not created alongside the foreign apex TXT")
}
if out.ConflictCount != 0 {
t.Errorf("a coexisting foreign TXT is not a conflict, got ConflictCount=%d", out.ConflictCount)
}
}
// A name that admits only one value (the mail host A record) already pointing
// somewhere else is a genuine conflict: silently repointing it could take down
// an unrelated service.
func TestSyncDnsRecords_ForeignARecordIsReportedAsConflict(t *testing.T) {
dns := newFakeDnsProvider()
dns.seed(entity.DnsRecord{
Type: "A",
Name: "mail.example.test",
Value: "198.51.100.9",
Comment: "managed by something else",
})
uc := NewSyncDnsRecordsUseCase(dns, nil)
out, err := uc.Execute(context.Background(), conflictTestInput())
if err != nil {
t.Fatalf("Execute: %v", err)
}
records := dns.find("A", "mail.example.test")
if len(records) != 1 || records[0].Value != "198.51.100.9" {
t.Errorf("foreign A record was not left untouched: %+v", records)
}
if out.ConflictCount != 1 {
t.Fatalf("ConflictCount = %d, want 1", out.ConflictCount)
}
if out.Status != SyncStatusPartial {
t.Errorf("Status = %s, want %s", out.Status, SyncStatusPartial)
}
if !strings.Contains(out.Conflicts[0].String(), "198.51.100.9") {
t.Errorf("conflict report does not name the blocking value: %s", out.Conflicts[0])
}
}
// Our own records must still be updated in place when they drift.
func TestSyncDnsRecords_OwnRecordDriftIsCorrected(t *testing.T) {
dns := newFakeDnsProvider()
uc := NewSyncDnsRecordsUseCase(dns, nil)
ctx := context.Background()
if _, err := uc.Execute(ctx, conflictTestInput()); err != nil {
t.Fatalf("initial Execute: %v", err)
}
for i, r := range dns.records {
if r.Type == "A" && r.Name == "mail.example.test" {
dns.records[i].Value = "203.0.113.7"
}
}
out, err := uc.Execute(ctx, conflictTestInput())
if err != nil {
t.Fatalf("second Execute: %v", err)
}
if out.UpdatedCount != 1 || out.ConflictCount != 0 {
t.Errorf("updated=%d conflicts=%d, want updated=1 conflicts=0", out.UpdatedCount, out.ConflictCount)
}
if records := dns.find("A", "mail.example.test"); records[0].Value != "192.0.2.1" {
t.Errorf("drifted A record was not corrected: got %q", records[0].Value)
}
if out.Status != SyncStatusVerified {
t.Errorf("Status = %s, want %s", out.Status, SyncStatusVerified)
}
}
type stubDnsVerifier struct {
invisible map[string]bool
}
func (s stubDnsVerifier) VerifyRecord(_ context.Context, record entity.DnsRecord) (bool, string) {
if s.invisible[record.Name] {
return false, "no answer"
}
return true, ""
}
// PLAN.md section 7.5 verifies through public resolvers after writing. A
// record the provider accepted but nobody can resolve is drift, not success.
func TestSyncDnsRecords_UnresolvableRecordIsReportedAsDrift(t *testing.T) {
dns := newFakeDnsProvider()
uc := NewSyncDnsRecordsUseCase(dns, nil)
uc.SetVerifier(stubDnsVerifier{invisible: map[string]bool{"mail.example.test": true}})
out, err := uc.Execute(context.Background(), conflictTestInput())
if err != nil {
t.Fatalf("Execute: %v", err)
}
if out.Status != SyncStatusDrift {
t.Errorf("Status = %s, want DRIFT", out.Status)
}
if len(out.Unverified) == 0 {
t.Fatal("the unresolvable record was not reported")
}
if !strings.Contains(out.Unverified[0], "mail.example.test") {
t.Errorf("Unverified = %v, want it to name the record", out.Unverified)
}
}
func TestSyncDnsRecords_AllRecordsVisibleIsVerified(t *testing.T) {
dns := newFakeDnsProvider()
uc := NewSyncDnsRecordsUseCase(dns, nil)
uc.SetVerifier(stubDnsVerifier{})
out, err := uc.Execute(context.Background(), conflictTestInput())
if err != nil {
t.Fatalf("Execute: %v", err)
}
if out.Status != SyncStatusVerified {
t.Errorf("Status = %s, want VERIFIED", out.Status)
}
if len(out.Unverified) != 0 {
t.Errorf("Unverified = %v, want empty", out.Unverified)
}
}