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