Fix the try: guard shared test state with mutexes, pgxpool over one conn, a short batch gap for integration builds, unique+forced database drops (issues 1431, 1524, 1517)
This commit is contained in:
@@ -12,9 +12,47 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// recordedArgs is a mutex-guarded holder: a stand-in's own closure (called from the player's readLoop
|
||||
// goroutine, runtime_integration_test.go's handleBundleRequest) records what it was asked here, and the
|
||||
// test (its own, main goroutine) reads it back safely afterward. The coordinator's own fix for issue 1523:
|
||||
// the merge check's `go test -race` found this exact unsynchronized pattern — a bare variable written by
|
||||
// one goroutine and read by another, with no memory synchronization the race detector can see (the two
|
||||
// communicate only over a stdio pipe and an HTTP round trip, neither of which it tracks as ordinary Go
|
||||
// synchronization).
|
||||
type recordedArgs struct {
|
||||
mu sync.Mutex
|
||||
args map[string]any
|
||||
set bool
|
||||
}
|
||||
|
||||
func (r *recordedArgs) record(args map[string]any) {
|
||||
r.mu.Lock()
|
||||
r.args, r.set = args, true
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *recordedArgs) clear() {
|
||||
r.mu.Lock()
|
||||
r.args, r.set = nil, false
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *recordedArgs) get() map[string]any {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
return r.args
|
||||
}
|
||||
|
||||
func (r *recordedArgs) wasSet() bool {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
return r.set
|
||||
}
|
||||
|
||||
func TestAcceptanceMailCorrectionAndUndoReachTheStandIn(t *testing.T) {
|
||||
mw := startMailWeb(t, acceptanceAccounts(), []string{"alice"})
|
||||
mw.mailReading.Status = func(map[string]any) (any, error) {
|
||||
@@ -33,13 +71,13 @@ func TestAcceptanceMailCorrectionAndUndoReachTheStandIn(t *testing.T) {
|
||||
{"id": 9, "sender-name": "Bob", "subject": "fyi", "change": "mark: neither -> matters", "made": "2026-10-20T10:00:00Z"},
|
||||
}}, nil
|
||||
}
|
||||
var correctArgs, undoArgs map[string]any
|
||||
var correctArgs, undoArgs recordedArgs
|
||||
mw.mailReading.Correct = func(args map[string]any) (any, error) {
|
||||
correctArgs = args
|
||||
correctArgs.record(args)
|
||||
return map[string]any{"id": args["id"]}, nil
|
||||
}
|
||||
mw.mailReading.Undo = func(args map[string]any) (any, error) {
|
||||
undoArgs = args
|
||||
undoArgs.record(args)
|
||||
return map[string]any{"undone": args["id"]}, nil
|
||||
}
|
||||
|
||||
@@ -65,19 +103,19 @@ func TestAcceptanceMailCorrectionAndUndoReachTheStandIn(t *testing.T) {
|
||||
if correctResp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("the correction: status %d: %s", correctResp.StatusCode, correctBody)
|
||||
}
|
||||
if correctArgs["id"] != float64(1) || correctArgs["mark"] != "matters" || correctArgs["group"] != "bills" {
|
||||
t.Fatalf("mail-reading.correct was asked %v, want id 1, mark matters, group bills", correctArgs)
|
||||
if got := correctArgs.get(); got["id"] != float64(1) || got["mark"] != "matters" || got["group"] != "bills" {
|
||||
t.Fatalf("mail-reading.correct was asked %v, want id 1, mark matters, group bills", got)
|
||||
}
|
||||
|
||||
// Without the token, the correction never reaches the stand-in (1523/AC1).
|
||||
correctArgs = nil
|
||||
correctArgs.clear()
|
||||
noTokenResp := doForm(t, mw, "/mail-correct", url.Values{"id": {"1"}, "mark": {"neither"}}, cookie)
|
||||
_ = noTokenResp.Body.Close()
|
||||
if noTokenResp.StatusCode != http.StatusForbidden {
|
||||
t.Fatalf("a correction with no token: status %d, want 403", noTokenResp.StatusCode)
|
||||
}
|
||||
if correctArgs != nil {
|
||||
t.Fatalf("mail-reading.correct was asked %v with no token", correctArgs)
|
||||
if correctArgs.wasSet() {
|
||||
t.Fatalf("mail-reading.correct was asked %v with no token", correctArgs.get())
|
||||
}
|
||||
|
||||
// 1523/AC2: an undo posted with the session's own token reaches the stand-in's Undo.
|
||||
@@ -87,8 +125,8 @@ func TestAcceptanceMailCorrectionAndUndoReachTheStandIn(t *testing.T) {
|
||||
if undoResp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("the undo: status %d: %s", undoResp.StatusCode, undoBody)
|
||||
}
|
||||
if undoArgs["id"] != float64(9) {
|
||||
t.Fatalf("mail-reading.undo was asked %v, want id 9", undoArgs)
|
||||
if got := undoArgs.get(); got["id"] != float64(9) {
|
||||
t.Fatalf("mail-reading.undo was asked %v, want id 9", got)
|
||||
}
|
||||
|
||||
// 1523/AC4: nothing of mail is read but through mail-store — this flow never asks mail-store.search or
|
||||
|
||||
Reference in New Issue
Block a user