okami.mom/albedo

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

Aly Raffaufperf(tui): reuse prepared startup dataed54422

main
4.0 KiB86 linesraw
1// Acknowledgements race old listings and previews. A daemon E2E cannot control
2// their delivery order or inspect search, focus and preview caches in the TUI.
3package tui
4
5import (
6	"encoding/json"
7	"net/http"
8	"testing"
9
10	"albedo/cli/internal/daemon"
11	"albedo/cli/internal/testwire"
12	tea "charm.land/bubbletea/v2"
13)
14
15func TestPartialPreferencesKeepSharedFactsAndPickerInteraction(t *testing.T) {
16	conn := daemon.NewConnection(daemon.ConnectionSnapshot{Port: 1}, nil)
17	active := daemon.Session{ID: "active", Title: "Current", ETag: "active-a", Model: "model"}
18	m := NewAppModel(conn, Bootstrap{}, &active, "", false, nil)
19	t.Cleanup(m.Chat.Close)
20	m.applySessionList([]daemon.Session{active, {ID: "other", Title: "Other", Pinned: true, Opens: 3, ETag: "other-a"}})
21	m.ApplyUI(daemon.UIPreferences{ETag: "ui-a", Thinking: true, DismissedNotices: []string{"migration"}})
22	m.SessionPicker.SearchInput.SetValue("Other")
23	m.SessionPicker.applyFilter()
24	m.SessionPicker.focus("other")
25	cache := &cachedPreview{SessionPreview: daemon.SessionPreview{Items: []daemon.PreviewItem{{Preview: "kept"}}}}
26	m.SessionPicker.previews["other"] = cache
27	oldGeneration := m.SessionGen
28
29	m.ApplyUI(daemon.UIPreferences{SessionETags: map[string]string{"active": "active-b"}, Pinned: []string{"active"}})
30	m.ApplyUI(daemon.UIPreferences{Opens: map[string]int{"active": 2}})
31	m.ApplyUI(daemon.UIPreferences{ETag: "ui-b", Tools: true})
32	_, _ = m.Update(sessionsLoadedMsg{Gen: oldGeneration, Sessions: []daemon.Session{active}})
33	_, _ = m.Update(SessionPreviewMsg{ID: "active", ExpectedETag: "active-a", Preview: daemon.SessionPreview{Session: &active}})
34
35	if !m.ActiveSession.Pinned || m.ActiveSession.ETag != "active-b" || m.ActiveSession.Opens != 2 || m.ActiveSession.Model != "model" {
36		t.Fatalf("lost acknowledged active facts: %+v", m.ActiveSession)
37	}
38	other, _ := m.session("other")
39	if !other.Pinned || other.Opens != 3 || other.ETag != "other-a" {
40		t.Fatalf("partial reply replaced another session: %+v", other)
41	}
42	if m.Chat.Flags.Thinking || !m.Chat.Flags.Tools || len(m.UI.DismissedNotices) != 1 || m.UI.ETag != "ui-b" {
43		t.Fatalf("global preference merge lost facts: %+v", m.UI)
44	}
45	item, ok := m.SessionPicker.Highlighted()
46	if !ok || item.ID != "other" || m.SessionPicker.SearchInput.Value() != "Other" || m.SessionPicker.previews["other"] != cache {
47		t.Fatal("shared fact update reset picker focus, search or preview cache")
48	}
49	// Archiving only the active session must preserve another session's pin.
50	m.ApplyUI(daemon.UIPreferences{SessionETags: map[string]string{"active": "active-c"}, Archived: []string{"active"}})
51	if !m.ActiveSession.Archived || m.ActiveSession.Pinned || !m.Sessions[1].Pinned {
52		t.Fatal("archive acknowledgement did not merge per session")
53	}
54	// Picker snapshots cannot write back into the root owner.
55	m.SessionPicker.raw[1].Pinned = false
56	if !m.Sessions[1].Pinned {
57		t.Fatal("picker snapshot aliases shared session facts")
58	}
59}
60
61func TestNavigationRefreshRejectsOlderSettingsReply(t *testing.T) {
62	settingsReads, sessionLists := 0, 0
63	conn := commandTestConnection(t, func(w http.ResponseWriter, r *http.Request) {
64		switch r.URL.Path {
65		case "/settings":
66			settingsReads++
67			_ = json.NewEncoder(w).Encode(testwire.Settings())
68		case "/sessions":
69			sessionLists++
70			_ = json.NewEncoder(w).Encode(map[string]any{"items": []any{}, "next": nil})
71		default:
72			t.Errorf("unexpected refresh request: %s", r.URL)
73		}
74	})
75	m := NewAppModel(conn, Bootstrap{}, nil, "", false, nil)
76	m.ApplyUI(daemon.UIPreferences{ETag: "prepared", Tools: true})
77	previous := m.SettingsGen
78	batch := m.openSessions()().(tea.BatchMsg)
79	for _, command := range batch {
80		_, _ = m.Update(command())
81	}
82	_, _ = m.Update(settingsLoadedMsg{Gen: previous, Settings: daemon.Settings{UI: daemon.UIPreferences{ETag: "obsolete", Tools: true}}})
83	if sessionLists != 1 || settingsReads != 1 || m.UI.ETag != `"ui-a"` || !m.UI.Thinking || m.UI.Tools {
84		t.Fatalf("navigation lost fresh settings or skipped refresh: lists=%d settings=%d ui=%+v", sessionLists, settingsReads, m.UI)
85	}
86}