Files
counters/review/_proof_tests/a/internal/server/review_test.go
T

97 lines
3.6 KiB
Go

package server
import (
"fmt"
"net"
"strings"
"testing"
"time"
"migadu_counters/internal/counting"
"migadu_counters/internal/domains"
)
func rawPostAndClose(t *testing.T, addr, path, body string) {
c, err := net.Dial("tcp", addr)
if err != nil {
t.Fatal(err)
}
req := fmt.Sprintf("POST %s HTTP/1.1\r\nHost: x\r\nAuthorization: Bearer %s\r\nContent-Type: application/json\r\nContent-Length: %d\r\n\r\n%s", path, token, len(body), body)
if _, err := c.Write([]byte(req)); err != nil {
t.Fatal(err)
}
c.Close() // fire-and-forget client that does not wait for the reply
}
func TestReview_ClientDisconnectLosesCount(t *testing.T) {
h := newHarness(t, options{})
real := *h.lookup.Load()
slow := counting.LookupFunc(func(d string) (*domains.Domain, error) {
time.Sleep(50 * time.Millisecond) // any latency before the Redis call
return real(d)
})
h.lookup.Store(&slow)
addr := strings.TrimPrefix(h.api.URL, "http://")
const n = 20
for i := 0; i < n; i++ {
rawPostAndClose(t, addr, "/in", `{"trace_id":"t","mail_from":"s@gmail.com","recipient":"a@yagolivas.com"}`)
}
time.Sleep(1500 * time.Millisecond)
v, _ := h.write.Get("cnt/A/a@yagolivas.com/d/" + day + "/i")
t.Logf("sent %d /in requests, counter = %q", n, v)
t.Logf("txlog tail:\n%s", lastLines(h.tx.String(), 3))
}
func TestReview_ClientDisconnectNoDelay(t *testing.T) {
h := newHarness(t, options{})
addr := strings.TrimPrefix(h.api.URL, "http://")
const n = 300
for i := 0; i < n; i++ {
rawPostAndClose(t, addr, "/in", `{"trace_id":"t","mail_from":"s@gmail.com","recipient":"b@yagolivas.com"}`)
}
time.Sleep(2 * time.Second)
v, _ := h.write.Get("cnt/A/b@yagolivas.com/d/" + day + "/i")
t.Logf("sent %d /in requests without delay, counter = %q", n, v)
}
func lastLines(s string, n int) string {
l := strings.Split(strings.TrimRight(s, "\n"), "\n")
if len(l) > n {
l = l[len(l)-n:]
}
return strings.Join(l, "\n")
}
func TestReview_MasterLoginAndOddSenders(t *testing.T) {
h := newHarness(t, options{})
cases := []struct{ path, body string }{
{"/out", `{"trace_id":"m1","auth_user":"x@yagolivas.com@SUPPORTTOKEN","mail_from":"x@yagolivas.com","recipient":"y@yagolivas.com"}`},
{"/in", `{"trace_id":"i1","mail_from":"a b@spammer.example","recipient":"y@yagolivas.com"}`},
{"/in", `{"trace_id":"i2","mail_from":"u@[192.0.2.1]","recipient":"y@yagolivas.com"}`},
{"/in", `{"trace_id":"i3","mail_from":"u@münchen.de","recipient":"y@yagolivas.com"}`},
{"/in", `{"trace_id":"i4","mail_from":"u@example.com.","recipient":"y@yagolivas.com"}`},
{"/in", `{"trace_id":"i5","mail_from":"a@b@example.com","recipient":"y@yagolivas.com"}`},
}
for _, c := range cases {
st, body := h.post(c.path, c.body)
t.Logf("%s %s -> %d %s", c.path, c.body, st, strings.TrimSpace(body))
}
t.Logf("keys in redis: %v", h.write.Keys())
}
func TestReview_PartialExecFailure(t *testing.T) {
h := newHarness(t, options{})
// A counter key holding a non-integer (or wrong type) value.
h.write.Set("cnt/O/1049/d/"+day+"/o", "garbage")
// A/o limit is 20 for yagolivas.com; preset A/o to 19 so this request crosses it.
h.write.Set("cnt/A/x@yagolivas.com/d/"+day+"/o", "19")
st, body := h.post("/out", `{"trace_id":"p1","auth_user":"x@yagolivas.com","recipient":"z@gmail.com"}`)
t.Logf("status %d body %s", st, strings.TrimSpace(body))
a, _ := h.write.Get("cnt/A/x@yagolivas.com/d/" + day + "/o")
d, _ := h.write.Get("cnt/D/yagolivas.com/d/" + day + "/o")
t.Logf("after: A/o=%s (limit 20 crossed) D/o=%s", a, d)
time.Sleep(300 * time.Millisecond)
t.Logf("address notifications sent: %d", h.addrURL.count())
t.Logf("txlog:\n%s", h.tx.String())
}