package main import ( "net/http" "net/http/httptest" "strings" "testing" ) func postMsg(t *testing.T, ts *httptest.Server, id, body string) (int, map[string]any) { t.Helper() return post(t, ts.URL+"/api/nests/"+id+"/messages", body) } func postRequest(t *testing.T, ts *httptest.Server, id, mode, text string) string { t.Helper() code, out := postMsg(t, ts, id, `{"author":"owner","kind":"request","mode":"`+mode+`","text":"`+text+`"}`) if code != http.StatusCreated { t.Fatalf("request: %d %v", code, out) } return out["msg_id"].(string) } func TestSoloSecondClaimRefused(t *testing.T) { ts, s := newTestNestServer(t) id := newNest(t, ts, "Lab", `"Hugin","Munin"`) r := postRequest(t, ts, id, "solo", "count the eggs") code1, c1 := postMsg(t, ts, id, `{"author":"Hugin","kind":"claim","refers_to":"`+r+`","text":"on it"}`) if code1 != http.StatusCreated || c1["to"] != "owner" { t.Fatalf("first claim should win and address requester: %d %v", code1, c1) } code2, out := postMsg(t, ts, id, `{"author":"Munin","kind":"claim","refers_to":"`+r+`","text":"mine too"}`) if code2 != http.StatusConflict { t.Fatalf("second solo claim must be refused 409, got %d %v", code2, out) } if msg, _ := out["message"].(string); !strings.Contains(msg, "Hugin") { t.Fatalf("refusal must name the owner Hugin, got %q", msg) } if len(s.msgs.All(id)) != 2 { // request + Hugin's claim, not Munin's t.Fatalf("refused claim must not be stored: %+v", s.msgs.All(id)) } } func TestConspiracyMultipleClaims(t *testing.T) { ts, s := newTestNestServer(t) id := newNest(t, ts, "Lab", `"Hugin","Munin"`) r := postRequest(t, ts, id, "conspiracy", "is this plan risky") for _, c := range []string{"Hugin", "Munin"} { code, out := postMsg(t, ts, id, `{"author":"`+c+`","kind":"claim","refers_to":"`+r+`","text":"lens `+c+`"}`) if code != http.StatusCreated || out["refers_to"] != r { t.Fatalf("conspiracy claim by %s refused: %d %v", c, code, out) } } claims := 0 for _, m := range s.msgs.All(id) { if m.Kind == "claim" && m.RefersTo == r { claims++ } } if claims != 2 { t.Fatalf("want 2 conspiracy claims stored, got %d", claims) } } func TestSplitSubtaskFirstClaimWins(t *testing.T) { ts, _ := newTestNestServer(t) id := newNest(t, ts, "Lab", `"Hugin","Munin"`) r := postRequest(t, ts, id, "split", "build and test") subOf := func(text string) string { code, out := postMsg(t, ts, id, `{"author":"owner","kind":"request","mode":"solo","text":"`+text+`","refers_to":"`+r+`"}`) if code != http.StatusCreated { t.Fatalf("subtask: %d %v", code, out) } return out["msg_id"].(string) } a := subOf("build") b := subOf("test") // same subtask: second claim refused if code, _ := postMsg(t, ts, id, `{"author":"Hugin","kind":"claim","refers_to":"`+a+`","text":"b1"}`); code != http.StatusCreated { t.Fatalf("first subtask claim: %d", code) } if code, _ := postMsg(t, ts, id, `{"author":"Munin","kind":"claim","refers_to":"`+a+`","text":"b2"}`); code != http.StatusConflict { t.Fatalf("second claim on same subtask must 409, got %d", code) } // different subtask: allowed if code, _ := postMsg(t, ts, id, `{"author":"Munin","kind":"claim","refers_to":"`+b+`","text":"t1"}`); code != http.StatusCreated { t.Fatalf("claim on other subtask: %d", code) } }