132 lines
3.9 KiB
Go
132 lines
3.9 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// fakeNoulJev answers every question in a request with a fixed noul probability.
|
|
func fakeNoulJev(t *testing.T, noul float64) *JevClient {
|
|
t.Helper()
|
|
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
var body struct {
|
|
Questions map[string]any `json:"questions"`
|
|
}
|
|
json.NewDecoder(r.Body).Decode(&body)
|
|
out := map[string]any{}
|
|
for q := range body.Questions {
|
|
out[q] = map[string]any{"type": "noul", "noul": noul}
|
|
}
|
|
json.NewEncoder(w).Encode(map[string]any{"model": "jev-1.14.0", "answers": out,
|
|
"usage": map[string]int{"input_tokens": 40, "output_tokens": 0}})
|
|
}))
|
|
t.Cleanup(srv.Close)
|
|
return &JevClient{url: srv.URL, key: "tk", model: "jev-latest", threshold: 0.7, client: srv.Client()}
|
|
}
|
|
|
|
func postDeliver(t *testing.T, ts *httptest.Server, id, author, refersTo, text string) string {
|
|
t.Helper()
|
|
code, out := post(t, ts.URL+"/api/nests/"+id+"/messages",
|
|
`{"author":"`+author+`","kind":"deliver","refers_to":"`+refersTo+`","text":"`+text+`"}`)
|
|
if code != http.StatusCreated {
|
|
t.Fatalf("deliver %s: %d %v", author, code, out)
|
|
}
|
|
return out["msg_id"].(string)
|
|
}
|
|
|
|
func findNudge(ts *httptest.Server, id, target string) map[string]any {
|
|
for _, m := range nestMessagesNoT(ts, id) {
|
|
if m["kind"] == "nudge" && m["refers_to"] == target {
|
|
return m
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func nestMessagesNoT(ts *httptest.Server, id string) []map[string]any {
|
|
resp, err := http.Get(ts.URL + "/api/nests/" + id + "/messages")
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
defer resp.Body.Close()
|
|
var msgs []map[string]any
|
|
json.NewDecoder(resp.Body).Decode(&msgs)
|
|
return msgs
|
|
}
|
|
|
|
func waitForNudge(ts *httptest.Server, id, target string) map[string]any {
|
|
deadline := time.Now().Add(2 * time.Second)
|
|
for {
|
|
if n := findNudge(ts, id, target); n != nil {
|
|
return n
|
|
}
|
|
if time.Now().After(deadline) {
|
|
return nil
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
}
|
|
|
|
func TestRestatementGetsNudge(t *testing.T) {
|
|
ts, s := newTestNestServer(t)
|
|
s.p.Flock.SetClassifier(fakeNoulJev(t, 0.8)) // above band 0.7
|
|
id := newNest(t, ts, "Lab", `"Hugin","Munin"`)
|
|
r := postRequest(t, ts, id, "conspiracy", "is this risky")
|
|
postDeliver(t, ts, id, "Hugin", r, "ship it")
|
|
d2 := postDeliver(t, ts, id, "Munin", r, "also ship it")
|
|
|
|
n := waitForNudge(ts, id, d2)
|
|
if n == nil {
|
|
t.Fatalf("restated deliver must get a nudge, log: %+v", s.msgs.All(id))
|
|
}
|
|
if n["to"] != "Munin" || n["author"] != "harness" {
|
|
t.Fatalf("nudge must address the restating crow from the harness, got %v", n)
|
|
}
|
|
if !containsMsg(s.msgs.All(id), d2) {
|
|
t.Fatal("the restated deliver itself must stay in the log")
|
|
}
|
|
}
|
|
|
|
func TestDistinctDeliversNoNudge(t *testing.T) {
|
|
ts, s := newTestNestServer(t)
|
|
s.p.Flock.SetClassifier(fakeNoulJev(t, 0.2)) // below band
|
|
id := newNest(t, ts, "Lab", `"Hugin","Munin"`)
|
|
r := postRequest(t, ts, id, "conspiracy", "is this risky")
|
|
postDeliver(t, ts, id, "Hugin", r, "ship it")
|
|
d2 := postDeliver(t, ts, id, "Munin", r, "wait for the audit")
|
|
time.Sleep(300 * time.Millisecond)
|
|
if n := findNudge(ts, id, d2); n != nil {
|
|
t.Fatalf("a distinct deliver must not be nudged, got %v", n)
|
|
}
|
|
}
|
|
|
|
func TestNudgeSpendsNoModelTokens(t *testing.T) {
|
|
ts, s := newTestNestServer(t)
|
|
s.p.Flock.SetClassifier(fakeNoulJev(t, 0.8))
|
|
id := newNest(t, ts, "Lab", `"Hugin","Munin"`)
|
|
r := postRequest(t, ts, id, "conspiracy", "is this risky")
|
|
postDeliver(t, ts, id, "Hugin", r, "ship it")
|
|
|
|
d2, err := s.msgs.Append(Message{NestID: id, Kind: "deliver", Author: "Munin", Text: "ship", RefersTo: r, Timestamp: time.Now().UTC().Format(time.RFC3339)})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
b := &Budget{name: "nest", limit: 1}
|
|
s.checkDivergence(nestOf(s, id), d2, b)
|
|
if b.Spent() != 0 {
|
|
t.Fatalf("a review nudge must spend no model tokens, spent=%d", b.Spent())
|
|
}
|
|
}
|
|
|
|
func containsMsg(msgs []Message, msgID string) bool {
|
|
for _, m := range msgs {
|
|
if m.MsgID() == msgID {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|