eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1package main
2
3import (
4 "bytes"
5 "encoding/json"
6 "net/http"
7 "net/http/httptest"
8 "testing"
9)
10
11func postUsage(t *testing.T, base, token string, rows []botUsageRow) (int, map[string]any) {
12 t.Helper()
13 return postUsageReport(t, base, token, "aaaaaaaaaaaaaaaa", rows)
14}
15
16func postUsageReport(t *testing.T, base, token, reportID string, rows []botUsageRow) (int, map[string]any) {
17 t.Helper()
18 raw, _ := json.Marshal(map[string]any{"token": token, "report_id": reportID, "rows": rows})
19 resp, err := http.Post(base+"/api/bot-usage", "application/json", bytes.NewReader(raw))
20 if err != nil {
21 t.Fatal(err)
22 }
23 defer resp.Body.Close()
24 var body map[string]any
25 json.NewDecoder(resp.Body).Decode(&body)
26 return resp.StatusCode, body
27}
28
29// The relay end to end: a bot's report is validated, forwarded to the cloud
30// meter with the instance's bearer token, and the cloud's budget state comes
31// back to the sidecar untouched.
32func TestBotUsageRelay(t *testing.T) {
33 ts, s := newTestServer(t)
34 agent := provisionNamed(t, ts.URL, "Eleven", true)
35
36 var gotAuth string
37 var gotRows []botUsageRow
38 cloud := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
39 gotAuth = r.Header.Get("Authorization")
40 var body struct {
41 Rows []botUsageRow `json:"rows"`
42 }
43 json.NewDecoder(r.Body).Decode(&body)
44 gotRows = body.Rows
45 writeJSON(w, http.StatusOK, map[string]any{"ok": true, "budget": map[string]any{"state": "low"}})
46 }))
47 defer cloud.Close()
48 s.meterURL, s.meterToken = cloud.URL, "meter-tok"
49
50 rows := []botUsageRow{
51 {Day: "2026-07-14", Model: "claude-sonnet-5", Dimension: "llm_input_tokens", Quantity: 150},
52 {Day: "2026-07-14", Dimension: "charter_runs", Quantity: 1},
53 }
54 code, body := postUsage(t, ts.URL, agent.Token, rows)
55 if code != http.StatusOK {
56 t.Fatalf("relay: %d %v", code, body)
57 }
58 if gotAuth != "Bearer meter-tok" {
59 t.Fatalf("cloud auth = %q", gotAuth)
60 }
61 if len(gotRows) != 2 || gotRows[0].Quantity != 150 {
62 t.Fatalf("cloud rows = %+v", gotRows)
63 }
64 if budget, _ := body["budget"].(map[string]any); budget["state"] != "low" {
65 t.Fatalf("budget not passed through: %v", body)
66 }
67}
68
69// Unconfigured (self-hosted / BYO key): accept, drop, and say "unmetered".
70func TestBotUsageUnmetered(t *testing.T) {
71 ts, _ := newTestServer(t)
72 agent := provisionNamed(t, ts.URL, "Eleven", true)
73 code, body := postUsage(t, ts.URL, agent.Token, []botUsageRow{
74 {Day: "2026-07-14", Dimension: "llm_requests", Quantity: 1}})
75 if code != http.StatusOK {
76 t.Fatalf("unmetered: %d %v", code, body)
77 }
78 if budget, _ := body["budget"].(map[string]any); budget["state"] != "unmetered" {
79 t.Fatalf("want unmetered, got %v", body)
80 }
81}
82
83// The gate: bots only, and malformed rows never leave the instance.
84func TestBotUsageGateAndValidation(t *testing.T) {
85 ts, s := newTestServer(t)
86 alice := provisionNamed(t, ts.URL, "Alice", false)
87 agent := provisionNamed(t, ts.URL, "Eleven", true)
88 s.meterURL, s.meterToken = "http://cloud.invalid", "tok" // must never be dialed below
89
90 good := []botUsageRow{{Day: "2026-07-14", Dimension: "llm_requests", Quantity: 1}}
91 if code, _ := postUsage(t, ts.URL, alice.Token, good); code != http.StatusForbidden {
92 t.Fatalf("member token: want 403, got %d", code)
93 }
94 if code, _ := postUsage(t, ts.URL, "nonsense", good); code != http.StatusForbidden {
95 t.Fatalf("stranger: want 403, got %d", code)
96 }
97 for name, rows := range map[string][]botUsageRow{
98 "empty": {},
99 "bad day": {{Day: "yesterday", Dimension: "llm_requests", Quantity: 1}},
100 "bad dimension": {{Day: "2026-07-14", Dimension: "plaintext_words", Quantity: 1}},
101 "bad quantity": {{Day: "2026-07-14", Dimension: "llm_requests", Quantity: 0}},
102 } {
103 if code, _ := postUsage(t, ts.URL, agent.Token, rows); code != http.StatusBadRequest {
104 t.Fatalf("%s: want 400, got %d", name, code)
105 }
106 }
107 // A report without a well-formed dedupe id is refused too.
108 if code, _ := postUsageReport(t, ts.URL, agent.Token, "not hex!", good); code != http.StatusBadRequest {
109 t.Fatal("bad report id: want 400")
110 }
111}
112
113// A cloud blip is a 502 — the sidecar keeps its counters and retries.
114func TestBotUsageCloudDown(t *testing.T) {
115 ts, s := newTestServer(t)
116 agent := provisionNamed(t, ts.URL, "Eleven", true)
117 dead := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
118 dead.Close() // connection refused from here on
119 s.meterURL, s.meterToken = dead.URL, "tok"
120
121 code, _ := postUsage(t, ts.URL, agent.Token, []botUsageRow{
122 {Day: "2026-07-14", Dimension: "llm_requests", Quantity: 1}})
123 if code != http.StatusBadGateway {
124 t.Fatalf("cloud down: want 502, got %d", code)
125 }
126}
127