test(navigation): harden identity and wire coverage
- commit
- 932c0f31b72263453a701a01ebe69b6c5aeaf6a1
- author
- brice · · 2026-08-18 20:28 UTC
- committer
- brice · · 2026-08-19 04:15 UTC
- parent
- 86aeb12
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
9 files · +78 -55
M internal/ports/routes_test.go +18 -2
| @@ -197,8 +197,24 @@ func TestRecentRouteSnapshotRejectsOversizedFrameBeforeParsing(t *testing.T) { | |||
| 197 | 197 | } | |
| 198 | 198 | ||
| 199 | 199 | func TestRouteLabelBoundsMatchRemoteDisplayOrigin(t *testing.T) { | |
| 200 | - | require.NoError(t, ValidateRouteLabel(strings.Repeat("a", 256), false)) | |
| 201 | - | require.Error(t, ValidateRouteLabel(strings.Repeat("a", 257), false)) | |
| 200 | + | tests := []struct { | |
| 201 | + | name string | |
| 202 | + | length int | |
| 203 | + | wantErr bool | |
| 204 | + | }{ | |
| 205 | + | {name: "maximum", length: 256}, | |
| 206 | + | {name: "over maximum", length: 257, wantErr: true}, | |
| 207 | + | } | |
| 208 | + | for _, tt := range tests { | |
| 209 | + | t.Run(tt.name, func(t *testing.T) { | |
| 210 | + | err := ValidateRouteLabel(strings.Repeat("a", tt.length), false) | |
| 211 | + | if tt.wantErr { | |
| 212 | + | require.Error(t, err) | |
| 213 | + | return | |
| 214 | + | } | |
| 215 | + | require.NoError(t, err) | |
| 216 | + | }) | |
| 217 | + | } | |
| 202 | 218 | } | |
| 203 | 219 | ||
| 204 | 220 | func TestRouteLabelsRejectTerminalUnsafeText(t *testing.T) { | |
| @@ -197,8 +197,24 @@ func TestRecentRouteSnapshotRejectsOversizedFrameBeforeParsing(t *testing.T) { | |||||
| 197 | } | 197 | } | ||
| 198 | 198 | ||||
| 199 | func TestRouteLabelBoundsMatchRemoteDisplayOrigin(t *testing.T) { | 199 | func TestRouteLabelBoundsMatchRemoteDisplayOrigin(t *testing.T) { | ||
| 200 | - | require.NoError(t, ValidateRouteLabel(strings.Repeat("a", 256), false)) | 200 | + | tests := []struct { |
| 201 | - | require.Error(t, ValidateRouteLabel(strings.Repeat("a", 257), false)) | 201 | + | name string |
| 202 | + | length int | |||
| 203 | + | wantErr bool | |||
| 204 | + | }{ | |||
| 205 | + | {name: "maximum", length: 256}, | |||
| 206 | + | {name: "over maximum", length: 257, wantErr: true}, | |||
| 207 | + | } | |||
| 208 | + | for _, tt := range tests { | |||
| 209 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 210 | + | err := ValidateRouteLabel(strings.Repeat("a", tt.length), false) | |||
| 211 | + | if tt.wantErr { | |||
| 212 | + | require.Error(t, err) | |||
| 213 | + | return | |||
| 214 | + | } | |||
| 215 | + | require.NoError(t, err) | |||
| 216 | + | }) | |||
| 217 | + | } | |||
| 202 | } | 218 | } | ||
| 203 | 219 | ||||
| 204 | func TestRouteLabelsRejectTerminalUnsafeText(t *testing.T) { | 220 | func TestRouteLabelsRejectTerminalUnsafeText(t *testing.T) { | ||
M internal/ports/wire_test.go +5 -5
| @@ -173,15 +173,15 @@ func TestHelloEnvironmentCodec(t *testing.T) { | |||
| 173 | 173 | base := MarshalHello(Hello{Version: ProtocolVersion, Size: domain.Size{Cols: 1, Rows: 1}}) | |
| 174 | 174 | withCount := func(count byte) []byte { | |
| 175 | 175 | payload := append([]byte(nil), base...) | |
| 176 | - | // The uint32 environment count occupies bytes len-13 through len-10. | |
| 177 | - | payload[len(payload)-10] = count | |
| 176 | + | // The uint32 environment count occupies bytes len-11 through len-8. | |
| 177 | + | payload[len(payload)-8] = count | |
| 178 | 178 | return payload | |
| 179 | 179 | } | |
| 180 | 180 | withEntries := func(count byte, entries ...byte) []byte { | |
| 181 | 181 | payload := withCount(count) | |
| 182 | - | // The nine-byte tail follows the environment-count field. | |
| 183 | - | tail := append([]byte(nil), payload[len(payload)-9:]...) | |
| 184 | - | payload = append(payload[:len(payload)-9], entries...) | |
| 182 | + | // The seven-byte tail follows the environment-count field. | |
| 183 | + | tail := append([]byte(nil), payload[len(payload)-7:]...) | |
| 184 | + | payload = append(payload[:len(payload)-7], entries...) | |
| 185 | 185 | return append(payload, tail...) | |
| 186 | 186 | } | |
| 187 | 187 | tests := []struct { | |
| @@ -173,15 +173,15 @@ func TestHelloEnvironmentCodec(t *testing.T) { | |||||
| 173 | base := MarshalHello(Hello{Version: ProtocolVersion, Size: domain.Size{Cols: 1, Rows: 1}}) | 173 | base := MarshalHello(Hello{Version: ProtocolVersion, Size: domain.Size{Cols: 1, Rows: 1}}) | ||
| 174 | withCount := func(count byte) []byte { | 174 | withCount := func(count byte) []byte { | ||
| 175 | payload := append([]byte(nil), base...) | 175 | payload := append([]byte(nil), base...) | ||
| 176 | - | // The uint32 environment count occupies bytes len-13 through len-10. | 176 | + | // The uint32 environment count occupies bytes len-11 through len-8. |
| 177 | - | payload[len(payload)-10] = count | 177 | + | payload[len(payload)-8] = count |
| 178 | return payload | 178 | return payload | ||
| 179 | } | 179 | } | ||
| 180 | withEntries := func(count byte, entries ...byte) []byte { | 180 | withEntries := func(count byte, entries ...byte) []byte { | ||
| 181 | payload := withCount(count) | 181 | payload := withCount(count) | ||
| 182 | - | // The nine-byte tail follows the environment-count field. | 182 | + | // The seven-byte tail follows the environment-count field. |
| 183 | - | tail := append([]byte(nil), payload[len(payload)-9:]...) | 183 | + | tail := append([]byte(nil), payload[len(payload)-7:]...) |
| 184 | - | payload = append(payload[:len(payload)-9], entries...) | 184 | + | payload = append(payload[:len(payload)-7], entries...) |
| 185 | return append(payload, tail...) | 185 | return append(payload, tail...) | ||
| 186 | } | 186 | } | ||
| 187 | tests := []struct { | 187 | tests := []struct { | ||
M internal/usecase/daemon/palette.go +2 -2
| @@ -84,7 +84,7 @@ func (d *Daemon) paletteResults(current *session, commands []command.Command, ro | |||
| 84 | 84 | continue | |
| 85 | 85 | } | |
| 86 | 86 | target := ports.ExactSessionTarget{LifecycleID: snap.incarnation, SessionName: snap.name} | |
| 87 | - | active = append(active, palette.NewActiveSessionResult(snap.name, time.Unix(0, snap.createdAt), target)) | |
| 87 | + | active = append(active, palette.NewActiveSessionResult(target, time.Unix(0, snap.createdAt))) | |
| 88 | 88 | } | |
| 89 | 89 | sort.Slice(active, func(i, j int) bool { | |
| 90 | 90 | left, _ := active[i].SessionName() | |
| @@ -95,7 +95,7 @@ func (d *Daemon) paletteResults(current *session, commands []command.Command, ro | |||
| 95 | 95 | results = append(results, active...) | |
| 96 | 96 | for _, candidate := range stopped { | |
| 97 | 97 | target := ports.ExactSessionTarget{LifecycleID: candidate.incarnation, SessionName: candidate.name} | |
| 98 | - | results = append(results, palette.NewStoppedSessionResult(candidate.name, time.Unix(0, candidate.createdAt), target)) | |
| 98 | + | results = append(results, palette.NewStoppedSessionResult(target, time.Unix(0, candidate.createdAt))) | |
| 99 | 99 | localLifecycles[target.LifecycleID] = struct{}{} | |
| 100 | 100 | } | |
| 101 | 101 | formattedRoutes := formatRecentRouteSnapshot(routeSnapshot) | |
| @@ -84,7 +84,7 @@ func (d *Daemon) paletteResults(current *session, commands []command.Command, ro | |||||
| 84 | continue | 84 | continue | ||
| 85 | } | 85 | } | ||
| 86 | target := ports.ExactSessionTarget{LifecycleID: snap.incarnation, SessionName: snap.name} | 86 | target := ports.ExactSessionTarget{LifecycleID: snap.incarnation, SessionName: snap.name} | ||
| 87 | - | active = append(active, palette.NewActiveSessionResult(snap.name, time.Unix(0, snap.createdAt), target)) | 87 | + | active = append(active, palette.NewActiveSessionResult(target, time.Unix(0, snap.createdAt))) |
| 88 | } | 88 | } | ||
| 89 | sort.Slice(active, func(i, j int) bool { | 89 | sort.Slice(active, func(i, j int) bool { | ||
| 90 | left, _ := active[i].SessionName() | 90 | left, _ := active[i].SessionName() | ||
| @@ -95,7 +95,7 @@ func (d *Daemon) paletteResults(current *session, commands []command.Command, ro | |||||
| 95 | results = append(results, active...) | 95 | results = append(results, active...) | ||
| 96 | for _, candidate := range stopped { | 96 | for _, candidate := range stopped { | ||
| 97 | target := ports.ExactSessionTarget{LifecycleID: candidate.incarnation, SessionName: candidate.name} | 97 | target := ports.ExactSessionTarget{LifecycleID: candidate.incarnation, SessionName: candidate.name} | ||
| 98 | - | results = append(results, palette.NewStoppedSessionResult(candidate.name, time.Unix(0, candidate.createdAt), target)) | 98 | + | results = append(results, palette.NewStoppedSessionResult(target, time.Unix(0, candidate.createdAt))) |
| 99 | localLifecycles[target.LifecycleID] = struct{}{} | 99 | localLifecycles[target.LifecycleID] = struct{}{} | ||
| 100 | } | 100 | } | ||
| 101 | formattedRoutes := formatRecentRouteSnapshot(routeSnapshot) | 101 | formattedRoutes := formatRecentRouteSnapshot(routeSnapshot) | ||
M internal/usecase/daemon/status_test.go +28 -21
| @@ -110,30 +110,37 @@ func TestStatusCompositionGolden(t *testing.T) { | |||
| 110 | 110 | } | |
| 111 | 111 | ||
| 112 | 112 | func TestStatusSegmentsResolvesActiveLifecyclePresentation(t *testing.T) { | |
| 113 | - | p, releasePTY := newBlockingPTY(t) | |
| 114 | - | _, sess, ac, _ := newManualSessionWithPTYs(t, p) | |
| 115 | - | defer releasePTY() | |
| 116 | - | ||
| 117 | - | sess.name = "vive" | |
| 118 | - | sess.incarnation = domain.SessionLifecycleID{1} | |
| 119 | - | ref := ports.RouteRef{Key: 1, Generation: 1} | |
| 120 | - | target := ports.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name} | |
| 121 | - | snapshot := ports.RecentRouteSnapshot{ | |
| 122 | - | Generation: 1, | |
| 123 | - | Active: ref, | |
| 124 | - | ActiveEntry: ports.RecentRouteEntry{ | |
| 125 | - | Key: ref.Key, Generation: ref.Generation, Target: target, | |
| 126 | - | Name: "vive", HostLabel: "user@arch", Kind: ports.RouteKindRemote, | |
| 127 | - | }, | |
| 113 | + | tests := []struct { | |
| 114 | + | name string | |
| 115 | + | routeLifecycle domain.SessionLifecycleID | |
| 116 | + | wantPresentation string | |
| 117 | + | }{ | |
| 118 | + | {name: "matching lifecycle", routeLifecycle: domain.SessionLifecycleID{1}, wantPresentation: "vive@arch"}, | |
| 119 | + | {name: "stale lifecycle", routeLifecycle: domain.SessionLifecycleID{99}, wantPresentation: "vive"}, | |
| 128 | 120 | } | |
| 129 | - | ac.setRouteSnapshot(snapshot) | |
| 121 | + | for _, tt := range tests { | |
| 122 | + | t.Run(tt.name, func(t *testing.T) { | |
| 123 | + | p, releasePTY := newBlockingPTY(t) | |
| 124 | + | _, sess, ac, _ := newManualSessionWithPTYs(t, p) | |
| 125 | + | defer releasePTY() | |
| 130 | 126 | ||
| 131 | - | require.Equal(t, "vive@arch", sess.statusSegmentsFor(ac, true).session) | |
| 132 | - | require.Equal(t, "vive", sess.statusSegments(true).session, "route presentation belongs only to the selected attachment") | |
| 127 | + | sess.name = "vive" | |
| 128 | + | sess.incarnation = domain.SessionLifecycleID{1} | |
| 129 | + | ref := ports.RouteRef{Key: 1, Generation: 1} | |
| 130 | + | ac.setRouteSnapshot(ports.RecentRouteSnapshot{ | |
| 131 | + | Generation: 1, | |
| 132 | + | Active: ref, | |
| 133 | + | ActiveEntry: ports.RecentRouteEntry{ | |
| 134 | + | Key: ref.Key, Generation: ref.Generation, | |
| 135 | + | Target: ports.ExactSessionTarget{LifecycleID: tt.routeLifecycle, SessionName: sess.name}, | |
| 136 | + | Name: "vive", HostLabel: "user@arch", Kind: ports.RouteKindRemote, | |
| 137 | + | }, | |
| 138 | + | }) | |
| 133 | 139 | ||
| 134 | - | snapshot.ActiveEntry.Target.LifecycleID = domain.SessionLifecycleID{99} | |
| 135 | - | ac.setRouteSnapshot(snapshot) | |
| 136 | - | require.Equal(t, "vive", sess.statusSegmentsFor(ac, true).session, "a stale UUID must not label the active session") | |
| 140 | + | require.Equal(t, tt.wantPresentation, sess.statusSegmentsFor(ac, true).session) | |
| 141 | + | require.Equal(t, "vive", sess.statusSegments(true).session, "route presentation belongs only to the selected attachment") | |
| 142 | + | }) | |
| 143 | + | } | |
| 137 | 144 | } | |
| 138 | 145 | ||
| 139 | 146 | func TestStatusSegmentsIncludesFocusedPaneTitle(t *testing.T) { | |
| @@ -110,30 +110,37 @@ func TestStatusCompositionGolden(t *testing.T) { | |||||
| 110 | } | 110 | } | ||
| 111 | 111 | ||||
| 112 | func TestStatusSegmentsResolvesActiveLifecyclePresentation(t *testing.T) { | 112 | func TestStatusSegmentsResolvesActiveLifecyclePresentation(t *testing.T) { | ||
| 113 | - | p, releasePTY := newBlockingPTY(t) | 113 | + | tests := []struct { |
| 114 | - | _, sess, ac, _ := newManualSessionWithPTYs(t, p) | 114 | + | name string |
| 115 | - | defer releasePTY() | 115 | + | routeLifecycle domain.SessionLifecycleID |
| 116 | - | 116 | + | wantPresentation string | |
| 117 | - | sess.name = "vive" | 117 | + | }{ |
| 118 | - | sess.incarnation = domain.SessionLifecycleID{1} | 118 | + | {name: "matching lifecycle", routeLifecycle: domain.SessionLifecycleID{1}, wantPresentation: "vive@arch"}, |
| 119 | - | ref := ports.RouteRef{Key: 1, Generation: 1} | 119 | + | {name: "stale lifecycle", routeLifecycle: domain.SessionLifecycleID{99}, wantPresentation: "vive"}, |
| 120 | - | target := ports.ExactSessionTarget{LifecycleID: sess.incarnation, SessionName: sess.name} | |||
| 121 | - | snapshot := ports.RecentRouteSnapshot{ | |||
| 122 | - | Generation: 1, | |||
| 123 | - | Active: ref, | |||
| 124 | - | ActiveEntry: ports.RecentRouteEntry{ | |||
| 125 | - | Key: ref.Key, Generation: ref.Generation, Target: target, | |||
| 126 | - | Name: "vive", HostLabel: "user@arch", Kind: ports.RouteKindRemote, | |||
| 127 | - | }, | |||
| 128 | } | 120 | } | ||
| 129 | - | ac.setRouteSnapshot(snapshot) | 121 | + | for _, tt := range tests { |
| 122 | + | t.Run(tt.name, func(t *testing.T) { | |||
| 123 | + | p, releasePTY := newBlockingPTY(t) | |||
| 124 | + | _, sess, ac, _ := newManualSessionWithPTYs(t, p) | |||
| 125 | + | defer releasePTY() | |||
| 130 | 126 | ||||
| 131 | - | require.Equal(t, "vive@arch", sess.statusSegmentsFor(ac, true).session) | 127 | + | sess.name = "vive" |
| 132 | - | require.Equal(t, "vive", sess.statusSegments(true).session, "route presentation belongs only to the selected attachment") | 128 | + | sess.incarnation = domain.SessionLifecycleID{1} |
| 129 | + | ref := ports.RouteRef{Key: 1, Generation: 1} | |||
| 130 | + | ac.setRouteSnapshot(ports.RecentRouteSnapshot{ | |||
| 131 | + | Generation: 1, | |||
| 132 | + | Active: ref, | |||
| 133 | + | ActiveEntry: ports.RecentRouteEntry{ | |||
| 134 | + | Key: ref.Key, Generation: ref.Generation, | |||
| 135 | + | Target: ports.ExactSessionTarget{LifecycleID: tt.routeLifecycle, SessionName: sess.name}, | |||
| 136 | + | Name: "vive", HostLabel: "user@arch", Kind: ports.RouteKindRemote, | |||
| 137 | + | }, | |||
| 138 | + | }) | |||
| 133 | 139 | ||||
| 134 | - | snapshot.ActiveEntry.Target.LifecycleID = domain.SessionLifecycleID{99} | 140 | + | require.Equal(t, tt.wantPresentation, sess.statusSegmentsFor(ac, true).session) |
| 135 | - | ac.setRouteSnapshot(snapshot) | 141 | + | require.Equal(t, "vive", sess.statusSegments(true).session, "route presentation belongs only to the selected attachment") |
| 136 | - | require.Equal(t, "vive", sess.statusSegmentsFor(ac, true).session, "a stale UUID must not label the active session") | 142 | + | }) |
| 143 | + | } | |||
| 137 | } | 144 | } | ||
| 138 | 145 | ||||
| 139 | func TestStatusSegmentsIncludesFocusedPaneTitle(t *testing.T) { | 146 | func TestStatusSegmentsIncludesFocusedPaneTitle(t *testing.T) { | ||
M internal/usecase/palette/action_test.go +1 -1
| @@ -54,7 +54,7 @@ func TestArgumentCommandKeepsExactRowVisible(t *testing.T) { | |||
| 54 | 54 | ||
| 55 | 55 | func TestArgumentResultReturnsTheMatchingRowAndCommand(t *testing.T) { | |
| 56 | 56 | jrs := command.Command{Code: "JRS", Arguments: command.ArgumentsRequired} | |
| 57 | - | results := []Result{NewStoppedSessionResult("JRS", time.Time{}, testExactTarget("JRS", 1)), NewCommandResult(jrs)} | |
| 57 | + | results := []Result{NewStoppedSessionResult(testExactTarget("JRS", 1), time.Time{}), NewCommandResult(jrs)} | |
| 58 | 58 | ||
| 59 | 59 | result, got, ok := argumentResult(results, "jrs 1") | |
| 60 | 60 | ||
| @@ -54,7 +54,7 @@ func TestArgumentCommandKeepsExactRowVisible(t *testing.T) { | |||||
| 54 | 54 | ||||
| 55 | func TestArgumentResultReturnsTheMatchingRowAndCommand(t *testing.T) { | 55 | func TestArgumentResultReturnsTheMatchingRowAndCommand(t *testing.T) { | ||
| 56 | jrs := command.Command{Code: "JRS", Arguments: command.ArgumentsRequired} | 56 | jrs := command.Command{Code: "JRS", Arguments: command.ArgumentsRequired} | ||
| 57 | - | results := []Result{NewStoppedSessionResult("JRS", time.Time{}, testExactTarget("JRS", 1)), NewCommandResult(jrs)} | 57 | + | results := []Result{NewStoppedSessionResult(testExactTarget("JRS", 1), time.Time{}), NewCommandResult(jrs)} |
| 58 | 58 | ||||
| 59 | result, got, ok := argumentResult(results, "jrs 1") | 59 | result, got, ok := argumentResult(results, "jrs 1") | ||
| 60 | 60 | ||||
M internal/usecase/palette/fuzzy_test.go +9 -9
| @@ -92,9 +92,9 @@ func TestFuzzyOrdersMixedResults(t *testing.T) { | |||
| 92 | 92 | { | |
| 93 | 93 | name: "command shortcode precedes sessions and description matches", | |
| 94 | 94 | results: []Result{ | |
| 95 | - | NewStoppedSessionResult("work", created, testExactTarget("work", 1)), | |
| 95 | + | NewStoppedSessionResult(testExactTarget("work", 1), created), | |
| 96 | 96 | NewCommandResult(cmd("WORK", "", "Create a workspace")), | |
| 97 | - | NewActiveSessionResult("work", created, testExactTarget("work", 2)), | |
| 97 | + | NewActiveSessionResult(testExactTarget("work", 2), created), | |
| 98 | 98 | NewCommandResult(cmd("WQORRK", "", "")), | |
| 99 | 99 | NewCommandResult(cmd("ZZZ", "", "work tools")), | |
| 100 | 100 | }, | |
| @@ -106,8 +106,8 @@ func TestFuzzyOrdersMixedResults(t *testing.T) { | |||
| 106 | 106 | { | |
| 107 | 107 | name: "equivalent sessions sort by normalized text", | |
| 108 | 108 | results: []Result{ | |
| 109 | - | NewStoppedSessionResult("aBravo", time.Time{}, testExactTarget("aBravo", 1)), | |
| 110 | - | NewStoppedSessionResult("aAlpha", time.Time{}, testExactTarget("aAlpha", 2)), | |
| 109 | + | NewStoppedSessionResult(testExactTarget("aBravo", 1), time.Time{}), | |
| 110 | + | NewStoppedSessionResult(testExactTarget("aAlpha", 2), time.Time{}), | |
| 111 | 111 | }, | |
| 112 | 112 | query: "a", | |
| 113 | 113 | wantText: []string{"Resume session aAlpha", "Resume session aBravo"}, | |
| @@ -117,11 +117,11 @@ func TestFuzzyOrdersMixedResults(t *testing.T) { | |||
| 117 | 117 | { | |
| 118 | 118 | name: "display prefix positions sort before later active names", | |
| 119 | 119 | results: []Result{ | |
| 120 | - | NewStoppedSessionResult("aBravo", time.Time{}, testExactTarget("aBravo", 1)), | |
| 121 | - | NewStoppedSessionResult("aAlpha", time.Time{}, testExactTarget("aAlpha", 2)), | |
| 122 | - | NewActiveSessionResult("aZulu", time.Time{}, testExactTarget("aZulu", 3)), | |
| 123 | - | NewActiveSessionResult("aEcho", time.Time{}, testExactTarget("aEcho", 4)), | |
| 124 | - | NewActiveSessionResult("aEcho", time.Time{}, testExactTarget("aEcho", 5)), | |
| 120 | + | NewStoppedSessionResult(testExactTarget("aBravo", 1), time.Time{}), | |
| 121 | + | NewStoppedSessionResult(testExactTarget("aAlpha", 2), time.Time{}), | |
| 122 | + | NewActiveSessionResult(testExactTarget("aZulu", 3), time.Time{}), | |
| 123 | + | NewActiveSessionResult(testExactTarget("aEcho", 4), time.Time{}), | |
| 124 | + | NewActiveSessionResult(testExactTarget("aEcho", 5), time.Time{}), | |
| 125 | 125 | NewCommandResult(cmd("AX", "", "")), | |
| 126 | 126 | }, | |
| 127 | 127 | query: "a", | |
| @@ -92,9 +92,9 @@ func TestFuzzyOrdersMixedResults(t *testing.T) { | |||||
| 92 | { | 92 | { | ||
| 93 | name: "command shortcode precedes sessions and description matches", | 93 | name: "command shortcode precedes sessions and description matches", | ||
| 94 | results: []Result{ | 94 | results: []Result{ | ||
| 95 | - | NewStoppedSessionResult("work", created, testExactTarget("work", 1)), | 95 | + | NewStoppedSessionResult(testExactTarget("work", 1), created), |
| 96 | NewCommandResult(cmd("WORK", "", "Create a workspace")), | 96 | NewCommandResult(cmd("WORK", "", "Create a workspace")), | ||
| 97 | - | NewActiveSessionResult("work", created, testExactTarget("work", 2)), | 97 | + | NewActiveSessionResult(testExactTarget("work", 2), created), |
| 98 | NewCommandResult(cmd("WQORRK", "", "")), | 98 | NewCommandResult(cmd("WQORRK", "", "")), | ||
| 99 | NewCommandResult(cmd("ZZZ", "", "work tools")), | 99 | NewCommandResult(cmd("ZZZ", "", "work tools")), | ||
| 100 | }, | 100 | }, | ||
| @@ -106,8 +106,8 @@ func TestFuzzyOrdersMixedResults(t *testing.T) { | |||||
| 106 | { | 106 | { | ||
| 107 | name: "equivalent sessions sort by normalized text", | 107 | name: "equivalent sessions sort by normalized text", | ||
| 108 | results: []Result{ | 108 | results: []Result{ | ||
| 109 | - | NewStoppedSessionResult("aBravo", time.Time{}, testExactTarget("aBravo", 1)), | 109 | + | NewStoppedSessionResult(testExactTarget("aBravo", 1), time.Time{}), |
| 110 | - | NewStoppedSessionResult("aAlpha", time.Time{}, testExactTarget("aAlpha", 2)), | 110 | + | NewStoppedSessionResult(testExactTarget("aAlpha", 2), time.Time{}), |
| 111 | }, | 111 | }, | ||
| 112 | query: "a", | 112 | query: "a", | ||
| 113 | wantText: []string{"Resume session aAlpha", "Resume session aBravo"}, | 113 | wantText: []string{"Resume session aAlpha", "Resume session aBravo"}, | ||
| @@ -117,11 +117,11 @@ func TestFuzzyOrdersMixedResults(t *testing.T) { | |||||
| 117 | { | 117 | { | ||
| 118 | name: "display prefix positions sort before later active names", | 118 | name: "display prefix positions sort before later active names", | ||
| 119 | results: []Result{ | 119 | results: []Result{ | ||
| 120 | - | NewStoppedSessionResult("aBravo", time.Time{}, testExactTarget("aBravo", 1)), | 120 | + | NewStoppedSessionResult(testExactTarget("aBravo", 1), time.Time{}), |
| 121 | - | NewStoppedSessionResult("aAlpha", time.Time{}, testExactTarget("aAlpha", 2)), | 121 | + | NewStoppedSessionResult(testExactTarget("aAlpha", 2), time.Time{}), |
| 122 | - | NewActiveSessionResult("aZulu", time.Time{}, testExactTarget("aZulu", 3)), | 122 | + | NewActiveSessionResult(testExactTarget("aZulu", 3), time.Time{}), |
| 123 | - | NewActiveSessionResult("aEcho", time.Time{}, testExactTarget("aEcho", 4)), | 123 | + | NewActiveSessionResult(testExactTarget("aEcho", 4), time.Time{}), |
| 124 | - | NewActiveSessionResult("aEcho", time.Time{}, testExactTarget("aEcho", 5)), | 124 | + | NewActiveSessionResult(testExactTarget("aEcho", 5), time.Time{}), |
| 125 | NewCommandResult(cmd("AX", "", "")), | 125 | NewCommandResult(cmd("AX", "", "")), | ||
| 126 | }, | 126 | }, | ||
| 127 | query: "a", | 127 | query: "a", | ||
M internal/usecase/palette/model_test.go +5 -5
| @@ -463,11 +463,11 @@ func TestModelUsesDefensiveTypedResultsAndKeepsSessionsCommandInert(t *testing.T | |||
| 463 | 463 | workTarget := testExactTarget("work", 1) | |
| 464 | 464 | results := []Result{ | |
| 465 | 465 | NewCommandResult(cmd("JRS", "Jump", "Jump to recent session")), | |
| 466 | - | NewActiveSessionResult("work", created, workTarget), | |
| 467 | - | NewStoppedSessionResult("work", created, testExactTarget("work", 2)), | |
| 466 | + | NewActiveSessionResult(workTarget, created), | |
| 467 | + | NewStoppedSessionResult(testExactTarget("work", 2), created), | |
| 468 | 468 | } | |
| 469 | 469 | m := New(results) | |
| 470 | - | results[1] = NewActiveSessionResult("changed", created, testExactTarget("changed", 3)) | |
| 470 | + | results[1] = NewActiveSessionResult(testExactTarget("changed", 3), created) | |
| 471 | 471 | ||
| 472 | 472 | for _, r := range "switch" { | |
| 473 | 473 | m.Insert(r) | |
| @@ -494,7 +494,7 @@ func TestModelUsesDefensiveTypedResultsAndKeepsSessionsCommandInert(t *testing.T | |||
| 494 | 494 | } | |
| 495 | 495 | ||
| 496 | 496 | func TestRenderStoppedSessionHighlightsNameAfterResumePrefix(t *testing.T) { | |
| 497 | - | m := New([]Result{NewStoppedSessionResult("work", time.Unix(0, 1), testExactTarget("work", 1))}) | |
| 497 | + | m := New([]Result{NewStoppedSessionResult(testExactTarget("work", 1), time.Unix(0, 1))}) | |
| 498 | 498 | m.Insert('w') | |
| 499 | 499 | m.Insert('k') | |
| 500 | 500 | ||
| @@ -507,7 +507,7 @@ func TestRenderStoppedSessionHighlightsNameAfterResumePrefix(t *testing.T) { | |||
| 507 | 507 | } | |
| 508 | 508 | ||
| 509 | 509 | func TestRenderFeedbackUsesSelectedSessionRowWithoutAddingResult(t *testing.T) { | |
| 510 | - | m := New([]Result{NewActiveSessionResult("work", time.Unix(0, 1), testExactTarget("work", 1))}) | |
| 510 | + | m := New([]Result{NewActiveSessionResult(testExactTarget("work", 1), time.Unix(0, 1))}) | |
| 511 | 511 | frame := m.Render(domain.Size{Cols: 64, Rows: 3}, RenderOptions{Styles: DefaultRenderStyles(), Feedback: "requested session is unavailable"}) | |
| 512 | 512 | require.Len(t, m.Matches(), 1) | |
| 513 | 513 | require.Contains(t, frameRow(frame, 1), "requested session is unavailable") | |
| @@ -463,11 +463,11 @@ func TestModelUsesDefensiveTypedResultsAndKeepsSessionsCommandInert(t *testing.T | |||||
| 463 | workTarget := testExactTarget("work", 1) | 463 | workTarget := testExactTarget("work", 1) | ||
| 464 | results := []Result{ | 464 | results := []Result{ | ||
| 465 | NewCommandResult(cmd("JRS", "Jump", "Jump to recent session")), | 465 | NewCommandResult(cmd("JRS", "Jump", "Jump to recent session")), | ||
| 466 | - | NewActiveSessionResult("work", created, workTarget), | 466 | + | NewActiveSessionResult(workTarget, created), |
| 467 | - | NewStoppedSessionResult("work", created, testExactTarget("work", 2)), | 467 | + | NewStoppedSessionResult(testExactTarget("work", 2), created), |
| 468 | } | 468 | } | ||
| 469 | m := New(results) | 469 | m := New(results) | ||
| 470 | - | results[1] = NewActiveSessionResult("changed", created, testExactTarget("changed", 3)) | 470 | + | results[1] = NewActiveSessionResult(testExactTarget("changed", 3), created) |
| 471 | 471 | ||||
| 472 | for _, r := range "switch" { | 472 | for _, r := range "switch" { | ||
| 473 | m.Insert(r) | 473 | m.Insert(r) | ||
| @@ -494,7 +494,7 @@ func TestModelUsesDefensiveTypedResultsAndKeepsSessionsCommandInert(t *testing.T | |||||
| 494 | } | 494 | } | ||
| 495 | 495 | ||||
| 496 | func TestRenderStoppedSessionHighlightsNameAfterResumePrefix(t *testing.T) { | 496 | func TestRenderStoppedSessionHighlightsNameAfterResumePrefix(t *testing.T) { | ||
| 497 | - | m := New([]Result{NewStoppedSessionResult("work", time.Unix(0, 1), testExactTarget("work", 1))}) | 497 | + | m := New([]Result{NewStoppedSessionResult(testExactTarget("work", 1), time.Unix(0, 1))}) |
| 498 | m.Insert('w') | 498 | m.Insert('w') | ||
| 499 | m.Insert('k') | 499 | m.Insert('k') | ||
| 500 | 500 | ||||
| @@ -507,7 +507,7 @@ func TestRenderStoppedSessionHighlightsNameAfterResumePrefix(t *testing.T) { | |||||
| 507 | } | 507 | } | ||
| 508 | 508 | ||||
| 509 | func TestRenderFeedbackUsesSelectedSessionRowWithoutAddingResult(t *testing.T) { | 509 | func TestRenderFeedbackUsesSelectedSessionRowWithoutAddingResult(t *testing.T) { | ||
| 510 | - | m := New([]Result{NewActiveSessionResult("work", time.Unix(0, 1), testExactTarget("work", 1))}) | 510 | + | m := New([]Result{NewActiveSessionResult(testExactTarget("work", 1), time.Unix(0, 1))}) |
| 511 | frame := m.Render(domain.Size{Cols: 64, Rows: 3}, RenderOptions{Styles: DefaultRenderStyles(), Feedback: "requested session is unavailable"}) | 511 | frame := m.Render(domain.Size{Cols: 64, Rows: 3}, RenderOptions{Styles: DefaultRenderStyles(), Feedback: "requested session is unavailable"}) | ||
| 512 | require.Len(t, m.Matches(), 1) | 512 | require.Len(t, m.Matches(), 1) | ||
| 513 | require.Contains(t, frameRow(frame, 1), "requested session is unavailable") | 513 | require.Contains(t, frameRow(frame, 1), "requested session is unavailable") | ||
M internal/usecase/palette/result.go +4 -4
| @@ -43,18 +43,18 @@ func NewCommandResult(cmd command.Command) Result { | |||
| 43 | 43 | } | |
| 44 | 44 | ||
| 45 | 45 | // NewActiveSessionResult creates an immutable active named-session target. | |
| 46 | - | func NewActiveSessionResult(name string, createdAt time.Time, target ports.ExactSessionTarget) Result { | |
| 46 | + | func NewActiveSessionResult(target ports.ExactSessionTarget, createdAt time.Time) Result { | |
| 47 | 47 | return Result{ | |
| 48 | 48 | kind: ResultKindActiveSession, | |
| 49 | - | session: sessionPayload{name: name, createdAt: createdAt, target: target}, | |
| 49 | + | session: sessionPayload{name: target.SessionName, createdAt: createdAt, target: target}, | |
| 50 | 50 | } | |
| 51 | 51 | } | |
| 52 | 52 | ||
| 53 | 53 | // NewStoppedSessionResult creates an immutable stopped named-session target. | |
| 54 | - | func NewStoppedSessionResult(name string, createdAt time.Time, target ports.ExactSessionTarget) Result { | |
| 54 | + | func NewStoppedSessionResult(target ports.ExactSessionTarget, createdAt time.Time) Result { | |
| 55 | 55 | return Result{ | |
| 56 | 56 | kind: ResultKindStoppedSession, | |
| 57 | - | session: sessionPayload{name: name, createdAt: createdAt, target: target}, | |
| 57 | + | session: sessionPayload{name: target.SessionName, createdAt: createdAt, target: target}, | |
| 58 | 58 | } | |
| 59 | 59 | } | |
| 60 | 60 | ||
| @@ -43,18 +43,18 @@ func NewCommandResult(cmd command.Command) Result { | |||||
| 43 | } | 43 | } | ||
| 44 | 44 | ||||
| 45 | // NewActiveSessionResult creates an immutable active named-session target. | 45 | // NewActiveSessionResult creates an immutable active named-session target. | ||
| 46 | - | func NewActiveSessionResult(name string, createdAt time.Time, target ports.ExactSessionTarget) Result { | 46 | + | func NewActiveSessionResult(target ports.ExactSessionTarget, createdAt time.Time) Result { |
| 47 | return Result{ | 47 | return Result{ | ||
| 48 | kind: ResultKindActiveSession, | 48 | kind: ResultKindActiveSession, | ||
| 49 | - | session: sessionPayload{name: name, createdAt: createdAt, target: target}, | 49 | + | session: sessionPayload{name: target.SessionName, createdAt: createdAt, target: target}, |
| 50 | } | 50 | } | ||
| 51 | } | 51 | } | ||
| 52 | 52 | ||||
| 53 | // NewStoppedSessionResult creates an immutable stopped named-session target. | 53 | // NewStoppedSessionResult creates an immutable stopped named-session target. | ||
| 54 | - | func NewStoppedSessionResult(name string, createdAt time.Time, target ports.ExactSessionTarget) Result { | 54 | + | func NewStoppedSessionResult(target ports.ExactSessionTarget, createdAt time.Time) Result { |
| 55 | return Result{ | 55 | return Result{ | ||
| 56 | kind: ResultKindStoppedSession, | 56 | kind: ResultKindStoppedSession, | ||
| 57 | - | session: sessionPayload{name: name, createdAt: createdAt, target: target}, | 57 | + | session: sessionPayload{name: target.SessionName, createdAt: createdAt, target: target}, |
| 58 | } | 58 | } | ||
| 59 | } | 59 | } | ||
| 60 | 60 | ||||
M internal/usecase/palette/result_test.go +6 -6
| @@ -19,8 +19,8 @@ func TestResultKindsAndSessionLifecycleTargets(t *testing.T) { | |||
| 19 | 19 | commandResult := NewCommandResult(command.Command{Code: "NT", Desc: "Create tab"}) | |
| 20 | 20 | activeTarget := testExactTarget("work", 1) | |
| 21 | 21 | stoppedTarget := testExactTarget("archive", 2) | |
| 22 | - | active := NewActiveSessionResult("work", created, activeTarget) | |
| 23 | - | stopped := NewStoppedSessionResult("archive", created, stoppedTarget) | |
| 22 | + | active := NewActiveSessionResult(activeTarget, created) | |
| 23 | + | stopped := NewStoppedSessionResult(stoppedTarget, created) | |
| 24 | 24 | ||
| 25 | 25 | require.Equal(t, ResultKindCommand, commandResult.Kind()) | |
| 26 | 26 | require.Equal(t, "NT", commandResult.DisplayText()) | |
| @@ -78,10 +78,10 @@ func TestSessionResultsSearchDisplayedActionText(t *testing.T) { | |||
| 78 | 78 | query string | |
| 79 | 79 | positions []int | |
| 80 | 80 | }{ | |
| 81 | - | {name: "active prefix", result: NewActiveSessionResult("work", created, testExactTarget("work", 1)), query: "switch", positions: []int{0, 1, 2, 3, 4, 5}}, | |
| 82 | - | {name: "active name", result: NewActiveSessionResult("work", created, testExactTarget("work", 1)), query: "work", positions: []int{1, 8, 20, 21}}, | |
| 83 | - | {name: "stopped prefix", result: NewStoppedSessionResult("archive", created, testExactTarget("archive", 2)), query: "resume", positions: []int{0, 1, 2, 3, 4, 5}}, | |
| 84 | - | {name: "stopped name", result: NewStoppedSessionResult("archive", created, testExactTarget("archive", 2)), query: "archive", positions: []int{15, 16, 17, 18, 19, 20, 21}}, | |
| 81 | + | {name: "active prefix", result: NewActiveSessionResult(testExactTarget("work", 1), created), query: "switch", positions: []int{0, 1, 2, 3, 4, 5}}, | |
| 82 | + | {name: "active name", result: NewActiveSessionResult(testExactTarget("work", 1), created), query: "work", positions: []int{1, 8, 20, 21}}, | |
| 83 | + | {name: "stopped prefix", result: NewStoppedSessionResult(testExactTarget("archive", 2), created), query: "resume", positions: []int{0, 1, 2, 3, 4, 5}}, | |
| 84 | + | {name: "stopped name", result: NewStoppedSessionResult(testExactTarget("archive", 2), created), query: "archive", positions: []int{15, 16, 17, 18, 19, 20, 21}}, | |
| 85 | 85 | } | |
| 86 | 86 | ||
| 87 | 87 | for _, tt := range tests { | |
| @@ -19,8 +19,8 @@ func TestResultKindsAndSessionLifecycleTargets(t *testing.T) { | |||||
| 19 | commandResult := NewCommandResult(command.Command{Code: "NT", Desc: "Create tab"}) | 19 | commandResult := NewCommandResult(command.Command{Code: "NT", Desc: "Create tab"}) | ||
| 20 | activeTarget := testExactTarget("work", 1) | 20 | activeTarget := testExactTarget("work", 1) | ||
| 21 | stoppedTarget := testExactTarget("archive", 2) | 21 | stoppedTarget := testExactTarget("archive", 2) | ||
| 22 | - | active := NewActiveSessionResult("work", created, activeTarget) | 22 | + | active := NewActiveSessionResult(activeTarget, created) |
| 23 | - | stopped := NewStoppedSessionResult("archive", created, stoppedTarget) | 23 | + | stopped := NewStoppedSessionResult(stoppedTarget, created) |
| 24 | 24 | ||||
| 25 | require.Equal(t, ResultKindCommand, commandResult.Kind()) | 25 | require.Equal(t, ResultKindCommand, commandResult.Kind()) | ||
| 26 | require.Equal(t, "NT", commandResult.DisplayText()) | 26 | require.Equal(t, "NT", commandResult.DisplayText()) | ||
| @@ -78,10 +78,10 @@ func TestSessionResultsSearchDisplayedActionText(t *testing.T) { | |||||
| 78 | query string | 78 | query string | ||
| 79 | positions []int | 79 | positions []int | ||
| 80 | }{ | 80 | }{ | ||
| 81 | - | {name: "active prefix", result: NewActiveSessionResult("work", created, testExactTarget("work", 1)), query: "switch", positions: []int{0, 1, 2, 3, 4, 5}}, | 81 | + | {name: "active prefix", result: NewActiveSessionResult(testExactTarget("work", 1), created), query: "switch", positions: []int{0, 1, 2, 3, 4, 5}}, |
| 82 | - | {name: "active name", result: NewActiveSessionResult("work", created, testExactTarget("work", 1)), query: "work", positions: []int{1, 8, 20, 21}}, | 82 | + | {name: "active name", result: NewActiveSessionResult(testExactTarget("work", 1), created), query: "work", positions: []int{1, 8, 20, 21}}, |
| 83 | - | {name: "stopped prefix", result: NewStoppedSessionResult("archive", created, testExactTarget("archive", 2)), query: "resume", positions: []int{0, 1, 2, 3, 4, 5}}, | 83 | + | {name: "stopped prefix", result: NewStoppedSessionResult(testExactTarget("archive", 2), created), query: "resume", positions: []int{0, 1, 2, 3, 4, 5}}, |
| 84 | - | {name: "stopped name", result: NewStoppedSessionResult("archive", created, testExactTarget("archive", 2)), query: "archive", positions: []int{15, 16, 17, 18, 19, 20, 21}}, | 84 | + | {name: "stopped name", result: NewStoppedSessionResult(testExactTarget("archive", 2), created), query: "archive", positions: []int{15, 16, 17, 18, 19, 20, 21}}, |
| 85 | } | 85 | } | ||
| 86 | 86 | ||||
| 87 | for _, tt := range tests { | 87 | for _, tt := range tests { | ||