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 KiB74 linesraw
1// The HTTP adapter must preserve date units across metadata and stream consumers.
2// A controlled peer supplies subsecond dates that ordinary daemon timestamps
3// cannot deterministically place relative to the presentation clock.
4package daemon
5
6import (
7	"albedo/cli/internal/presentation"
8	"encoding/json"
9	"net/http"
10	"testing"
11	"time"
12)
13
14func TestMetadataDatesProduceCorrectAges(t *testing.T) {
15	activity := time.Date(2026, 9, 20, 12, 30, 0, 123456789, time.UTC)
16	conn := controlledConnection(t, func(w http.ResponseWriter, r *http.Request) {
17		switch r.URL.Path {
18		case "/sessions/s":
19			session := canonicalSession("s", generationA, 0)
20			session["activity_at"] = activity.Format(time.RFC3339Nano)
21			if r.Header.Get("Accept") == "text/event-stream" {
22				session["usage"].(map[string]any)["observed_at"] = activity.Format(time.RFC3339Nano)
23				w.Header().Set("Content-Type", "text/event-stream")
24				writeBatch(w, map[string]any{"generation": generationA, "cursor": 0, "snapshot": session, "events": []any{map[string]any{"type": "reset", "data": map[string]any{"reason": "initial"}}}})
25				return
26			}
27			_ = json.NewEncoder(w).Encode(session)
28		case "/workspaces":
29			_ = json.NewEncoder(w).Encode(map[string]any{
30				"directory": "/work", "parent": "/", "home": "/home", "host": nil, "next": nil,
31				"items": []any{map[string]any{"name": "project", "location": "/work/project", "vcs": "git", "modified_at": activity.Format(time.RFC3339Nano), "hidden": false}},
32				"preview": map[string]any{
33					"repository":            map[string]any{"kind": "git", "root": "/work", "branch": "main", "revision": "revision", "dirty": false, "added": 0, "modified": 0, "removed": 0, "changed": 0, "touched_at": activity.Format(time.RFC3339Nano)},
34					"repository_diagnostic": nil, "languages": []any{}, "tree": []any{}, "more": 0,
35				},
36			})
37		default:
38			t.Errorf("unexpected request: %s", r.URL)
39		}
40	})
41	session, err := GetSession(t.Context(), conn, "s")
42	if err != nil {
43		t.Fatal(err)
44	}
45	folders, err := ListFolders(t.Context(), conn, "/work")
46	if err != nil {
47		t.Fatal(err)
48	}
49	repo, err := FolderRepo(t.Context(), conn, "/work")
50	if err != nil || repo == nil {
51		t.Fatalf("repository: %v, %v", repo, err)
52	}
53	if len(folders.Entries) != 1 {
54		t.Fatalf("folders: %v", folders.Entries)
55	}
56	now := activity.Add(72 * time.Hour)
57	for name, timestamp := range map[string]*int64{"session": session.LastAssistantAt, "folder": &folders.Entries[0].Modified, "repository": repo.Touched} {
58		if age := presentation.AssistantAge(timestamp, now); age != "3d ago" {
59			t.Errorf("%s date produced %q", name, age)
60		}
61	}
62	var recordedAt *int64
63	if err := NewChatClient(conn, "s").Stream(t.Context(), 0, func(event StreamEvent) error {
64		if event.Type == EventUsage {
65			recordedAt = event.Usage.RecordedAt
66		}
67		return nil
68	}); err != nil {
69		t.Fatal(err)
70	}
71	if recordedAt == nil || *recordedAt != activity.UnixMilli() {
72		t.Fatalf("usage lost its millisecond timestamp: %v", recordedAt)
73	}
74}