package display import ( "context" "net/http" "net/http/httptest" "os" "reflect" "strings" "testing" "time" core "dappco.re/go/core" "forge.lthn.ai/core/gui/pkg/menu" "forge.lthn.ai/core/gui/pkg/systray" "forge.lthn.ai/core/gui/pkg/window" "github.com/gorilla/websocket" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // --- Test helpers --- // newTestDisplayService creates a display service registered with Core for IPC testing. func newTestDisplayService(t *testing.T) (*Service, *core.Core) { t.Helper() c := core.New( core.WithService(Register(nil)), core.WithServiceLock(), ) require.True(t, c.ServiceStartup(context.Background(), nil).OK) svc := core.MustServiceFor[*Service](c, "display") return svc, c } // newTestConclave creates a full 4-service conclave for integration testing. func newTestConclave(t *testing.T) *core.Core { t.Helper() c := core.New( core.WithService(Register(nil)), core.WithService(window.Register(window.NewMockPlatform())), core.WithService(systray.Register(systray.NewMockPlatform())), core.WithService(menu.Register(menu.NewMockPlatform())), core.WithServiceLock(), ) require.True(t, c.ServiceStartup(context.Background(), nil).OK) return c } func taskRun(c *core.Core, name string, task any) core.Result { return c.Action(name).Run(context.Background(), core.NewOptions( core.Option{Key: "task", Value: task}, )) } // --- Tests --- func TestNew_Good(t *testing.T) { service, err := New() assert.NoError(t, err) assert.NotNil(t, service) } func TestNew_Good_IndependentInstances(t *testing.T) { service1, err1 := New() service2, err2 := New() assert.NoError(t, err1) assert.NoError(t, err2) assert.NotSame(t, service1, service2) } func TestRegister_Good(t *testing.T) { factory := Register(nil) // nil wailsApp for testing assert.NotNil(t, factory) c := core.New( core.WithService(factory), core.WithServiceLock(), ) require.True(t, c.ServiceStartup(context.Background(), nil).OK) svc := core.MustServiceFor[*Service](c, "display") assert.NotNil(t, svc) } func TestConfigQuery_Good(t *testing.T) { svc, c := newTestDisplayService(t) // Set window config svc.configData["window"] = map[string]any{ "default_width": 1024, } r := c.QUERY(window.QueryConfig{}) require.True(t, r.OK) cfg := r.Value.(map[string]any) assert.Equal(t, 1024, cfg["default_width"]) } func TestConfigQuery_Bad(t *testing.T) { // No display service — window config query returns handled=false c := core.New(core.WithServiceLock()) r := c.QUERY(window.QueryConfig{}) assert.False(t, r.OK) } func TestConfigTask_Good(t *testing.T) { _, c := newTestDisplayService(t) newCfg := map[string]any{"default_width": 800} r := taskRun(c, "display.saveWindowConfig", window.TaskSaveConfig{Config: newCfg}) require.True(t, r.OK) // Verify config was saved r2 := c.QUERY(window.QueryConfig{}) cfg := r2.Value.(map[string]any) assert.Equal(t, 800, cfg["default_width"]) } func TestStorageTask_Bad(t *testing.T) { _, c := newTestDisplayService(t) r := c.Action("display.storage.set").Run(context.Background(), core.NewOptions( core.Option{Key: "origin", Value: "core://settings"}, core.Option{Key: "bucket", Value: "localStorage"}, core.Option{Key: "key", Value: strings.Repeat("k", maxStorageKeyBytes+1)}, core.Option{Key: "value", Value: "dark"}, )) require.False(t, r.OK) assert.Contains(t, r.Value.(error).Error(), "invalid storage entry") } func TestResolveScheme_StoreRoute_Good(t *testing.T) { svc, _ := newTestDisplayService(t) result := svc.ResolveScheme(context.Background(), "core://store?q=alpha") require.True(t, result.OK) payload, ok := result.Value.(map[string]any) require.True(t, ok) assert.Equal(t, "text/html", payload["content_type"]) body, ok := payload["body"].(string) require.True(t, ok) assert.Contains(t, body, "core://store") assert.Contains(t, body, "storage scopes") assert.Contains(t, body, "Search the in-memory storage scopes") } // --- Conclave integration tests --- func TestServiceConclave_Good(t *testing.T) { c := newTestConclave(t) // Open a window via IPC r := taskRun(c, "window.open", window.TaskOpenWindow{ Window: &window.Window{Name: "main"}, }) require.True(t, r.OK) info := r.Value.(window.WindowInfo) assert.Equal(t, "main", info.Name) // Query window config from display r2 := c.QUERY(window.QueryConfig{}) require.True(t, r2.OK) assert.NotNil(t, r2.Value) // Set app menu via IPC r3 := taskRun(c, "menu.setAppMenu", menu.TaskSetAppMenu{Items: []menu.MenuItem{ {Label: "File"}, }}) require.True(t, r3.OK) // Query app menu via IPC r4 := c.QUERY(menu.QueryGetAppMenu{}) assert.True(t, r4.OK) items := r4.Value.([]menu.MenuItem) assert.Len(t, items, 1) } func TestServiceConclave_Bad(t *testing.T) { // Sub-service starts without display — config QUERY returns handled=false c := core.New( core.WithService(window.Register(window.NewMockPlatform())), core.WithServiceLock(), ) require.True(t, c.ServiceStartup(context.Background(), nil).OK) r := c.QUERY(window.QueryConfig{}) assert.False(t, r.OK, "no display service means no config handler") } // --- IPC delegation tests (full conclave) --- func TestOpenWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") t.Run("creates window with default options", func(t *testing.T) { err := svc.OpenWindow() assert.NoError(t, err) // Verify via IPC query infos := svc.ListWindowInfos() assert.GreaterOrEqual(t, len(infos), 1) }) t.Run("creates window with custom options", func(t *testing.T) { err := svc.OpenWindow( window.WithName("custom-window"), window.WithTitle("Custom Title"), window.WithSize(640, 480), window.WithURL("/custom"), ) assert.NoError(t, err) r := c.QUERY(window.QueryWindowByName{Name: "custom-window"}) info := r.Value.(*window.WindowInfo) assert.Equal(t, "custom-window", info.Name) }) } func TestGetWindowInfo_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow( window.WithName("test-win"), window.WithSize(800, 600), ) // Modify position via IPC taskRun(c, "window.setPosition", window.TaskSetPosition{Name: "test-win", X: 100, Y: 200}) info, err := svc.GetWindowInfo("test-win") require.NoError(t, err) assert.Equal(t, "test-win", info.Name) assert.Equal(t, 100, info.X) assert.Equal(t, 200, info.Y) assert.Equal(t, 800, info.Width) assert.Equal(t, 600, info.Height) } func TestGetWindowInfo_Bad(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") info, err := svc.GetWindowInfo("nonexistent") // QueryWindowByName returns nil for nonexistent — handled=true, result=nil assert.NoError(t, err) assert.Nil(t, info) } func TestGetWindowInfo_BadType(t *testing.T) { svc, c := newTestDisplayService(t) c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result { switch q.(type) { case window.QueryWindowByName: return core.Result{Value: "unexpected", OK: true} default: return core.Result{} } }) info, err := svc.GetWindowInfo("broken") require.Error(t, err) assert.Nil(t, info) } func TestListWindowInfos_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("win-1")) _ = svc.OpenWindow(window.WithName("win-2")) infos := svc.ListWindowInfos() assert.Len(t, infos, 2) } func TestSetWindowPosition_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("pos-win")) err := svc.SetWindowPosition("pos-win", 300, 400) assert.NoError(t, err) info, _ := svc.GetWindowInfo("pos-win") assert.Equal(t, 300, info.X) assert.Equal(t, 400, info.Y) } func TestSetWindowPosition_Bad(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") err := svc.SetWindowPosition("nonexistent", 0, 0) assert.Error(t, err) } func TestSetWindowPosition_ActionFailure(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") c.Action("window.setPosition", func(_ context.Context, _ core.Options) core.Result { return core.Result{OK: false} }) err := svc.SetWindowPosition("pos-win", 300, 400) require.Error(t, err) assert.Contains(t, err.Error(), "window.setPosition") } func TestSetWindowSize_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("size-win")) err := svc.SetWindowSize("size-win", 1024, 768) assert.NoError(t, err) info, _ := svc.GetWindowInfo("size-win") assert.Equal(t, 1024, info.Width) assert.Equal(t, 768, info.Height) } func TestSetWindowBounds_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("bounds-win")) err := svc.SetWindowBounds("bounds-win", 10, 20, 640, 480) assert.NoError(t, err) info, _ := svc.GetWindowInfo("bounds-win") assert.Equal(t, 10, info.X) assert.Equal(t, 20, info.Y) assert.Equal(t, 640, info.Width) assert.Equal(t, 480, info.Height) } func TestSetWindowBounds_Bad(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") err := svc.SetWindowBounds("missing", 1, 2, 3, 4) assert.Error(t, err) } func TestSetWindowBounds_Ugly(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("bounds-win")) err := svc.SetWindowBounds("bounds-win", -10, -20, 0, 1) assert.NoError(t, err) info, _ := svc.GetWindowInfo("bounds-win") assert.Equal(t, -10, info.X) assert.Equal(t, -20, info.Y) assert.Equal(t, 0, info.Width) assert.Equal(t, 1, info.Height) } func TestMaximizeWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("max-win")) err := svc.MaximizeWindow("max-win") assert.NoError(t, err) info, _ := svc.GetWindowInfo("max-win") assert.True(t, info.Maximized) } func TestRestoreWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("restore-win")) _ = svc.MaximizeWindow("restore-win") err := svc.RestoreWindow("restore-win") assert.NoError(t, err) info, _ := svc.GetWindowInfo("restore-win") assert.False(t, info.Maximized) } func TestFocusWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("focus-win")) err := svc.FocusWindow("focus-win") assert.NoError(t, err) info, _ := svc.GetWindowInfo("focus-win") assert.True(t, info.Focused) } func TestCloseWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("close-win")) err := svc.CloseWindow("close-win") assert.NoError(t, err) // Window should be removed info, _ := svc.GetWindowInfo("close-win") assert.Nil(t, info) } func TestSetWindowVisibility_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("vis-win")) err := svc.SetWindowVisibility("vis-win", false) assert.NoError(t, err) err = svc.SetWindowVisibility("vis-win", true) assert.NoError(t, err) } func TestSetWindowAlwaysOnTop_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("ontop-win")) err := svc.SetWindowAlwaysOnTop("ontop-win", true) assert.NoError(t, err) } func TestSetWindowTitle_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("title-win")) err := svc.SetWindowTitle("title-win", "New Title") assert.NoError(t, err) } func TestSetWindowFullscreen_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") windowSvc := core.MustServiceFor[*window.Service](c, "window") _ = svc.OpenWindow(window.WithName("full-win")) err := svc.SetWindowFullscreen("full-win", true) require.NoError(t, err) pw, ok := windowSvc.Manager().Get("full-win") require.True(t, ok) assert.True(t, pw.IsFullscreen()) } func TestSetWindowFullscreen_Bad(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") err := svc.SetWindowFullscreen("missing", true) require.Error(t, err) } func TestLayoutBesideEditor_ActionFailure(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") c.Action("window.layoutBesideEditor", func(_ context.Context, _ core.Options) core.Result { return core.Result{OK: false} }) result, err := svc.LayoutBesideEditor("preview", "code", "right", 0.62) require.Error(t, err) assert.Zero(t, result) assert.Contains(t, err.Error(), "window.layoutBesideEditor") } func TestSetWindowFullscreen_Ugly(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") windowSvc := core.MustServiceFor[*window.Service](c, "window") _ = svc.OpenWindow(window.WithName("full-win")) require.NoError(t, svc.SetWindowFullscreen("full-win", true)) err := svc.SetWindowFullscreen("full-win", false) require.NoError(t, err) pw, ok := windowSvc.Manager().Get("full-win") require.True(t, ok) assert.False(t, pw.IsFullscreen()) } func TestGetWindowTitle_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("title-win"), window.WithTitle("Inspector")) title, err := svc.GetWindowTitle("title-win") require.NoError(t, err) assert.Equal(t, "Inspector", title) } func TestGetWindowTitle_Bad(t *testing.T) { svc, _ := newTestDisplayService(t) title, err := svc.GetWindowTitle("missing") require.Error(t, err) assert.Empty(t, title) } func TestGetWindowTitle_Ugly(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("title-win"), window.WithTitle("Line 1 \nTabbed")) title, err := svc.GetWindowTitle("title-win") require.NoError(t, err) assert.Equal(t, "Line 1 \nTabbed", title) } func TestMinimizeWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") windowSvc := core.MustServiceFor[*window.Service](c, "window") _ = svc.OpenWindow(window.WithName("min-win")) err := svc.MinimizeWindow("min-win") require.NoError(t, err) pw, ok := windowSvc.Manager().Get("min-win") require.True(t, ok) assert.True(t, pw.IsMinimised()) } func TestMinimizeWindow_Bad(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") err := svc.MinimizeWindow("missing") require.Error(t, err) } func TestMinimizeWindow_Ugly(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") windowSvc := core.MustServiceFor[*window.Service](c, "window") _ = svc.OpenWindow(window.WithName("min-win")) require.NoError(t, svc.MinimizeWindow("min-win")) err := svc.MinimizeWindow("min-win") require.NoError(t, err) pw, ok := windowSvc.Manager().Get("min-win") require.True(t, ok) assert.True(t, pw.IsMinimised()) } func TestHandleWSMessage_SetWindowOpacity_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("opacity-win")) r := svc.handleWSMessage(WSMessage{ Action: "window:set-opacity", Data: map[string]any{ "name": "opacity-win", "opacity": 0.35, }, }) require.True(t, r.OK) info, err := svc.GetWindowInfo("opacity-win") require.NoError(t, err) require.NotNil(t, info) assert.InDelta(t, 0.35, info.Opacity, 0.0001) } func TestDisplay_requireStringField_Good(t *testing.T) { value, err := requireStringField(map[string]any{"window": "main"}, "window") require.NoError(t, err) assert.Equal(t, "main", value) } func TestDisplay_requireStringField_Bad(t *testing.T) { value, err := requireStringField(map[string]any{"window": ""}, "window") require.Error(t, err) assert.Empty(t, value) } func TestDisplay_requireStringField_Ugly(t *testing.T) { value, err := requireStringField(map[string]any{"window": 42}, "window") require.Error(t, err) assert.Empty(t, value) } func TestDisplay_optionsFromMap_Good(t *testing.T) { opts := optionsFromMap(map[string]any{"alpha": "one", "beta": 2}) require.Equal(t, 2, opts.Len()) got := map[string]any{} for _, opt := range opts.Items() { got[opt.Key] = opt.Value } assert.True(t, reflect.DeepEqual(map[string]any{"alpha": "one", "beta": 2}, got)) } func TestDisplay_optionsFromMap_Bad(t *testing.T) { opts := optionsFromMap(nil) require.NotNil(t, opts) assert.Equal(t, 0, opts.Len()) } func TestDisplay_optionsFromMap_Ugly(t *testing.T) { opts := wsOptions(map[string]any{"nested": map[string]any{"value": "x"}}) require.Equal(t, 1, opts.Len()) item := opts.Items()[0] assert.Equal(t, "nested", item.Key) assert.Equal(t, map[string]any{"value": "x"}, item.Value) } func TestDisplay_handleWSMessage_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("opacity-win")) result := svc.handleWSMessage(WSMessage{ Action: "window:set-opacity", Data: map[string]any{ "name": "opacity-win", "opacity": 0.55, }, }) require.True(t, result.OK) info, err := svc.GetWindowInfo("opacity-win") require.NoError(t, err) require.NotNil(t, info) assert.InDelta(t, 0.55, info.Opacity, 0.0001) } func TestDisplay_handleWSMessage_Bad(t *testing.T) { svc, _ := newTestDisplayService(t) result := svc.handleWSMessage(WSMessage{Action: "unknown:action"}) require.False(t, result.OK) assert.Contains(t, result.Value.(error).Error(), "unknown websocket action") } func TestDisplay_handleWSMessage_Ugly(t *testing.T) { svc, _ := newTestDisplayService(t) result := svc.handleWSMessage(WSMessage{ Action: "window:set-opacity", Data: map[string]any{ "name": "main", }, }) require.False(t, result.OK) assert.Contains(t, result.Value.(error).Error(), "missing required field \"opacity\"") } func TestDisplay_handleWSMessage_LayoutCommands_Good(t *testing.T) { cases := []struct { name string action string msg WSMessage check func(*testing.T, core.Options) }{ { name: "LayoutBesideEditor", action: "window.layoutBesideEditor", msg: WSMessage{ Action: "layout:beside-editor", Data: map[string]any{ "name": "preview", "editor": "code", "side": "right", "ratio": 0.62, }, }, check: func(t *testing.T, opts core.Options) { t.Helper() task := opts.Get("task").Value.(window.TaskLayoutBesideEditor) assert.Equal(t, "preview", task.Name) assert.Equal(t, "code", task.Editor) assert.Equal(t, "right", task.Side) assert.InDelta(t, 0.62, task.Ratio, 0.0001) }, }, { name: "LayoutSuggest", action: "window.layoutSuggest", msg: WSMessage{ Action: "layout:suggest", Data: map[string]any{ "screen_id": "screen-1", "window_count": 3, }, }, check: func(t *testing.T, opts core.Options) { t.Helper() task := opts.Get("task").Value.(window.TaskLayoutSuggest) assert.Equal(t, "screen-1", task.ScreenID) assert.Equal(t, 3, task.WindowCount) }, }, { name: "FindScreenSpace", action: "window.findSpace", msg: WSMessage{ Action: "screen:find-space", Data: map[string]any{ "screen_id": "screen-1", "width": 800, "height": 600, "padding": 24, }, }, check: func(t *testing.T, opts core.Options) { t.Helper() task := opts.Get("task").Value.(window.TaskScreenFindSpace) assert.Equal(t, "screen-1", task.ScreenID) assert.Equal(t, 800, task.Width) assert.Equal(t, 600, task.Height) assert.Equal(t, 24, task.Padding) }, }, { name: "ArrangeWindowPair", action: "window.arrangePair", msg: WSMessage{ Action: "window:arrange-pair", Data: map[string]any{ "primary": "editor", "secondary": "preview", "screen_id": "screen-1", "ratio": 0.55, }, }, check: func(t *testing.T, opts core.Options) { t.Helper() task := opts.Get("task").Value.(window.TaskWindowArrangePair) assert.Equal(t, "editor", task.Primary) assert.Equal(t, "preview", task.Secondary) assert.Equal(t, "screen-1", task.ScreenID) assert.InDelta(t, 0.55, task.Ratio, 0.0001) }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { svc, c := newTestDisplayAPIService(t) called := false c.Action(tc.action, func(_ context.Context, opts core.Options) core.Result { called = true tc.check(t, opts) return core.Result{OK: true} }) result := svc.handleWSMessage(tc.msg) require.True(t, result.OK) assert.True(t, called) }) } } func TestDisplay_handleWSMessage_LayoutCommands_Bad(t *testing.T) { cases := []struct { name string action string msg WSMessage field string }{ { name: "LayoutBesideEditor", action: "window.layoutBesideEditor", msg: WSMessage{ Action: "layout:beside-editor", Data: map[string]any{ "name": "preview", "editor": "code", "side": "right", }, }, field: "ratio", }, { name: "LayoutSuggest", action: "window.layoutSuggest", msg: WSMessage{ Action: "layout:suggest", Data: map[string]any{ "screen_id": "screen-1", }, }, field: "window_count", }, { name: "FindScreenSpace", action: "window.findSpace", msg: WSMessage{ Action: "screen:find-space", Data: map[string]any{ "screen_id": "screen-1", "width": 800, "height": 600, }, }, field: "padding", }, { name: "ArrangeWindowPair", action: "window.arrangePair", msg: WSMessage{ Action: "window:arrange-pair", Data: map[string]any{ "primary": "editor", "secondary": "preview", "screen_id": "screen-1", }, }, field: "ratio", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { svc, c := newTestDisplayAPIService(t) called := false c.Action(tc.action, func(_ context.Context, _ core.Options) core.Result { called = true return core.Result{OK: true} }) result := svc.handleWSMessage(tc.msg) require.False(t, result.OK) assert.False(t, called) assert.Contains(t, result.Value.(error).Error(), "missing required field \""+tc.field+"\"") }) } } func TestDisplay_handleWSMessage_LayoutCommands_Ugly(t *testing.T) { cases := []struct { name string action string msg WSMessage field string }{ { name: "LayoutBesideEditor", action: "window.layoutBesideEditor", msg: WSMessage{ Action: "layout:beside-editor", Data: map[string]any{ "name": "preview", "editor": "code", "side": "right", "ratio": "0.62", }, }, field: "ratio", }, { name: "LayoutSuggest", action: "window.layoutSuggest", msg: WSMessage{ Action: "layout:suggest", Data: map[string]any{ "screen_id": "screen-1", "window_count": 2.5, }, }, field: "window_count", }, { name: "FindScreenSpace", action: "window.findSpace", msg: WSMessage{ Action: "screen:find-space", Data: map[string]any{ "screen_id": "screen-1", "width": "800", "height": 600, "padding": 24, }, }, field: "width", }, { name: "ArrangeWindowPair", action: "window.arrangePair", msg: WSMessage{ Action: "window:arrange-pair", Data: map[string]any{ "primary": "editor", "secondary": "preview", "screen_id": "screen-1", "ratio": true, }, }, field: "ratio", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { svc, c := newTestDisplayAPIService(t) called := false c.Action(tc.action, func(_ context.Context, _ core.Options) core.Result { called = true return core.Result{OK: true} }) result := svc.handleWSMessage(tc.msg) require.False(t, result.OK) assert.False(t, called) assert.Contains(t, result.Value.(error).Error(), "invalid required field \""+tc.field+"\"") }) } } func TestDisplay_handleTrayAction_Good(t *testing.T) { platform := window.NewMockPlatform() c := core.New( core.WithService(Register(nil)), core.WithService(window.Register(platform)), core.WithService(systray.Register(systray.NewMockPlatform())), core.WithService(menu.Register(menu.NewMockPlatform())), core.WithServiceLock(), ) require.True(t, c.ServiceStartup(context.Background(), nil).OK) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("one")) _ = svc.OpenWindow(window.WithName("two")) svc.handleTrayAction("open-desktop") require.Len(t, platform.Windows, 2) assert.True(t, platform.Windows[0].IsFocused()) assert.True(t, platform.Windows[1].IsFocused()) svc.handleTrayAction("close-desktop") assert.False(t, platform.Windows[0].IsVisible()) assert.False(t, platform.Windows[1].IsVisible()) } func TestGetFocusedWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("win-a")) _ = svc.OpenWindow(window.WithName("win-b")) _ = svc.FocusWindow("win-b") focused := svc.GetFocusedWindow() assert.Equal(t, "win-b", focused) } func TestGetFocusedWindow_Good_NoneSelected(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _ = svc.OpenWindow(window.WithName("win-a")) focused := svc.GetFocusedWindow() assert.Equal(t, "", focused) } func TestCreateWindow_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") info, err := svc.CreateWindow(CreateWindowOptions{ Name: "new-win", Title: "New Window", URL: "/new", Width: 600, Height: 400, }) require.NoError(t, err) assert.Equal(t, "new-win", info.Name) } func TestCreateWindow_Bad(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") _, err := svc.CreateWindow(CreateWindowOptions{}) assert.Error(t, err) assert.Contains(t, err.Error(), "window name is required") } func TestResetWindowState_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") err := svc.ResetWindowState() assert.NoError(t, err) } func TestGetSavedWindowStates_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") states := svc.GetSavedWindowStates() assert.NotNil(t, states) } func TestDisplay_PublicCollections_AreNilSafe(t *testing.T) { svc, _ := newTestDisplayService(t) infos := svc.ListWindowInfos() layouts := svc.ListLayouts() states := svc.GetSavedWindowStates() require.NotNil(t, infos) require.NotNil(t, layouts) require.NotNil(t, states) assert.Empty(t, infos) assert.Empty(t, layouts) assert.Empty(t, states) } func TestDisplay_WindowService_NilSafe(t *testing.T) { svc := &Service{} assert.NotPanics(t, func() { svc.ResetWindowState() }) assert.NotPanics(t, func() { states := svc.GetSavedWindowStates() require.NotNil(t, states) assert.Empty(t, states) }) } func TestHandleIPCEvents_WindowOpened_Good(t *testing.T) { c := newTestConclave(t) // Open a window — this should trigger ActionWindowOpened // which HandleIPCEvents should convert to a WS event r := taskRun(c, "window.open", window.TaskOpenWindow{ Window: &window.Window{Name: "test"}, }) require.True(t, r.OK) info := r.Value.(window.WindowInfo) assert.Equal(t, "test", info.Name) } func TestHandleListWorkspaces_Good(t *testing.T) { c := newTestConclave(t) svc := core.MustServiceFor[*Service](c, "display") // handleListWorkspaces should not panic when workspace service is not available assert.NotPanics(t, func() { svc.handleListWorkspaces() }) } func TestWSEventManager_Good(t *testing.T) { em := NewWSEventManager() defer em.Close() assert.NotNil(t, em) assert.Equal(t, 0, em.ConnectedClients()) } func TestService_OnShutdown_ClosesEventManager(t *testing.T) { em := NewWSEventManager() svc := &Service{events: em} server := httptest.NewServer(http.HandlerFunc(em.HandleWebSocket)) t.Cleanup(server.Close) wsURL := "ws" + strings.TrimPrefix(server.URL, "http") conn, _, err := websocket.DefaultDialer.Dial(wsURL, nil) require.NoError(t, err) defer func() { _ = conn.Close() }() defer em.Close() require.True(t, svc.OnShutdown(context.Background()).OK) assert.Nil(t, svc.events) require.Eventually(t, func() bool { return em.ConnectedClients() == 0 }, 2*time.Second, 20*time.Millisecond) _ = conn.SetReadDeadline(time.Now().Add(100 * time.Millisecond)) _, _, err = conn.ReadMessage() require.Error(t, err) } // --- Config file loading tests --- func TestLoadConfig_Good(t *testing.T) { // Create temp config file dir := t.TempDir() cfgPath := core.JoinPath(dir, ".core", "gui", "config.yaml") require.NoError(t, os.MkdirAll(core.PathDir(cfgPath), 0o755)) require.NoError(t, os.WriteFile(cfgPath, []byte(` window: default_width: 1280 default_height: 720 systray: tooltip: "Test App" menu: show_dev_tools: false `), 0o644)) s, _ := New() s.loadConfigFrom(cfgPath) // Verify configData was populated from file assert.Equal(t, 1280, s.configData["window"]["default_width"]) assert.Equal(t, "Test App", s.configData["systray"]["tooltip"]) assert.Equal(t, false, s.configData["menu"]["show_dev_tools"]) } func TestLoadConfig_Bad_MissingFile(t *testing.T) { s, _ := New() s.loadConfigFrom(core.JoinPath(t.TempDir(), "nonexistent.yaml")) // Should not panic, configData stays at empty defaults assert.Empty(t, s.configData["window"]) assert.Empty(t, s.configData["systray"]) assert.Empty(t, s.configData["menu"]) } func TestHandleConfigTask_Persists_Good(t *testing.T) { dir := t.TempDir() cfgPath := core.JoinPath(dir, "config.yaml") s, _ := New() s.loadConfigFrom(cfgPath) // Creates empty config (file doesn't exist yet) // Simulate a TaskSaveConfig through the handler c := core.New( core.WithService(func(c *core.Core) core.Result { s.ServiceRuntime = core.NewServiceRuntime[Options](c, Options{}) return core.Result{Value: s, OK: true} }), core.WithServiceLock(), ) c.ServiceStartup(context.Background(), nil) r := taskRun(c, "display.saveWindowConfig", window.TaskSaveConfig{ Config: map[string]any{"default_width": 1920}, }) require.True(t, r.OK) // Verify file was written data, err := os.ReadFile(cfgPath) require.NoError(t, err) assert.Contains(t, string(data), "default_width") } func TestDisplay_LayoutSuggest_Good(t *testing.T) { svc, c := newTestDisplayAPIService(t) var gotTask window.TaskLayoutSuggest c.Action("window.layoutSuggest", func(_ context.Context, opts core.Options) core.Result { gotTask = opts.Get("task").Value.(window.TaskLayoutSuggest) return core.Result{ Value: window.LayoutSuggestion{ Mode: "coding", Reason: "two-pane split", ScreenID: "screen-1", Width: 1280, Height: 720, }, OK: true, } }) got, err := svc.LayoutSuggest("screen-1", 2) require.NoError(t, err) assert.Equal(t, "coding", got.Mode) assert.Equal(t, "two-pane split", got.Reason) assert.Equal(t, "screen-1", got.ScreenID) assert.Equal(t, 1280, got.Width) assert.Equal(t, 720, got.Height) assert.Equal(t, "screen-1", gotTask.ScreenID) assert.Equal(t, 2, gotTask.WindowCount) } func TestDisplay_LayoutSuggest_Bad(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.layoutSuggest", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: assert.AnError, OK: false} }) got, err := svc.LayoutSuggest("", 0) require.Error(t, err) assert.Equal(t, window.LayoutSuggestion{}, got) assert.Equal(t, assert.AnError, err) } func TestDisplay_LayoutSuggest_Ugly(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.layoutSuggest", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: "unexpected", OK: true} }) got, err := svc.LayoutSuggest("screen-1", 1) require.Error(t, err) assert.Equal(t, window.LayoutSuggestion{}, got) assert.Contains(t, err.Error(), "unexpected result type") } func TestDisplay_GetLayout_Good(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result { switch typed := q.(type) { case window.QueryLayoutGet: assert.Equal(t, "development", typed.Name) return core.Result{ Value: &window.Layout{ Name: "development", Windows: map[string]window.WindowState{ "editor": {}, "terminal": {}, }, CreatedAt: 1, UpdatedAt: 2, }, OK: true, } default: return core.Result{} } }) got := svc.GetLayout("development") require.NotNil(t, got) assert.Equal(t, "development", got.Name) assert.Len(t, got.Windows, 2) assert.Equal(t, int64(1), got.CreatedAt) assert.Equal(t, int64(2), got.UpdatedAt) } func TestDisplay_GetLayout_Bad(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result { switch q.(type) { case window.QueryLayoutGet: return core.Result{Value: nil, OK: true} default: return core.Result{} } }) got := svc.GetLayout("missing") assert.Nil(t, got) } func TestDisplay_GetLayout_Ugly(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.RegisterQuery(func(_ *core.Core, q core.Query) core.Result { switch q.(type) { case window.QueryLayoutGet: return core.Result{Value: "unexpected", OK: true} default: return core.Result{} } }) got := svc.GetLayout("broken") assert.Nil(t, got) } func TestDisplay_SaveLayout_Good(t *testing.T) { svc, c := newTestDisplayAPIService(t) var gotTask window.TaskSaveLayout c.Action("window.saveLayout", func(_ context.Context, opts core.Options) core.Result { gotTask = opts.Get("task").Value.(window.TaskSaveLayout) return core.Result{OK: true} }) err := svc.SaveLayout("development") require.NoError(t, err) assert.Equal(t, "development", gotTask.Name) } func TestDisplay_SaveLayout_Bad(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.saveLayout", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: assert.AnError, OK: false} }) err := svc.SaveLayout("development") require.Error(t, err) assert.Equal(t, assert.AnError, err) } func TestDisplay_SaveLayout_Ugly(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.saveLayout", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: "unexpected", OK: false} }) err := svc.SaveLayout("") require.Error(t, err) assert.Contains(t, err.Error(), "window.saveLayout") } func TestDisplay_RestoreLayout_Good(t *testing.T) { svc, c := newTestDisplayAPIService(t) var gotTask window.TaskRestoreLayout c.Action("window.restoreLayout", func(_ context.Context, opts core.Options) core.Result { gotTask = opts.Get("task").Value.(window.TaskRestoreLayout) return core.Result{OK: true} }) err := svc.RestoreLayout("development") require.NoError(t, err) assert.Equal(t, "development", gotTask.Name) } func TestDisplay_RestoreLayout_Bad(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.restoreLayout", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: assert.AnError, OK: false} }) err := svc.RestoreLayout("development") require.Error(t, err) assert.Equal(t, assert.AnError, err) } func TestDisplay_RestoreLayout_Ugly(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.restoreLayout", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: "unexpected", OK: false} }) err := svc.RestoreLayout("") require.Error(t, err) assert.Contains(t, err.Error(), "window.restoreLayout") } func TestDisplay_SetWindowBackgroundColour_Good(t *testing.T) { svc, c := newTestDisplayAPIService(t) var gotTask window.TaskSetBackgroundColour c.Action("window.setBackgroundColour", func(_ context.Context, opts core.Options) core.Result { gotTask = opts.Get("task").Value.(window.TaskSetBackgroundColour) return core.Result{OK: true} }) err := svc.SetWindowBackgroundColour("main", 1, 2, 3, 4) require.NoError(t, err) assert.Equal(t, "main", gotTask.Name) assert.Equal(t, uint8(1), gotTask.Red) assert.Equal(t, uint8(2), gotTask.Green) assert.Equal(t, uint8(3), gotTask.Blue) assert.Equal(t, uint8(4), gotTask.Alpha) } func TestDisplay_SetWindowBackgroundColour_Bad(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.setBackgroundColour", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: assert.AnError, OK: false} }) err := svc.SetWindowBackgroundColour("main", 1, 2, 3, 4) require.Error(t, err) assert.Equal(t, assert.AnError, err) } func TestDisplay_SetWindowBackgroundColour_Ugly(t *testing.T) { svc, c := newTestDisplayAPIService(t) c.Action("window.setBackgroundColour", func(_ context.Context, _ core.Options) core.Result { return core.Result{Value: "unexpected", OK: false} }) err := svc.SetWindowBackgroundColour("", 0, 0, 0, 0) require.Error(t, err) assert.Contains(t, err.Error(), "window.setBackgroundColour") }