refactor(session): rename lifecycle states
- commit
- 01fd03bdf583da7890aad807a44133cb0ee60a2c
- author
- brice · · 2026-08-07 09:49 UTC
- parent
- 16ea2ba
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
31 files · +207 -150
M internal/adapters/remote/catalog.go +1 -1
| @@ -185,7 +185,7 @@ func rejectTrailingJSON(dec *json.Decoder) error { | |||
| 185 | 185 | ||
| 186 | 186 | func validCatalogState(state string) bool { | |
| 187 | 187 | switch state { | |
| 188 | - | case "running", "stopped", "broken": | |
| 188 | + | case "up", "down", "broken": | |
| 189 | 189 | return true | |
| 190 | 190 | default: | |
| 191 | 191 | return false | |
| @@ -185,7 +185,7 @@ func rejectTrailingJSON(dec *json.Decoder) error { | |||||
| 185 | 185 | ||||
| 186 | func validCatalogState(state string) bool { | 186 | func validCatalogState(state string) bool { | ||
| 187 | switch state { | 187 | switch state { | ||
| 188 | - | case "running", "stopped", "broken": | 188 | + | case "up", "down", "broken": |
| 189 | return true | 189 | return true | ||
| 190 | default: | 190 | default: | ||
| 191 | return false | 191 | return false | ||
M internal/adapters/remote/catalog_cache_test.go +5 -5
| @@ -30,8 +30,8 @@ func TestCatalogCacheStoreLoad(t *testing.T) { | |||
| 30 | 30 | Host: "zebra", | |
| 31 | 31 | FetchedAt: fetchedAt, | |
| 32 | 32 | Sessions: []ports.RemoteCatalogSession{ | |
| 33 | - | {Name: "work", State: "running", Tabs: 2}, | |
| 34 | - | {Name: "alpha", State: "stopped", Ephemeral: true, Tabs: 1, Attached: true}, | |
| 33 | + | {Name: "work", State: "up", Tabs: 2}, | |
| 34 | + | {Name: "alpha", State: "down", Ephemeral: true, Tabs: 1, Attached: true}, | |
| 35 | 35 | }, | |
| 36 | 36 | }, | |
| 37 | 37 | { | |
| @@ -72,8 +72,8 @@ func TestCatalogCacheStoreLoad(t *testing.T) { | |||
| 72 | 72 | Host: "zebra", | |
| 73 | 73 | FetchedAt: fetchedAt, | |
| 74 | 74 | Sessions: []ports.RemoteCatalogSession{ | |
| 75 | - | {Name: "alpha", State: "stopped", Ephemeral: true, Tabs: 1, Attached: true}, | |
| 76 | - | {Name: "work", State: "running", Tabs: 2}, | |
| 75 | + | {Name: "alpha", State: "down", Ephemeral: true, Tabs: 1, Attached: true}, | |
| 76 | + | {Name: "work", State: "up", Tabs: 2}, | |
| 77 | 77 | }, | |
| 78 | 78 | }, | |
| 79 | 79 | } | |
| @@ -122,7 +122,7 @@ func TestCatalogCacheLoadRejectsInvalidFilesWithoutReplacingThem(t *testing.T) { | |||
| 122 | 122 | {name: "zero fetched at", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":0,"sessions":[]}]}`)}, | |
| 123 | 123 | {name: "negative fetched at", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":-1,"sessions":[]}]}`)}, | |
| 124 | 124 | {name: "duplicate hosts", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":1,"sessions":[]},{"target":"arch","fetched_at_unix_nano":2,"sessions":[]}]}`)}, | |
| 125 | - | {name: "duplicate sessions", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":1,"sessions":[{"name":"work","state":"running","ephemeral":false,"tabs":1,"attached":false},{"name":"work","state":"running","ephemeral":false,"tabs":1,"attached":false}]}]}`)}, | |
| 125 | + | {name: "duplicate sessions", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":1,"sessions":[{"name":"work","state":"up","ephemeral":false,"tabs":1,"attached":false},{"name":"work","state":"up","ephemeral":false,"tabs":1,"attached":false}]}]}`)}, | |
| 126 | 126 | {name: "invalid utf8", raw: []byte("{\"version\":1,\"hosts\":[{\"target\":\"\xff\",\"fetched_at_unix_nano\":1,\"sessions\":[]}]}")}, | |
| 127 | 127 | } | |
| 128 | 128 | for _, tc := range cases { | |
| @@ -30,8 +30,8 @@ func TestCatalogCacheStoreLoad(t *testing.T) { | |||||
| 30 | Host: "zebra", | 30 | Host: "zebra", | ||
| 31 | FetchedAt: fetchedAt, | 31 | FetchedAt: fetchedAt, | ||
| 32 | Sessions: []ports.RemoteCatalogSession{ | 32 | Sessions: []ports.RemoteCatalogSession{ | ||
| 33 | - | {Name: "work", State: "running", Tabs: 2}, | 33 | + | {Name: "work", State: "up", Tabs: 2}, |
| 34 | - | {Name: "alpha", State: "stopped", Ephemeral: true, Tabs: 1, Attached: true}, | 34 | + | {Name: "alpha", State: "down", Ephemeral: true, Tabs: 1, Attached: true}, |
| 35 | }, | 35 | }, | ||
| 36 | }, | 36 | }, | ||
| 37 | { | 37 | { | ||
| @@ -72,8 +72,8 @@ func TestCatalogCacheStoreLoad(t *testing.T) { | |||||
| 72 | Host: "zebra", | 72 | Host: "zebra", | ||
| 73 | FetchedAt: fetchedAt, | 73 | FetchedAt: fetchedAt, | ||
| 74 | Sessions: []ports.RemoteCatalogSession{ | 74 | Sessions: []ports.RemoteCatalogSession{ | ||
| 75 | - | {Name: "alpha", State: "stopped", Ephemeral: true, Tabs: 1, Attached: true}, | 75 | + | {Name: "alpha", State: "down", Ephemeral: true, Tabs: 1, Attached: true}, |
| 76 | - | {Name: "work", State: "running", Tabs: 2}, | 76 | + | {Name: "work", State: "up", Tabs: 2}, |
| 77 | }, | 77 | }, | ||
| 78 | }, | 78 | }, | ||
| 79 | } | 79 | } | ||
| @@ -122,7 +122,7 @@ func TestCatalogCacheLoadRejectsInvalidFilesWithoutReplacingThem(t *testing.T) { | |||||
| 122 | {name: "zero fetched at", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":0,"sessions":[]}]}`)}, | 122 | {name: "zero fetched at", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":0,"sessions":[]}]}`)}, | ||
| 123 | {name: "negative fetched at", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":-1,"sessions":[]}]}`)}, | 123 | {name: "negative fetched at", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":-1,"sessions":[]}]}`)}, | ||
| 124 | {name: "duplicate hosts", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":1,"sessions":[]},{"target":"arch","fetched_at_unix_nano":2,"sessions":[]}]}`)}, | 124 | {name: "duplicate hosts", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":1,"sessions":[]},{"target":"arch","fetched_at_unix_nano":2,"sessions":[]}]}`)}, | ||
| 125 | - | {name: "duplicate sessions", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":1,"sessions":[{"name":"work","state":"running","ephemeral":false,"tabs":1,"attached":false},{"name":"work","state":"running","ephemeral":false,"tabs":1,"attached":false}]}]}`)}, | 125 | + | {name: "duplicate sessions", raw: []byte(`{"version":1,"hosts":[{"target":"arch","fetched_at_unix_nano":1,"sessions":[{"name":"work","state":"up","ephemeral":false,"tabs":1,"attached":false},{"name":"work","state":"up","ephemeral":false,"tabs":1,"attached":false}]}]}`)}, |
| 126 | {name: "invalid utf8", raw: []byte("{\"version\":1,\"hosts\":[{\"target\":\"\xff\",\"fetched_at_unix_nano\":1,\"sessions\":[]}]}")}, | 126 | {name: "invalid utf8", raw: []byte("{\"version\":1,\"hosts\":[{\"target\":\"\xff\",\"fetched_at_unix_nano\":1,\"sessions\":[]}]}")}, | ||
| 127 | } | 127 | } | ||
| 128 | for _, tc := range cases { | 128 | for _, tc := range cases { | ||
M internal/adapters/remote/catalog_test.go +2 -2
| @@ -83,7 +83,7 @@ func TestRemoteCatalogClientDecode(t *testing.T) { | |||
| 83 | 83 | valid := ports.RemoteCatalog{ | |
| 84 | 84 | ProtocolVersion: ports.ProtocolVersion, | |
| 85 | 85 | Sessions: []ports.RemoteCatalogSession{ | |
| 86 | - | {Name: "work", State: "running", Ephemeral: false, Tabs: 2, Attached: true}, | |
| 86 | + | {Name: "work", State: "up", Ephemeral: false, Tabs: 2, Attached: true}, | |
| 87 | 87 | }, | |
| 88 | 88 | } | |
| 89 | 89 | validJSON := mustCatalogJSON(t, valid) | |
| @@ -148,7 +148,7 @@ func TestRemoteCatalogClientDecode(t *testing.T) { | |||
| 148 | 148 | }, | |
| 149 | 149 | { | |
| 150 | 150 | name: "invalid session name", | |
| 151 | - | stdout: mustCatalogJSON(t, ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "bad name", State: "running"}}}), | |
| 151 | + | stdout: mustCatalogJSON(t, ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "bad name", State: "up"}}}), | |
| 152 | 152 | wantErr: errCatalogSession, | |
| 153 | 153 | }, | |
| 154 | 154 | { | |
| @@ -83,7 +83,7 @@ func TestRemoteCatalogClientDecode(t *testing.T) { | |||||
| 83 | valid := ports.RemoteCatalog{ | 83 | valid := ports.RemoteCatalog{ | ||
| 84 | ProtocolVersion: ports.ProtocolVersion, | 84 | ProtocolVersion: ports.ProtocolVersion, | ||
| 85 | Sessions: []ports.RemoteCatalogSession{ | 85 | Sessions: []ports.RemoteCatalogSession{ | ||
| 86 | - | {Name: "work", State: "running", Ephemeral: false, Tabs: 2, Attached: true}, | 86 | + | {Name: "work", State: "up", Ephemeral: false, Tabs: 2, Attached: true}, |
| 87 | }, | 87 | }, | ||
| 88 | } | 88 | } | ||
| 89 | validJSON := mustCatalogJSON(t, valid) | 89 | validJSON := mustCatalogJSON(t, valid) | ||
| @@ -148,7 +148,7 @@ func TestRemoteCatalogClientDecode(t *testing.T) { | |||||
| 148 | }, | 148 | }, | ||
| 149 | { | 149 | { | ||
| 150 | name: "invalid session name", | 150 | name: "invalid session name", | ||
| 151 | - | stdout: mustCatalogJSON(t, ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "bad name", State: "running"}}}), | 151 | + | stdout: mustCatalogJSON(t, ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "bad name", State: "up"}}}), |
| 152 | wantErr: errCatalogSession, | 152 | wantErr: errCatalogSession, | ||
| 153 | }, | 153 | }, | ||
| 154 | { | 154 | { | ||
M internal/app/remote_hosts.go +4 -4
| @@ -205,10 +205,10 @@ func catalogSessionsAsInfo(host string, sessions []ports.RemoteCatalogSession) [ | |||
| 205 | 205 | Attached: session.Attached, | |
| 206 | 206 | } | |
| 207 | 207 | switch session.State { | |
| 208 | - | case "running": | |
| 209 | - | info.State = ports.SessionRunning | |
| 210 | - | case "stopped": | |
| 211 | - | info.State = ports.SessionStopped | |
| 208 | + | case "up": | |
| 209 | + | info.State = ports.SessionUp | |
| 210 | + | case "down": | |
| 211 | + | info.State = ports.SessionDown | |
| 212 | 212 | case "broken": | |
| 213 | 213 | info.State = ports.SessionBroken | |
| 214 | 214 | default: | |
| @@ -205,10 +205,10 @@ func catalogSessionsAsInfo(host string, sessions []ports.RemoteCatalogSession) [ | |||||
| 205 | Attached: session.Attached, | 205 | Attached: session.Attached, | ||
| 206 | } | 206 | } | ||
| 207 | switch session.State { | 207 | switch session.State { | ||
| 208 | - | case "running": | 208 | + | case "up": |
| 209 | - | info.State = ports.SessionRunning | 209 | + | info.State = ports.SessionUp |
| 210 | - | case "stopped": | 210 | + | case "down": |
| 211 | - | info.State = ports.SessionStopped | 211 | + | info.State = ports.SessionDown |
| 212 | case "broken": | 212 | case "broken": | ||
| 213 | info.State = ports.SessionBroken | 213 | info.State = ports.SessionBroken | ||
| 214 | default: | 214 | default: | ||
M internal/app/remote_test.go +9 -9
| @@ -171,7 +171,7 @@ func TestRemoteHostListingUsesUnifiedStore(t *testing.T) { | |||
| 171 | 171 | store := portsmocks.NewMockRemoteHostStore(t) | |
| 172 | 172 | store.EXPECT().Hosts().Return([]string{"arch"}, []string{"beta"}, nil).Once() | |
| 173 | 173 | catalog := portsmocks.NewMockRemoteCatalogClient(t) | |
| 174 | - | catalog.EXPECT().List(mock.Anything, "arch").Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "build", State: "running"}}}, nil).Once() | |
| 174 | + | catalog.EXPECT().List(mock.Anything, "arch").Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "build", State: "up"}}}, nil).Once() | |
| 175 | 175 | var out bytes.Buffer | |
| 176 | 176 | err := runRemoteList(context.Background(), command{listHost: "arch"}, remoteHostDeps{ | |
| 177 | 177 | store: store, | |
| @@ -194,7 +194,7 @@ func TestListAllSessionsInvariants(t *testing.T) { | |||
| 194 | 194 | var out bytes.Buffer | |
| 195 | 195 | err := listAllSessions(context.Background(), remoteHostDeps{ | |
| 196 | 196 | localList: func(context.Context) ([]ports.SessionInfo, error) { | |
| 197 | - | return []ports.SessionInfo{{Name: "local", State: ports.SessionRunning}}, localErr | |
| 197 | + | return []ports.SessionInfo{{Name: "local", State: ports.SessionUp}}, localErr | |
| 198 | 198 | }, | |
| 199 | 199 | stdout: &out, | |
| 200 | 200 | }, nil) | |
| @@ -210,13 +210,13 @@ func TestListAllSessionsInvariants(t *testing.T) { | |||
| 210 | 210 | catalog := portsmocks.NewMockRemoteCatalogClient(t) | |
| 211 | 211 | catalog.EXPECT().List(mock.Anything, "arch").RunAndReturn(func(context.Context, string) (ports.RemoteCatalog, error) { | |
| 212 | 212 | cancel() | |
| 213 | - | return ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "running"}}}, nil | |
| 213 | + | return ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "up"}}}, nil | |
| 214 | 214 | }).Once() | |
| 215 | 215 | var out bytes.Buffer | |
| 216 | 216 | err := listAllSessions(ctx, remoteHostDeps{ | |
| 217 | 217 | catalog: catalog, | |
| 218 | 218 | localList: func(context.Context) ([]ports.SessionInfo, error) { | |
| 219 | - | return []ports.SessionInfo{{Name: "local", State: ports.SessionRunning}}, nil | |
| 219 | + | return []ports.SessionInfo{{Name: "local", State: ports.SessionUp}}, nil | |
| 220 | 220 | }, | |
| 221 | 221 | stdout: &out, | |
| 222 | 222 | }, []domain.RemoteHost{{Target: "arch"}, {Target: "mule"}}) | |
| @@ -233,15 +233,15 @@ func TestListAllSessionsInvariants(t *testing.T) { | |||
| 233 | 233 | var calls []string | |
| 234 | 234 | catalog.EXPECT().List(mock.Anything, "arch").Run(func(_ context.Context, target string) { | |
| 235 | 235 | calls = append(calls, target) | |
| 236 | - | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "running"}}}, nil).Once() | |
| 236 | + | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "up"}}}, nil).Once() | |
| 237 | 237 | catalog.EXPECT().List(mock.Anything, "mule").Run(func(_ context.Context, target string) { | |
| 238 | 238 | calls = append(calls, target) | |
| 239 | - | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "ops", State: "running"}}}, nil).Once() | |
| 239 | + | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "ops", State: "up"}}}, nil).Once() | |
| 240 | 240 | var out bytes.Buffer | |
| 241 | 241 | err := listAllSessions(context.Background(), remoteHostDeps{ | |
| 242 | 242 | catalog: catalog, | |
| 243 | 243 | localList: func(context.Context) ([]ports.SessionInfo, error) { | |
| 244 | - | return []ports.SessionInfo{{Name: "local", State: ports.SessionRunning}}, nil | |
| 244 | + | return []ports.SessionInfo{{Name: "local", State: ports.SessionUp}}, nil | |
| 245 | 245 | }, | |
| 246 | 246 | stdout: &out, | |
| 247 | 247 | }, []domain.RemoteHost{{Target: "arch"}, {Target: "mule"}}) | |
| @@ -265,8 +265,8 @@ func TestCatalogSessionsAsInfoInvariants(t *testing.T) { | |||
| 265 | 265 | wantState ports.SessionState | |
| 266 | 266 | wantTabs uint16 | |
| 267 | 267 | }{ | |
| 268 | - | {name: "running", session: ports.RemoteCatalogSession{Name: "dev", State: "running", Tabs: 2}, wantState: ports.SessionRunning, wantTabs: 2}, | |
| 269 | - | {name: "stopped", session: ports.RemoteCatalogSession{Name: "dev", State: "stopped", Tabs: 2}, wantState: ports.SessionStopped, wantTabs: 2}, | |
| 268 | + | {name: "up", session: ports.RemoteCatalogSession{Name: "dev", State: "up", Tabs: 2}, wantState: ports.SessionUp, wantTabs: 2}, | |
| 269 | + | {name: "down", session: ports.RemoteCatalogSession{Name: "dev", State: "down", Tabs: 2}, wantState: ports.SessionDown, wantTabs: 2}, | |
| 270 | 270 | {name: "broken", session: ports.RemoteCatalogSession{Name: "dev", State: "broken", Tabs: 2}, wantState: ports.SessionBroken, wantTabs: 2}, | |
| 271 | 271 | {name: "unknown fails closed", session: ports.RemoteCatalogSession{Name: "dev", State: "unknown", Tabs: 2}, wantState: ports.SessionBroken, wantTabs: 2}, | |
| 272 | 272 | } | |
| @@ -171,7 +171,7 @@ func TestRemoteHostListingUsesUnifiedStore(t *testing.T) { | |||||
| 171 | store := portsmocks.NewMockRemoteHostStore(t) | 171 | store := portsmocks.NewMockRemoteHostStore(t) | ||
| 172 | store.EXPECT().Hosts().Return([]string{"arch"}, []string{"beta"}, nil).Once() | 172 | store.EXPECT().Hosts().Return([]string{"arch"}, []string{"beta"}, nil).Once() | ||
| 173 | catalog := portsmocks.NewMockRemoteCatalogClient(t) | 173 | catalog := portsmocks.NewMockRemoteCatalogClient(t) | ||
| 174 | - | catalog.EXPECT().List(mock.Anything, "arch").Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "build", State: "running"}}}, nil).Once() | 174 | + | catalog.EXPECT().List(mock.Anything, "arch").Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "build", State: "up"}}}, nil).Once() |
| 175 | var out bytes.Buffer | 175 | var out bytes.Buffer | ||
| 176 | err := runRemoteList(context.Background(), command{listHost: "arch"}, remoteHostDeps{ | 176 | err := runRemoteList(context.Background(), command{listHost: "arch"}, remoteHostDeps{ | ||
| 177 | store: store, | 177 | store: store, | ||
| @@ -194,7 +194,7 @@ func TestListAllSessionsInvariants(t *testing.T) { | |||||
| 194 | var out bytes.Buffer | 194 | var out bytes.Buffer | ||
| 195 | err := listAllSessions(context.Background(), remoteHostDeps{ | 195 | err := listAllSessions(context.Background(), remoteHostDeps{ | ||
| 196 | localList: func(context.Context) ([]ports.SessionInfo, error) { | 196 | localList: func(context.Context) ([]ports.SessionInfo, error) { | ||
| 197 | - | return []ports.SessionInfo{{Name: "local", State: ports.SessionRunning}}, localErr | 197 | + | return []ports.SessionInfo{{Name: "local", State: ports.SessionUp}}, localErr |
| 198 | }, | 198 | }, | ||
| 199 | stdout: &out, | 199 | stdout: &out, | ||
| 200 | }, nil) | 200 | }, nil) | ||
| @@ -210,13 +210,13 @@ func TestListAllSessionsInvariants(t *testing.T) { | |||||
| 210 | catalog := portsmocks.NewMockRemoteCatalogClient(t) | 210 | catalog := portsmocks.NewMockRemoteCatalogClient(t) | ||
| 211 | catalog.EXPECT().List(mock.Anything, "arch").RunAndReturn(func(context.Context, string) (ports.RemoteCatalog, error) { | 211 | catalog.EXPECT().List(mock.Anything, "arch").RunAndReturn(func(context.Context, string) (ports.RemoteCatalog, error) { | ||
| 212 | cancel() | 212 | cancel() | ||
| 213 | - | return ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "running"}}}, nil | 213 | + | return ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "up"}}}, nil |
| 214 | }).Once() | 214 | }).Once() | ||
| 215 | var out bytes.Buffer | 215 | var out bytes.Buffer | ||
| 216 | err := listAllSessions(ctx, remoteHostDeps{ | 216 | err := listAllSessions(ctx, remoteHostDeps{ | ||
| 217 | catalog: catalog, | 217 | catalog: catalog, | ||
| 218 | localList: func(context.Context) ([]ports.SessionInfo, error) { | 218 | localList: func(context.Context) ([]ports.SessionInfo, error) { | ||
| 219 | - | return []ports.SessionInfo{{Name: "local", State: ports.SessionRunning}}, nil | 219 | + | return []ports.SessionInfo{{Name: "local", State: ports.SessionUp}}, nil |
| 220 | }, | 220 | }, | ||
| 221 | stdout: &out, | 221 | stdout: &out, | ||
| 222 | }, []domain.RemoteHost{{Target: "arch"}, {Target: "mule"}}) | 222 | }, []domain.RemoteHost{{Target: "arch"}, {Target: "mule"}}) | ||
| @@ -233,15 +233,15 @@ func TestListAllSessionsInvariants(t *testing.T) { | |||||
| 233 | var calls []string | 233 | var calls []string | ||
| 234 | catalog.EXPECT().List(mock.Anything, "arch").Run(func(_ context.Context, target string) { | 234 | catalog.EXPECT().List(mock.Anything, "arch").Run(func(_ context.Context, target string) { | ||
| 235 | calls = append(calls, target) | 235 | calls = append(calls, target) | ||
| 236 | - | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "running"}}}, nil).Once() | 236 | + | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "dev", State: "up"}}}, nil).Once() |
| 237 | catalog.EXPECT().List(mock.Anything, "mule").Run(func(_ context.Context, target string) { | 237 | catalog.EXPECT().List(mock.Anything, "mule").Run(func(_ context.Context, target string) { | ||
| 238 | calls = append(calls, target) | 238 | calls = append(calls, target) | ||
| 239 | - | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "ops", State: "running"}}}, nil).Once() | 239 | + | }).Return(ports.RemoteCatalog{Sessions: []ports.RemoteCatalogSession{{Name: "ops", State: "up"}}}, nil).Once() |
| 240 | var out bytes.Buffer | 240 | var out bytes.Buffer | ||
| 241 | err := listAllSessions(context.Background(), remoteHostDeps{ | 241 | err := listAllSessions(context.Background(), remoteHostDeps{ | ||
| 242 | catalog: catalog, | 242 | catalog: catalog, | ||
| 243 | localList: func(context.Context) ([]ports.SessionInfo, error) { | 243 | localList: func(context.Context) ([]ports.SessionInfo, error) { | ||
| 244 | - | return []ports.SessionInfo{{Name: "local", State: ports.SessionRunning}}, nil | 244 | + | return []ports.SessionInfo{{Name: "local", State: ports.SessionUp}}, nil |
| 245 | }, | 245 | }, | ||
| 246 | stdout: &out, | 246 | stdout: &out, | ||
| 247 | }, []domain.RemoteHost{{Target: "arch"}, {Target: "mule"}}) | 247 | }, []domain.RemoteHost{{Target: "arch"}, {Target: "mule"}}) | ||
| @@ -265,8 +265,8 @@ func TestCatalogSessionsAsInfoInvariants(t *testing.T) { | |||||
| 265 | wantState ports.SessionState | 265 | wantState ports.SessionState | ||
| 266 | wantTabs uint16 | 266 | wantTabs uint16 | ||
| 267 | }{ | 267 | }{ | ||
| 268 | - | {name: "running", session: ports.RemoteCatalogSession{Name: "dev", State: "running", Tabs: 2}, wantState: ports.SessionRunning, wantTabs: 2}, | 268 | + | {name: "up", session: ports.RemoteCatalogSession{Name: "dev", State: "up", Tabs: 2}, wantState: ports.SessionUp, wantTabs: 2}, |
| 269 | - | {name: "stopped", session: ports.RemoteCatalogSession{Name: "dev", State: "stopped", Tabs: 2}, wantState: ports.SessionStopped, wantTabs: 2}, | 269 | + | {name: "down", session: ports.RemoteCatalogSession{Name: "dev", State: "down", Tabs: 2}, wantState: ports.SessionDown, wantTabs: 2}, |
| 270 | {name: "broken", session: ports.RemoteCatalogSession{Name: "dev", State: "broken", Tabs: 2}, wantState: ports.SessionBroken, wantTabs: 2}, | 270 | {name: "broken", session: ports.RemoteCatalogSession{Name: "dev", State: "broken", Tabs: 2}, wantState: ports.SessionBroken, wantTabs: 2}, | ||
| 271 | {name: "unknown fails closed", session: ports.RemoteCatalogSession{Name: "dev", State: "unknown", Tabs: 2}, wantState: ports.SessionBroken, wantTabs: 2}, | 271 | {name: "unknown fails closed", session: ports.RemoteCatalogSession{Name: "dev", State: "unknown", Tabs: 2}, wantState: ports.SessionBroken, wantTabs: 2}, | ||
| 272 | } | 272 | } | ||
M internal/app/run.go +4 -4
| @@ -1434,7 +1434,7 @@ func listLocalSessions(ctx context.Context) (_ []ports.SessionInfo, retErr error | |||
| 1434 | 1434 | } | |
| 1435 | 1435 | infos := make([]ports.SessionInfo, 0, len(records)) | |
| 1436 | 1436 | for _, r := range records { | |
| 1437 | - | state := ports.SessionStopped | |
| 1437 | + | state := ports.SessionDown | |
| 1438 | 1438 | if r.DegradedReason != "" { | |
| 1439 | 1439 | state = ports.SessionBroken | |
| 1440 | 1440 | } | |
| @@ -1481,12 +1481,12 @@ func printSessions(w io.Writer, sessions []ports.SessionInfo) { | |||
| 1481 | 1481 | tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) | |
| 1482 | 1482 | _, _ = fmt.Fprintln(tw, "NAME\tSTATE\tTABS\tATTACHED") | |
| 1483 | 1483 | for _, s := range sessions { | |
| 1484 | - | state := "running" | |
| 1484 | + | state := "up" | |
| 1485 | 1485 | tabs := fmt.Sprintf("%d", s.Tabs) | |
| 1486 | 1486 | attached := "no" | |
| 1487 | 1487 | switch s.State { | |
| 1488 | - | case ports.SessionStopped: | |
| 1489 | - | state = "stopped" | |
| 1488 | + | case ports.SessionDown: | |
| 1489 | + | state = "down" | |
| 1490 | 1490 | tabs = "-" | |
| 1491 | 1491 | case ports.SessionBroken: | |
| 1492 | 1492 | state = "broken" | |
| @@ -1434,7 +1434,7 @@ func listLocalSessions(ctx context.Context) (_ []ports.SessionInfo, retErr error | |||||
| 1434 | } | 1434 | } | ||
| 1435 | infos := make([]ports.SessionInfo, 0, len(records)) | 1435 | infos := make([]ports.SessionInfo, 0, len(records)) | ||
| 1436 | for _, r := range records { | 1436 | for _, r := range records { | ||
| 1437 | - | state := ports.SessionStopped | 1437 | + | state := ports.SessionDown |
| 1438 | if r.DegradedReason != "" { | 1438 | if r.DegradedReason != "" { | ||
| 1439 | state = ports.SessionBroken | 1439 | state = ports.SessionBroken | ||
| 1440 | } | 1440 | } | ||
| @@ -1481,12 +1481,12 @@ func printSessions(w io.Writer, sessions []ports.SessionInfo) { | |||||
| 1481 | tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) | 1481 | tw := tabwriter.NewWriter(w, 0, 4, 2, ' ', 0) | ||
| 1482 | _, _ = fmt.Fprintln(tw, "NAME\tSTATE\tTABS\tATTACHED") | 1482 | _, _ = fmt.Fprintln(tw, "NAME\tSTATE\tTABS\tATTACHED") | ||
| 1483 | for _, s := range sessions { | 1483 | for _, s := range sessions { | ||
| 1484 | - | state := "running" | 1484 | + | state := "up" |
| 1485 | tabs := fmt.Sprintf("%d", s.Tabs) | 1485 | tabs := fmt.Sprintf("%d", s.Tabs) | ||
| 1486 | attached := "no" | 1486 | attached := "no" | ||
| 1487 | switch s.State { | 1487 | switch s.State { | ||
| 1488 | - | case ports.SessionStopped: | 1488 | + | case ports.SessionDown: |
| 1489 | - | state = "stopped" | 1489 | + | state = "down" |
| 1490 | tabs = "-" | 1490 | tabs = "-" | ||
| 1491 | case ports.SessionBroken: | 1491 | case ports.SessionBroken: | ||
| 1492 | state = "broken" | 1492 | state = "broken" | ||
M internal/app/run_test.go +13 -13
| @@ -588,26 +588,26 @@ func TestAttachTargetsCreateCommonSessionRequest(t *testing.T) { | |||
| 588 | 588 | func TestListShowsBroken(t *testing.T) { | |
| 589 | 589 | var out bytes.Buffer | |
| 590 | 590 | printSessions(&out, []ports.SessionInfo{ | |
| 591 | - | {Name: "fresh", State: ports.SessionStopped}, | |
| 592 | - | {Name: "loading", State: ports.SessionStopped}, | |
| 591 | + | {Name: "fresh", State: ports.SessionDown}, | |
| 592 | + | {Name: "loading", State: ports.SessionDown}, | |
| 593 | 593 | {Name: "broken", State: ports.SessionBroken}, | |
| 594 | 594 | }) | |
| 595 | 595 | require.Contains(t, out.String(), "fresh") | |
| 596 | - | require.Contains(t, out.String(), "stopped") | |
| 596 | + | require.Contains(t, out.String(), "down") | |
| 597 | 597 | require.Contains(t, out.String(), "loading") | |
| 598 | 598 | require.Contains(t, out.String(), "broken") | |
| 599 | 599 | require.NotContains(t, out.String(), "restoring") | |
| 600 | 600 | require.NotContains(t, out.String(), "degraded") | |
| 601 | 601 | } | |
| 602 | 602 | ||
| 603 | - | func TestPrintSessionsShowsStoppedState(t *testing.T) { | |
| 603 | + | func TestPrintSessionsShowsDownState(t *testing.T) { | |
| 604 | 604 | var out bytes.Buffer | |
| 605 | 605 | printSessions(&out, []ports.SessionInfo{ | |
| 606 | - | {Name: "main", State: ports.SessionRunning, Tabs: 2, Attached: true}, | |
| 607 | - | {Name: "old", State: ports.SessionStopped}, | |
| 606 | + | {Name: "main", State: ports.SessionUp, Tabs: 2, Attached: true}, | |
| 607 | + | {Name: "old", State: ports.SessionDown}, | |
| 608 | 608 | }) | |
| 609 | 609 | got := out.String() | |
| 610 | - | for _, want := range []string{"NAME", "STATE", "main", "running", "2", "yes", "old", "stopped", "-"} { | |
| 610 | + | for _, want := range []string{"NAME", "STATE", "main", "up", "2", "yes", "old", "down", "-"} { | |
| 611 | 611 | if !strings.Contains(got, want) { | |
| 612 | 612 | t.Fatalf("printSessions output %q missing %q", got, want) | |
| 613 | 613 | } | |
| @@ -617,12 +617,12 @@ func TestPrintSessionsShowsStoppedState(t *testing.T) { | |||
| 617 | 617 | func TestPrintSessionsMarksEphemeral(t *testing.T) { | |
| 618 | 618 | var buf bytes.Buffer | |
| 619 | 619 | printSessions(&buf, []ports.SessionInfo{ | |
| 620 | - | {Name: "0", State: ports.SessionRunning, Ephemeral: true, Tabs: 1, Attached: false}, | |
| 621 | - | {Name: "work", State: ports.SessionRunning, Tabs: 2, Attached: true}, | |
| 622 | - | {Name: "old", State: ports.SessionStopped}, | |
| 620 | + | {Name: "0", State: ports.SessionUp, Ephemeral: true, Tabs: 1, Attached: false}, | |
| 621 | + | {Name: "work", State: ports.SessionUp, Tabs: 2, Attached: true}, | |
| 622 | + | {Name: "old", State: ports.SessionDown}, | |
| 623 | 623 | }) | |
| 624 | 624 | out := buf.String() | |
| 625 | - | for _, want := range []string{"0", "temporary", "work", "running", "old", "stopped"} { | |
| 625 | + | for _, want := range []string{"0", "temporary", "work", "up", "old", "down"} { | |
| 626 | 626 | require.Contains(t, out, want) | |
| 627 | 627 | } | |
| 628 | 628 | } | |
| @@ -634,7 +634,7 @@ func newTestPersister(t *testing.T, stateDir string) *persist.Persister { | |||
| 634 | 634 | return persist.New(store) | |
| 635 | 635 | } | |
| 636 | 636 | ||
| 637 | - | func TestRunListReadsStoppedSessionsWithoutDaemon(t *testing.T) { | |
| 637 | + | func TestRunListReadsDownSessionsWithoutDaemon(t *testing.T) { | |
| 638 | 638 | stateRoot, runtimeRoot := t.TempDir(), t.TempDir() | |
| 639 | 639 | t.Setenv("XDG_STATE_HOME", stateRoot) | |
| 640 | 640 | t.Setenv("XDG_RUNTIME_DIR", runtimeRoot) | |
| @@ -653,7 +653,7 @@ func TestRunListReadsStoppedSessionsWithoutDaemon(t *testing.T) { | |||
| 653 | 653 | t.Fatalf("runList error = %v", err) | |
| 654 | 654 | } | |
| 655 | 655 | }) | |
| 656 | - | for _, want := range []string{"stored", "stopped", "-"} { | |
| 656 | + | for _, want := range []string{"stored", "down", "-"} { | |
| 657 | 657 | if !strings.Contains(got, want) { | |
| 658 | 658 | t.Fatalf("runList output %q missing %q", got, want) | |
| 659 | 659 | } | |
| @@ -588,26 +588,26 @@ func TestAttachTargetsCreateCommonSessionRequest(t *testing.T) { | |||||
| 588 | func TestListShowsBroken(t *testing.T) { | 588 | func TestListShowsBroken(t *testing.T) { | ||
| 589 | var out bytes.Buffer | 589 | var out bytes.Buffer | ||
| 590 | printSessions(&out, []ports.SessionInfo{ | 590 | printSessions(&out, []ports.SessionInfo{ | ||
| 591 | - | {Name: "fresh", State: ports.SessionStopped}, | 591 | + | {Name: "fresh", State: ports.SessionDown}, |
| 592 | - | {Name: "loading", State: ports.SessionStopped}, | 592 | + | {Name: "loading", State: ports.SessionDown}, |
| 593 | {Name: "broken", State: ports.SessionBroken}, | 593 | {Name: "broken", State: ports.SessionBroken}, | ||
| 594 | }) | 594 | }) | ||
| 595 | require.Contains(t, out.String(), "fresh") | 595 | require.Contains(t, out.String(), "fresh") | ||
| 596 | - | require.Contains(t, out.String(), "stopped") | 596 | + | require.Contains(t, out.String(), "down") |
| 597 | require.Contains(t, out.String(), "loading") | 597 | require.Contains(t, out.String(), "loading") | ||
| 598 | require.Contains(t, out.String(), "broken") | 598 | require.Contains(t, out.String(), "broken") | ||
| 599 | require.NotContains(t, out.String(), "restoring") | 599 | require.NotContains(t, out.String(), "restoring") | ||
| 600 | require.NotContains(t, out.String(), "degraded") | 600 | require.NotContains(t, out.String(), "degraded") | ||
| 601 | } | 601 | } | ||
| 602 | 602 | ||||
| 603 | - | func TestPrintSessionsShowsStoppedState(t *testing.T) { | 603 | + | func TestPrintSessionsShowsDownState(t *testing.T) { |
| 604 | var out bytes.Buffer | 604 | var out bytes.Buffer | ||
| 605 | printSessions(&out, []ports.SessionInfo{ | 605 | printSessions(&out, []ports.SessionInfo{ | ||
| 606 | - | {Name: "main", State: ports.SessionRunning, Tabs: 2, Attached: true}, | 606 | + | {Name: "main", State: ports.SessionUp, Tabs: 2, Attached: true}, |
| 607 | - | {Name: "old", State: ports.SessionStopped}, | 607 | + | {Name: "old", State: ports.SessionDown}, |
| 608 | }) | 608 | }) | ||
| 609 | got := out.String() | 609 | got := out.String() | ||
| 610 | - | for _, want := range []string{"NAME", "STATE", "main", "running", "2", "yes", "old", "stopped", "-"} { | 610 | + | for _, want := range []string{"NAME", "STATE", "main", "up", "2", "yes", "old", "down", "-"} { |
| 611 | if !strings.Contains(got, want) { | 611 | if !strings.Contains(got, want) { | ||
| 612 | t.Fatalf("printSessions output %q missing %q", got, want) | 612 | t.Fatalf("printSessions output %q missing %q", got, want) | ||
| 613 | } | 613 | } | ||
| @@ -617,12 +617,12 @@ func TestPrintSessionsShowsStoppedState(t *testing.T) { | |||||
| 617 | func TestPrintSessionsMarksEphemeral(t *testing.T) { | 617 | func TestPrintSessionsMarksEphemeral(t *testing.T) { | ||
| 618 | var buf bytes.Buffer | 618 | var buf bytes.Buffer | ||
| 619 | printSessions(&buf, []ports.SessionInfo{ | 619 | printSessions(&buf, []ports.SessionInfo{ | ||
| 620 | - | {Name: "0", State: ports.SessionRunning, Ephemeral: true, Tabs: 1, Attached: false}, | 620 | + | {Name: "0", State: ports.SessionUp, Ephemeral: true, Tabs: 1, Attached: false}, |
| 621 | - | {Name: "work", State: ports.SessionRunning, Tabs: 2, Attached: true}, | 621 | + | {Name: "work", State: ports.SessionUp, Tabs: 2, Attached: true}, |
| 622 | - | {Name: "old", State: ports.SessionStopped}, | 622 | + | {Name: "old", State: ports.SessionDown}, |
| 623 | }) | 623 | }) | ||
| 624 | out := buf.String() | 624 | out := buf.String() | ||
| 625 | - | for _, want := range []string{"0", "temporary", "work", "running", "old", "stopped"} { | 625 | + | for _, want := range []string{"0", "temporary", "work", "up", "old", "down"} { |
| 626 | require.Contains(t, out, want) | 626 | require.Contains(t, out, want) | ||
| 627 | } | 627 | } | ||
| 628 | } | 628 | } | ||
| @@ -634,7 +634,7 @@ func newTestPersister(t *testing.T, stateDir string) *persist.Persister { | |||||
| 634 | return persist.New(store) | 634 | return persist.New(store) | ||
| 635 | } | 635 | } | ||
| 636 | 636 | ||||
| 637 | - | func TestRunListReadsStoppedSessionsWithoutDaemon(t *testing.T) { | 637 | + | func TestRunListReadsDownSessionsWithoutDaemon(t *testing.T) { |
| 638 | stateRoot, runtimeRoot := t.TempDir(), t.TempDir() | 638 | stateRoot, runtimeRoot := t.TempDir(), t.TempDir() | ||
| 639 | t.Setenv("XDG_STATE_HOME", stateRoot) | 639 | t.Setenv("XDG_STATE_HOME", stateRoot) | ||
| 640 | t.Setenv("XDG_RUNTIME_DIR", runtimeRoot) | 640 | t.Setenv("XDG_RUNTIME_DIR", runtimeRoot) | ||
| @@ -653,7 +653,7 @@ func TestRunListReadsStoppedSessionsWithoutDaemon(t *testing.T) { | |||||
| 653 | t.Fatalf("runList error = %v", err) | 653 | t.Fatalf("runList error = %v", err) | ||
| 654 | } | 654 | } | ||
| 655 | }) | 655 | }) | ||
| 656 | - | for _, want := range []string{"stored", "stopped", "-"} { | 656 | + | for _, want := range []string{"stored", "down", "-"} { |
| 657 | if !strings.Contains(got, want) { | 657 | if !strings.Contains(got, want) { | ||
| 658 | t.Fatalf("runList output %q missing %q", got, want) | 658 | t.Fatalf("runList output %q missing %q", got, want) | ||
| 659 | } | 659 | } | ||
M internal/domain/lifecycle.go +3 -3
| @@ -138,15 +138,15 @@ func (t RemoteSessionTarget) Validate() error { | |||
| 138 | 138 | } | |
| 139 | 139 | if t.Stopped { | |
| 140 | 140 | if t.LiveTabID != "" || t.StoppedTab.Validate() != nil { | |
| 141 | - | return errors.New("stopped remote target requires an ordinal or stable tab selector") | |
| 141 | + | return errors.New("down remote target requires an ordinal or stable tab selector") | |
| 142 | 142 | } | |
| 143 | 143 | return nil | |
| 144 | 144 | } | |
| 145 | 145 | if err := ValidateTabStableID(t.LiveTabID); err != nil { | |
| 146 | - | return errors.New("running remote target requires a stable tab ID") | |
| 146 | + | return errors.New("up remote target requires a stable tab ID") | |
| 147 | 147 | } | |
| 148 | 148 | if t.StoppedTab != (TabSelector{}) { | |
| 149 | - | return errors.New("running remote target cannot carry a stopped selector") | |
| 149 | + | return errors.New("up remote target cannot carry a down selector") | |
| 150 | 150 | } | |
| 151 | 151 | return nil | |
| 152 | 152 | } | |
| @@ -138,15 +138,15 @@ func (t RemoteSessionTarget) Validate() error { | |||||
| 138 | } | 138 | } | ||
| 139 | if t.Stopped { | 139 | if t.Stopped { | ||
| 140 | if t.LiveTabID != "" || t.StoppedTab.Validate() != nil { | 140 | if t.LiveTabID != "" || t.StoppedTab.Validate() != nil { | ||
| 141 | - | return errors.New("stopped remote target requires an ordinal or stable tab selector") | 141 | + | return errors.New("down remote target requires an ordinal or stable tab selector") |
| 142 | } | 142 | } | ||
| 143 | return nil | 143 | return nil | ||
| 144 | } | 144 | } | ||
| 145 | if err := ValidateTabStableID(t.LiveTabID); err != nil { | 145 | if err := ValidateTabStableID(t.LiveTabID); err != nil { | ||
| 146 | - | return errors.New("running remote target requires a stable tab ID") | 146 | + | return errors.New("up remote target requires a stable tab ID") |
| 147 | } | 147 | } | ||
| 148 | if t.StoppedTab != (TabSelector{}) { | 148 | if t.StoppedTab != (TabSelector{}) { | ||
| 149 | - | return errors.New("running remote target cannot carry a stopped selector") | 149 | + | return errors.New("up remote target cannot carry a down selector") |
| 150 | } | 150 | } | ||
| 151 | return nil | 151 | return nil | ||
| 152 | } | 152 | } | ||
M internal/ports/remote.go +9 -9
| @@ -64,7 +64,7 @@ type RemotePreviewClient interface { | |||
| 64 | 64 | const ( | |
| 65 | 65 | // RemoteCatalogSchemaVersion is independent from the IPC protocol. A | |
| 66 | 66 | // catalogue can be rejected without changing the attachment wire layout. | |
| 67 | - | RemoteCatalogSchemaVersion uint16 = 1 | |
| 67 | + | RemoteCatalogSchemaVersion uint16 = 2 | |
| 68 | 68 | ||
| 69 | 69 | RemoteCatalogMaxHosts = 64 | |
| 70 | 70 | RemoteCatalogMaxSessions = 256 | |
| @@ -103,7 +103,7 @@ type RemoteCatalogTab struct { | |||
| 103 | 103 | } | |
| 104 | 104 | ||
| 105 | 105 | // RemoteCatalogSession is one session in the remote discovery catalog. State | |
| 106 | - | // is an explicit string contract (running|stopped|broken), not SessionState. | |
| 106 | + | // is an explicit string contract (up|down|broken), not SessionState. | |
| 107 | 107 | // | |
| 108 | 108 | // Tabs is interface-typed only to keep old v0.x callers that supplied a count | |
| 109 | 109 | // source-compatible while the versioned schema carries []RemoteCatalogTab. | |
| @@ -301,7 +301,7 @@ func ValidateRemoteCatalog(c RemoteCatalog) error { | |||
| 301 | 301 | if session.ActiveTabID != "" { | |
| 302 | 302 | return fmt.Errorf("%w: active tab is absent", ErrInvalidRemoteCatalog) | |
| 303 | 303 | } | |
| 304 | - | if session.State == "running" && session.Tabs == nil { | |
| 304 | + | if session.State == "up" && session.Tabs == nil { | |
| 305 | 305 | return fmt.Errorf("%w: missing tab list", ErrInvalidRemoteCatalog) | |
| 306 | 306 | } | |
| 307 | 307 | } | |
| @@ -320,8 +320,8 @@ func ValidateRemoteCatalog(c RemoteCatalog) error { | |||
| 320 | 320 | if tab.Index != uint16(i) { | |
| 321 | 321 | return fmt.Errorf("%w: tab indexes are not ordered", ErrInvalidRemoteCatalog) | |
| 322 | 322 | } | |
| 323 | - | if session.State == "running" && tab.ID == "" { | |
| 324 | - | return fmt.Errorf("%w: running tab has zero ID", ErrInvalidRemoteCatalog) | |
| 323 | + | if session.State == "up" && tab.ID == "" { | |
| 324 | + | return fmt.Errorf("%w: up tab has zero ID", ErrInvalidRemoteCatalog) | |
| 325 | 325 | } | |
| 326 | 326 | if tab.ID != "" { | |
| 327 | 327 | if err := domain.ValidateTabStableID(domain.TabStableID(tab.ID)); err != nil { | |
| @@ -335,8 +335,8 @@ func ValidateRemoteCatalog(c RemoteCatalog) error { | |||
| 335 | 335 | bytes += len(tab.ID) + len(tab.Name) + len(tab.Detail) | |
| 336 | 336 | } | |
| 337 | 337 | if session.ActiveTabID != "" { | |
| 338 | - | if session.State != "running" { | |
| 339 | - | return fmt.Errorf("%w: stopped or broken session has an active tab", ErrInvalidRemoteCatalog) | |
| 338 | + | if session.State != "up" { | |
| 339 | + | return fmt.Errorf("%w: down or broken session has an active tab", ErrInvalidRemoteCatalog) | |
| 340 | 340 | } | |
| 341 | 341 | if _, ok := tabIDs[session.ActiveTabID]; !ok { | |
| 342 | 342 | return fmt.Errorf("%w: active tab is absent", ErrInvalidRemoteCatalog) | |
| @@ -404,7 +404,7 @@ func validRemoteCatalogText(value string) bool { | |||
| 404 | 404 | ||
| 405 | 405 | func validRemoteCatalogState(state string) bool { | |
| 406 | 406 | switch state { | |
| 407 | - | case "running", "stopped", "broken": | |
| 407 | + | case "up", "down", "broken": | |
| 408 | 408 | return true | |
| 409 | 409 | default: | |
| 410 | 410 | return false | |
| @@ -413,7 +413,7 @@ func validRemoteCatalogState(state string) bool { | |||
| 413 | 413 | ||
| 414 | 414 | func validRemoteCatalogReason(reason string) bool { | |
| 415 | 415 | switch reason { | |
| 416 | - | case "", "refreshing", "catalog_stale", "host_unreachable", "version_mismatch", "session_stopped", "session_broken", "identity_changed", "not_found", "timeout", "malformed": | |
| 416 | + | case "", "refreshing", "catalog_stale", "host_unreachable", "version_mismatch", "session_down", "session_broken", "identity_changed", "not_found", "timeout", "malformed": | |
| 417 | 417 | return true | |
| 418 | 418 | default: | |
| 419 | 419 | return false | |
| @@ -64,7 +64,7 @@ type RemotePreviewClient interface { | |||||
| 64 | const ( | 64 | const ( | ||
| 65 | // RemoteCatalogSchemaVersion is independent from the IPC protocol. A | 65 | // RemoteCatalogSchemaVersion is independent from the IPC protocol. A | ||
| 66 | // catalogue can be rejected without changing the attachment wire layout. | 66 | // catalogue can be rejected without changing the attachment wire layout. | ||
| 67 | - | RemoteCatalogSchemaVersion uint16 = 1 | 67 | + | RemoteCatalogSchemaVersion uint16 = 2 |
| 68 | 68 | ||||
| 69 | RemoteCatalogMaxHosts = 64 | 69 | RemoteCatalogMaxHosts = 64 | ||
| 70 | RemoteCatalogMaxSessions = 256 | 70 | RemoteCatalogMaxSessions = 256 | ||
| @@ -103,7 +103,7 @@ type RemoteCatalogTab struct { | |||||
| 103 | } | 103 | } | ||
| 104 | 104 | ||||
| 105 | // RemoteCatalogSession is one session in the remote discovery catalog. State | 105 | // RemoteCatalogSession is one session in the remote discovery catalog. State | ||
| 106 | - | // is an explicit string contract (running|stopped|broken), not SessionState. | 106 | + | // is an explicit string contract (up|down|broken), not SessionState. |
| 107 | // | 107 | // | ||
| 108 | // Tabs is interface-typed only to keep old v0.x callers that supplied a count | 108 | // Tabs is interface-typed only to keep old v0.x callers that supplied a count | ||
| 109 | // source-compatible while the versioned schema carries []RemoteCatalogTab. | 109 | // source-compatible while the versioned schema carries []RemoteCatalogTab. | ||
| @@ -301,7 +301,7 @@ func ValidateRemoteCatalog(c RemoteCatalog) error { | |||||
| 301 | if session.ActiveTabID != "" { | 301 | if session.ActiveTabID != "" { | ||
| 302 | return fmt.Errorf("%w: active tab is absent", ErrInvalidRemoteCatalog) | 302 | return fmt.Errorf("%w: active tab is absent", ErrInvalidRemoteCatalog) | ||
| 303 | } | 303 | } | ||
| 304 | - | if session.State == "running" && session.Tabs == nil { | 304 | + | if session.State == "up" && session.Tabs == nil { |
| 305 | return fmt.Errorf("%w: missing tab list", ErrInvalidRemoteCatalog) | 305 | return fmt.Errorf("%w: missing tab list", ErrInvalidRemoteCatalog) | ||
| 306 | } | 306 | } | ||
| 307 | } | 307 | } | ||
| @@ -320,8 +320,8 @@ func ValidateRemoteCatalog(c RemoteCatalog) error { | |||||
| 320 | if tab.Index != uint16(i) { | 320 | if tab.Index != uint16(i) { | ||
| 321 | return fmt.Errorf("%w: tab indexes are not ordered", ErrInvalidRemoteCatalog) | 321 | return fmt.Errorf("%w: tab indexes are not ordered", ErrInvalidRemoteCatalog) | ||
| 322 | } | 322 | } | ||
| 323 | - | if session.State == "running" && tab.ID == "" { | 323 | + | if session.State == "up" && tab.ID == "" { |
| 324 | - | return fmt.Errorf("%w: running tab has zero ID", ErrInvalidRemoteCatalog) | 324 | + | return fmt.Errorf("%w: up tab has zero ID", ErrInvalidRemoteCatalog) |
| 325 | } | 325 | } | ||
| 326 | if tab.ID != "" { | 326 | if tab.ID != "" { | ||
| 327 | if err := domain.ValidateTabStableID(domain.TabStableID(tab.ID)); err != nil { | 327 | if err := domain.ValidateTabStableID(domain.TabStableID(tab.ID)); err != nil { | ||
| @@ -335,8 +335,8 @@ func ValidateRemoteCatalog(c RemoteCatalog) error { | |||||
| 335 | bytes += len(tab.ID) + len(tab.Name) + len(tab.Detail) | 335 | bytes += len(tab.ID) + len(tab.Name) + len(tab.Detail) | ||
| 336 | } | 336 | } | ||
| 337 | if session.ActiveTabID != "" { | 337 | if session.ActiveTabID != "" { | ||
| 338 | - | if session.State != "running" { | 338 | + | if session.State != "up" { |
| 339 | - | return fmt.Errorf("%w: stopped or broken session has an active tab", ErrInvalidRemoteCatalog) | 339 | + | return fmt.Errorf("%w: down or broken session has an active tab", ErrInvalidRemoteCatalog) |
| 340 | } | 340 | } | ||
| 341 | if _, ok := tabIDs[session.ActiveTabID]; !ok { | 341 | if _, ok := tabIDs[session.ActiveTabID]; !ok { | ||
| 342 | return fmt.Errorf("%w: active tab is absent", ErrInvalidRemoteCatalog) | 342 | return fmt.Errorf("%w: active tab is absent", ErrInvalidRemoteCatalog) | ||
| @@ -404,7 +404,7 @@ func validRemoteCatalogText(value string) bool { | |||||
| 404 | 404 | ||||
| 405 | func validRemoteCatalogState(state string) bool { | 405 | func validRemoteCatalogState(state string) bool { | ||
| 406 | switch state { | 406 | switch state { | ||
| 407 | - | case "running", "stopped", "broken": | 407 | + | case "up", "down", "broken": |
| 408 | return true | 408 | return true | ||
| 409 | default: | 409 | default: | ||
| 410 | return false | 410 | return false | ||
| @@ -413,7 +413,7 @@ func validRemoteCatalogState(state string) bool { | |||||
| 413 | 413 | ||||
| 414 | func validRemoteCatalogReason(reason string) bool { | 414 | func validRemoteCatalogReason(reason string) bool { | ||
| 415 | switch reason { | 415 | switch reason { | ||
| 416 | - | case "", "refreshing", "catalog_stale", "host_unreachable", "version_mismatch", "session_stopped", "session_broken", "identity_changed", "not_found", "timeout", "malformed": | 416 | + | case "", "refreshing", "catalog_stale", "host_unreachable", "version_mismatch", "session_down", "session_broken", "identity_changed", "not_found", "timeout", "malformed": |
| 417 | return true | 417 | return true | ||
| 418 | default: | 418 | default: | ||
| 419 | return false | 419 | return false | ||
A internal/ports/remote_state_test.go +27 -0
| @@ -0,0 +1,27 @@ | |||
| 1 | + | package ports | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "testing" | |
| 5 | + | ||
| 6 | + | "github.com/stretchr/testify/require" | |
| 7 | + | ) | |
| 8 | + | ||
| 9 | + | func TestRemoteCatalogStateContract(t *testing.T) { | |
| 10 | + | require.Equal(t, uint16(2), RemoteCatalogSchemaVersion) | |
| 11 | + | ||
| 12 | + | for _, state := range []string{"up", "down", "broken"} { | |
| 13 | + | t.Run(state, func(t *testing.T) { | |
| 14 | + | err := ValidateRemoteCatalog(RemoteCatalog{ | |
| 15 | + | ProtocolVersion: ProtocolVersion, | |
| 16 | + | Sessions: []RemoteCatalogSession{{Name: "work", State: state, Tabs: 1}}, | |
| 17 | + | }) | |
| 18 | + | require.NoError(t, err) | |
| 19 | + | }) | |
| 20 | + | } | |
| 21 | + | ||
| 22 | + | err := ValidateRemoteCatalog(RemoteCatalog{ | |
| 23 | + | ProtocolVersion: ProtocolVersion, | |
| 24 | + | Sessions: []RemoteCatalogSession{{Name: "work", State: "running", Tabs: 1}}, | |
| 25 | + | }) | |
| 26 | + | require.ErrorIs(t, err, ErrRemoteCatalogUnknownState) | |
| 27 | + | } | |
| @@ -0,0 +1,27 @@ | |||||
| 1 | + | package ports | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "testing" | |||
| 5 | + | ||||
| 6 | + | "github.com/stretchr/testify/require" | |||
| 7 | + | ) | |||
| 8 | + | ||||
| 9 | + | func TestRemoteCatalogStateContract(t *testing.T) { | |||
| 10 | + | require.Equal(t, uint16(2), RemoteCatalogSchemaVersion) | |||
| 11 | + | ||||
| 12 | + | for _, state := range []string{"up", "down", "broken"} { | |||
| 13 | + | t.Run(state, func(t *testing.T) { | |||
| 14 | + | err := ValidateRemoteCatalog(RemoteCatalog{ | |||
| 15 | + | ProtocolVersion: ProtocolVersion, | |||
| 16 | + | Sessions: []RemoteCatalogSession{{Name: "work", State: state, Tabs: 1}}, | |||
| 17 | + | }) | |||
| 18 | + | require.NoError(t, err) | |||
| 19 | + | }) | |||
| 20 | + | } | |||
| 21 | + | ||||
| 22 | + | err := ValidateRemoteCatalog(RemoteCatalog{ | |||
| 23 | + | ProtocolVersion: ProtocolVersion, | |||
| 24 | + | Sessions: []RemoteCatalogSession{{Name: "work", State: "running", Tabs: 1}}, | |||
| 25 | + | }) | |||
| 26 | + | require.ErrorIs(t, err, ErrRemoteCatalogUnknownState) | |||
| 27 | + | } | |||
M internal/ports/wire.go +2 -2
| @@ -288,8 +288,8 @@ type Kill struct { | |||
| 288 | 288 | type SessionState uint8 | |
| 289 | 289 | ||
| 290 | 290 | const ( | |
| 291 | - | SessionRunning SessionState = iota | |
| 292 | - | SessionStopped | |
| 291 | + | SessionUp SessionState = iota | |
| 292 | + | SessionDown | |
| 293 | 293 | SessionBroken | |
| 294 | 294 | ) | |
| 295 | 295 | ||
| @@ -288,8 +288,8 @@ type Kill struct { | |||||
| 288 | type SessionState uint8 | 288 | type SessionState uint8 | ||
| 289 | 289 | ||||
| 290 | const ( | 290 | const ( | ||
| 291 | - | SessionRunning SessionState = iota | 291 | + | SessionUp SessionState = iota |
| 292 | - | SessionStopped | 292 | + | SessionDown |
| 293 | SessionBroken | 293 | SessionBroken | ||
| 294 | ) | 294 | ) | ||
| 295 | 295 | ||||
M internal/ports/wire_final_test.go +2 -2
| @@ -237,7 +237,7 @@ func TestFinalClosedWireValuesRejectUnknownEnumsAndBooleans(t *testing.T) { | |||
| 237 | 237 | { | |
| 238 | 238 | name: "sessions ephemeral boolean", | |
| 239 | 239 | payload: func() []byte { | |
| 240 | - | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionRunning}}}) | |
| 240 | + | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionUp}}}) | |
| 241 | 241 | b[6] = 2 | |
| 242 | 242 | return b | |
| 243 | 243 | }(), | |
| @@ -246,7 +246,7 @@ func TestFinalClosedWireValuesRejectUnknownEnumsAndBooleans(t *testing.T) { | |||
| 246 | 246 | { | |
| 247 | 247 | name: "sessions attached boolean", | |
| 248 | 248 | payload: func() []byte { | |
| 249 | - | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionRunning}}}) | |
| 249 | + | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionUp}}}) | |
| 250 | 250 | b[9] = 2 | |
| 251 | 251 | return b | |
| 252 | 252 | }(), | |
| @@ -237,7 +237,7 @@ func TestFinalClosedWireValuesRejectUnknownEnumsAndBooleans(t *testing.T) { | |||||
| 237 | { | 237 | { | ||
| 238 | name: "sessions ephemeral boolean", | 238 | name: "sessions ephemeral boolean", | ||
| 239 | payload: func() []byte { | 239 | payload: func() []byte { | ||
| 240 | - | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionRunning}}}) | 240 | + | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionUp}}}) |
| 241 | b[6] = 2 | 241 | b[6] = 2 | ||
| 242 | return b | 242 | return b | ||
| 243 | }(), | 243 | }(), | ||
| @@ -246,7 +246,7 @@ func TestFinalClosedWireValuesRejectUnknownEnumsAndBooleans(t *testing.T) { | |||||
| 246 | { | 246 | { | ||
| 247 | name: "sessions attached boolean", | 247 | name: "sessions attached boolean", | ||
| 248 | payload: func() []byte { | 248 | payload: func() []byte { | ||
| 249 | - | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionRunning}}}) | 249 | + | b := MarshalSessions(Sessions{Sessions: []SessionInfo{{State: SessionUp}}}) |
| 250 | b[9] = 2 | 250 | b[9] = 2 | ||
| 251 | return b | 251 | return b | ||
| 252 | }(), | 252 | }(), | ||
M internal/ports/wire_test.go +4 -4
| @@ -776,8 +776,8 @@ func TestSessionInfoRecoveryState(t *testing.T) { | |||
| 776 | 776 | state SessionState | |
| 777 | 777 | want []byte | |
| 778 | 778 | }{ | |
| 779 | - | {name: "running", state: SessionRunning, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | |
| 780 | - | {name: "stopped", state: SessionStopped, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, | |
| 779 | + | {name: "up", state: SessionUp, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | |
| 780 | + | {name: "down", state: SessionDown, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, | |
| 781 | 781 | {name: "broken", state: SessionBroken, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2}}, | |
| 782 | 782 | } | |
| 783 | 783 | for _, tt := range tests { | |
| @@ -813,8 +813,8 @@ func TestSessionsGoldenAndRoundTrip(t *testing.T) { | |||
| 813 | 813 | { | |
| 814 | 814 | name: "two", | |
| 815 | 815 | msg: Sessions{Sessions: []SessionInfo{ | |
| 816 | - | {SessionID: "0", Name: "0", State: SessionRunning, Ephemeral: true, Tabs: 1, Attached: false}, | |
| 817 | - | {SessionID: "work", Name: "proj", State: SessionStopped, Ephemeral: false, Tabs: 5, Attached: true}, | |
| 816 | + | {SessionID: "0", Name: "0", State: SessionUp, Ephemeral: true, Tabs: 1, Attached: false}, | |
| 817 | + | {SessionID: "work", Name: "proj", State: SessionDown, Ephemeral: false, Tabs: 5, Attached: true}, | |
| 818 | 818 | }}, | |
| 819 | 819 | want: []byte{ | |
| 820 | 820 | 0x00, 0x02, | |
| @@ -776,8 +776,8 @@ func TestSessionInfoRecoveryState(t *testing.T) { | |||||
| 776 | state SessionState | 776 | state SessionState | ||
| 777 | want []byte | 777 | want []byte | ||
| 778 | }{ | 778 | }{ | ||
| 779 | - | {name: "running", state: SessionRunning, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, | 779 | + | {name: "up", state: SessionUp, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, |
| 780 | - | {name: "stopped", state: SessionStopped, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, | 780 | + | {name: "down", state: SessionDown, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1}}, |
| 781 | {name: "broken", state: SessionBroken, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2}}, | 781 | {name: "broken", state: SessionBroken, want: []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2}}, | ||
| 782 | } | 782 | } | ||
| 783 | for _, tt := range tests { | 783 | for _, tt := range tests { | ||
| @@ -813,8 +813,8 @@ func TestSessionsGoldenAndRoundTrip(t *testing.T) { | |||||
| 813 | { | 813 | { | ||
| 814 | name: "two", | 814 | name: "two", | ||
| 815 | msg: Sessions{Sessions: []SessionInfo{ | 815 | msg: Sessions{Sessions: []SessionInfo{ | ||
| 816 | - | {SessionID: "0", Name: "0", State: SessionRunning, Ephemeral: true, Tabs: 1, Attached: false}, | 816 | + | {SessionID: "0", Name: "0", State: SessionUp, Ephemeral: true, Tabs: 1, Attached: false}, |
| 817 | - | {SessionID: "work", Name: "proj", State: SessionStopped, Ephemeral: false, Tabs: 5, Attached: true}, | 817 | + | {SessionID: "work", Name: "proj", State: SessionDown, Ephemeral: false, Tabs: 5, Attached: true}, |
| 818 | }}, | 818 | }}, | ||
| 819 | want: []byte{ | 819 | want: []byte{ | ||
| 820 | 0x00, 0x02, | 820 | 0x00, 0x02, | ||
M internal/usecase/daemon/coderabbit_regression_test.go +4 -4
| @@ -120,7 +120,7 @@ func TestRestoreFailureCannotOverwriteDiscardReplacement(t *testing.T) { | |||
| 120 | 120 | entry := d.stopped[record.Name] | |
| 121 | 121 | d.mu.Unlock() | |
| 122 | 122 | require.Equal(t, persisted, entry.record) | |
| 123 | - | require.Equal(t, ports.SessionStopped, entry.state) | |
| 123 | + | require.Equal(t, ports.SessionDown, entry.state) | |
| 124 | 124 | } | |
| 125 | 125 | ||
| 126 | 126 | func TestStaleIncompatibleRestoreCannotOverwriteNewerAuthority(t *testing.T) { | |
| @@ -138,7 +138,7 @@ func TestStaleIncompatibleRestoreCannotOverwriteNewerAuthority(t *testing.T) { | |||
| 138 | 138 | record.Committed = &ref | |
| 139 | 139 | return record | |
| 140 | 140 | }, | |
| 141 | - | state: ports.SessionStopped, | |
| 141 | + | state: ports.SessionDown, | |
| 142 | 142 | }, | |
| 143 | 143 | { | |
| 144 | 144 | name: "newer incarnation", | |
| @@ -146,7 +146,7 @@ func TestStaleIncompatibleRestoreCannotOverwriteNewerAuthority(t *testing.T) { | |||
| 146 | 146 | record.IncarnationID = domain.IncarnationID{9} | |
| 147 | 147 | return record | |
| 148 | 148 | }, | |
| 149 | - | state: ports.SessionStopped, | |
| 149 | + | state: ports.SessionDown, | |
| 150 | 150 | }, | |
| 151 | 151 | { | |
| 152 | 152 | name: "degraded authority", | |
| @@ -223,7 +223,7 @@ func TestCreateSessionRechecksShutdownAfterCatalogueRead(t *testing.T) { | |||
| 223 | 223 | d := newTestDaemon(t, nil, stubClock{}) | |
| 224 | 224 | d.catalogue = catalogue | |
| 225 | 225 | d.persistEnabled = true | |
| 226 | - | d.stopped[record.Name] = stoppedSessionFromRecord(record, ports.SessionStopped, make(chan struct{})) | |
| 226 | + | d.stopped[record.Name] = stoppedSessionFromRecord(record, ports.SessionDown, make(chan struct{})) | |
| 227 | 227 | ||
| 228 | 228 | result := make(chan error, 1) | |
| 229 | 229 | go func() { | |
| @@ -120,7 +120,7 @@ func TestRestoreFailureCannotOverwriteDiscardReplacement(t *testing.T) { | |||||
| 120 | entry := d.stopped[record.Name] | 120 | entry := d.stopped[record.Name] | ||
| 121 | d.mu.Unlock() | 121 | d.mu.Unlock() | ||
| 122 | require.Equal(t, persisted, entry.record) | 122 | require.Equal(t, persisted, entry.record) | ||
| 123 | - | require.Equal(t, ports.SessionStopped, entry.state) | 123 | + | require.Equal(t, ports.SessionDown, entry.state) |
| 124 | } | 124 | } | ||
| 125 | 125 | ||||
| 126 | func TestStaleIncompatibleRestoreCannotOverwriteNewerAuthority(t *testing.T) { | 126 | func TestStaleIncompatibleRestoreCannotOverwriteNewerAuthority(t *testing.T) { | ||
| @@ -138,7 +138,7 @@ func TestStaleIncompatibleRestoreCannotOverwriteNewerAuthority(t *testing.T) { | |||||
| 138 | record.Committed = &ref | 138 | record.Committed = &ref | ||
| 139 | return record | 139 | return record | ||
| 140 | }, | 140 | }, | ||
| 141 | - | state: ports.SessionStopped, | 141 | + | state: ports.SessionDown, |
| 142 | }, | 142 | }, | ||
| 143 | { | 143 | { | ||
| 144 | name: "newer incarnation", | 144 | name: "newer incarnation", | ||
| @@ -146,7 +146,7 @@ func TestStaleIncompatibleRestoreCannotOverwriteNewerAuthority(t *testing.T) { | |||||
| 146 | record.IncarnationID = domain.IncarnationID{9} | 146 | record.IncarnationID = domain.IncarnationID{9} | ||
| 147 | return record | 147 | return record | ||
| 148 | }, | 148 | }, | ||
| 149 | - | state: ports.SessionStopped, | 149 | + | state: ports.SessionDown, |
| 150 | }, | 150 | }, | ||
| 151 | { | 151 | { | ||
| 152 | name: "degraded authority", | 152 | name: "degraded authority", | ||
| @@ -223,7 +223,7 @@ func TestCreateSessionRechecksShutdownAfterCatalogueRead(t *testing.T) { | |||||
| 223 | d := newTestDaemon(t, nil, stubClock{}) | 223 | d := newTestDaemon(t, nil, stubClock{}) | ||
| 224 | d.catalogue = catalogue | 224 | d.catalogue = catalogue | ||
| 225 | d.persistEnabled = true | 225 | d.persistEnabled = true | ||
| 226 | - | d.stopped[record.Name] = stoppedSessionFromRecord(record, ports.SessionStopped, make(chan struct{})) | 226 | + | d.stopped[record.Name] = stoppedSessionFromRecord(record, ports.SessionDown, make(chan struct{})) |
| 227 | 227 | ||||
| 228 | result := make(chan error, 1) | 228 | result := make(chan error, 1) | ||
| 229 | go func() { | 229 | go func() { | ||
M internal/usecase/daemon/control.go +4 -4
| @@ -643,7 +643,7 @@ func (e controlExec) SessionRecovery(action string) (string, error) { | |||
| 643 | 643 | return "", err | |
| 644 | 644 | } | |
| 645 | 645 | if ok { | |
| 646 | - | e.d.setStoppedRecovery(record, ports.SessionStopped) | |
| 646 | + | e.d.setStoppedRecovery(record, ports.SessionDown) | |
| 647 | 647 | } | |
| 648 | 648 | return "", nil | |
| 649 | 649 | } | |
| @@ -736,7 +736,7 @@ func (e controlExec) RemoteCatalog(asJSON bool) (string, error) { | |||
| 736 | 736 | row := ports.RemoteCatalogSession{ | |
| 737 | 737 | LifecycleID: snap.incarnation, | |
| 738 | 738 | Name: snap.name, | |
| 739 | - | State: "running", | |
| 739 | + | State: "up", | |
| 740 | 740 | Ephemeral: snap.ephemeral, | |
| 741 | 741 | Tabs: tabs, | |
| 742 | 742 | Attached: snap.attached, | |
| @@ -752,7 +752,7 @@ func (e controlExec) RemoteCatalog(asJSON bool) (string, error) { | |||
| 752 | 752 | if err != nil { | |
| 753 | 753 | return "", err | |
| 754 | 754 | } | |
| 755 | - | state := "stopped" | |
| 755 | + | state := "down" | |
| 756 | 756 | reason := "" | |
| 757 | 757 | if entry.state == ports.SessionBroken || entry.record.DegradedReason != "" { | |
| 758 | 758 | state = "broken" | |
| @@ -791,7 +791,7 @@ func (e controlExec) remoteCatalogLegacy(sessions []*session) (string, error) { | |||
| 791 | 791 | snap := sess.snapshotView(viewOptions{}) | |
| 792 | 792 | rows = append(rows, ports.RemoteCatalogSession{ | |
| 793 | 793 | Name: snap.name, | |
| 794 | - | State: "running", | |
| 794 | + | State: "up", | |
| 795 | 795 | Ephemeral: snap.ephemeral, | |
| 796 | 796 | Tabs: ports.SaturateUint16(snap.tabCount), | |
| 797 | 797 | Attached: snap.attached, | |
| @@ -643,7 +643,7 @@ func (e controlExec) SessionRecovery(action string) (string, error) { | |||||
| 643 | return "", err | 643 | return "", err | ||
| 644 | } | 644 | } | ||
| 645 | if ok { | 645 | if ok { | ||
| 646 | - | e.d.setStoppedRecovery(record, ports.SessionStopped) | 646 | + | e.d.setStoppedRecovery(record, ports.SessionDown) |
| 647 | } | 647 | } | ||
| 648 | return "", nil | 648 | return "", nil | ||
| 649 | } | 649 | } | ||
| @@ -736,7 +736,7 @@ func (e controlExec) RemoteCatalog(asJSON bool) (string, error) { | |||||
| 736 | row := ports.RemoteCatalogSession{ | 736 | row := ports.RemoteCatalogSession{ | ||
| 737 | LifecycleID: snap.incarnation, | 737 | LifecycleID: snap.incarnation, | ||
| 738 | Name: snap.name, | 738 | Name: snap.name, | ||
| 739 | - | State: "running", | 739 | + | State: "up", |
| 740 | Ephemeral: snap.ephemeral, | 740 | Ephemeral: snap.ephemeral, | ||
| 741 | Tabs: tabs, | 741 | Tabs: tabs, | ||
| 742 | Attached: snap.attached, | 742 | Attached: snap.attached, | ||
| @@ -752,7 +752,7 @@ func (e controlExec) RemoteCatalog(asJSON bool) (string, error) { | |||||
| 752 | if err != nil { | 752 | if err != nil { | ||
| 753 | return "", err | 753 | return "", err | ||
| 754 | } | 754 | } | ||
| 755 | - | state := "stopped" | 755 | + | state := "down" |
| 756 | reason := "" | 756 | reason := "" | ||
| 757 | if entry.state == ports.SessionBroken || entry.record.DegradedReason != "" { | 757 | if entry.state == ports.SessionBroken || entry.record.DegradedReason != "" { | ||
| 758 | state = "broken" | 758 | state = "broken" | ||
| @@ -791,7 +791,7 @@ func (e controlExec) remoteCatalogLegacy(sessions []*session) (string, error) { | |||||
| 791 | snap := sess.snapshotView(viewOptions{}) | 791 | snap := sess.snapshotView(viewOptions{}) | ||
| 792 | rows = append(rows, ports.RemoteCatalogSession{ | 792 | rows = append(rows, ports.RemoteCatalogSession{ | ||
| 793 | Name: snap.name, | 793 | Name: snap.name, | ||
| 794 | - | State: "running", | 794 | + | State: "up", |
| 795 | Ephemeral: snap.ephemeral, | 795 | Ephemeral: snap.ephemeral, | ||
| 796 | Tabs: ports.SaturateUint16(snap.tabCount), | 796 | Tabs: ports.SaturateUint16(snap.tabCount), | ||
| 797 | Attached: snap.attached, | 797 | Attached: snap.attached, | ||
M internal/usecase/daemon/control_test.go +5 -5
| @@ -735,7 +735,7 @@ func TestRemoteCatalogJSONOutput(t *testing.T) { | |||
| 735 | 735 | build.mruAt.Store(1) | |
| 736 | 736 | ||
| 737 | 737 | d.mu.Lock() | |
| 738 | - | d.stopped["old"] = stoppedSession{name: "old", cwd: "/tmp/old", createdAt: 1, state: ports.SessionStopped} | |
| 738 | + | d.stopped["old"] = stoppedSession{name: "old", cwd: "/tmp/old", createdAt: 1, state: ports.SessionDown} | |
| 739 | 739 | d.mu.Unlock() | |
| 740 | 740 | ||
| 741 | 741 | listBefore := sendCommand(t, d, ports.CommandRequest{Slug: "list-sessions"}) | |
| @@ -765,12 +765,12 @@ func TestRemoteCatalogJSONOutput(t *testing.T) { | |||
| 765 | 765 | require.True(t, hasProtocol && hasSessions) | |
| 766 | 766 | ||
| 767 | 767 | require.Equal(t, []ports.RemoteCatalogSession{ | |
| 768 | - | {Name: "build", State: "running", Ephemeral: true, Tabs: 1, Attached: false}, | |
| 769 | - | {Name: "work", State: "running", Ephemeral: false, Tabs: 2, Attached: true}, | |
| 768 | + | {Name: "build", State: "up", Ephemeral: true, Tabs: 1, Attached: false}, | |
| 769 | + | {Name: "work", State: "up", Ephemeral: false, Tabs: 2, Attached: true}, | |
| 770 | 770 | }, catalog.Sessions) | |
| 771 | 771 | ||
| 772 | 772 | for _, session := range catalog.Sessions { | |
| 773 | - | require.Equal(t, "running", session.State) | |
| 773 | + | require.Equal(t, "up", session.State) | |
| 774 | 774 | require.NotEqual(t, "old", session.Name) | |
| 775 | 775 | } | |
| 776 | 776 | ||
| @@ -797,7 +797,7 @@ func TestRemoteCatalogTabCountSaturates(t *testing.T) { | |||
| 797 | 797 | var catalog ports.RemoteCatalog | |
| 798 | 798 | require.NoError(t, json.Unmarshal([]byte(result.Output), &catalog)) | |
| 799 | 799 | require.Equal(t, []ports.RemoteCatalogSession{{ | |
| 800 | - | Name: "work", State: "running", Ephemeral: true, Tabs: int(^uint16(0)), Attached: false, | |
| 800 | + | Name: "work", State: "up", Ephemeral: true, Tabs: int(^uint16(0)), Attached: false, | |
| 801 | 801 | }}, catalog.Sessions) | |
| 802 | 802 | } | |
| 803 | 803 | ||
| @@ -735,7 +735,7 @@ func TestRemoteCatalogJSONOutput(t *testing.T) { | |||||
| 735 | build.mruAt.Store(1) | 735 | build.mruAt.Store(1) | ||
| 736 | 736 | ||||
| 737 | d.mu.Lock() | 737 | d.mu.Lock() | ||
| 738 | - | d.stopped["old"] = stoppedSession{name: "old", cwd: "/tmp/old", createdAt: 1, state: ports.SessionStopped} | 738 | + | d.stopped["old"] = stoppedSession{name: "old", cwd: "/tmp/old", createdAt: 1, state: ports.SessionDown} |
| 739 | d.mu.Unlock() | 739 | d.mu.Unlock() | ||
| 740 | 740 | ||||
| 741 | listBefore := sendCommand(t, d, ports.CommandRequest{Slug: "list-sessions"}) | 741 | listBefore := sendCommand(t, d, ports.CommandRequest{Slug: "list-sessions"}) | ||
| @@ -765,12 +765,12 @@ func TestRemoteCatalogJSONOutput(t *testing.T) { | |||||
| 765 | require.True(t, hasProtocol && hasSessions) | 765 | require.True(t, hasProtocol && hasSessions) | ||
| 766 | 766 | ||||
| 767 | require.Equal(t, []ports.RemoteCatalogSession{ | 767 | require.Equal(t, []ports.RemoteCatalogSession{ | ||
| 768 | - | {Name: "build", State: "running", Ephemeral: true, Tabs: 1, Attached: false}, | 768 | + | {Name: "build", State: "up", Ephemeral: true, Tabs: 1, Attached: false}, |
| 769 | - | {Name: "work", State: "running", Ephemeral: false, Tabs: 2, Attached: true}, | 769 | + | {Name: "work", State: "up", Ephemeral: false, Tabs: 2, Attached: true}, |
| 770 | }, catalog.Sessions) | 770 | }, catalog.Sessions) | ||
| 771 | 771 | ||||
| 772 | for _, session := range catalog.Sessions { | 772 | for _, session := range catalog.Sessions { | ||
| 773 | - | require.Equal(t, "running", session.State) | 773 | + | require.Equal(t, "up", session.State) |
| 774 | require.NotEqual(t, "old", session.Name) | 774 | require.NotEqual(t, "old", session.Name) | ||
| 775 | } | 775 | } | ||
| 776 | 776 | ||||
| @@ -797,7 +797,7 @@ func TestRemoteCatalogTabCountSaturates(t *testing.T) { | |||||
| 797 | var catalog ports.RemoteCatalog | 797 | var catalog ports.RemoteCatalog | ||
| 798 | require.NoError(t, json.Unmarshal([]byte(result.Output), &catalog)) | 798 | require.NoError(t, json.Unmarshal([]byte(result.Output), &catalog)) | ||
| 799 | require.Equal(t, []ports.RemoteCatalogSession{{ | 799 | require.Equal(t, []ports.RemoteCatalogSession{{ | ||
| 800 | - | Name: "work", State: "running", Ephemeral: true, Tabs: int(^uint16(0)), Attached: false, | 800 | + | Name: "work", State: "up", Ephemeral: true, Tabs: int(^uint16(0)), Attached: false, |
| 801 | }}, catalog.Sessions) | 801 | }}, catalog.Sessions) | ||
| 802 | } | 802 | } | ||
| 803 | 803 | ||||
M internal/usecase/daemon/daemon.go +3 -3
| @@ -671,7 +671,7 @@ func New(ptys ports.PTYFactory, clock ports.Clock, log *slog.Logger, opts ...Opt | |||
| 671 | 671 | state, done := initialSessionState(r) | |
| 672 | 672 | d.stopped[r.Name] = stoppedSessionFromRecord(r, state, done) | |
| 673 | 673 | } else { | |
| 674 | - | d.stopped[r.Name] = stoppedSessionFromRecord(r, ports.SessionStopped, nil) | |
| 674 | + | d.stopped[r.Name] = stoppedSessionFromRecord(r, ports.SessionDown, nil) | |
| 675 | 675 | } | |
| 676 | 676 | if !hasCreatedAt || r.CreatedAt > maxCreatedAt { | |
| 677 | 677 | maxCreatedAt = r.CreatedAt | |
| @@ -1083,7 +1083,7 @@ func (d *Daemon) handleList(tr ports.Transport) { | |||
| 1083 | 1083 | info := ports.SessionInfo{ | |
| 1084 | 1084 | SessionID: string(s.id), | |
| 1085 | 1085 | Name: s.name, | |
| 1086 | - | State: ports.SessionRunning, | |
| 1086 | + | State: ports.SessionUp, | |
| 1087 | 1087 | Ephemeral: s.ephemeral, | |
| 1088 | 1088 | Tabs: uint16(len(s.tabs)), | |
| 1089 | 1089 | Attached: len(s.attachments) != 0, | |
| @@ -1096,7 +1096,7 @@ func (d *Daemon) handleList(tr ports.Transport) { | |||
| 1096 | 1096 | if _, live := liveNames[name]; live { | |
| 1097 | 1097 | continue | |
| 1098 | 1098 | } | |
| 1099 | - | state := ports.SessionStopped | |
| 1099 | + | state := ports.SessionDown | |
| 1100 | 1100 | if stopped.purging || stopped.state == ports.SessionBroken { | |
| 1101 | 1101 | // Purge is the dominant externally visible state: restoration must | |
| 1102 | 1102 | // never make a deletion-reserved record appear attachable. | |
| @@ -671,7 +671,7 @@ func New(ptys ports.PTYFactory, clock ports.Clock, log *slog.Logger, opts ...Opt | |||||
| 671 | state, done := initialSessionState(r) | 671 | state, done := initialSessionState(r) | ||
| 672 | d.stopped[r.Name] = stoppedSessionFromRecord(r, state, done) | 672 | d.stopped[r.Name] = stoppedSessionFromRecord(r, state, done) | ||
| 673 | } else { | 673 | } else { | ||
| 674 | - | d.stopped[r.Name] = stoppedSessionFromRecord(r, ports.SessionStopped, nil) | 674 | + | d.stopped[r.Name] = stoppedSessionFromRecord(r, ports.SessionDown, nil) |
| 675 | } | 675 | } | ||
| 676 | if !hasCreatedAt || r.CreatedAt > maxCreatedAt { | 676 | if !hasCreatedAt || r.CreatedAt > maxCreatedAt { | ||
| 677 | maxCreatedAt = r.CreatedAt | 677 | maxCreatedAt = r.CreatedAt | ||
| @@ -1083,7 +1083,7 @@ func (d *Daemon) handleList(tr ports.Transport) { | |||||
| 1083 | info := ports.SessionInfo{ | 1083 | info := ports.SessionInfo{ | ||
| 1084 | SessionID: string(s.id), | 1084 | SessionID: string(s.id), | ||
| 1085 | Name: s.name, | 1085 | Name: s.name, | ||
| 1086 | - | State: ports.SessionRunning, | 1086 | + | State: ports.SessionUp, |
| 1087 | Ephemeral: s.ephemeral, | 1087 | Ephemeral: s.ephemeral, | ||
| 1088 | Tabs: uint16(len(s.tabs)), | 1088 | Tabs: uint16(len(s.tabs)), | ||
| 1089 | Attached: len(s.attachments) != 0, | 1089 | Attached: len(s.attachments) != 0, | ||
| @@ -1096,7 +1096,7 @@ func (d *Daemon) handleList(tr ports.Transport) { | |||||
| 1096 | if _, live := liveNames[name]; live { | 1096 | if _, live := liveNames[name]; live { | ||
| 1097 | continue | 1097 | continue | ||
| 1098 | } | 1098 | } | ||
| 1099 | - | state := ports.SessionStopped | 1099 | + | state := ports.SessionDown |
| 1100 | if stopped.purging || stopped.state == ports.SessionBroken { | 1100 | if stopped.purging || stopped.state == ports.SessionBroken { | ||
| 1101 | // Purge is the dominant externally visible state: restoration must | 1101 | // Purge is the dominant externally visible state: restoration must | ||
| 1102 | // never make a deletion-reserved record appear attachable. | 1102 | // never make a deletion-reserved record appear attachable. | ||
M internal/usecase/daemon/durable_recovery_test.go +13 -13
| @@ -304,7 +304,7 @@ func TestCatalogueRestoreIndependent(t *testing.T) { | |||
| 304 | 304 | healthyEntry := d.stopped[healthy.Name] | |
| 305 | 305 | brokenEntry := d.stopped[broken.Name] | |
| 306 | 306 | d.mu.Unlock() | |
| 307 | - | require.Equal(t, ports.SessionStopped, healthyEntry.state) | |
| 307 | + | require.Equal(t, ports.SessionDown, healthyEntry.state) | |
| 308 | 308 | require.Equal(t, ports.SessionBroken, brokenEntry.state) | |
| 309 | 309 | for name, done := range map[string]<-chan struct{}{healthy.Name: healthyEntry.restoreDone, broken.Name: brokenEntry.restoreDone} { | |
| 310 | 310 | select { | |
| @@ -344,14 +344,14 @@ func TestCatalogueRestoreResetsOnlyIncompatibleCheckpoint(t *testing.T) { | |||
| 344 | 344 | require.Empty(t, fresh.DegradedReason) | |
| 345 | 345 | require.Empty(t, fresh.TabNames) | |
| 346 | 346 | require.Equal(t, []domain.IncarnationID{incompatible.IncarnationID}, repository.deleted) | |
| 347 | - | require.Equal(t, ports.SessionStopped, d.stopped[incompatible.Name].state) | |
| 347 | + | require.Equal(t, ports.SessionDown, d.stopped[incompatible.Name].state) | |
| 348 | 348 | require.Equal(t, fresh, d.stopped[incompatible.Name].record) | |
| 349 | 349 | ||
| 350 | 350 | restoredSibling, ok, err := catalogue.Record(healthy.Name) | |
| 351 | 351 | require.NoError(t, err) | |
| 352 | 352 | require.True(t, ok) | |
| 353 | 353 | require.Equal(t, healthy, restoredSibling) | |
| 354 | - | require.Equal(t, ports.SessionStopped, d.stopped[healthy.Name].state) | |
| 354 | + | require.Equal(t, ports.SessionDown, d.stopped[healthy.Name].state) | |
| 355 | 355 | require.Equal(t, 1, repository.loads[healthy.Name]) | |
| 356 | 356 | } | |
| 357 | 357 | ||
| @@ -381,7 +381,7 @@ func TestIncompatibleCheckpointDeleteFailurePublishesFreshStoppedAuthority(t *te | |||
| 381 | 381 | d.mu.Lock() | |
| 382 | 382 | entry := d.stopped[record.Name] | |
| 383 | 383 | d.mu.Unlock() | |
| 384 | - | require.Equal(t, ports.SessionStopped, entry.state) | |
| 384 | + | require.Equal(t, ports.SessionDown, entry.state) | |
| 385 | 385 | require.Equal(t, fresh, entry.record) | |
| 386 | 386 | require.Equal(t, restoreDone, entry.restoreDone) | |
| 387 | 387 | select { | |
| @@ -394,7 +394,7 @@ func TestIncompatibleCheckpointDeleteFailurePublishesFreshStoppedAuthority(t *te | |||
| 394 | 394 | d.mu.Lock() | |
| 395 | 395 | entry = d.stopped[record.Name] | |
| 396 | 396 | d.mu.Unlock() | |
| 397 | - | require.Equal(t, ports.SessionStopped, entry.state) | |
| 397 | + | require.Equal(t, ports.SessionDown, entry.state) | |
| 398 | 398 | require.Equal(t, fresh, entry.record) | |
| 399 | 399 | select { | |
| 400 | 400 | case <-entry.restoreDone: | |
| @@ -472,7 +472,7 @@ func TestCatalogueRestoreAggregateOver4096(t *testing.T) { | |||
| 472 | 472 | ||
| 473 | 473 | require.Len(t, d.stopped, count) | |
| 474 | 474 | for _, record := range records { | |
| 475 | - | require.Equal(t, ports.SessionStopped, d.stopped[record.Name].state) | |
| 475 | + | require.Equal(t, ports.SessionDown, d.stopped[record.Name].state) | |
| 476 | 476 | require.Equal(t, 1, repository.loads[record.Name]) | |
| 477 | 477 | } | |
| 478 | 478 | } | |
| @@ -486,7 +486,7 @@ func TestCatalogueRestoreSingleSessionOver4096(t *testing.T) { | |||
| 486 | 486 | // discovery API and therefore performs exactly one direct catalogue lookup. | |
| 487 | 487 | d.restoreCatalogue(context.Background(), mustDurableRecords(t, catalogue)) | |
| 488 | 488 | ||
| 489 | - | require.Equal(t, ports.SessionStopped, d.stopped[record.Name].state) | |
| 489 | + | require.Equal(t, ports.SessionDown, d.stopped[record.Name].state) | |
| 490 | 490 | require.Equal(t, 1, repository.loads[record.Name]) | |
| 491 | 491 | } | |
| 492 | 492 | ||
| @@ -709,7 +709,7 @@ func TestCreateNamedSessionRegistrationFailureRollsBackDurableState(t *testing.T | |||
| 709 | 709 | name: record.Name, cwd: record.Cwd, createdAt: record.CreatedAt, | |
| 710 | 710 | incarnation: record.IncarnationID, lastUsedSeq: record.LastUsedSeq, | |
| 711 | 711 | tabNames: append([]string(nil), record.TabNames...), record: record, | |
| 712 | - | state: ports.SessionStopped, restoreDone: make(chan struct{}), | |
| 712 | + | state: ports.SessionDown, restoreDone: make(chan struct{}), | |
| 713 | 713 | } | |
| 714 | 714 | } | |
| 715 | 715 | beforeStopped, hadStopped := d.stopped[tc.sessName] | |
| @@ -769,7 +769,7 @@ func TestSessionRecoveryCommand(t *testing.T) { | |||
| 769 | 769 | ||
| 770 | 770 | _, err := (controlExec{d: d, recoveryName: degraded.Name}).SessionRecovery("discard") | |
| 771 | 771 | require.NoError(t, err) | |
| 772 | - | require.Equal(t, ports.SessionStopped, d.stopped[degraded.Name].state) | |
| 772 | + | require.Equal(t, ports.SessionDown, d.stopped[degraded.Name].state) | |
| 773 | 773 | require.Equal(t, fresh, d.stopped[degraded.Name].record) | |
| 774 | 774 | } | |
| 775 | 775 | ||
| @@ -783,8 +783,8 @@ func TestListShowsDegraded(t *testing.T) { | |||
| 783 | 783 | ||
| 784 | 784 | listed := listSessions(t, d) | |
| 785 | 785 | require.Equal(t, []ports.SessionInfo{ | |
| 786 | - | {Name: fresh.Name, State: ports.SessionStopped}, | |
| 787 | - | {Name: restoring.Name, State: ports.SessionStopped}, | |
| 786 | + | {Name: fresh.Name, State: ports.SessionDown}, | |
| 787 | + | {Name: restoring.Name, State: ports.SessionDown}, | |
| 788 | 788 | {Name: degraded.Name, State: ports.SessionBroken}, | |
| 789 | 789 | }, listed.Sessions) | |
| 790 | 790 | } | |
| @@ -795,7 +795,7 @@ func TestListPurgingDominatesRestoringState(t *testing.T) { | |||
| 795 | 795 | d.mu.Lock() | |
| 796 | 796 | entry := d.stopped[record.Name] | |
| 797 | 797 | entry.purging = true | |
| 798 | - | entry.state = ports.SessionStopped | |
| 798 | + | entry.state = ports.SessionDown | |
| 799 | 799 | d.stopped[record.Name] = entry | |
| 800 | 800 | d.mu.Unlock() | |
| 801 | 801 | ||
| @@ -935,7 +935,7 @@ func TestWaitForTargetRestoreDistinguishesHealthyRuntime(t *testing.T) { | |||
| 935 | 935 | state ports.SessionState | |
| 936 | 936 | message string | |
| 937 | 937 | }{ | |
| 938 | - | {name: "healthy record without runtime", state: ports.SessionStopped, message: "session was not restored into this daemon: " + record.Name}, | |
| 938 | + | {name: "healthy record without runtime", state: ports.SessionDown, message: "session was not restored into this daemon: " + record.Name}, | |
| 939 | 939 | {name: "broken durable state", state: ports.SessionBroken, message: "session durable state is broken: " + record.Name}, | |
| 940 | 940 | } | |
| 941 | 941 | for _, tt := range tests { | |
| @@ -304,7 +304,7 @@ func TestCatalogueRestoreIndependent(t *testing.T) { | |||||
| 304 | healthyEntry := d.stopped[healthy.Name] | 304 | healthyEntry := d.stopped[healthy.Name] | ||
| 305 | brokenEntry := d.stopped[broken.Name] | 305 | brokenEntry := d.stopped[broken.Name] | ||
| 306 | d.mu.Unlock() | 306 | d.mu.Unlock() | ||
| 307 | - | require.Equal(t, ports.SessionStopped, healthyEntry.state) | 307 | + | require.Equal(t, ports.SessionDown, healthyEntry.state) |
| 308 | require.Equal(t, ports.SessionBroken, brokenEntry.state) | 308 | require.Equal(t, ports.SessionBroken, brokenEntry.state) | ||
| 309 | for name, done := range map[string]<-chan struct{}{healthy.Name: healthyEntry.restoreDone, broken.Name: brokenEntry.restoreDone} { | 309 | for name, done := range map[string]<-chan struct{}{healthy.Name: healthyEntry.restoreDone, broken.Name: brokenEntry.restoreDone} { | ||
| 310 | select { | 310 | select { | ||
| @@ -344,14 +344,14 @@ func TestCatalogueRestoreResetsOnlyIncompatibleCheckpoint(t *testing.T) { | |||||
| 344 | require.Empty(t, fresh.DegradedReason) | 344 | require.Empty(t, fresh.DegradedReason) | ||
| 345 | require.Empty(t, fresh.TabNames) | 345 | require.Empty(t, fresh.TabNames) | ||
| 346 | require.Equal(t, []domain.IncarnationID{incompatible.IncarnationID}, repository.deleted) | 346 | require.Equal(t, []domain.IncarnationID{incompatible.IncarnationID}, repository.deleted) | ||
| 347 | - | require.Equal(t, ports.SessionStopped, d.stopped[incompatible.Name].state) | 347 | + | require.Equal(t, ports.SessionDown, d.stopped[incompatible.Name].state) |
| 348 | require.Equal(t, fresh, d.stopped[incompatible.Name].record) | 348 | require.Equal(t, fresh, d.stopped[incompatible.Name].record) | ||
| 349 | 349 | ||||
| 350 | restoredSibling, ok, err := catalogue.Record(healthy.Name) | 350 | restoredSibling, ok, err := catalogue.Record(healthy.Name) | ||
| 351 | require.NoError(t, err) | 351 | require.NoError(t, err) | ||
| 352 | require.True(t, ok) | 352 | require.True(t, ok) | ||
| 353 | require.Equal(t, healthy, restoredSibling) | 353 | require.Equal(t, healthy, restoredSibling) | ||
| 354 | - | require.Equal(t, ports.SessionStopped, d.stopped[healthy.Name].state) | 354 | + | require.Equal(t, ports.SessionDown, d.stopped[healthy.Name].state) |
| 355 | require.Equal(t, 1, repository.loads[healthy.Name]) | 355 | require.Equal(t, 1, repository.loads[healthy.Name]) | ||
| 356 | } | 356 | } | ||
| 357 | 357 | ||||
| @@ -381,7 +381,7 @@ func TestIncompatibleCheckpointDeleteFailurePublishesFreshStoppedAuthority(t *te | |||||
| 381 | d.mu.Lock() | 381 | d.mu.Lock() | ||
| 382 | entry := d.stopped[record.Name] | 382 | entry := d.stopped[record.Name] | ||
| 383 | d.mu.Unlock() | 383 | d.mu.Unlock() | ||
| 384 | - | require.Equal(t, ports.SessionStopped, entry.state) | 384 | + | require.Equal(t, ports.SessionDown, entry.state) |
| 385 | require.Equal(t, fresh, entry.record) | 385 | require.Equal(t, fresh, entry.record) | ||
| 386 | require.Equal(t, restoreDone, entry.restoreDone) | 386 | require.Equal(t, restoreDone, entry.restoreDone) | ||
| 387 | select { | 387 | select { | ||
| @@ -394,7 +394,7 @@ func TestIncompatibleCheckpointDeleteFailurePublishesFreshStoppedAuthority(t *te | |||||
| 394 | d.mu.Lock() | 394 | d.mu.Lock() | ||
| 395 | entry = d.stopped[record.Name] | 395 | entry = d.stopped[record.Name] | ||
| 396 | d.mu.Unlock() | 396 | d.mu.Unlock() | ||
| 397 | - | require.Equal(t, ports.SessionStopped, entry.state) | 397 | + | require.Equal(t, ports.SessionDown, entry.state) |
| 398 | require.Equal(t, fresh, entry.record) | 398 | require.Equal(t, fresh, entry.record) | ||
| 399 | select { | 399 | select { | ||
| 400 | case <-entry.restoreDone: | 400 | case <-entry.restoreDone: | ||
| @@ -472,7 +472,7 @@ func TestCatalogueRestoreAggregateOver4096(t *testing.T) { | |||||
| 472 | 472 | ||||
| 473 | require.Len(t, d.stopped, count) | 473 | require.Len(t, d.stopped, count) | ||
| 474 | for _, record := range records { | 474 | for _, record := range records { | ||
| 475 | - | require.Equal(t, ports.SessionStopped, d.stopped[record.Name].state) | 475 | + | require.Equal(t, ports.SessionDown, d.stopped[record.Name].state) |
| 476 | require.Equal(t, 1, repository.loads[record.Name]) | 476 | require.Equal(t, 1, repository.loads[record.Name]) | ||
| 477 | } | 477 | } | ||
| 478 | } | 478 | } | ||
| @@ -486,7 +486,7 @@ func TestCatalogueRestoreSingleSessionOver4096(t *testing.T) { | |||||
| 486 | // discovery API and therefore performs exactly one direct catalogue lookup. | 486 | // discovery API and therefore performs exactly one direct catalogue lookup. | ||
| 487 | d.restoreCatalogue(context.Background(), mustDurableRecords(t, catalogue)) | 487 | d.restoreCatalogue(context.Background(), mustDurableRecords(t, catalogue)) | ||
| 488 | 488 | ||||
| 489 | - | require.Equal(t, ports.SessionStopped, d.stopped[record.Name].state) | 489 | + | require.Equal(t, ports.SessionDown, d.stopped[record.Name].state) |
| 490 | require.Equal(t, 1, repository.loads[record.Name]) | 490 | require.Equal(t, 1, repository.loads[record.Name]) | ||
| 491 | } | 491 | } | ||
| 492 | 492 | ||||
| @@ -709,7 +709,7 @@ func TestCreateNamedSessionRegistrationFailureRollsBackDurableState(t *testing.T | |||||
| 709 | name: record.Name, cwd: record.Cwd, createdAt: record.CreatedAt, | 709 | name: record.Name, cwd: record.Cwd, createdAt: record.CreatedAt, | ||
| 710 | incarnation: record.IncarnationID, lastUsedSeq: record.LastUsedSeq, | 710 | incarnation: record.IncarnationID, lastUsedSeq: record.LastUsedSeq, | ||
| 711 | tabNames: append([]string(nil), record.TabNames...), record: record, | 711 | tabNames: append([]string(nil), record.TabNames...), record: record, | ||
| 712 | - | state: ports.SessionStopped, restoreDone: make(chan struct{}), | 712 | + | state: ports.SessionDown, restoreDone: make(chan struct{}), |
| 713 | } | 713 | } | ||
| 714 | } | 714 | } | ||
| 715 | beforeStopped, hadStopped := d.stopped[tc.sessName] | 715 | beforeStopped, hadStopped := d.stopped[tc.sessName] | ||
| @@ -769,7 +769,7 @@ func TestSessionRecoveryCommand(t *testing.T) { | |||||
| 769 | 769 | ||||
| 770 | _, err := (controlExec{d: d, recoveryName: degraded.Name}).SessionRecovery("discard") | 770 | _, err := (controlExec{d: d, recoveryName: degraded.Name}).SessionRecovery("discard") | ||
| 771 | require.NoError(t, err) | 771 | require.NoError(t, err) | ||
| 772 | - | require.Equal(t, ports.SessionStopped, d.stopped[degraded.Name].state) | 772 | + | require.Equal(t, ports.SessionDown, d.stopped[degraded.Name].state) |
| 773 | require.Equal(t, fresh, d.stopped[degraded.Name].record) | 773 | require.Equal(t, fresh, d.stopped[degraded.Name].record) | ||
| 774 | } | 774 | } | ||
| 775 | 775 | ||||
| @@ -783,8 +783,8 @@ func TestListShowsDegraded(t *testing.T) { | |||||
| 783 | 783 | ||||
| 784 | listed := listSessions(t, d) | 784 | listed := listSessions(t, d) | ||
| 785 | require.Equal(t, []ports.SessionInfo{ | 785 | require.Equal(t, []ports.SessionInfo{ | ||
| 786 | - | {Name: fresh.Name, State: ports.SessionStopped}, | 786 | + | {Name: fresh.Name, State: ports.SessionDown}, |
| 787 | - | {Name: restoring.Name, State: ports.SessionStopped}, | 787 | + | {Name: restoring.Name, State: ports.SessionDown}, |
| 788 | {Name: degraded.Name, State: ports.SessionBroken}, | 788 | {Name: degraded.Name, State: ports.SessionBroken}, | ||
| 789 | }, listed.Sessions) | 789 | }, listed.Sessions) | ||
| 790 | } | 790 | } | ||
| @@ -795,7 +795,7 @@ func TestListPurgingDominatesRestoringState(t *testing.T) { | |||||
| 795 | d.mu.Lock() | 795 | d.mu.Lock() | ||
| 796 | entry := d.stopped[record.Name] | 796 | entry := d.stopped[record.Name] | ||
| 797 | entry.purging = true | 797 | entry.purging = true | ||
| 798 | - | entry.state = ports.SessionStopped | 798 | + | entry.state = ports.SessionDown |
| 799 | d.stopped[record.Name] = entry | 799 | d.stopped[record.Name] = entry | ||
| 800 | d.mu.Unlock() | 800 | d.mu.Unlock() | ||
| 801 | 801 | ||||
| @@ -935,7 +935,7 @@ func TestWaitForTargetRestoreDistinguishesHealthyRuntime(t *testing.T) { | |||||
| 935 | state ports.SessionState | 935 | state ports.SessionState | ||
| 936 | message string | 936 | message string | ||
| 937 | }{ | 937 | }{ | ||
| 938 | - | {name: "healthy record without runtime", state: ports.SessionStopped, message: "session was not restored into this daemon: " + record.Name}, | 938 | + | {name: "healthy record without runtime", state: ports.SessionDown, message: "session was not restored into this daemon: " + record.Name}, |
| 939 | {name: "broken durable state", state: ports.SessionBroken, message: "session durable state is broken: " + record.Name}, | 939 | {name: "broken durable state", state: ports.SessionBroken, message: "session durable state is broken: " + record.Name}, | ||
| 940 | } | 940 | } | ||
| 941 | for _, tt := range tests { | 941 | for _, tt := range tests { | ||
M internal/usecase/daemon/navigation_attachment_handoff_test.go +2 -2
| @@ -56,7 +56,7 @@ func TestNavigationHandoffsDropReplacedInitiatorWithoutMutation(t *testing.T) { | |||
| 56 | 56 | run: func(d *Daemon, _ *session, target *session, _ *attachedClient, effect *attachmentEffectTicket) error { | |
| 57 | 57 | d.mu.Lock() | |
| 58 | 58 | delete(d.sessions, target.id) | |
| 59 | - | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 9, state: ports.SessionStopped} | |
| 59 | + | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 9, state: ports.SessionDown} | |
| 60 | 60 | d.mu.Unlock() | |
| 61 | 61 | expectedCreatedAt := int64(9) | |
| 62 | 62 | return d.switchToTargetForAttachment(effect.connectionToken(), picker.Target{Name: "stopped", Stopped: true, ExpectedCreatedAt: &expectedCreatedAt}, sessionHandoffGuard{}, "stopped-session") | |
| @@ -149,7 +149,7 @@ func TestNavigationHandoffsDropReplacedInitiatorWithoutMutation(t *testing.T) { | |||
| 149 | 149 | func TestStoppedSessionHandoffDoesNotResumeAfterInitiatorReplacement(t *testing.T) { | |
| 150 | 150 | d, source, old, _ := newManualSessionWithPTYs(t, nil) | |
| 151 | 151 | d.mu.Lock() | |
| 152 | - | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 7, state: ports.SessionStopped} | |
| 152 | + | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 7, state: ports.SessionDown} | |
| 153 | 153 | d.mu.Unlock() | |
| 154 | 154 | ||
| 155 | 155 | rc := d.attachCoordinator(source, nil, old, true) | |
| @@ -56,7 +56,7 @@ func TestNavigationHandoffsDropReplacedInitiatorWithoutMutation(t *testing.T) { | |||||
| 56 | run: func(d *Daemon, _ *session, target *session, _ *attachedClient, effect *attachmentEffectTicket) error { | 56 | run: func(d *Daemon, _ *session, target *session, _ *attachedClient, effect *attachmentEffectTicket) error { | ||
| 57 | d.mu.Lock() | 57 | d.mu.Lock() | ||
| 58 | delete(d.sessions, target.id) | 58 | delete(d.sessions, target.id) | ||
| 59 | - | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 9, state: ports.SessionStopped} | 59 | + | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 9, state: ports.SessionDown} |
| 60 | d.mu.Unlock() | 60 | d.mu.Unlock() | ||
| 61 | expectedCreatedAt := int64(9) | 61 | expectedCreatedAt := int64(9) | ||
| 62 | return d.switchToTargetForAttachment(effect.connectionToken(), picker.Target{Name: "stopped", Stopped: true, ExpectedCreatedAt: &expectedCreatedAt}, sessionHandoffGuard{}, "stopped-session") | 62 | return d.switchToTargetForAttachment(effect.connectionToken(), picker.Target{Name: "stopped", Stopped: true, ExpectedCreatedAt: &expectedCreatedAt}, sessionHandoffGuard{}, "stopped-session") | ||
| @@ -149,7 +149,7 @@ func TestNavigationHandoffsDropReplacedInitiatorWithoutMutation(t *testing.T) { | |||||
| 149 | func TestStoppedSessionHandoffDoesNotResumeAfterInitiatorReplacement(t *testing.T) { | 149 | func TestStoppedSessionHandoffDoesNotResumeAfterInitiatorReplacement(t *testing.T) { | ||
| 150 | d, source, old, _ := newManualSessionWithPTYs(t, nil) | 150 | d, source, old, _ := newManualSessionWithPTYs(t, nil) | ||
| 151 | d.mu.Lock() | 151 | d.mu.Lock() | ||
| 152 | - | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 7, state: ports.SessionStopped} | 152 | + | d.stopped["stopped"] = stoppedSession{name: "stopped", cwd: "/tmp", createdAt: 7, state: ports.SessionDown} |
| 153 | d.mu.Unlock() | 153 | d.mu.Unlock() | ||
| 154 | 154 | ||||
| 155 | rc := d.attachCoordinator(source, nil, old, true) | 155 | rc := d.attachCoordinator(source, nil, old, true) | ||
M internal/usecase/daemon/picker.go +4 -4
| @@ -252,8 +252,8 @@ func remotePickerReasonText(reason string) string { | |||
| 252 | 252 | return "host unreachable" | |
| 253 | 253 | case "version_mismatch": | |
| 254 | 254 | return "version mismatch" | |
| 255 | - | case "session_stopped": | |
| 256 | - | return "session stopped" | |
| 255 | + | case "session_down": | |
| 256 | + | return "session down" | |
| 257 | 257 | case "session_broken": | |
| 258 | 258 | return "session broken" | |
| 259 | 259 | case "refreshing": | |
| @@ -543,7 +543,7 @@ func (d *Daemon) startRemotePickerPreview(ac *attachedClient, target picker.Targ | |||
| 543 | 543 | remoteTarget := *target.RemoteTarget | |
| 544 | 544 | message := "loading remote preview…" | |
| 545 | 545 | if remoteTarget.Stopped { | |
| 546 | - | message = "stopped session — preview unavailable" | |
| 546 | + | message = "down session — preview unavailable" | |
| 547 | 547 | } | |
| 548 | 548 | ac.overlays.pickerRemotePreview = staticRemotePickerPreview(width, height, message) | |
| 549 | 549 | if remoteTarget.Stopped { | |
| @@ -980,7 +980,7 @@ func (d *Daemon) remotePickerTargetReadyTarget(target domain.RemoteSessionTarget | |||
| 980 | 980 | if session.Name != target.SessionName || session.LifecycleID != target.LifecycleID { | |
| 981 | 981 | continue | |
| 982 | 982 | } | |
| 983 | - | if target.Stopped != (session.State == "stopped") || session.State == "broken" { | |
| 983 | + | if target.Stopped != (session.State == "down") || session.State == "broken" { | |
| 984 | 984 | return false | |
| 985 | 985 | } | |
| 986 | 986 | tabs := ports.CatalogTabs(session) | |
| @@ -252,8 +252,8 @@ func remotePickerReasonText(reason string) string { | |||||
| 252 | return "host unreachable" | 252 | return "host unreachable" | ||
| 253 | case "version_mismatch": | 253 | case "version_mismatch": | ||
| 254 | return "version mismatch" | 254 | return "version mismatch" | ||
| 255 | - | case "session_stopped": | 255 | + | case "session_down": |
| 256 | - | return "session stopped" | 256 | + | return "session down" |
| 257 | case "session_broken": | 257 | case "session_broken": | ||
| 258 | return "session broken" | 258 | return "session broken" | ||
| 259 | case "refreshing": | 259 | case "refreshing": | ||
| @@ -543,7 +543,7 @@ func (d *Daemon) startRemotePickerPreview(ac *attachedClient, target picker.Targ | |||||
| 543 | remoteTarget := *target.RemoteTarget | 543 | remoteTarget := *target.RemoteTarget | ||
| 544 | message := "loading remote preview…" | 544 | message := "loading remote preview…" | ||
| 545 | if remoteTarget.Stopped { | 545 | if remoteTarget.Stopped { | ||
| 546 | - | message = "stopped session — preview unavailable" | 546 | + | message = "down session — preview unavailable" |
| 547 | } | 547 | } | ||
| 548 | ac.overlays.pickerRemotePreview = staticRemotePickerPreview(width, height, message) | 548 | ac.overlays.pickerRemotePreview = staticRemotePickerPreview(width, height, message) | ||
| 549 | if remoteTarget.Stopped { | 549 | if remoteTarget.Stopped { | ||
| @@ -980,7 +980,7 @@ func (d *Daemon) remotePickerTargetReadyTarget(target domain.RemoteSessionTarget | |||||
| 980 | if session.Name != target.SessionName || session.LifecycleID != target.LifecycleID { | 980 | if session.Name != target.SessionName || session.LifecycleID != target.LifecycleID { | ||
| 981 | continue | 981 | continue | ||
| 982 | } | 982 | } | ||
| 983 | - | if target.Stopped != (session.State == "stopped") || session.State == "broken" { | 983 | + | if target.Stopped != (session.State == "down") || session.State == "broken" { |
| 984 | return false | 984 | return false | ||
| 985 | } | 985 | } | ||
| 986 | tabs := ports.CatalogTabs(session) | 986 | tabs := ports.CatalogTabs(session) | ||
M internal/usecase/daemon/picker_test.go +2 -2
| @@ -741,7 +741,7 @@ func TestPickerRejectsCatalogueTargetsWithoutFreshRuntime(t *testing.T) { | |||
| 741 | 741 | runtimeState ports.SessionState | |
| 742 | 742 | }{ | |
| 743 | 743 | {name: "broken", broken: true, runtimeState: ports.SessionBroken}, | |
| 744 | - | {name: "healthy without restored runtime", runtimeState: ports.SessionStopped}, | |
| 744 | + | {name: "healthy without restored runtime", runtimeState: ports.SessionDown}, | |
| 745 | 745 | } | |
| 746 | 746 | ||
| 747 | 747 | for _, tt := range tests { | |
| @@ -800,7 +800,7 @@ func TestPickerResumesStoppedSessionWithPersistedTabNames(t *testing.T) { | |||
| 800 | 800 | defer release3() | |
| 801 | 801 | d, from, ac, sends := newManualSessionWithPTYs(t, p1) | |
| 802 | 802 | d.ptys = newFactorySeq(t, p2, p3) | |
| 803 | - | d.stopped["work"] = stoppedSession{name: "work", cwd: "/tmp/work", createdAt: 7, tabNames: []string{"shell", "logs"}, record: domain.CatalogueRecord{Name: "work"}, state: ports.SessionStopped} | |
| 803 | + | d.stopped["work"] = stoppedSession{name: "work", cwd: "/tmp/work", createdAt: 7, tabNames: []string{"shell", "logs"}, record: domain.CatalogueRecord{Name: "work"}, state: ports.SessionDown} | |
| 804 | 804 | ||
| 805 | 805 | d.resumeStoppedAndSwitch(from, ac, picker.Target{Name: "work", Stopped: true}) | |
| 806 | 806 | awaitFrame(t, sends, ports.MsgOutput) | |
| @@ -741,7 +741,7 @@ func TestPickerRejectsCatalogueTargetsWithoutFreshRuntime(t *testing.T) { | |||||
| 741 | runtimeState ports.SessionState | 741 | runtimeState ports.SessionState | ||
| 742 | }{ | 742 | }{ | ||
| 743 | {name: "broken", broken: true, runtimeState: ports.SessionBroken}, | 743 | {name: "broken", broken: true, runtimeState: ports.SessionBroken}, | ||
| 744 | - | {name: "healthy without restored runtime", runtimeState: ports.SessionStopped}, | 744 | + | {name: "healthy without restored runtime", runtimeState: ports.SessionDown}, |
| 745 | } | 745 | } | ||
| 746 | 746 | ||||
| 747 | for _, tt := range tests { | 747 | for _, tt := range tests { | ||
| @@ -800,7 +800,7 @@ func TestPickerResumesStoppedSessionWithPersistedTabNames(t *testing.T) { | |||||
| 800 | defer release3() | 800 | defer release3() | ||
| 801 | d, from, ac, sends := newManualSessionWithPTYs(t, p1) | 801 | d, from, ac, sends := newManualSessionWithPTYs(t, p1) | ||
| 802 | d.ptys = newFactorySeq(t, p2, p3) | 802 | d.ptys = newFactorySeq(t, p2, p3) | ||
| 803 | - | d.stopped["work"] = stoppedSession{name: "work", cwd: "/tmp/work", createdAt: 7, tabNames: []string{"shell", "logs"}, record: domain.CatalogueRecord{Name: "work"}, state: ports.SessionStopped} | 803 | + | d.stopped["work"] = stoppedSession{name: "work", cwd: "/tmp/work", createdAt: 7, tabNames: []string{"shell", "logs"}, record: domain.CatalogueRecord{Name: "work"}, state: ports.SessionDown} |
| 804 | 804 | ||||
| 805 | d.resumeStoppedAndSwitch(from, ac, picker.Target{Name: "work", Stopped: true}) | 805 | d.resumeStoppedAndSwitch(from, ac, picker.Target{Name: "work", Stopped: true}) | ||
| 806 | awaitFrame(t, sends, ports.MsgOutput) | 806 | awaitFrame(t, sends, ports.MsgOutput) | ||
M internal/usecase/daemon/remote_attach_test.go +1 -1
| @@ -31,7 +31,7 @@ func TestRemotePickerSelectionRejectsReplacedLifecycle(t *testing.T) { | |||
| 31 | 31 | cachedLifecycle[0]++ | |
| 32 | 32 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 33 | 33 | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | |
| 34 | - | LifecycleID: cachedLifecycle, Name: "work", State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 34 | + | LifecycleID: cachedLifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 35 | 35 | }}, | |
| 36 | 36 | }}) | |
| 37 | 37 | d.remoteCatalog.mu.Lock() | |
| @@ -31,7 +31,7 @@ func TestRemotePickerSelectionRejectsReplacedLifecycle(t *testing.T) { | |||||
| 31 | cachedLifecycle[0]++ | 31 | cachedLifecycle[0]++ | ||
| 32 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 32 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 33 | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | 33 | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | ||
| 34 | - | LifecycleID: cachedLifecycle, Name: "work", State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | 34 | + | LifecycleID: cachedLifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, |
| 35 | }}, | 35 | }}, | ||
| 36 | }}) | 36 | }}) | ||
| 37 | d.remoteCatalog.mu.Lock() | 37 | d.remoteCatalog.mu.Lock() | ||
M internal/usecase/daemon/remote_catalog_state_test.go +4 -4
| @@ -42,9 +42,9 @@ func TestRemoteCatalogCacheStartup(t *testing.T) { | |||
| 42 | 42 | { | |
| 43 | 43 | name: "loads cached entries", | |
| 44 | 44 | load: func() ([]ports.RemoteCatalogCacheEntry, error) { | |
| 45 | - | return []ports.RemoteCatalogCacheEntry{{Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}}}, nil | |
| 45 | + | return []ports.RemoteCatalogCacheEntry{{Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}}}, nil | |
| 46 | 46 | }, | |
| 47 | - | wantCache: map[string]ports.RemoteCatalogCacheEntry{"arch": {Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}}}, | |
| 47 | + | wantCache: map[string]ports.RemoteCatalogCacheEntry{"arch": {Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}}}, | |
| 48 | 48 | wantStatus: map[string]remoteHostStatus{"arch": remoteHostCached}, | |
| 49 | 49 | }, | |
| 50 | 50 | { | |
| @@ -101,14 +101,14 @@ func TestRemoteCatalogCacheStoreSerializesLockFreeIO(t *testing.T) { | |||
| 101 | 101 | }} | |
| 102 | 102 | d = New(nil, nil, slog.New(slog.NewTextHandler(io.Discard, nil)), WithRemoteDiscovery(nil, nil, cache, nil, ports.RemoteTransportUDP)) | |
| 103 | 103 | d.remoteCatalog.mu.Lock() | |
| 104 | - | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 1), Sessions: []ports.RemoteCatalogSession{{Name: "one", State: "running"}}} | |
| 104 | + | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 1), Sessions: []ports.RemoteCatalogSession{{Name: "one", State: "up"}}} | |
| 105 | 105 | d.remoteCatalog.mu.Unlock() | |
| 106 | 106 | ||
| 107 | 107 | done := make(chan struct{}, 2) | |
| 108 | 108 | go func() { d.persistRemoteCatalogCache(); done <- struct{}{} }() | |
| 109 | 109 | <-entered | |
| 110 | 110 | d.remoteCatalog.mu.Lock() | |
| 111 | - | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 2), Sessions: []ports.RemoteCatalogSession{{Name: "two", State: "running"}}} | |
| 111 | + | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 2), Sessions: []ports.RemoteCatalogSession{{Name: "two", State: "up"}}} | |
| 112 | 112 | d.remoteCatalog.mu.Unlock() | |
| 113 | 113 | go func() { d.persistRemoteCatalogCache(); done <- struct{}{} }() | |
| 114 | 114 | ||
| @@ -42,9 +42,9 @@ func TestRemoteCatalogCacheStartup(t *testing.T) { | |||||
| 42 | { | 42 | { | ||
| 43 | name: "loads cached entries", | 43 | name: "loads cached entries", | ||
| 44 | load: func() ([]ports.RemoteCatalogCacheEntry, error) { | 44 | load: func() ([]ports.RemoteCatalogCacheEntry, error) { | ||
| 45 | - | return []ports.RemoteCatalogCacheEntry{{Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}}}, nil | 45 | + | return []ports.RemoteCatalogCacheEntry{{Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}}}, nil |
| 46 | }, | 46 | }, | ||
| 47 | - | wantCache: map[string]ports.RemoteCatalogCacheEntry{"arch": {Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}}}, | 47 | + | wantCache: map[string]ports.RemoteCatalogCacheEntry{"arch": {Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}}}, |
| 48 | wantStatus: map[string]remoteHostStatus{"arch": remoteHostCached}, | 48 | wantStatus: map[string]remoteHostStatus{"arch": remoteHostCached}, | ||
| 49 | }, | 49 | }, | ||
| 50 | { | 50 | { | ||
| @@ -101,14 +101,14 @@ func TestRemoteCatalogCacheStoreSerializesLockFreeIO(t *testing.T) { | |||||
| 101 | }} | 101 | }} | ||
| 102 | d = New(nil, nil, slog.New(slog.NewTextHandler(io.Discard, nil)), WithRemoteDiscovery(nil, nil, cache, nil, ports.RemoteTransportUDP)) | 102 | d = New(nil, nil, slog.New(slog.NewTextHandler(io.Discard, nil)), WithRemoteDiscovery(nil, nil, cache, nil, ports.RemoteTransportUDP)) | ||
| 103 | d.remoteCatalog.mu.Lock() | 103 | d.remoteCatalog.mu.Lock() | ||
| 104 | - | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 1), Sessions: []ports.RemoteCatalogSession{{Name: "one", State: "running"}}} | 104 | + | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 1), Sessions: []ports.RemoteCatalogSession{{Name: "one", State: "up"}}} |
| 105 | d.remoteCatalog.mu.Unlock() | 105 | d.remoteCatalog.mu.Unlock() | ||
| 106 | 106 | ||||
| 107 | done := make(chan struct{}, 2) | 107 | done := make(chan struct{}, 2) | ||
| 108 | go func() { d.persistRemoteCatalogCache(); done <- struct{}{} }() | 108 | go func() { d.persistRemoteCatalogCache(); done <- struct{}{} }() | ||
| 109 | <-entered | 109 | <-entered | ||
| 110 | d.remoteCatalog.mu.Lock() | 110 | d.remoteCatalog.mu.Lock() | ||
| 111 | - | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 2), Sessions: []ports.RemoteCatalogSession{{Name: "two", State: "running"}}} | 111 | + | d.remoteCatalog.cache["arch"] = ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(0, 2), Sessions: []ports.RemoteCatalogSession{{Name: "two", State: "up"}}} |
| 112 | d.remoteCatalog.mu.Unlock() | 112 | d.remoteCatalog.mu.Unlock() | ||
| 113 | go func() { d.persistRemoteCatalogCache(); done <- struct{}{} }() | 113 | go func() { d.persistRemoteCatalogCache(); done <- struct{}{} }() | ||
| 114 | 114 | ||||
M internal/usecase/daemon/remote_picker.go +5 -5
| @@ -133,7 +133,7 @@ func remotePickerView(key domain.RemoteSessionKey, session ports.RemoteCatalogSe | |||
| 133 | 133 | key.LifecycleID = session.LifecycleID | |
| 134 | 134 | key.DisplayOrigin = remoteSessionOrigin(key.Host) | |
| 135 | 135 | availability := remotePickerAvailability(status) | |
| 136 | - | stopped := session.State == "stopped" | |
| 136 | + | stopped := session.State == "down" | |
| 137 | 137 | broken := session.State == "broken" | |
| 138 | 138 | tabs := ports.CatalogTabs(session) | |
| 139 | 139 | viewTabs := make([]picker.TabEntry, 0, len(tabs)) | |
| @@ -201,13 +201,13 @@ func remotePickerView(key domain.RemoteSessionKey, session ports.RemoteCatalogSe | |||
| 201 | 201 | detail := remotePickerStatusDetail(status, fetchedAt) | |
| 202 | 202 | if detail == "" { | |
| 203 | 203 | switch session.State { | |
| 204 | - | case "running": | |
| 205 | - | detail = "running" | |
| 204 | + | case "up": | |
| 205 | + | detail = "up" | |
| 206 | 206 | if session.Attached { | |
| 207 | 207 | detail += " · attached" | |
| 208 | 208 | } | |
| 209 | - | case "stopped": | |
| 210 | - | detail = "stopped" | |
| 209 | + | case "down": | |
| 210 | + | detail = "down" | |
| 211 | 211 | case "broken": | |
| 212 | 212 | detail = "broken" | |
| 213 | 213 | } | |
| @@ -133,7 +133,7 @@ func remotePickerView(key domain.RemoteSessionKey, session ports.RemoteCatalogSe | |||||
| 133 | key.LifecycleID = session.LifecycleID | 133 | key.LifecycleID = session.LifecycleID | ||
| 134 | key.DisplayOrigin = remoteSessionOrigin(key.Host) | 134 | key.DisplayOrigin = remoteSessionOrigin(key.Host) | ||
| 135 | availability := remotePickerAvailability(status) | 135 | availability := remotePickerAvailability(status) | ||
| 136 | - | stopped := session.State == "stopped" | 136 | + | stopped := session.State == "down" |
| 137 | broken := session.State == "broken" | 137 | broken := session.State == "broken" | ||
| 138 | tabs := ports.CatalogTabs(session) | 138 | tabs := ports.CatalogTabs(session) | ||
| 139 | viewTabs := make([]picker.TabEntry, 0, len(tabs)) | 139 | viewTabs := make([]picker.TabEntry, 0, len(tabs)) | ||
| @@ -201,13 +201,13 @@ func remotePickerView(key domain.RemoteSessionKey, session ports.RemoteCatalogSe | |||||
| 201 | detail := remotePickerStatusDetail(status, fetchedAt) | 201 | detail := remotePickerStatusDetail(status, fetchedAt) | ||
| 202 | if detail == "" { | 202 | if detail == "" { | ||
| 203 | switch session.State { | 203 | switch session.State { | ||
| 204 | - | case "running": | 204 | + | case "up": |
| 205 | - | detail = "running" | 205 | + | detail = "up" |
| 206 | if session.Attached { | 206 | if session.Attached { | ||
| 207 | detail += " · attached" | 207 | detail += " · attached" | ||
| 208 | } | 208 | } | ||
| 209 | - | case "stopped": | 209 | + | case "down": |
| 210 | - | detail = "stopped" | 210 | + | detail = "down" |
| 211 | case "broken": | 211 | case "broken": | ||
| 212 | detail = "broken" | 212 | detail = "broken" | ||
| 213 | } | 213 | } | ||
M internal/usecase/daemon/remote_picker_test.go +52 -28
| @@ -64,7 +64,7 @@ func TestRemotePickerCatalogExpiresAtAttachTTL(t *testing.T) { | |||
| 64 | 64 | lifecycle := remoteLifecycleForTest() | |
| 65 | 65 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 66 | 66 | Host: "arch", FetchedAt: now.Add(-remotePickerAttachTTL), Sessions: []ports.RemoteCatalogSession{{ | |
| 67 | - | LifecycleID: lifecycle, Name: "work", State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 67 | + | LifecycleID: lifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 68 | 68 | }}, | |
| 69 | 69 | }}) | |
| 70 | 70 | d.remoteCatalog.mu.Lock() | |
| @@ -85,7 +85,7 @@ func TestRemotePickerTargetReadyExpiresAfterPickerBuildWithoutSnapshot(t *testin | |||
| 85 | 85 | lifecycle := remoteLifecycleForTest() | |
| 86 | 86 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 87 | 87 | Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{ | |
| 88 | - | LifecycleID: lifecycle, Name: "work", State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 88 | + | LifecycleID: lifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 89 | 89 | }}, | |
| 90 | 90 | }}) | |
| 91 | 91 | d.remoteCatalog.mu.Lock() | |
| @@ -152,7 +152,7 @@ func TestRemotePickerSelectsCatalogActiveTab(t *testing.T) { | |||
| 152 | 152 | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | |
| 153 | 153 | LifecycleID: lifecycle, | |
| 154 | 154 | Name: "vive", | |
| 155 | - | State: "running", | |
| 155 | + | State: "up", | |
| 156 | 156 | Tabs: test.tabs, | |
| 157 | 157 | ActiveTabID: test.activeTabID, | |
| 158 | 158 | }}, | |
| @@ -191,7 +191,7 @@ func TestRemotePickerAvailabilityUsesCachedRowsForFailures(t *testing.T) { | |||
| 191 | 191 | t.Run(test.name, func(t *testing.T) { | |
| 192 | 192 | d := newRemotePickerDaemon(nil) | |
| 193 | 193 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 194 | - | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}, | |
| 194 | + | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}, | |
| 195 | 195 | }}) | |
| 196 | 196 | d.remoteCatalog.mu.Lock() | |
| 197 | 197 | d.remoteCatalog.status["arch"] = test.status | |
| @@ -205,12 +205,36 @@ func TestRemotePickerAvailabilityUsesCachedRowsForFailures(t *testing.T) { | |||
| 205 | 205 | } | |
| 206 | 206 | } | |
| 207 | 207 | ||
| 208 | + | func TestRemotePickerViewUsesShortStateLabels(t *testing.T) { | |
| 209 | + | key := domain.RemoteSessionKey{Host: "arch", Name: "work"} | |
| 210 | + | lifecycle := remoteLifecycleForTest() | |
| 211 | + | for _, test := range []struct { | |
| 212 | + | state string | |
| 213 | + | detail string | |
| 214 | + | stopped bool | |
| 215 | + | }{ | |
| 216 | + | {state: "up", detail: "up"}, | |
| 217 | + | {state: "down", detail: "down", stopped: true}, | |
| 218 | + | } { | |
| 219 | + | t.Run(test.state, func(t *testing.T) { | |
| 220 | + | view := remotePickerView(key, ports.RemoteCatalogSession{ | |
| 221 | + | LifecycleID: lifecycle, | |
| 222 | + | Name: key.Name, | |
| 223 | + | State: test.state, | |
| 224 | + | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 225 | + | }, remoteHostFresh, time.Unix(1, 0)) | |
| 226 | + | require.Equal(t, test.detail, view.RemoteDetail) | |
| 227 | + | require.Equal(t, test.stopped, view.Stopped) | |
| 228 | + | }) | |
| 229 | + | } | |
| 230 | + | } | |
| 231 | + | ||
| 208 | 232 | func TestRemotePickerStaleDetailUsesLastSuccessfulFetch(t *testing.T) { | |
| 209 | 233 | d := newRemotePickerDaemon(nil) | |
| 210 | 234 | fetchedAt := time.Date(2025, time.January, 2, 3, 4, 5, 0, time.UTC) | |
| 211 | 235 | failedAt := fetchedAt.Add(6 * time.Hour) | |
| 212 | 236 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 213 | - | Host: "arch", FetchedAt: fetchedAt, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}, | |
| 237 | + | Host: "arch", FetchedAt: fetchedAt, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}, | |
| 214 | 238 | }}) | |
| 215 | 239 | d.remoteCatalog.mu.Lock() | |
| 216 | 240 | d.remoteCatalog.status["arch"] = remoteHostUnreachable | |
| @@ -393,7 +417,7 @@ func TestRemoteRefreshStartsPerHostAndCancelsSupersededGeneration(t *testing.T) | |||
| 393 | 417 | ac.overlays.pickerMu.Unlock() | |
| 394 | 418 | require.True(t, d.registerRemotePicker(instance)) | |
| 395 | 419 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 396 | - | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "cached", State: "running"}}, | |
| 420 | + | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "cached", State: "up"}}, | |
| 397 | 421 | }}) | |
| 398 | 422 | ||
| 399 | 423 | firstGeneration := d.startRemotePickerRefresh(instance) | |
| @@ -406,7 +430,7 @@ func TestRemoteRefreshStartsPerHostAndCancelsSupersededGeneration(t *testing.T) | |||
| 406 | 430 | require.Contains(t, first, "mule") | |
| 407 | 431 | requireNoRemoteLockViolations(t, catalog, nil) | |
| 408 | 432 | d.remoteCatalog.mu.Lock() | |
| 409 | - | require.Equal(t, []ports.RemoteCatalogSession{{Name: "cached", State: "running"}}, d.remoteCatalog.cache["arch"].Sessions, "cached rows publish before any remote completion") | |
| 433 | + | require.Equal(t, []ports.RemoteCatalogSession{{Name: "cached", State: "up"}}, d.remoteCatalog.cache["arch"].Sessions, "cached rows publish before any remote completion") | |
| 410 | 434 | require.Equal(t, remoteHostRefreshing, d.remoteCatalog.status["arch"]) | |
| 411 | 435 | d.remoteCatalog.mu.Unlock() | |
| 412 | 436 | ||
| @@ -442,7 +466,7 @@ func TestRemotePickerRefreshDisablesCachedTargetUntilCatalogCompletes(t *testing | |||
| 442 | 466 | Sessions: []ports.RemoteCatalogSession{{ | |
| 443 | 467 | LifecycleID: lifecycle, | |
| 444 | 468 | Name: "work", | |
| 445 | - | State: "running", | |
| 469 | + | State: "up", | |
| 446 | 470 | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 447 | 471 | }}, | |
| 448 | 472 | }}) | |
| @@ -472,7 +496,7 @@ func TestRemotePickerRefreshDisablesCachedTargetUntilCatalogCompletes(t *testing | |||
| 472 | 496 | request.result <- remoteRefreshResult{catalog: ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, SchemaVersion: ports.RemoteCatalogSchemaVersion, Sessions: []ports.RemoteCatalogSession{{ | |
| 473 | 497 | LifecycleID: lifecycle, | |
| 474 | 498 | Name: "work", | |
| 475 | - | State: "running", | |
| 499 | + | State: "up", | |
| 476 | 500 | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |
| 477 | 501 | }}}} | |
| 478 | 502 | receiveRemotePicker(t, cache.stores, "remote cache store") | |
| @@ -525,8 +549,8 @@ func TestRemoteRefreshResultPreservesFailuresAndEvictsSuccessfulOmissions(t *tes | |||
| 525 | 549 | hosts := &remoteRefreshHostStore{hosts: []string{"arch", "mule"}} | |
| 526 | 550 | d, _, cache := newRemoteRefreshDaemon(t, hosts, now) | |
| 527 | 551 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{ | |
| 528 | - | {Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "running"}}}, | |
| 529 | - | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "running"}}}, | |
| 552 | + | {Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "up"}}}, | |
| 553 | + | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "up"}}}, | |
| 530 | 554 | }) | |
| 531 | 555 | d.remoteCatalog.mu.Lock() | |
| 532 | 556 | d.remoteCatalog.refresh = 7 | |
| @@ -534,7 +558,7 @@ func TestRemoteRefreshResultPreservesFailuresAndEvictsSuccessfulOmissions(t *tes | |||
| 534 | 558 | ||
| 535 | 559 | require.False(t, d.applyRemoteRefreshResult(7, "mule", ports.RemoteCatalog{}, errors.New("offline"))) | |
| 536 | 560 | d.remoteCatalog.mu.Lock() | |
| 537 | - | require.Equal(t, []ports.RemoteCatalogSession{{Name: "stale", State: "running"}}, d.remoteCatalog.cache["mule"].Sessions) | |
| 561 | + | require.Equal(t, []ports.RemoteCatalogSession{{Name: "stale", State: "up"}}, d.remoteCatalog.cache["mule"].Sessions) | |
| 538 | 562 | require.Equal(t, time.Unix(20, 0), d.remoteCatalog.cache["mule"].FetchedAt) | |
| 539 | 563 | require.Equal(t, remoteHostUnreachable, d.remoteCatalog.status["mule"]) | |
| 540 | 564 | d.remoteCatalog.mu.Unlock() | |
| @@ -543,7 +567,7 @@ func TestRemoteRefreshResultPreservesFailuresAndEvictsSuccessfulOmissions(t *tes | |||
| 543 | 567 | stored := receiveRemotePicker(t, cache.stores, "remote cache store") | |
| 544 | 568 | require.Equal(t, []ports.RemoteCatalogCacheEntry{ | |
| 545 | 569 | {Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{}}, | |
| 546 | - | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "running"}}}, | |
| 570 | + | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "up"}}}, | |
| 547 | 571 | }, stored, "every successful write persists the newest full cache atomically") | |
| 548 | 572 | requireNoRemoteLockViolations(t, nil, cache) | |
| 549 | 573 | d.remoteCatalog.mu.Lock() | |
| @@ -556,15 +580,15 @@ func TestRemoteRefreshRejectsRemovedHostAndLateGeneration(t *testing.T) { | |||
| 556 | 580 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | |
| 557 | 581 | d, _, cache := newRemoteRefreshDaemon(t, hosts, time.Unix(300, 0)) | |
| 558 | 582 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 559 | - | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "running"}}, | |
| 583 | + | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "up"}}, | |
| 560 | 584 | }}) | |
| 561 | 585 | d.remoteCatalog.mu.Lock() | |
| 562 | 586 | d.remoteCatalog.refresh = 9 | |
| 563 | 587 | d.remoteCatalog.mu.Unlock() | |
| 564 | 588 | ||
| 565 | - | require.False(t, d.applyRemoteRefreshResult(8, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "late", State: "running"}}}, nil)) | |
| 589 | + | require.False(t, d.applyRemoteRefreshResult(8, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "late", State: "up"}}}, nil)) | |
| 566 | 590 | hosts.set() | |
| 567 | - | require.True(t, d.applyRemoteRefreshResult(9, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "removed", State: "running"}}}, nil)) | |
| 591 | + | require.True(t, d.applyRemoteRefreshResult(9, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "removed", State: "up"}}}, nil)) | |
| 568 | 592 | require.Empty(t, receiveRemotePicker(t, cache.stores, "remote cache store")) | |
| 569 | 593 | requireNoRemoteLockViolations(t, nil, cache) | |
| 570 | 594 | d.remoteCatalog.mu.Lock() | |
| @@ -580,7 +604,7 @@ func TestRemoteRefreshValidationSentinelsRemainMalformed(t *testing.T) { | |||
| 580 | 604 | session ports.RemoteCatalogSession | |
| 581 | 605 | }{ | |
| 582 | 606 | {name: "unknown state", session: ports.RemoteCatalogSession{LifecycleID: lifecycle, Name: "work", State: "unknown"}}, | |
| 583 | - | {name: "invalid reason", session: ports.RemoteCatalogSession{LifecycleID: lifecycle, Name: "work", State: "running", Reason: "bogus"}}, | |
| 607 | + | {name: "invalid reason", session: ports.RemoteCatalogSession{LifecycleID: lifecycle, Name: "work", State: "up", Reason: "bogus"}}, | |
| 584 | 608 | } { | |
| 585 | 609 | t.Run(test.name, func(t *testing.T) { | |
| 586 | 610 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | |
| @@ -598,7 +622,7 @@ func TestRemoteRefreshValidationSentinelsRemainMalformed(t *testing.T) { | |||
| 598 | 622 | func TestRemoteRefreshVersionMismatchPreservesStaleRows(t *testing.T) { | |
| 599 | 623 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | |
| 600 | 624 | d, _, _ := newRemoteRefreshDaemon(t, hosts, time.Unix(400, 0)) | |
| 601 | - | old := ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}} | |
| 625 | + | old := ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}} | |
| 602 | 626 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{old}) | |
| 603 | 627 | d.remoteCatalog.mu.Lock() | |
| 604 | 628 | d.remoteCatalog.refresh = 3 | |
| @@ -617,7 +641,7 @@ func TestRemotePickerSelectionKeepsLocalConnectionUntilRemoteViewIsReady(t *test | |||
| 617 | 641 | require.NoError(t, target.Validate()) | |
| 618 | 642 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 619 | 643 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | |
| 620 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 644 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 621 | 645 | }}, | |
| 622 | 646 | }}) | |
| 623 | 647 | d.remoteCatalog.mu.Lock() | |
| @@ -710,8 +734,8 @@ func TestRemotePickerSelectionTransitionsBetweenRemoteViewsAndSharesAttachmentMR | |||
| 710 | 734 | second.LiveTabID = "tab-2" | |
| 711 | 735 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 712 | 736 | Host: first.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{ | |
| 713 | - | {LifecycleID: first.LifecycleID, Name: first.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(first.LiveTabID), Index: 0, Name: "main"}}}, | |
| 714 | - | {LifecycleID: second.LifecycleID, Name: second.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(second.LiveTabID), Index: 0, Name: "main"}}}, | |
| 737 | + | {LifecycleID: first.LifecycleID, Name: first.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(first.LiveTabID), Index: 0, Name: "main"}}}, | |
| 738 | + | {LifecycleID: second.LifecycleID, Name: second.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(second.LiveTabID), Index: 0, Name: "main"}}}, | |
| 715 | 739 | }, | |
| 716 | 740 | }}) | |
| 717 | 741 | d.remoteCatalog.mu.Lock() | |
| @@ -800,7 +824,7 @@ func TestRemotePickerReselectUsesWarmRemoteViewWithoutSecondDial(t *testing.T) { | |||
| 800 | 824 | target := remoteLinkTestTarget() | |
| 801 | 825 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 802 | 826 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | |
| 803 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 827 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 804 | 828 | }}, | |
| 805 | 829 | }}) | |
| 806 | 830 | d.remoteCatalog.mu.Lock() | |
| @@ -879,7 +903,7 @@ func TestRemotePickerWarmReselectActivatesSelectedAlternateTabWithoutSecondDial( | |||
| 879 | 903 | const alternateName = "alternate" | |
| 880 | 904 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 881 | 905 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | |
| 882 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", | |
| 906 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", | |
| 883 | 907 | Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}, {ID: string(alternate.LiveTabID), Index: 1, Name: alternateName}}, | |
| 884 | 908 | }}, | |
| 885 | 909 | }}) | |
| @@ -963,7 +987,7 @@ func TestRemotePickerSelectionFailureKeepsSourceAndPickerOpen(t *testing.T) { | |||
| 963 | 987 | target := remoteLinkTestTarget() | |
| 964 | 988 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 965 | 989 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | |
| 966 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 990 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 967 | 991 | }}, | |
| 968 | 992 | }}) | |
| 969 | 993 | d.remoteCatalog.mu.Lock() | |
| @@ -1012,7 +1036,7 @@ func TestRemotePickerSelectionEscapeCancelsWithoutDetachingSource(t *testing.T) | |||
| 1012 | 1036 | target := remoteLinkTestTarget() | |
| 1013 | 1037 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | |
| 1014 | 1038 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | |
| 1015 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 1039 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | |
| 1016 | 1040 | }}, | |
| 1017 | 1041 | }}) | |
| 1018 | 1042 | d.remoteCatalog.mu.Lock() | |
| @@ -1097,7 +1121,7 @@ func TestRemoteRefreshUpdatesAllOpenPickersPreservingSelection(t *testing.T) { | |||
| 1097 | 1121 | ||
| 1098 | 1122 | require.True(t, d.applyRemoteRefreshResult(4, "arch", ports.RemoteCatalog{ | |
| 1099 | 1123 | ProtocolVersion: ports.ProtocolVersion, | |
| 1100 | - | Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running", Tabs: 2}}, | |
| 1124 | + | Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up", Tabs: 2}}, | |
| 1101 | 1125 | }, nil)) | |
| 1102 | 1126 | receiveRemotePicker(t, cache.stores, "remote cache store") | |
| 1103 | 1127 | requireNoRemoteLockViolations(t, catalog, cache) | |
| @@ -1410,7 +1434,7 @@ func TestRemotePickerStaleRefreshCannotOverwriteReopenedModel(t *testing.T) { | |||
| 1410 | 1434 | func TestRemoteRowsRequireFreshRichIdentityForPickerActivation(t *testing.T) { | |
| 1411 | 1435 | key := domain.RemoteSessionKey{Host: "arch", Name: "work"} | |
| 1412 | 1436 | session := ports.RemoteCatalogSession{ | |
| 1413 | - | LifecycleID: remoteLifecycleForTest(), Name: key.Name, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Name: "main"}}, | |
| 1437 | + | LifecycleID: remoteLifecycleForTest(), Name: key.Name, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Name: "main"}}, | |
| 1414 | 1438 | } | |
| 1415 | 1439 | for _, test := range []struct { | |
| 1416 | 1440 | status remoteHostStatus | |
| @@ -1430,7 +1454,7 @@ func TestRemoteRowsRequireFreshRichIdentityForPickerActivation(t *testing.T) { | |||
| 1430 | 1454 | require.Equal(t, test.want, view.RemoteAttachReady, "status %d", test.status) | |
| 1431 | 1455 | } | |
| 1432 | 1456 | ||
| 1433 | - | legacy := remotePickerView(key, ports.RemoteCatalogSession{Name: key.Name, State: "running"}, remoteHostFresh, time.Unix(1, 0)) | |
| 1457 | + | legacy := remotePickerView(key, ports.RemoteCatalogSession{Name: key.Name, State: "up"}, remoteHostFresh, time.Unix(1, 0)) | |
| 1434 | 1458 | require.False(t, legacy.RemoteAttachReady, "count-only cached rows cannot initiate proxied picker activation") | |
| 1435 | 1459 | _, selectable := picker.New([]picker.SessionView{legacy}, picker.SelectionConfig{Mode: picker.SelectNavigationTab}).Selected() | |
| 1436 | 1460 | require.False(t, selectable) | |
| @@ -64,7 +64,7 @@ func TestRemotePickerCatalogExpiresAtAttachTTL(t *testing.T) { | |||||
| 64 | lifecycle := remoteLifecycleForTest() | 64 | lifecycle := remoteLifecycleForTest() | ||
| 65 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 65 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 66 | Host: "arch", FetchedAt: now.Add(-remotePickerAttachTTL), Sessions: []ports.RemoteCatalogSession{{ | 66 | Host: "arch", FetchedAt: now.Add(-remotePickerAttachTTL), Sessions: []ports.RemoteCatalogSession{{ | ||
| 67 | - | LifecycleID: lifecycle, Name: "work", State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | 67 | + | LifecycleID: lifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, |
| 68 | }}, | 68 | }}, | ||
| 69 | }}) | 69 | }}) | ||
| 70 | d.remoteCatalog.mu.Lock() | 70 | d.remoteCatalog.mu.Lock() | ||
| @@ -85,7 +85,7 @@ func TestRemotePickerTargetReadyExpiresAfterPickerBuildWithoutSnapshot(t *testin | |||||
| 85 | lifecycle := remoteLifecycleForTest() | 85 | lifecycle := remoteLifecycleForTest() | ||
| 86 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 86 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 87 | Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{ | 87 | Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{{ | ||
| 88 | - | LifecycleID: lifecycle, Name: "work", State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | 88 | + | LifecycleID: lifecycle, Name: "work", State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, |
| 89 | }}, | 89 | }}, | ||
| 90 | }}) | 90 | }}) | ||
| 91 | d.remoteCatalog.mu.Lock() | 91 | d.remoteCatalog.mu.Lock() | ||
| @@ -152,7 +152,7 @@ func TestRemotePickerSelectsCatalogActiveTab(t *testing.T) { | |||||
| 152 | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | 152 | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | ||
| 153 | LifecycleID: lifecycle, | 153 | LifecycleID: lifecycle, | ||
| 154 | Name: "vive", | 154 | Name: "vive", | ||
| 155 | - | State: "running", | 155 | + | State: "up", |
| 156 | Tabs: test.tabs, | 156 | Tabs: test.tabs, | ||
| 157 | ActiveTabID: test.activeTabID, | 157 | ActiveTabID: test.activeTabID, | ||
| 158 | }}, | 158 | }}, | ||
| @@ -191,7 +191,7 @@ func TestRemotePickerAvailabilityUsesCachedRowsForFailures(t *testing.T) { | |||||
| 191 | t.Run(test.name, func(t *testing.T) { | 191 | t.Run(test.name, func(t *testing.T) { | ||
| 192 | d := newRemotePickerDaemon(nil) | 192 | d := newRemotePickerDaemon(nil) | ||
| 193 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 193 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 194 | - | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}, | 194 | + | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}, |
| 195 | }}) | 195 | }}) | ||
| 196 | d.remoteCatalog.mu.Lock() | 196 | d.remoteCatalog.mu.Lock() | ||
| 197 | d.remoteCatalog.status["arch"] = test.status | 197 | d.remoteCatalog.status["arch"] = test.status | ||
| @@ -205,12 +205,36 @@ func TestRemotePickerAvailabilityUsesCachedRowsForFailures(t *testing.T) { | |||||
| 205 | } | 205 | } | ||
| 206 | } | 206 | } | ||
| 207 | 207 | ||||
| 208 | + | func TestRemotePickerViewUsesShortStateLabels(t *testing.T) { | |||
| 209 | + | key := domain.RemoteSessionKey{Host: "arch", Name: "work"} | |||
| 210 | + | lifecycle := remoteLifecycleForTest() | |||
| 211 | + | for _, test := range []struct { | |||
| 212 | + | state string | |||
| 213 | + | detail string | |||
| 214 | + | stopped bool | |||
| 215 | + | }{ | |||
| 216 | + | {state: "up", detail: "up"}, | |||
| 217 | + | {state: "down", detail: "down", stopped: true}, | |||
| 218 | + | } { | |||
| 219 | + | t.Run(test.state, func(t *testing.T) { | |||
| 220 | + | view := remotePickerView(key, ports.RemoteCatalogSession{ | |||
| 221 | + | LifecycleID: lifecycle, | |||
| 222 | + | Name: key.Name, | |||
| 223 | + | State: test.state, | |||
| 224 | + | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | |||
| 225 | + | }, remoteHostFresh, time.Unix(1, 0)) | |||
| 226 | + | require.Equal(t, test.detail, view.RemoteDetail) | |||
| 227 | + | require.Equal(t, test.stopped, view.Stopped) | |||
| 228 | + | }) | |||
| 229 | + | } | |||
| 230 | + | } | |||
| 231 | + | ||||
| 208 | func TestRemotePickerStaleDetailUsesLastSuccessfulFetch(t *testing.T) { | 232 | func TestRemotePickerStaleDetailUsesLastSuccessfulFetch(t *testing.T) { | ||
| 209 | d := newRemotePickerDaemon(nil) | 233 | d := newRemotePickerDaemon(nil) | ||
| 210 | fetchedAt := time.Date(2025, time.January, 2, 3, 4, 5, 0, time.UTC) | 234 | fetchedAt := time.Date(2025, time.January, 2, 3, 4, 5, 0, time.UTC) | ||
| 211 | failedAt := fetchedAt.Add(6 * time.Hour) | 235 | failedAt := fetchedAt.Add(6 * time.Hour) | ||
| 212 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 236 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 213 | - | Host: "arch", FetchedAt: fetchedAt, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}, | 237 | + | Host: "arch", FetchedAt: fetchedAt, Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}, |
| 214 | }}) | 238 | }}) | ||
| 215 | d.remoteCatalog.mu.Lock() | 239 | d.remoteCatalog.mu.Lock() | ||
| 216 | d.remoteCatalog.status["arch"] = remoteHostUnreachable | 240 | d.remoteCatalog.status["arch"] = remoteHostUnreachable | ||
| @@ -393,7 +417,7 @@ func TestRemoteRefreshStartsPerHostAndCancelsSupersededGeneration(t *testing.T) | |||||
| 393 | ac.overlays.pickerMu.Unlock() | 417 | ac.overlays.pickerMu.Unlock() | ||
| 394 | require.True(t, d.registerRemotePicker(instance)) | 418 | require.True(t, d.registerRemotePicker(instance)) | ||
| 395 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 419 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 396 | - | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "cached", State: "running"}}, | 420 | + | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "cached", State: "up"}}, |
| 397 | }}) | 421 | }}) | ||
| 398 | 422 | ||||
| 399 | firstGeneration := d.startRemotePickerRefresh(instance) | 423 | firstGeneration := d.startRemotePickerRefresh(instance) | ||
| @@ -406,7 +430,7 @@ func TestRemoteRefreshStartsPerHostAndCancelsSupersededGeneration(t *testing.T) | |||||
| 406 | require.Contains(t, first, "mule") | 430 | require.Contains(t, first, "mule") | ||
| 407 | requireNoRemoteLockViolations(t, catalog, nil) | 431 | requireNoRemoteLockViolations(t, catalog, nil) | ||
| 408 | d.remoteCatalog.mu.Lock() | 432 | d.remoteCatalog.mu.Lock() | ||
| 409 | - | require.Equal(t, []ports.RemoteCatalogSession{{Name: "cached", State: "running"}}, d.remoteCatalog.cache["arch"].Sessions, "cached rows publish before any remote completion") | 433 | + | require.Equal(t, []ports.RemoteCatalogSession{{Name: "cached", State: "up"}}, d.remoteCatalog.cache["arch"].Sessions, "cached rows publish before any remote completion") |
| 410 | require.Equal(t, remoteHostRefreshing, d.remoteCatalog.status["arch"]) | 434 | require.Equal(t, remoteHostRefreshing, d.remoteCatalog.status["arch"]) | ||
| 411 | d.remoteCatalog.mu.Unlock() | 435 | d.remoteCatalog.mu.Unlock() | ||
| 412 | 436 | ||||
| @@ -442,7 +466,7 @@ func TestRemotePickerRefreshDisablesCachedTargetUntilCatalogCompletes(t *testing | |||||
| 442 | Sessions: []ports.RemoteCatalogSession{{ | 466 | Sessions: []ports.RemoteCatalogSession{{ | ||
| 443 | LifecycleID: lifecycle, | 467 | LifecycleID: lifecycle, | ||
| 444 | Name: "work", | 468 | Name: "work", | ||
| 445 | - | State: "running", | 469 | + | State: "up", |
| 446 | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | 470 | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | ||
| 447 | }}, | 471 | }}, | ||
| 448 | }}) | 472 | }}) | ||
| @@ -472,7 +496,7 @@ func TestRemotePickerRefreshDisablesCachedTargetUntilCatalogCompletes(t *testing | |||||
| 472 | request.result <- remoteRefreshResult{catalog: ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, SchemaVersion: ports.RemoteCatalogSchemaVersion, Sessions: []ports.RemoteCatalogSession{{ | 496 | request.result <- remoteRefreshResult{catalog: ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, SchemaVersion: ports.RemoteCatalogSchemaVersion, Sessions: []ports.RemoteCatalogSession{{ | ||
| 473 | LifecycleID: lifecycle, | 497 | LifecycleID: lifecycle, | ||
| 474 | Name: "work", | 498 | Name: "work", | ||
| 475 | - | State: "running", | 499 | + | State: "up", |
| 476 | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | 500 | Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Index: 0, Name: "main"}}, | ||
| 477 | }}}} | 501 | }}}} | ||
| 478 | receiveRemotePicker(t, cache.stores, "remote cache store") | 502 | receiveRemotePicker(t, cache.stores, "remote cache store") | ||
| @@ -525,8 +549,8 @@ func TestRemoteRefreshResultPreservesFailuresAndEvictsSuccessfulOmissions(t *tes | |||||
| 525 | hosts := &remoteRefreshHostStore{hosts: []string{"arch", "mule"}} | 549 | hosts := &remoteRefreshHostStore{hosts: []string{"arch", "mule"}} | ||
| 526 | d, _, cache := newRemoteRefreshDaemon(t, hosts, now) | 550 | d, _, cache := newRemoteRefreshDaemon(t, hosts, now) | ||
| 527 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{ | 551 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{ | ||
| 528 | - | {Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "running"}}}, | 552 | + | {Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "up"}}}, |
| 529 | - | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "running"}}}, | 553 | + | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "up"}}}, |
| 530 | }) | 554 | }) | ||
| 531 | d.remoteCatalog.mu.Lock() | 555 | d.remoteCatalog.mu.Lock() | ||
| 532 | d.remoteCatalog.refresh = 7 | 556 | d.remoteCatalog.refresh = 7 | ||
| @@ -534,7 +558,7 @@ func TestRemoteRefreshResultPreservesFailuresAndEvictsSuccessfulOmissions(t *tes | |||||
| 534 | 558 | ||||
| 535 | require.False(t, d.applyRemoteRefreshResult(7, "mule", ports.RemoteCatalog{}, errors.New("offline"))) | 559 | require.False(t, d.applyRemoteRefreshResult(7, "mule", ports.RemoteCatalog{}, errors.New("offline"))) | ||
| 536 | d.remoteCatalog.mu.Lock() | 560 | d.remoteCatalog.mu.Lock() | ||
| 537 | - | require.Equal(t, []ports.RemoteCatalogSession{{Name: "stale", State: "running"}}, d.remoteCatalog.cache["mule"].Sessions) | 561 | + | require.Equal(t, []ports.RemoteCatalogSession{{Name: "stale", State: "up"}}, d.remoteCatalog.cache["mule"].Sessions) |
| 538 | require.Equal(t, time.Unix(20, 0), d.remoteCatalog.cache["mule"].FetchedAt) | 562 | require.Equal(t, time.Unix(20, 0), d.remoteCatalog.cache["mule"].FetchedAt) | ||
| 539 | require.Equal(t, remoteHostUnreachable, d.remoteCatalog.status["mule"]) | 563 | require.Equal(t, remoteHostUnreachable, d.remoteCatalog.status["mule"]) | ||
| 540 | d.remoteCatalog.mu.Unlock() | 564 | d.remoteCatalog.mu.Unlock() | ||
| @@ -543,7 +567,7 @@ func TestRemoteRefreshResultPreservesFailuresAndEvictsSuccessfulOmissions(t *tes | |||||
| 543 | stored := receiveRemotePicker(t, cache.stores, "remote cache store") | 567 | stored := receiveRemotePicker(t, cache.stores, "remote cache store") | ||
| 544 | require.Equal(t, []ports.RemoteCatalogCacheEntry{ | 568 | require.Equal(t, []ports.RemoteCatalogCacheEntry{ | ||
| 545 | {Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{}}, | 569 | {Host: "arch", FetchedAt: now, Sessions: []ports.RemoteCatalogSession{}}, | ||
| 546 | - | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "running"}}}, | 570 | + | {Host: "mule", FetchedAt: time.Unix(20, 0), Sessions: []ports.RemoteCatalogSession{{Name: "stale", State: "up"}}}, |
| 547 | }, stored, "every successful write persists the newest full cache atomically") | 571 | }, stored, "every successful write persists the newest full cache atomically") | ||
| 548 | requireNoRemoteLockViolations(t, nil, cache) | 572 | requireNoRemoteLockViolations(t, nil, cache) | ||
| 549 | d.remoteCatalog.mu.Lock() | 573 | d.remoteCatalog.mu.Lock() | ||
| @@ -556,15 +580,15 @@ func TestRemoteRefreshRejectsRemovedHostAndLateGeneration(t *testing.T) { | |||||
| 556 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | 580 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | ||
| 557 | d, _, cache := newRemoteRefreshDaemon(t, hosts, time.Unix(300, 0)) | 581 | d, _, cache := newRemoteRefreshDaemon(t, hosts, time.Unix(300, 0)) | ||
| 558 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 582 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 559 | - | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "running"}}, | 583 | + | Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "old", State: "up"}}, |
| 560 | }}) | 584 | }}) | ||
| 561 | d.remoteCatalog.mu.Lock() | 585 | d.remoteCatalog.mu.Lock() | ||
| 562 | d.remoteCatalog.refresh = 9 | 586 | d.remoteCatalog.refresh = 9 | ||
| 563 | d.remoteCatalog.mu.Unlock() | 587 | d.remoteCatalog.mu.Unlock() | ||
| 564 | 588 | ||||
| 565 | - | require.False(t, d.applyRemoteRefreshResult(8, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "late", State: "running"}}}, nil)) | 589 | + | require.False(t, d.applyRemoteRefreshResult(8, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "late", State: "up"}}}, nil)) |
| 566 | hosts.set() | 590 | hosts.set() | ||
| 567 | - | require.True(t, d.applyRemoteRefreshResult(9, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "removed", State: "running"}}}, nil)) | 591 | + | require.True(t, d.applyRemoteRefreshResult(9, "arch", ports.RemoteCatalog{ProtocolVersion: ports.ProtocolVersion, Sessions: []ports.RemoteCatalogSession{{Name: "removed", State: "up"}}}, nil)) |
| 568 | require.Empty(t, receiveRemotePicker(t, cache.stores, "remote cache store")) | 592 | require.Empty(t, receiveRemotePicker(t, cache.stores, "remote cache store")) | ||
| 569 | requireNoRemoteLockViolations(t, nil, cache) | 593 | requireNoRemoteLockViolations(t, nil, cache) | ||
| 570 | d.remoteCatalog.mu.Lock() | 594 | d.remoteCatalog.mu.Lock() | ||
| @@ -580,7 +604,7 @@ func TestRemoteRefreshValidationSentinelsRemainMalformed(t *testing.T) { | |||||
| 580 | session ports.RemoteCatalogSession | 604 | session ports.RemoteCatalogSession | ||
| 581 | }{ | 605 | }{ | ||
| 582 | {name: "unknown state", session: ports.RemoteCatalogSession{LifecycleID: lifecycle, Name: "work", State: "unknown"}}, | 606 | {name: "unknown state", session: ports.RemoteCatalogSession{LifecycleID: lifecycle, Name: "work", State: "unknown"}}, | ||
| 583 | - | {name: "invalid reason", session: ports.RemoteCatalogSession{LifecycleID: lifecycle, Name: "work", State: "running", Reason: "bogus"}}, | 607 | + | {name: "invalid reason", session: ports.RemoteCatalogSession{LifecycleID: lifecycle, Name: "work", State: "up", Reason: "bogus"}}, |
| 584 | } { | 608 | } { | ||
| 585 | t.Run(test.name, func(t *testing.T) { | 609 | t.Run(test.name, func(t *testing.T) { | ||
| 586 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | 610 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | ||
| @@ -598,7 +622,7 @@ func TestRemoteRefreshValidationSentinelsRemainMalformed(t *testing.T) { | |||||
| 598 | func TestRemoteRefreshVersionMismatchPreservesStaleRows(t *testing.T) { | 622 | func TestRemoteRefreshVersionMismatchPreservesStaleRows(t *testing.T) { | ||
| 599 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | 623 | hosts := &remoteRefreshHostStore{hosts: []string{"arch"}} | ||
| 600 | d, _, _ := newRemoteRefreshDaemon(t, hosts, time.Unix(400, 0)) | 624 | d, _, _ := newRemoteRefreshDaemon(t, hosts, time.Unix(400, 0)) | ||
| 601 | - | old := ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running"}}} | 625 | + | old := ports.RemoteCatalogCacheEntry{Host: "arch", FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up"}}} |
| 602 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{old}) | 626 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{old}) | ||
| 603 | d.remoteCatalog.mu.Lock() | 627 | d.remoteCatalog.mu.Lock() | ||
| 604 | d.remoteCatalog.refresh = 3 | 628 | d.remoteCatalog.refresh = 3 | ||
| @@ -617,7 +641,7 @@ func TestRemotePickerSelectionKeepsLocalConnectionUntilRemoteViewIsReady(t *test | |||||
| 617 | require.NoError(t, target.Validate()) | 641 | require.NoError(t, target.Validate()) | ||
| 618 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 642 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 619 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | 643 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | ||
| 620 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | 644 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, |
| 621 | }}, | 645 | }}, | ||
| 622 | }}) | 646 | }}) | ||
| 623 | d.remoteCatalog.mu.Lock() | 647 | d.remoteCatalog.mu.Lock() | ||
| @@ -710,8 +734,8 @@ func TestRemotePickerSelectionTransitionsBetweenRemoteViewsAndSharesAttachmentMR | |||||
| 710 | second.LiveTabID = "tab-2" | 734 | second.LiveTabID = "tab-2" | ||
| 711 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 735 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 712 | Host: first.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{ | 736 | Host: first.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{ | ||
| 713 | - | {LifecycleID: first.LifecycleID, Name: first.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(first.LiveTabID), Index: 0, Name: "main"}}}, | 737 | + | {LifecycleID: first.LifecycleID, Name: first.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(first.LiveTabID), Index: 0, Name: "main"}}}, |
| 714 | - | {LifecycleID: second.LifecycleID, Name: second.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(second.LiveTabID), Index: 0, Name: "main"}}}, | 738 | + | {LifecycleID: second.LifecycleID, Name: second.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(second.LiveTabID), Index: 0, Name: "main"}}}, |
| 715 | }, | 739 | }, | ||
| 716 | }}) | 740 | }}) | ||
| 717 | d.remoteCatalog.mu.Lock() | 741 | d.remoteCatalog.mu.Lock() | ||
| @@ -800,7 +824,7 @@ func TestRemotePickerReselectUsesWarmRemoteViewWithoutSecondDial(t *testing.T) { | |||||
| 800 | target := remoteLinkTestTarget() | 824 | target := remoteLinkTestTarget() | ||
| 801 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 825 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 802 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | 826 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | ||
| 803 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | 827 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, |
| 804 | }}, | 828 | }}, | ||
| 805 | }}) | 829 | }}) | ||
| 806 | d.remoteCatalog.mu.Lock() | 830 | d.remoteCatalog.mu.Lock() | ||
| @@ -879,7 +903,7 @@ func TestRemotePickerWarmReselectActivatesSelectedAlternateTabWithoutSecondDial( | |||||
| 879 | const alternateName = "alternate" | 903 | const alternateName = "alternate" | ||
| 880 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 904 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 881 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | 905 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | ||
| 882 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", | 906 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", |
| 883 | Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}, {ID: string(alternate.LiveTabID), Index: 1, Name: alternateName}}, | 907 | Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}, {ID: string(alternate.LiveTabID), Index: 1, Name: alternateName}}, | ||
| 884 | }}, | 908 | }}, | ||
| 885 | }}) | 909 | }}) | ||
| @@ -963,7 +987,7 @@ func TestRemotePickerSelectionFailureKeepsSourceAndPickerOpen(t *testing.T) { | |||||
| 963 | target := remoteLinkTestTarget() | 987 | target := remoteLinkTestTarget() | ||
| 964 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 988 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 965 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | 989 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | ||
| 966 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | 990 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, |
| 967 | }}, | 991 | }}, | ||
| 968 | }}) | 992 | }}) | ||
| 969 | d.remoteCatalog.mu.Lock() | 993 | d.remoteCatalog.mu.Lock() | ||
| @@ -1012,7 +1036,7 @@ func TestRemotePickerSelectionEscapeCancelsWithoutDetachingSource(t *testing.T) | |||||
| 1012 | target := remoteLinkTestTarget() | 1036 | target := remoteLinkTestTarget() | ||
| 1013 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | 1037 | d.remoteCatalog.replaceCache([]ports.RemoteCatalogCacheEntry{{ | ||
| 1014 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | 1038 | Host: target.Endpoint, FetchedAt: time.Unix(10, 0), Sessions: []ports.RemoteCatalogSession{{ | ||
| 1015 | - | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, | 1039 | + | LifecycleID: target.LifecycleID, Name: target.SessionName, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: string(target.LiveTabID), Index: 0, Name: "main"}}, |
| 1016 | }}, | 1040 | }}, | ||
| 1017 | }}) | 1041 | }}) | ||
| 1018 | d.remoteCatalog.mu.Lock() | 1042 | d.remoteCatalog.mu.Lock() | ||
| @@ -1097,7 +1121,7 @@ func TestRemoteRefreshUpdatesAllOpenPickersPreservingSelection(t *testing.T) { | |||||
| 1097 | 1121 | ||||
| 1098 | require.True(t, d.applyRemoteRefreshResult(4, "arch", ports.RemoteCatalog{ | 1122 | require.True(t, d.applyRemoteRefreshResult(4, "arch", ports.RemoteCatalog{ | ||
| 1099 | ProtocolVersion: ports.ProtocolVersion, | 1123 | ProtocolVersion: ports.ProtocolVersion, | ||
| 1100 | - | Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "running", Tabs: 2}}, | 1124 | + | Sessions: []ports.RemoteCatalogSession{{Name: "work", State: "up", Tabs: 2}}, |
| 1101 | }, nil)) | 1125 | }, nil)) | ||
| 1102 | receiveRemotePicker(t, cache.stores, "remote cache store") | 1126 | receiveRemotePicker(t, cache.stores, "remote cache store") | ||
| 1103 | requireNoRemoteLockViolations(t, catalog, cache) | 1127 | requireNoRemoteLockViolations(t, catalog, cache) | ||
| @@ -1410,7 +1434,7 @@ func TestRemotePickerStaleRefreshCannotOverwriteReopenedModel(t *testing.T) { | |||||
| 1410 | func TestRemoteRowsRequireFreshRichIdentityForPickerActivation(t *testing.T) { | 1434 | func TestRemoteRowsRequireFreshRichIdentityForPickerActivation(t *testing.T) { | ||
| 1411 | key := domain.RemoteSessionKey{Host: "arch", Name: "work"} | 1435 | key := domain.RemoteSessionKey{Host: "arch", Name: "work"} | ||
| 1412 | session := ports.RemoteCatalogSession{ | 1436 | session := ports.RemoteCatalogSession{ | ||
| 1413 | - | LifecycleID: remoteLifecycleForTest(), Name: key.Name, State: "running", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Name: "main"}}, | 1437 | + | LifecycleID: remoteLifecycleForTest(), Name: key.Name, State: "up", Tabs: []ports.RemoteCatalogTab{{ID: "tab-1", Name: "main"}}, |
| 1414 | } | 1438 | } | ||
| 1415 | for _, test := range []struct { | 1439 | for _, test := range []struct { | ||
| 1416 | status remoteHostStatus | 1440 | status remoteHostStatus | ||
| @@ -1430,7 +1454,7 @@ func TestRemoteRowsRequireFreshRichIdentityForPickerActivation(t *testing.T) { | |||||
| 1430 | require.Equal(t, test.want, view.RemoteAttachReady, "status %d", test.status) | 1454 | require.Equal(t, test.want, view.RemoteAttachReady, "status %d", test.status) | ||
| 1431 | } | 1455 | } | ||
| 1432 | 1456 | ||||
| 1433 | - | legacy := remotePickerView(key, ports.RemoteCatalogSession{Name: key.Name, State: "running"}, remoteHostFresh, time.Unix(1, 0)) | 1457 | + | legacy := remotePickerView(key, ports.RemoteCatalogSession{Name: key.Name, State: "up"}, remoteHostFresh, time.Unix(1, 0)) |
| 1434 | require.False(t, legacy.RemoteAttachReady, "count-only cached rows cannot initiate proxied picker activation") | 1458 | require.False(t, legacy.RemoteAttachReady, "count-only cached rows cannot initiate proxied picker activation") | ||
| 1435 | _, selectable := picker.New([]picker.SessionView{legacy}, picker.SelectionConfig{Mode: picker.SelectNavigationTab}).Selected() | 1459 | _, selectable := picker.New([]picker.SessionView{legacy}, picker.SelectionConfig{Mode: picker.SelectNavigationTab}).Selected() | ||
| 1436 | require.False(t, selectable) | 1460 | require.False(t, selectable) | ||
M internal/usecase/daemon/session.go +1 -1
| @@ -1576,7 +1576,7 @@ func (d *Daemon) killSessionWithSnapshotDeadline(sess *session, reason uint8, pu | |||
| 1576 | 1576 | unlockSessions() | |
| 1577 | 1577 | d.notices.routingMu.Unlock() | |
| 1578 | 1578 | if !ephemeral { | |
| 1579 | - | stopped := stoppedSession{name: stoppedName, cwd: stoppedCwd, createdAt: createdAt, incarnation: incarnation, lastUsedSeq: sess.mruAt.Load(), tabNames: tabNames, tabRecords: tabRecords, record: stoppedRecord, state: ports.SessionStopped, purging: purge} | |
| 1579 | + | stopped := stoppedSession{name: stoppedName, cwd: stoppedCwd, createdAt: createdAt, incarnation: incarnation, lastUsedSeq: sess.mruAt.Load(), tabNames: tabNames, tabRecords: tabRecords, record: stoppedRecord, state: ports.SessionDown, purging: purge} | |
| 1580 | 1580 | d.stopped[stoppedName] = stopped | |
| 1581 | 1581 | } | |
| 1582 | 1582 | empty := len(d.sessions) == 0 | |
| @@ -1576,7 +1576,7 @@ func (d *Daemon) killSessionWithSnapshotDeadline(sess *session, reason uint8, pu | |||||
| 1576 | unlockSessions() | 1576 | unlockSessions() | ||
| 1577 | d.notices.routingMu.Unlock() | 1577 | d.notices.routingMu.Unlock() | ||
| 1578 | if !ephemeral { | 1578 | if !ephemeral { | ||
| 1579 | - | stopped := stoppedSession{name: stoppedName, cwd: stoppedCwd, createdAt: createdAt, incarnation: incarnation, lastUsedSeq: sess.mruAt.Load(), tabNames: tabNames, tabRecords: tabRecords, record: stoppedRecord, state: ports.SessionStopped, purging: purge} | 1579 | + | stopped := stoppedSession{name: stoppedName, cwd: stoppedCwd, createdAt: createdAt, incarnation: incarnation, lastUsedSeq: sess.mruAt.Load(), tabNames: tabNames, tabRecords: tabRecords, record: stoppedRecord, state: ports.SessionDown, purging: purge} |
| 1580 | d.stopped[stoppedName] = stopped | 1580 | d.stopped[stoppedName] = stopped | ||
| 1581 | } | 1581 | } | ||
| 1582 | empty := len(d.sessions) == 0 | 1582 | empty := len(d.sessions) == 0 | ||
M internal/usecase/daemon/session_test.go +2 -2
| @@ -779,7 +779,7 @@ func TestDaemonLoadsPersistedSessionsAsStopped(t *testing.T) { | |||
| 779 | 779 | require.Equal(t, int64(7), stopped.createdAt) | |
| 780 | 780 | require.Equal(t, incarnation, stopped.incarnation) | |
| 781 | 781 | require.Equal(t, uint64(9), stopped.lastUsedSeq) | |
| 782 | - | require.Equal(t, ports.SessionStopped, stopped.state) | |
| 782 | + | require.Equal(t, ports.SessionDown, stopped.state) | |
| 783 | 783 | require.NotNil(t, stopped.restoreDone) | |
| 784 | 784 | require.Equal(t, uint64(9), d.mruSeq.Load()) | |
| 785 | 785 | } | |
| @@ -1718,7 +1718,7 @@ func TestCatalogueRecordsConstructExpectedSessionRegistry(t *testing.T) { | |||
| 1718 | 1718 | ||
| 1719 | 1719 | alpha := d.stopped["alpha"] | |
| 1720 | 1720 | require.Equal(t, records[0], alpha.record) | |
| 1721 | - | require.Equal(t, ports.SessionStopped, alpha.state) | |
| 1721 | + | require.Equal(t, ports.SessionDown, alpha.state) | |
| 1722 | 1722 | require.NotNil(t, alpha.restoreDone) | |
| 1723 | 1723 | select { | |
| 1724 | 1724 | case <-alpha.restoreDone: | |
| @@ -779,7 +779,7 @@ func TestDaemonLoadsPersistedSessionsAsStopped(t *testing.T) { | |||||
| 779 | require.Equal(t, int64(7), stopped.createdAt) | 779 | require.Equal(t, int64(7), stopped.createdAt) | ||
| 780 | require.Equal(t, incarnation, stopped.incarnation) | 780 | require.Equal(t, incarnation, stopped.incarnation) | ||
| 781 | require.Equal(t, uint64(9), stopped.lastUsedSeq) | 781 | require.Equal(t, uint64(9), stopped.lastUsedSeq) | ||
| 782 | - | require.Equal(t, ports.SessionStopped, stopped.state) | 782 | + | require.Equal(t, ports.SessionDown, stopped.state) |
| 783 | require.NotNil(t, stopped.restoreDone) | 783 | require.NotNil(t, stopped.restoreDone) | ||
| 784 | require.Equal(t, uint64(9), d.mruSeq.Load()) | 784 | require.Equal(t, uint64(9), d.mruSeq.Load()) | ||
| 785 | } | 785 | } | ||
| @@ -1718,7 +1718,7 @@ func TestCatalogueRecordsConstructExpectedSessionRegistry(t *testing.T) { | |||||
| 1718 | 1718 | ||||
| 1719 | alpha := d.stopped["alpha"] | 1719 | alpha := d.stopped["alpha"] | ||
| 1720 | require.Equal(t, records[0], alpha.record) | 1720 | require.Equal(t, records[0], alpha.record) | ||
| 1721 | - | require.Equal(t, ports.SessionStopped, alpha.state) | 1721 | + | require.Equal(t, ports.SessionDown, alpha.state) |
| 1722 | require.NotNil(t, alpha.restoreDone) | 1722 | require.NotNil(t, alpha.restoreDone) | ||
| 1723 | select { | 1723 | select { | ||
| 1724 | case <-alpha.restoreDone: | 1724 | case <-alpha.restoreDone: | ||
M internal/usecase/daemon/snapshot_restore_incremental.go +4 -4
| @@ -21,7 +21,7 @@ var errRetryableRestoreLoad = errors.New("snapshot: retryable checkpoint load") | |||
| 21 | 21 | ||
| 22 | 22 | func initialSessionState(record domain.CatalogueRecord) (ports.SessionState, chan struct{}) { | |
| 23 | 23 | done := make(chan struct{}) | |
| 24 | - | state := ports.SessionStopped | |
| 24 | + | state := ports.SessionDown | |
| 25 | 25 | if record.DegradedReason != "" { | |
| 26 | 26 | state = ports.SessionBroken | |
| 27 | 27 | close(done) | |
| @@ -179,7 +179,7 @@ func (d *Daemon) restoreRecord(ctx context.Context, record domain.CatalogueRecor | |||
| 179 | 179 | return nil | |
| 180 | 180 | } | |
| 181 | 181 | if record.Committed == nil { | |
| 182 | - | d.setStoppedRecovery(record, ports.SessionStopped) | |
| 182 | + | d.setStoppedRecovery(record, ports.SessionDown) | |
| 183 | 183 | d.logSessionRestoreComplete(record, 0, false) | |
| 184 | 184 | return nil | |
| 185 | 185 | } | |
| @@ -231,7 +231,7 @@ func (d *Daemon) restoreRecord(ctx context.Context, record domain.CatalogueRecor | |||
| 231 | 231 | if err := d.restoreSession(ctx, selectedSnapshot, selectedGeneration.Generation, selected); err != nil { | |
| 232 | 232 | return err | |
| 233 | 233 | } | |
| 234 | - | d.setStoppedRecovery(record, ports.SessionStopped) | |
| 234 | + | d.setStoppedRecovery(record, ports.SessionDown) | |
| 235 | 235 | d.logSessionRestoreComplete(record, selected.Generation, false) | |
| 236 | 236 | return nil | |
| 237 | 237 | } | |
| @@ -252,7 +252,7 @@ func (d *Daemon) resetIncompatibleCheckpoint(ctx context.Context, record domain. | |||
| 252 | 252 | ||
| 253 | 253 | d.mu.Lock() | |
| 254 | 254 | if entry, ok := d.stopped[record.Name]; ok { | |
| 255 | - | d.stopped[record.Name] = stoppedSessionFromRecord(fresh, ports.SessionStopped, entry.restoreDone) | |
| 255 | + | d.stopped[record.Name] = stoppedSessionFromRecord(fresh, ports.SessionDown, entry.restoreDone) | |
| 256 | 256 | } | |
| 257 | 257 | d.mu.Unlock() | |
| 258 | 258 | ||
| @@ -21,7 +21,7 @@ var errRetryableRestoreLoad = errors.New("snapshot: retryable checkpoint load") | |||||
| 21 | 21 | ||||
| 22 | func initialSessionState(record domain.CatalogueRecord) (ports.SessionState, chan struct{}) { | 22 | func initialSessionState(record domain.CatalogueRecord) (ports.SessionState, chan struct{}) { | ||
| 23 | done := make(chan struct{}) | 23 | done := make(chan struct{}) | ||
| 24 | - | state := ports.SessionStopped | 24 | + | state := ports.SessionDown |
| 25 | if record.DegradedReason != "" { | 25 | if record.DegradedReason != "" { | ||
| 26 | state = ports.SessionBroken | 26 | state = ports.SessionBroken | ||
| 27 | close(done) | 27 | close(done) | ||
| @@ -179,7 +179,7 @@ func (d *Daemon) restoreRecord(ctx context.Context, record domain.CatalogueRecor | |||||
| 179 | return nil | 179 | return nil | ||
| 180 | } | 180 | } | ||
| 181 | if record.Committed == nil { | 181 | if record.Committed == nil { | ||
| 182 | - | d.setStoppedRecovery(record, ports.SessionStopped) | 182 | + | d.setStoppedRecovery(record, ports.SessionDown) |
| 183 | d.logSessionRestoreComplete(record, 0, false) | 183 | d.logSessionRestoreComplete(record, 0, false) | ||
| 184 | return nil | 184 | return nil | ||
| 185 | } | 185 | } | ||
| @@ -231,7 +231,7 @@ func (d *Daemon) restoreRecord(ctx context.Context, record domain.CatalogueRecor | |||||
| 231 | if err := d.restoreSession(ctx, selectedSnapshot, selectedGeneration.Generation, selected); err != nil { | 231 | if err := d.restoreSession(ctx, selectedSnapshot, selectedGeneration.Generation, selected); err != nil { | ||
| 232 | return err | 232 | return err | ||
| 233 | } | 233 | } | ||
| 234 | - | d.setStoppedRecovery(record, ports.SessionStopped) | 234 | + | d.setStoppedRecovery(record, ports.SessionDown) |
| 235 | d.logSessionRestoreComplete(record, selected.Generation, false) | 235 | d.logSessionRestoreComplete(record, selected.Generation, false) | ||
| 236 | return nil | 236 | return nil | ||
| 237 | } | 237 | } | ||
| @@ -252,7 +252,7 @@ func (d *Daemon) resetIncompatibleCheckpoint(ctx context.Context, record domain. | |||||
| 252 | 252 | ||||
| 253 | d.mu.Lock() | 253 | d.mu.Lock() | ||
| 254 | if entry, ok := d.stopped[record.Name]; ok { | 254 | if entry, ok := d.stopped[record.Name]; ok { | ||
| 255 | - | d.stopped[record.Name] = stoppedSessionFromRecord(fresh, ports.SessionStopped, entry.restoreDone) | 255 | + | d.stopped[record.Name] = stoppedSessionFromRecord(fresh, ports.SessionDown, entry.restoreDone) |
| 256 | } | 256 | } | ||
| 257 | d.mu.Unlock() | 257 | d.mu.Unlock() | ||
| 258 | 258 | ||||
M internal/usecase/picker/model.go +3 -3
| @@ -596,8 +596,8 @@ func (r row) remoteKeyPointer() *domain.RemoteSessionKey { | |||
| 596 | 596 | // renderList draws each visible row as up to three segments: a name segment | |
| 597 | 597 | // (bold when styles came from a truecolor theme), a base-styled attention | |
| 598 | 598 | // marker right after the name, and a muted detail segment (tab rows only) — | |
| 599 | - | // or a base-styled "(stopped)" suffix for stopped session headers. A tight | |
| 600 | - | // width ellipsizes the detail segment before eating into the name. | |
| 599 | + | // or a base-styled "(down)" suffix for down session headers. A tight width | |
| 600 | + | // ellipsizes the detail segment before eating into the name. | |
| 601 | 601 | func (m *Model) renderList(frame renderer.Frame, rect domain.Rect, styles RenderStyles) { | |
| 602 | 602 | if m == nil || rect.Width <= 0 || rect.Height <= 0 { | |
| 603 | 603 | return | |
| @@ -640,7 +640,7 @@ func (m *Model) renderList(frame renderer.Frame, rect domain.Rect, styles Render | |||
| 640 | 640 | } else { | |
| 641 | 641 | detail += " " | |
| 642 | 642 | } | |
| 643 | - | ui.DrawText(frame, x, rect.Y+y, clipX, detail+"(stopped)", base) | |
| 643 | + | ui.DrawText(frame, x, rect.Y+y, clipX, detail+"(down)", base) | |
| 644 | 644 | } else { | |
| 645 | 645 | ui.DrawText(frame, x, rect.Y+y, clipX, ui.TruncateText(r.detail, clipX-x), detailStyle) | |
| 646 | 646 | } | |
| @@ -596,8 +596,8 @@ func (r row) remoteKeyPointer() *domain.RemoteSessionKey { | |||||
| 596 | // renderList draws each visible row as up to three segments: a name segment | 596 | // renderList draws each visible row as up to three segments: a name segment | ||
| 597 | // (bold when styles came from a truecolor theme), a base-styled attention | 597 | // (bold when styles came from a truecolor theme), a base-styled attention | ||
| 598 | // marker right after the name, and a muted detail segment (tab rows only) — | 598 | // marker right after the name, and a muted detail segment (tab rows only) — | ||
| 599 | - | // or a base-styled "(stopped)" suffix for stopped session headers. A tight | 599 | + | // or a base-styled "(down)" suffix for down session headers. A tight width |
| 600 | - | // width ellipsizes the detail segment before eating into the name. | 600 | + | // ellipsizes the detail segment before eating into the name. |
| 601 | func (m *Model) renderList(frame renderer.Frame, rect domain.Rect, styles RenderStyles) { | 601 | func (m *Model) renderList(frame renderer.Frame, rect domain.Rect, styles RenderStyles) { | ||
| 602 | if m == nil || rect.Width <= 0 || rect.Height <= 0 { | 602 | if m == nil || rect.Width <= 0 || rect.Height <= 0 { | ||
| 603 | return | 603 | return | ||
| @@ -640,7 +640,7 @@ func (m *Model) renderList(frame renderer.Frame, rect domain.Rect, styles Render | |||||
| 640 | } else { | 640 | } else { | ||
| 641 | detail += " " | 641 | detail += " " | ||
| 642 | } | 642 | } | ||
| 643 | - | ui.DrawText(frame, x, rect.Y+y, clipX, detail+"(stopped)", base) | 643 | + | ui.DrawText(frame, x, rect.Y+y, clipX, detail+"(down)", base) |
| 644 | } else { | 644 | } else { | ||
| 645 | ui.DrawText(frame, x, rect.Y+y, clipX, ui.TruncateText(r.detail, clipX-x), detailStyle) | 645 | ui.DrawText(frame, x, rect.Y+y, clipX, ui.TruncateText(r.detail, clipX-x), detailStyle) | ||
| 646 | } | 646 | } | ||
M internal/usecase/picker/model_test.go +8 -2
| @@ -2,6 +2,7 @@ package picker | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "reflect" | |
| 5 | + | "strings" | |
| 5 | 6 | "testing" | |
| 6 | 7 | ||
| 7 | 8 | "github.com/bnema/vev/internal/domain" | |
| @@ -498,6 +499,11 @@ func TestStoppedSessionSelectableAndRendered(t *testing.T) { | |||
| 498 | 499 | frame := m.Render(domain.Size{Cols: 24, Rows: 4}, Preview{}) | |
| 499 | 500 | require.Equal(t, 'w', frame.At(0, 0).Rune) | |
| 500 | 501 | require.Equal(t, '(', frame.At(5, 0).Rune) | |
| 502 | + | var label strings.Builder | |
| 503 | + | for _, cell := range frame.Row(0) { | |
| 504 | + | label.WriteRune(cell.Rune) | |
| 505 | + | } | |
| 506 | + | require.Contains(t, label.String(), "(down)") | |
| 501 | 507 | } | |
| 502 | 508 | ||
| 503 | 509 | func TestRenderStopsStoppedRowsDimItalic(t *testing.T) { | |
| @@ -510,7 +516,7 @@ func TestRenderStopsStoppedRowsDimItalic(t *testing.T) { | |||
| 510 | 516 | require.Equal(t, stoppedStyle, styles.Stopped) | |
| 511 | 517 | ||
| 512 | 518 | frame := m.Render(domain.Size{Cols: 15, Rows: 6}, Preview{}) | |
| 513 | - | // Rows: 0 "work" header, 1 " tab" (selected), 2 "old (stopped)" header, 3 its tab row. | |
| 519 | + | // Rows: 0 "work" header, 1 " tab" (selected), 2 "old (down)" header, 3 its tab row. | |
| 514 | 520 | require.Equal(t, stoppedStyle, frame.Row(2)[0].Style, "stopped header must be dim italic") | |
| 515 | 521 | require.Equal(t, stoppedStyle, frame.Row(3)[0].Style, "stopped tab row must be dim italic") | |
| 516 | 522 | require.NotEqual(t, stoppedStyle, frame.Row(0)[0].Style, "live header keeps base style") | |
| @@ -587,7 +593,7 @@ func TestRenderListDrawsNameAndDetailSegmentsWithDistinctStyles(t *testing.T) { | |||
| 587 | 593 | } | |
| 588 | 594 | ||
| 589 | 595 | m := New([]SessionView{{ID: "s1", Name: "one", Tabs: []TabEntry{ | |
| 590 | - | {Name: "alpha", Detail: " (running)"}, | |
| 596 | + | {Name: "alpha", Detail: " (up)"}, | |
| 591 | 597 | {Name: "beta", Detail: " (idle)", Attention: true}, | |
| 592 | 598 | }, Active: 0}}, SelectionConfig{Mode: SelectNavigationTab}) | |
| 593 | 599 | ||
| @@ -2,6 +2,7 @@ package picker | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "reflect" | 4 | "reflect" | ||
| 5 | + | "strings" | |||
| 5 | "testing" | 6 | "testing" | ||
| 6 | 7 | ||||
| 7 | "github.com/bnema/vev/internal/domain" | 8 | "github.com/bnema/vev/internal/domain" | ||
| @@ -498,6 +499,11 @@ func TestStoppedSessionSelectableAndRendered(t *testing.T) { | |||||
| 498 | frame := m.Render(domain.Size{Cols: 24, Rows: 4}, Preview{}) | 499 | frame := m.Render(domain.Size{Cols: 24, Rows: 4}, Preview{}) | ||
| 499 | require.Equal(t, 'w', frame.At(0, 0).Rune) | 500 | require.Equal(t, 'w', frame.At(0, 0).Rune) | ||
| 500 | require.Equal(t, '(', frame.At(5, 0).Rune) | 501 | require.Equal(t, '(', frame.At(5, 0).Rune) | ||
| 502 | + | var label strings.Builder | |||
| 503 | + | for _, cell := range frame.Row(0) { | |||
| 504 | + | label.WriteRune(cell.Rune) | |||
| 505 | + | } | |||
| 506 | + | require.Contains(t, label.String(), "(down)") | |||
| 501 | } | 507 | } | ||
| 502 | 508 | ||||
| 503 | func TestRenderStopsStoppedRowsDimItalic(t *testing.T) { | 509 | func TestRenderStopsStoppedRowsDimItalic(t *testing.T) { | ||
| @@ -510,7 +516,7 @@ func TestRenderStopsStoppedRowsDimItalic(t *testing.T) { | |||||
| 510 | require.Equal(t, stoppedStyle, styles.Stopped) | 516 | require.Equal(t, stoppedStyle, styles.Stopped) | ||
| 511 | 517 | ||||
| 512 | frame := m.Render(domain.Size{Cols: 15, Rows: 6}, Preview{}) | 518 | frame := m.Render(domain.Size{Cols: 15, Rows: 6}, Preview{}) | ||
| 513 | - | // Rows: 0 "work" header, 1 " tab" (selected), 2 "old (stopped)" header, 3 its tab row. | 519 | + | // Rows: 0 "work" header, 1 " tab" (selected), 2 "old (down)" header, 3 its tab row. |
| 514 | require.Equal(t, stoppedStyle, frame.Row(2)[0].Style, "stopped header must be dim italic") | 520 | require.Equal(t, stoppedStyle, frame.Row(2)[0].Style, "stopped header must be dim italic") | ||
| 515 | require.Equal(t, stoppedStyle, frame.Row(3)[0].Style, "stopped tab row must be dim italic") | 521 | require.Equal(t, stoppedStyle, frame.Row(3)[0].Style, "stopped tab row must be dim italic") | ||
| 516 | require.NotEqual(t, stoppedStyle, frame.Row(0)[0].Style, "live header keeps base style") | 522 | require.NotEqual(t, stoppedStyle, frame.Row(0)[0].Style, "live header keeps base style") | ||
| @@ -587,7 +593,7 @@ func TestRenderListDrawsNameAndDetailSegmentsWithDistinctStyles(t *testing.T) { | |||||
| 587 | } | 593 | } | ||
| 588 | 594 | ||||
| 589 | m := New([]SessionView{{ID: "s1", Name: "one", Tabs: []TabEntry{ | 595 | m := New([]SessionView{{ID: "s1", Name: "one", Tabs: []TabEntry{ | ||
| 590 | - | {Name: "alpha", Detail: " (running)"}, | 596 | + | {Name: "alpha", Detail: " (up)"}, |
| 591 | {Name: "beta", Detail: " (idle)", Attention: true}, | 597 | {Name: "beta", Detail: " (idle)", Attention: true}, | ||
| 592 | }, Active: 0}}, SelectionConfig{Mode: SelectNavigationTab}) | 598 | }, Active: 0}}, SelectionConfig{Mode: SelectNavigationTab}) | ||
| 593 | 599 | ||||
M scripts/remote-picker-harness/main.go +5 -5
| @@ -94,7 +94,7 @@ func runLiveStdioPhase(ctx context.Context, target string, catalog ports.RemoteC | |||
| 94 | 94 | if err := sendInputAndAwait(ctx, stdio, "printf 'VEV_REMOTE_HARNESS_ENV=%s:%s:%s:%s\\n' \"$VEV_TEST_ENV\" \"$XDG_RUNTIME_DIR\" \"$WAYLAND_DISPLAY\" \"$VEV\"", "local:/run/local:wayland-local:"); err != nil { | |
| 95 | 95 | return fmt.Errorf("direct client environment: %w", err) | |
| 96 | 96 | } | |
| 97 | - | live, err := waitForSession(ctx, catalog, target, sessionName, "running") | |
| 97 | + | live, err := waitForSession(ctx, catalog, target, sessionName, "up") | |
| 98 | 98 | if err != nil { | |
| 99 | 99 | return fmt.Errorf("live catalog: %w", err) | |
| 100 | 100 | } | |
| @@ -108,7 +108,7 @@ func runLiveStdioPhase(ctx context.Context, target string, catalog ports.RemoteC | |||
| 108 | 108 | if err := sendCommand(ctx, stdio, "new-tab"); err != nil { | |
| 109 | 109 | return fmt.Errorf("create second tab: %w", err) | |
| 110 | 110 | } | |
| 111 | - | liveWithTabs, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "running", 2) | |
| 111 | + | liveWithTabs, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "up", 2) | |
| 112 | 112 | if err != nil { | |
| 113 | 113 | return fmt.Errorf("multi-tab catalog: %w", err) | |
| 114 | 114 | } | |
| @@ -116,7 +116,7 @@ func runLiveStdioPhase(ctx context.Context, target string, catalog ports.RemoteC | |||
| 116 | 116 | if err := sendCommand(ctx, stdio, "close-tab"); err != nil { | |
| 117 | 117 | return fmt.Errorf("remove tab: %w", err) | |
| 118 | 118 | } | |
| 119 | - | after, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "running", 1) | |
| 119 | + | after, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "up", 1) | |
| 120 | 120 | if err != nil { | |
| 121 | 121 | return fmt.Errorf("removed-tab catalog: %w", err) | |
| 122 | 122 | } | |
| @@ -192,7 +192,7 @@ func runLocalPickerUnitedPhase(ctx context.Context, target string, catalog ports | |||
| 192 | 192 | if err := sendInputAndAwait(ctx, remote, "printf '"+previewMarker+"\\n'", previewMarker); err != nil { | |
| 193 | 193 | return fmt.Errorf("seed remote picker preview: %w", err) | |
| 194 | 194 | } | |
| 195 | - | remoteCatalog, err := waitForSessionWithTabCount(ctx, catalog, target, remoteSession, "running", 2) | |
| 195 | + | remoteCatalog, err := waitForSessionWithTabCount(ctx, catalog, target, remoteSession, "up", 2) | |
| 196 | 196 | if err != nil { | |
| 197 | 197 | return fmt.Errorf("remote picker catalog: %w", err) | |
| 198 | 198 | } | |
| @@ -335,7 +335,7 @@ func runRestartResumePhase(ctx context.Context, target string, catalog ports.Rem | |||
| 335 | 335 | if err := runRemoteCommand(ctx, target, "vev", "--daemon-launcher"); err != nil { | |
| 336 | 336 | return domain.RemoteSessionTarget{}, fmt.Errorf("restart remote daemon: %w", err) | |
| 337 | 337 | } | |
| 338 | - | resumedCatalog, err := waitForSession(ctx, catalog, target, sessionName, "running") | |
| 338 | + | resumedCatalog, err := waitForSession(ctx, catalog, target, sessionName, "up") | |
| 339 | 339 | if err != nil { | |
| 340 | 340 | return domain.RemoteSessionTarget{}, fmt.Errorf("resumed catalog: %w", err) | |
| 341 | 341 | } | |
| @@ -94,7 +94,7 @@ func runLiveStdioPhase(ctx context.Context, target string, catalog ports.RemoteC | |||||
| 94 | if err := sendInputAndAwait(ctx, stdio, "printf 'VEV_REMOTE_HARNESS_ENV=%s:%s:%s:%s\\n' \"$VEV_TEST_ENV\" \"$XDG_RUNTIME_DIR\" \"$WAYLAND_DISPLAY\" \"$VEV\"", "local:/run/local:wayland-local:"); err != nil { | 94 | if err := sendInputAndAwait(ctx, stdio, "printf 'VEV_REMOTE_HARNESS_ENV=%s:%s:%s:%s\\n' \"$VEV_TEST_ENV\" \"$XDG_RUNTIME_DIR\" \"$WAYLAND_DISPLAY\" \"$VEV\"", "local:/run/local:wayland-local:"); err != nil { | ||
| 95 | return fmt.Errorf("direct client environment: %w", err) | 95 | return fmt.Errorf("direct client environment: %w", err) | ||
| 96 | } | 96 | } | ||
| 97 | - | live, err := waitForSession(ctx, catalog, target, sessionName, "running") | 97 | + | live, err := waitForSession(ctx, catalog, target, sessionName, "up") |
| 98 | if err != nil { | 98 | if err != nil { | ||
| 99 | return fmt.Errorf("live catalog: %w", err) | 99 | return fmt.Errorf("live catalog: %w", err) | ||
| 100 | } | 100 | } | ||
| @@ -108,7 +108,7 @@ func runLiveStdioPhase(ctx context.Context, target string, catalog ports.RemoteC | |||||
| 108 | if err := sendCommand(ctx, stdio, "new-tab"); err != nil { | 108 | if err := sendCommand(ctx, stdio, "new-tab"); err != nil { | ||
| 109 | return fmt.Errorf("create second tab: %w", err) | 109 | return fmt.Errorf("create second tab: %w", err) | ||
| 110 | } | 110 | } | ||
| 111 | - | liveWithTabs, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "running", 2) | 111 | + | liveWithTabs, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "up", 2) |
| 112 | if err != nil { | 112 | if err != nil { | ||
| 113 | return fmt.Errorf("multi-tab catalog: %w", err) | 113 | return fmt.Errorf("multi-tab catalog: %w", err) | ||
| 114 | } | 114 | } | ||
| @@ -116,7 +116,7 @@ func runLiveStdioPhase(ctx context.Context, target string, catalog ports.RemoteC | |||||
| 116 | if err := sendCommand(ctx, stdio, "close-tab"); err != nil { | 116 | if err := sendCommand(ctx, stdio, "close-tab"); err != nil { | ||
| 117 | return fmt.Errorf("remove tab: %w", err) | 117 | return fmt.Errorf("remove tab: %w", err) | ||
| 118 | } | 118 | } | ||
| 119 | - | after, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "running", 1) | 119 | + | after, err := waitForSessionWithTabCount(ctx, catalog, target, sessionName, "up", 1) |
| 120 | if err != nil { | 120 | if err != nil { | ||
| 121 | return fmt.Errorf("removed-tab catalog: %w", err) | 121 | return fmt.Errorf("removed-tab catalog: %w", err) | ||
| 122 | } | 122 | } | ||
| @@ -192,7 +192,7 @@ func runLocalPickerUnitedPhase(ctx context.Context, target string, catalog ports | |||||
| 192 | if err := sendInputAndAwait(ctx, remote, "printf '"+previewMarker+"\\n'", previewMarker); err != nil { | 192 | if err := sendInputAndAwait(ctx, remote, "printf '"+previewMarker+"\\n'", previewMarker); err != nil { | ||
| 193 | return fmt.Errorf("seed remote picker preview: %w", err) | 193 | return fmt.Errorf("seed remote picker preview: %w", err) | ||
| 194 | } | 194 | } | ||
| 195 | - | remoteCatalog, err := waitForSessionWithTabCount(ctx, catalog, target, remoteSession, "running", 2) | 195 | + | remoteCatalog, err := waitForSessionWithTabCount(ctx, catalog, target, remoteSession, "up", 2) |
| 196 | if err != nil { | 196 | if err != nil { | ||
| 197 | return fmt.Errorf("remote picker catalog: %w", err) | 197 | return fmt.Errorf("remote picker catalog: %w", err) | ||
| 198 | } | 198 | } | ||
| @@ -335,7 +335,7 @@ func runRestartResumePhase(ctx context.Context, target string, catalog ports.Rem | |||||
| 335 | if err := runRemoteCommand(ctx, target, "vev", "--daemon-launcher"); err != nil { | 335 | if err := runRemoteCommand(ctx, target, "vev", "--daemon-launcher"); err != nil { | ||
| 336 | return domain.RemoteSessionTarget{}, fmt.Errorf("restart remote daemon: %w", err) | 336 | return domain.RemoteSessionTarget{}, fmt.Errorf("restart remote daemon: %w", err) | ||
| 337 | } | 337 | } | ||
| 338 | - | resumedCatalog, err := waitForSession(ctx, catalog, target, sessionName, "running") | 338 | + | resumedCatalog, err := waitForSession(ctx, catalog, target, sessionName, "up") |
| 339 | if err != nil { | 339 | if err != nil { | ||
| 340 | return domain.RemoteSessionTarget{}, fmt.Errorf("resumed catalog: %w", err) | 340 | return domain.RemoteSessionTarget{}, fmt.Errorf("resumed catalog: %w", err) | ||
| 341 | } | 341 | } | ||