(#25) Divergence check -> review nudge #32
@@ -0,0 +1,131 @@
|
||||
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
|
||||
}
|
||||
Reference in new issue
Block a user