mesh-mail: tighten tests that proved less than their criterion — attempt 5 and no mail received (1198/AC4), mail_health actually turning unhealthy (1199/AC2), and the words mail_send_status gives, not only its status (1199/AC4) (review round 1 finding 6)
This commit is contained in:
@@ -273,8 +273,14 @@ func TestMailSenderAcceptanceARefusedSignInFailsAfterItsAttemptsAndNothingIsRece
|
||||
<-done
|
||||
|
||||
kept, _, ok, err := getSend(store, id)
|
||||
if err != nil || !ok || kept.Decided == nil || kept.Decided.Outcome == "sent" {
|
||||
t.Fatalf("the kept send ended as sent, or could not be read: %v %v %+v", err, ok, kept.Decided)
|
||||
if err != nil || !ok || kept.Decided == nil || !strings.HasPrefix(kept.Decided.Outcome, "failed: ") {
|
||||
t.Fatalf("the kept send did not end failed: %v %v %+v", err, ok, kept.Decided)
|
||||
}
|
||||
if kept.Decided.Attempt != MostAttempts {
|
||||
t.Fatalf("failed after %d attempt(s), want all %d", kept.Decided.Attempt, MostAttempts)
|
||||
}
|
||||
if n := s.receivedCount(); n != 0 {
|
||||
t.Fatalf("the scripted server received %d message(s), want none: a refused sign-in never reaches DATA", n)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -114,3 +114,39 @@ func TestTheWatchJudgesFromTheFilesAndTheIndex(t *testing.T) {
|
||||
t.Fatalf("the status file's shape: %s", raw)
|
||||
}
|
||||
}
|
||||
|
||||
// 1199/AC2: mail_health (the tool, not only Overdue or WithOverdue in isolation) turns unhealthy naming an
|
||||
// overdue send, even while the watch's own verdict is healthy (novox/hq review round 1 finding 6).
|
||||
func TestMailHealthToolTurnsUnhealthyNamingAnOverdueSend(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
path := filepath.Join(dir, "health.json")
|
||||
if err := writeStatus(path, Status{Answer: health.Healthy("1 account(s) watched"), At: judgedAt}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sends := newMemSends()
|
||||
overdueID := "send-overdue1"
|
||||
req := SendRequest{ID: overdueID, Account: "mesh", At: judgedAt}
|
||||
req.Decided = &SendDecision{At: judgedAt.Add(-20 * time.Minute), Outcome: "queued"}
|
||||
if _, err := sends.Create(req.ID, req); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := addSenderPending(sends, req.ID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
b := &Bundle{Env: Env{Status: path}, Sends: sends, Now: func() time.Time { return judgedAt }}
|
||||
|
||||
out, err := b.healthTool(nil)
|
||||
if err != nil {
|
||||
t.Fatalf("mail_health: %v", err)
|
||||
}
|
||||
a, ok := out.(health.Answer)
|
||||
if !ok {
|
||||
t.Fatalf("mail_health answered %T, want health.Answer", out)
|
||||
}
|
||||
if a.State != health.StateUnhealthy {
|
||||
t.Fatalf("mail_health: %+v, want unhealthy", a)
|
||||
}
|
||||
if !strings.Contains(a.Why, overdueID) {
|
||||
t.Fatalf("mail_health's why does not name the overdue send: %q", a.Why)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"net"
|
||||
"net/smtp"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -31,6 +32,7 @@ func drop() step { return step{drop: true} }
|
||||
type scriptedSMTPServer struct {
|
||||
ln net.Listener
|
||||
ehlo, auth, mail, rcpt, dataOpen, dataEnd step
|
||||
received int32 // atomic: how many full DATA bodies this server has read
|
||||
}
|
||||
|
||||
func newScriptedSMTPServer(t *testing.T) *scriptedSMTPServer {
|
||||
@@ -48,6 +50,10 @@ func newScriptedSMTPServer(t *testing.T) *scriptedSMTPServer {
|
||||
|
||||
func (s *scriptedSMTPServer) addr() string { return s.ln.Addr().String() }
|
||||
|
||||
// receivedCount is how many full DATA bodies this server has read, for a test to prove nothing was
|
||||
// received at all (novox/hq review round 1 finding 6).
|
||||
func (s *scriptedSMTPServer) receivedCount() int { return int(atomic.LoadInt32(&s.received)) }
|
||||
|
||||
func (s *scriptedSMTPServer) dialer() SMTPDialer {
|
||||
addr := s.addr()
|
||||
return func(Server) (*smtp.Client, error) { return smtp.Dial(addr) }
|
||||
@@ -115,7 +121,8 @@ func (s *scriptedSMTPServer) handle(conn net.Conn) {
|
||||
break
|
||||
}
|
||||
}
|
||||
s.send(conn, s.dataEnd) // a drop here is "the connection lost after the data was written"
|
||||
atomic.AddInt32(&s.received, 1) // a full message body was read, whatever dataEnd then says
|
||||
s.send(conn, s.dataEnd) // a drop here is "the connection lost after the data was written"
|
||||
return
|
||||
case upper == "QUIT":
|
||||
fmt.Fprintf(conn, "221 bye\r\n")
|
||||
|
||||
Reference in New Issue
Block a user