feat(send): Phase 2 send path — SMTP, MIME, reply threading, outbound policy
Adds the `send` agent command and everything behind it: - store: Account carries SMTP host/port/security (NULL-safe scan/insert/select); admin `account add` gains --smtp-* flags (applied for RW accounts). - policy: OutboundRule.Check(recipients) → (ok, reason); RO ⇒ ro_mode, whitelist-out blocks the whole send if any recipient fails (no partial send). - mail: Header.References; OutgoingMessage + BuildMIME (plain text + attachments, In-Reply-To/References threading, Bcc envelope-only); SendSMTP (tls/starttls, SASL PLAIN, envelope send) via emersion/go-smtp. - cli: SendCmd gates outbound, resolves --reply-to under the inbound filter (filtered/absent source ⇒ not_found), reads attachments, audits, emits the JSON envelope; repeatable --to/--cc/--bcc/--attach flags wired into the router. Implemented test-first; full suite passes incl -race. Validated live against friday.mxlogin.com: real send to me@stevecliff.com, RO + whitelist-out blocks, and --reply-to threading off a live INBOX message. test-creds.md gitignored. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,25 @@ func TestAddGetAccountDecryptsSecret(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddGetAccountRoundTripsSMTP(t *testing.T) {
|
||||
s := openTemp(t)
|
||||
a := sampleAccount()
|
||||
a.Mode = "RW"
|
||||
a.SMTPHost = "smtp.example.com"
|
||||
a.SMTPPort = 465
|
||||
a.SMTPSecurity = "tls"
|
||||
if _, err := s.AddAccount(a); err != nil {
|
||||
t.Fatalf("AddAccount: %v", err)
|
||||
}
|
||||
got, err := s.GetAccount("work")
|
||||
if err != nil {
|
||||
t.Fatalf("GetAccount: %v", err)
|
||||
}
|
||||
if got.SMTPHost != "smtp.example.com" || got.SMTPPort != 465 || got.SMTPSecurity != "tls" {
|
||||
t.Fatalf("SMTP fields not round-tripped: %+v", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPasswordStoredEncrypted(t *testing.T) {
|
||||
s := openTemp(t)
|
||||
_, _ = s.AddAccount(sampleAccount())
|
||||
|
||||
Reference in New Issue
Block a user