okami.mom/albedo

git clone https://git.nkp.pet/okami.mom/albedo

Aly Raffauffeat(api)!: migrate daemon and clients to protocol 361c43ca

main
2.9 KiB83 linesraw
1// Controlled page boundaries catch ordering and turn selection bugs without
2// requiring hundreds of provider turns to cross the daemon's page limit.
3package app
4
5import (
6	"context"
7	"encoding/json"
8	"net"
9	"net/http"
10	"net/http/httptest"
11	"reflect"
12	"testing"
13
14	"albedo/cli/internal/daemon"
15	"albedo/cli/internal/testwire"
16)
17
18func TestReadFlattensHistoryPagesInChronologicalOrder(t *testing.T) {
19	var cursors []string
20	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
21		switch r.URL.Path {
22		case "/sessions":
23			_ = json.NewEncoder(w).Encode(map[string]any{"items": []any{testwire.Session("s", testwire.GenerationA, 0)}, "next": nil})
24		case "/sessions/s":
25			session := testwire.Session("s", testwire.GenerationA, 0)
26			session["status"].(map[string]any)["phase"] = "preparing"
27			_ = json.NewEncoder(w).Encode(session)
28		case "/sessions/s/history":
29			cursor := r.URL.Query().Get("before")
30			cursors = append(cursors, cursor)
31			position := int64(5)
32			text := "third"
33			var older any = "older"
34			switch cursor {
35			case "5":
36				position, text = 3, "second"
37			case "3":
38				position, text, older = 1, "first", nil
39			}
40			makeEntry := func(id, kind, text string, position int64) map[string]any {
41				return map[string]any{"id": id, "position": position, "kind": kind, "created_at": nil, "input_id": nil, "turn_id": nil, "checkpoint_id": nil, "content_complete": true, "content": []any{map[string]any{"kind": "text", "text": text}}, "tool": nil}
42			}
43			_ = json.NewEncoder(w).Encode(map[string]any{"items": []any{makeEntry(text, "user", text, position), makeEntry(text+"-reply", "assistant", text+" reply", position+1)}, "older": older, "newer": nil, "high_water": 6})
44		default:
45			t.Errorf("unexpected request %s", r.URL.Path)
46			w.WriteHeader(404)
47		}
48	}))
49	defer server.Close()
50	conn := daemon.NewConnection(daemon.ConnectionSnapshot{Port: server.Listener.Addr().(*net.TCPAddr).Port}, nil)
51	service := Service{Connect: func(context.Context) (*daemon.Connection, error) { return conn, nil }}
52	for _, turns := range []int{3, 2} {
53		cursors = nil
54		result, err := service.Read(context.Background(), "s", turns)
55		if err != nil {
56			t.Fatal(err)
57		}
58		var texts []string
59		for _, event := range result.Events {
60			if event.Type != daemon.EventCommitted {
61				texts = append(texts, event.Text)
62			}
63			if !event.Replayed {
64				t.Fatal("history event not marked replayed")
65			}
66		}
67		wanted := []string{"first", "first reply", "second", "second reply", "third", "third reply"}
68		if turns == 2 {
69			wanted = wanted[2:]
70		}
71		if !reflect.DeepEqual(texts, wanted) || !result.Running {
72			data, _ := json.Marshal(result)
73			t.Fatalf("history changed order or running state: %s", data)
74		}
75		expectedCursors := []string{"", "5", "3"}
76		if turns == 2 {
77			expectedCursors = expectedCursors[:2]
78		}
79		if !reflect.DeepEqual(cursors, expectedCursors) {
80			t.Fatalf("read fetched wrong pages: %v", cursors)
81		}
82	}
83}