fix(daemon): address stable identity review findings
- commit
- 5d3577fbb5fd031175389e199fbfd393c441b256
- author
- brice · · 2026-07-06 07:43 UTC
- committer
- brice · · 2026-07-06 07:44 UTC
- parent
- 884ab90
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
7 files · +79 -20
M internal/usecase/daemon/identity.go +8 -8
| @@ -3,24 +3,24 @@ package daemon | |||
| 3 | 3 | import ( | |
| 4 | 4 | "crypto/rand" | |
| 5 | 5 | "encoding/base32" | |
| 6 | + | "fmt" | |
| 6 | 7 | "io" | |
| 7 | 8 | "strings" | |
| 8 | 9 | ) | |
| 9 | 10 | ||
| 10 | - | var stableIDRand io.Reader = rand.Reader | |
| 11 | - | ||
| 12 | - | func newStableID() (string, error) { | |
| 11 | + | func newStableID(prefix string) (string, error) { | |
| 13 | 12 | var b [16]byte | |
| 14 | - | if _, err := io.ReadFull(stableIDRand, b[:]); err != nil { | |
| 15 | - | return "", err | |
| 13 | + | if _, err := io.ReadFull(rand.Reader, b[:]); err != nil { | |
| 14 | + | return "", fmt.Errorf("generate stable id: %w", err) | |
| 16 | 15 | } | |
| 17 | - | return strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b[:])), nil | |
| 16 | + | enc := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b[:]) | |
| 17 | + | return prefix + "_" + strings.ToLower(enc), nil | |
| 18 | 18 | } | |
| 19 | 19 | ||
| 20 | 20 | func mustTestStableID(prefix string) string { | |
| 21 | - | id, err := newStableID() | |
| 21 | + | id, err := newStableID(prefix) | |
| 22 | 22 | if err != nil { | |
| 23 | - | return prefix + "-unknown" | |
| 23 | + | return prefix + "_unknown" | |
| 24 | 24 | } | |
| 25 | 25 | return id | |
| 26 | 26 | } | |
| @@ -3,24 +3,24 @@ package daemon | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "crypto/rand" | 4 | "crypto/rand" | ||
| 5 | "encoding/base32" | 5 | "encoding/base32" | ||
| 6 | + | "fmt" | |||
| 6 | "io" | 7 | "io" | ||
| 7 | "strings" | 8 | "strings" | ||
| 8 | ) | 9 | ) | ||
| 9 | 10 | ||||
| 10 | - | var stableIDRand io.Reader = rand.Reader | 11 | + | func newStableID(prefix string) (string, error) { |
| 11 | - | ||||
| 12 | - | func newStableID() (string, error) { | |||
| 13 | var b [16]byte | 12 | var b [16]byte | ||
| 14 | - | if _, err := io.ReadFull(stableIDRand, b[:]); err != nil { | 13 | + | if _, err := io.ReadFull(rand.Reader, b[:]); err != nil { |
| 15 | - | return "", err | 14 | + | return "", fmt.Errorf("generate stable id: %w", err) |
| 16 | } | 15 | } | ||
| 17 | - | return strings.ToLower(base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b[:])), nil | 16 | + | enc := base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b[:]) |
| 17 | + | return prefix + "_" + strings.ToLower(enc), nil | |||
| 18 | } | 18 | } | ||
| 19 | 19 | ||||
| 20 | func mustTestStableID(prefix string) string { | 20 | func mustTestStableID(prefix string) string { | ||
| 21 | - | id, err := newStableID() | 21 | + | id, err := newStableID(prefix) |
| 22 | if err != nil { | 22 | if err != nil { | ||
| 23 | - | return prefix + "-unknown" | 23 | + | return prefix + "_unknown" |
| 24 | } | 24 | } | ||
| 25 | return id | 25 | return id | ||
| 26 | } | 26 | } | ||
M internal/usecase/daemon/pane.go +1 -1
| @@ -35,7 +35,7 @@ type pane struct { | |||
| 35 | 35 | } | |
| 36 | 36 | ||
| 37 | 37 | func newPane(id layout.PaneID, pty ports.PTY, sz domain.Size) *pane { | |
| 38 | - | return newPaneWithStableID(id, mustTestStableID("pane"), pty, sz) | |
| 38 | + | return newPaneWithStableID(id, mustTestStableID("p"), pty, sz) | |
| 39 | 39 | } | |
| 40 | 40 | ||
| 41 | 41 | func newPaneWithStableID(id layout.PaneID, stableID string, pty ports.PTY, sz domain.Size) *pane { | |
| @@ -35,7 +35,7 @@ type pane struct { | |||||
| 35 | } | 35 | } | ||
| 36 | 36 | ||||
| 37 | func newPane(id layout.PaneID, pty ports.PTY, sz domain.Size) *pane { | 37 | func newPane(id layout.PaneID, pty ports.PTY, sz domain.Size) *pane { | ||
| 38 | - | return newPaneWithStableID(id, mustTestStableID("pane"), pty, sz) | 38 | + | return newPaneWithStableID(id, mustTestStableID("p"), pty, sz) |
| 39 | } | 39 | } | ||
| 40 | 40 | ||||
| 41 | func newPaneWithStableID(id layout.PaneID, stableID string, pty ports.PTY, sz domain.Size) *pane { | 41 | func newPaneWithStableID(id layout.PaneID, stableID string, pty ports.PTY, sz domain.Size) *pane { | ||
M internal/usecase/daemon/pane_actions.go +1 -1
| @@ -60,7 +60,7 @@ func (d *Daemon) spawnPaneOp( | |||
| 60 | 60 | tabStableID := tb.stableID | |
| 61 | 61 | tb.mu.Unlock() | |
| 62 | 62 | ||
| 63 | - | paneStableID, err := newStableID() | |
| 63 | + | paneStableID, err := newStableID("p") | |
| 64 | 64 | if err != nil { | |
| 65 | 65 | tb.mu.Lock() | |
| 66 | 66 | _ = tb.tree.Close(newID) | |
| @@ -60,7 +60,7 @@ func (d *Daemon) spawnPaneOp( | |||||
| 60 | tabStableID := tb.stableID | 60 | tabStableID := tb.stableID | ||
| 61 | tb.mu.Unlock() | 61 | tb.mu.Unlock() | ||
| 62 | 62 | ||||
| 63 | - | paneStableID, err := newStableID() | 63 | + | paneStableID, err := newStableID("p") |
| 64 | if err != nil { | 64 | if err != nil { | ||
| 65 | tb.mu.Lock() | 65 | tb.mu.Lock() | ||
| 66 | _ = tb.tree.Close(newID) | 66 | _ = tb.tree.Close(newID) | ||
M internal/usecase/daemon/session.go +6 -6
| @@ -123,11 +123,11 @@ func (d *Daemon) touchMRU(sess *session) { | |||
| 123 | 123 | ||
| 124 | 124 | func (d *Daemon) createSessionLocked(name string, ephemeral bool, cwd string, sz domain.Size) (*session, error) { | |
| 125 | 125 | tbSize := tabSize(sz) | |
| 126 | - | tabStableID, err := newStableID() | |
| 126 | + | tabStableID, err := newStableID("t") | |
| 127 | 127 | if err != nil { | |
| 128 | 128 | return nil, fmt.Errorf("daemon: generating tab identity: %w", err) | |
| 129 | 129 | } | |
| 130 | - | paneStableID, err := newStableID() | |
| 130 | + | paneStableID, err := newStableID("p") | |
| 131 | 131 | if err != nil { | |
| 132 | 132 | return nil, fmt.Errorf("daemon: generating pane identity: %w", err) | |
| 133 | 133 | } | |
| @@ -141,7 +141,7 @@ func (d *Daemon) createSessionLocked(name string, ephemeral bool, cwd string, sz | |||
| 141 | 141 | d.nextID++ | |
| 142 | 142 | createdAt := time.Now().UnixNano() | |
| 143 | 143 | ||
| 144 | - | tb := newTab(pty, tbSize) | |
| 144 | + | tb := newTabWithStableID(tabStableID, paneStableID, pty, tbSize) | |
| 145 | 145 | sctx, cancel := context.WithCancel(d.serveCtx) | |
| 146 | 146 | tb.ctx, tb.cancel = context.WithCancel(sctx) | |
| 147 | 147 | lastUsedSeq := uint64(0) | |
| @@ -243,11 +243,11 @@ func (d *Daemon) createTab(sess *session, sz domain.Size) error { | |||
| 243 | 243 | cwd := sess.cwd | |
| 244 | 244 | client := sess.client | |
| 245 | 245 | sess.mu.Unlock() | |
| 246 | - | tabStableID, err := newStableID() | |
| 246 | + | tabStableID, err := newStableID("t") | |
| 247 | 247 | if err != nil { | |
| 248 | 248 | return fmt.Errorf("daemon: generating tab identity: %w", err) | |
| 249 | 249 | } | |
| 250 | - | paneStableID, err := newStableID() | |
| 250 | + | paneStableID, err := newStableID("p") | |
| 251 | 251 | if err != nil { | |
| 252 | 252 | return fmt.Errorf("daemon: generating pane identity: %w", err) | |
| 253 | 253 | } | |
| @@ -291,7 +291,7 @@ func (d *Daemon) createTab(sess *session, sz domain.Size) error { | |||
| 291 | 291 | } | |
| 292 | 292 | ||
| 293 | 293 | func newTab(pty ports.PTY, sz domain.Size) *tab { | |
| 294 | - | return newTabWithStableID(mustTestStableID("tab"), mustTestStableID("pane"), pty, sz) | |
| 294 | + | return newTabWithStableID(mustTestStableID("t"), mustTestStableID("p"), pty, sz) | |
| 295 | 295 | } | |
| 296 | 296 | ||
| 297 | 297 | func newTabWithStableID(tabStableID, paneStableID string, pty ports.PTY, sz domain.Size) *tab { | |
| @@ -123,11 +123,11 @@ func (d *Daemon) touchMRU(sess *session) { | |||||
| 123 | 123 | ||||
| 124 | func (d *Daemon) createSessionLocked(name string, ephemeral bool, cwd string, sz domain.Size) (*session, error) { | 124 | func (d *Daemon) createSessionLocked(name string, ephemeral bool, cwd string, sz domain.Size) (*session, error) { | ||
| 125 | tbSize := tabSize(sz) | 125 | tbSize := tabSize(sz) | ||
| 126 | - | tabStableID, err := newStableID() | 126 | + | tabStableID, err := newStableID("t") |
| 127 | if err != nil { | 127 | if err != nil { | ||
| 128 | return nil, fmt.Errorf("daemon: generating tab identity: %w", err) | 128 | return nil, fmt.Errorf("daemon: generating tab identity: %w", err) | ||
| 129 | } | 129 | } | ||
| 130 | - | paneStableID, err := newStableID() | 130 | + | paneStableID, err := newStableID("p") |
| 131 | if err != nil { | 131 | if err != nil { | ||
| 132 | return nil, fmt.Errorf("daemon: generating pane identity: %w", err) | 132 | return nil, fmt.Errorf("daemon: generating pane identity: %w", err) | ||
| 133 | } | 133 | } | ||
| @@ -141,7 +141,7 @@ func (d *Daemon) createSessionLocked(name string, ephemeral bool, cwd string, sz | |||||
| 141 | d.nextID++ | 141 | d.nextID++ | ||
| 142 | createdAt := time.Now().UnixNano() | 142 | createdAt := time.Now().UnixNano() | ||
| 143 | 143 | ||||
| 144 | - | tb := newTab(pty, tbSize) | 144 | + | tb := newTabWithStableID(tabStableID, paneStableID, pty, tbSize) |
| 145 | sctx, cancel := context.WithCancel(d.serveCtx) | 145 | sctx, cancel := context.WithCancel(d.serveCtx) | ||
| 146 | tb.ctx, tb.cancel = context.WithCancel(sctx) | 146 | tb.ctx, tb.cancel = context.WithCancel(sctx) | ||
| 147 | lastUsedSeq := uint64(0) | 147 | lastUsedSeq := uint64(0) | ||
| @@ -243,11 +243,11 @@ func (d *Daemon) createTab(sess *session, sz domain.Size) error { | |||||
| 243 | cwd := sess.cwd | 243 | cwd := sess.cwd | ||
| 244 | client := sess.client | 244 | client := sess.client | ||
| 245 | sess.mu.Unlock() | 245 | sess.mu.Unlock() | ||
| 246 | - | tabStableID, err := newStableID() | 246 | + | tabStableID, err := newStableID("t") |
| 247 | if err != nil { | 247 | if err != nil { | ||
| 248 | return fmt.Errorf("daemon: generating tab identity: %w", err) | 248 | return fmt.Errorf("daemon: generating tab identity: %w", err) | ||
| 249 | } | 249 | } | ||
| 250 | - | paneStableID, err := newStableID() | 250 | + | paneStableID, err := newStableID("p") |
| 251 | if err != nil { | 251 | if err != nil { | ||
| 252 | return fmt.Errorf("daemon: generating pane identity: %w", err) | 252 | return fmt.Errorf("daemon: generating pane identity: %w", err) | ||
| 253 | } | 253 | } | ||
| @@ -291,7 +291,7 @@ func (d *Daemon) createTab(sess *session, sz domain.Size) error { | |||||
| 291 | } | 291 | } | ||
| 292 | 292 | ||||
| 293 | func newTab(pty ports.PTY, sz domain.Size) *tab { | 293 | func newTab(pty ports.PTY, sz domain.Size) *tab { | ||
| 294 | - | return newTabWithStableID(mustTestStableID("tab"), mustTestStableID("pane"), pty, sz) | 294 | + | return newTabWithStableID(mustTestStableID("t"), mustTestStableID("p"), pty, sz) |
| 295 | } | 295 | } | ||
| 296 | 296 | ||||
| 297 | func newTabWithStableID(tabStableID, paneStableID string, pty ports.PTY, sz domain.Size) *tab { | 297 | func newTabWithStableID(tabStableID, paneStableID string, pty ports.PTY, sz domain.Size) *tab { | ||
M internal/usecase/daemon/session_test.go +44 -0
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "io" | |
| 6 | 6 | "log/slog" | |
| 7 | + | "strings" | |
| 7 | 8 | "sync" | |
| 8 | 9 | "sync/atomic" | |
| 9 | 10 | "testing" | |
| @@ -892,6 +893,49 @@ func TestPickerStoppedTargetKillPurges(t *testing.T) { | |||
| 892 | 893 | require.False(t, ok) | |
| 893 | 894 | } | |
| 894 | 895 | ||
| 896 | + | func TestNewSessionAssignsStableIDsAndChildEnv(t *testing.T) { | |
| 897 | + | sz := domain.Size{Cols: 80, Rows: 24} | |
| 898 | + | p, releasePTY := newBlockingPTY(t) | |
| 899 | + | defer releasePTY() | |
| 900 | + | ||
| 901 | + | var gotEnv []string | |
| 902 | + | f := portsmocks.NewMockPTYFactory(t) | |
| 903 | + | f.EXPECT().Open(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn( | |
| 904 | + | func(_ string, _ []string, env []string, _ string, _ domain.Size) (ports.PTY, error) { | |
| 905 | + | gotEnv = append([]string(nil), env...) | |
| 906 | + | return p, nil | |
| 907 | + | }, | |
| 908 | + | ).Once() | |
| 909 | + | d := newTestDaemon(t, f, stubClock{}) | |
| 910 | + | d.baseEnv = []string{"KEEP=1", "TERM=old", "VEV=old"} | |
| 911 | + | ||
| 912 | + | sess, err := d.createSessionLocked("work", false, "/tmp/work", sz) | |
| 913 | + | require.NoError(t, err) | |
| 914 | + | defer func() { | |
| 915 | + | _ = d.killSession(sess, ports.ReasonServerShutdown, false) | |
| 916 | + | releasePTY() | |
| 917 | + | d.sessWg.Wait() | |
| 918 | + | }() | |
| 919 | + | ||
| 920 | + | sess.mu.Lock() | |
| 921 | + | require.Len(t, sess.tabs, 1) | |
| 922 | + | tb := sess.tabs[0] | |
| 923 | + | sess.mu.Unlock() | |
| 924 | + | tb.mu.Lock() | |
| 925 | + | paneStableID := tb.panes["pane-1"].stableID | |
| 926 | + | tabStableID := tb.stableID | |
| 927 | + | tb.mu.Unlock() | |
| 928 | + | ||
| 929 | + | require.True(t, strings.HasPrefix(tabStableID, "t_"), tabStableID) | |
| 930 | + | require.True(t, strings.HasPrefix(paneStableID, "p_"), paneStableID) | |
| 931 | + | require.NotEqual(t, tabStableID, paneStableID) | |
| 932 | + | require.Contains(t, gotEnv, "KEEP=1") | |
| 933 | + | require.NotContains(t, gotEnv, "TERM=old") | |
| 934 | + | require.NotContains(t, gotEnv, "VEV=old") | |
| 935 | + | require.Contains(t, gotEnv, "TERM=xterm-direct") | |
| 936 | + | require.Contains(t, gotEnv, "VEV=session=work,tab="+tabStableID+",pane="+paneStableID) | |
| 937 | + | } | |
| 938 | + | ||
| 895 | 939 | func TestIntentNewStoppedNameRejected(t *testing.T) { | |
| 896 | 940 | d := newTestDaemon(t, portsmocks.NewMockPTYFactory(t), stubClock{}) | |
| 897 | 941 | d.stopped["taken"] = stoppedSession{name: "taken", cwd: "/tmp", createdAt: 1} | |
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "io" | 5 | "io" | ||
| 6 | "log/slog" | 6 | "log/slog" | ||
| 7 | + | "strings" | |||
| 7 | "sync" | 8 | "sync" | ||
| 8 | "sync/atomic" | 9 | "sync/atomic" | ||
| 9 | "testing" | 10 | "testing" | ||
| @@ -892,6 +893,49 @@ func TestPickerStoppedTargetKillPurges(t *testing.T) { | |||||
| 892 | require.False(t, ok) | 893 | require.False(t, ok) | ||
| 893 | } | 894 | } | ||
| 894 | 895 | ||||
| 896 | + | func TestNewSessionAssignsStableIDsAndChildEnv(t *testing.T) { | |||
| 897 | + | sz := domain.Size{Cols: 80, Rows: 24} | |||
| 898 | + | p, releasePTY := newBlockingPTY(t) | |||
| 899 | + | defer releasePTY() | |||
| 900 | + | ||||
| 901 | + | var gotEnv []string | |||
| 902 | + | f := portsmocks.NewMockPTYFactory(t) | |||
| 903 | + | f.EXPECT().Open(mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).RunAndReturn( | |||
| 904 | + | func(_ string, _ []string, env []string, _ string, _ domain.Size) (ports.PTY, error) { | |||
| 905 | + | gotEnv = append([]string(nil), env...) | |||
| 906 | + | return p, nil | |||
| 907 | + | }, | |||
| 908 | + | ).Once() | |||
| 909 | + | d := newTestDaemon(t, f, stubClock{}) | |||
| 910 | + | d.baseEnv = []string{"KEEP=1", "TERM=old", "VEV=old"} | |||
| 911 | + | ||||
| 912 | + | sess, err := d.createSessionLocked("work", false, "/tmp/work", sz) | |||
| 913 | + | require.NoError(t, err) | |||
| 914 | + | defer func() { | |||
| 915 | + | _ = d.killSession(sess, ports.ReasonServerShutdown, false) | |||
| 916 | + | releasePTY() | |||
| 917 | + | d.sessWg.Wait() | |||
| 918 | + | }() | |||
| 919 | + | ||||
| 920 | + | sess.mu.Lock() | |||
| 921 | + | require.Len(t, sess.tabs, 1) | |||
| 922 | + | tb := sess.tabs[0] | |||
| 923 | + | sess.mu.Unlock() | |||
| 924 | + | tb.mu.Lock() | |||
| 925 | + | paneStableID := tb.panes["pane-1"].stableID | |||
| 926 | + | tabStableID := tb.stableID | |||
| 927 | + | tb.mu.Unlock() | |||
| 928 | + | ||||
| 929 | + | require.True(t, strings.HasPrefix(tabStableID, "t_"), tabStableID) | |||
| 930 | + | require.True(t, strings.HasPrefix(paneStableID, "p_"), paneStableID) | |||
| 931 | + | require.NotEqual(t, tabStableID, paneStableID) | |||
| 932 | + | require.Contains(t, gotEnv, "KEEP=1") | |||
| 933 | + | require.NotContains(t, gotEnv, "TERM=old") | |||
| 934 | + | require.NotContains(t, gotEnv, "VEV=old") | |||
| 935 | + | require.Contains(t, gotEnv, "TERM=xterm-direct") | |||
| 936 | + | require.Contains(t, gotEnv, "VEV=session=work,tab="+tabStableID+",pane="+paneStableID) | |||
| 937 | + | } | |||
| 938 | + | ||||
| 895 | func TestIntentNewStoppedNameRejected(t *testing.T) { | 939 | func TestIntentNewStoppedNameRejected(t *testing.T) { | ||
| 896 | d := newTestDaemon(t, portsmocks.NewMockPTYFactory(t), stubClock{}) | 940 | d := newTestDaemon(t, portsmocks.NewMockPTYFactory(t), stubClock{}) | ||
| 897 | d.stopped["taken"] = stoppedSession{name: "taken", cwd: "/tmp", createdAt: 1} | 941 | d.stopped["taken"] = stoppedSession{name: "taken", cwd: "/tmp", createdAt: 1} | ||
M internal/usecase/daemon/snapshot.go +2 -2
| @@ -92,7 +92,7 @@ func (d *Daemon) restoreSession(ctx context.Context, snap snapcodec.Session) err | |||
| 92 | 92 | for _, pl := range placements { | |
| 93 | 93 | placementByPane[pl.ID] = pl.Content | |
| 94 | 94 | } | |
| 95 | - | tabStableID, err := newStableID() | |
| 95 | + | tabStableID, err := newStableID("t") | |
| 96 | 96 | if err != nil { | |
| 97 | 97 | closeOpened() | |
| 98 | 98 | return fmt.Errorf("snapshot: generating tab identity: %w", err) | |
| @@ -114,7 +114,7 @@ func (d *Daemon) restoreSession(ctx context.Context, snap snapcodec.Session) err | |||
| 114 | 114 | return fmt.Errorf("snapshot: missing pane placement") | |
| 115 | 115 | } | |
| 116 | 116 | contentSize := restorePTYSize(contentRect, tbSize) | |
| 117 | - | paneStableID, err := newStableID() | |
| 117 | + | paneStableID, err := newStableID("p") | |
| 118 | 118 | if err != nil { | |
| 119 | 119 | closeOpened() | |
| 120 | 120 | return fmt.Errorf("snapshot: generating pane identity: %w", err) | |
| @@ -92,7 +92,7 @@ func (d *Daemon) restoreSession(ctx context.Context, snap snapcodec.Session) err | |||||
| 92 | for _, pl := range placements { | 92 | for _, pl := range placements { | ||
| 93 | placementByPane[pl.ID] = pl.Content | 93 | placementByPane[pl.ID] = pl.Content | ||
| 94 | } | 94 | } | ||
| 95 | - | tabStableID, err := newStableID() | 95 | + | tabStableID, err := newStableID("t") |
| 96 | if err != nil { | 96 | if err != nil { | ||
| 97 | closeOpened() | 97 | closeOpened() | ||
| 98 | return fmt.Errorf("snapshot: generating tab identity: %w", err) | 98 | return fmt.Errorf("snapshot: generating tab identity: %w", err) | ||
| @@ -114,7 +114,7 @@ func (d *Daemon) restoreSession(ctx context.Context, snap snapcodec.Session) err | |||||
| 114 | return fmt.Errorf("snapshot: missing pane placement") | 114 | return fmt.Errorf("snapshot: missing pane placement") | ||
| 115 | } | 115 | } | ||
| 116 | contentSize := restorePTYSize(contentRect, tbSize) | 116 | contentSize := restorePTYSize(contentRect, tbSize) | ||
| 117 | - | paneStableID, err := newStableID() | 117 | + | paneStableID, err := newStableID("p") |
| 118 | if err != nil { | 118 | if err != nil { | ||
| 119 | closeOpened() | 119 | closeOpened() | ||
| 120 | return fmt.Errorf("snapshot: generating pane identity: %w", err) | 120 | return fmt.Errorf("snapshot: generating pane identity: %w", err) | ||
M internal/usecase/daemon/snapshot_restore_test.go +17 -2
| @@ -4,6 +4,7 @@ import ( | |||
| 4 | 4 | "context" | |
| 5 | 5 | "io" | |
| 6 | 6 | "math" | |
| 7 | + | "strings" | |
| 7 | 8 | "sync" | |
| 8 | 9 | "testing" | |
| 9 | 10 | "time" | |
| @@ -76,6 +77,19 @@ func TestRestoreSnapshotsRestoresLayoutCwdAndRows(t *testing.T) { | |||
| 76 | 77 | require.Equal(t, "old2", rowText(copySnap.Rows[0][:4])) | |
| 77 | 78 | require.Equal(t, "vis2", rowText(copySnap.Rows[1][:4])) | |
| 78 | 79 | p.mu.Unlock() | |
| 80 | + | ||
| 81 | + | tb.mu.Lock() | |
| 82 | + | tabStableID := tb.stableID | |
| 83 | + | pane1StableID := tb.panes["pane-1"].stableID | |
| 84 | + | pane2StableID := tb.panes["pane-2"].stableID | |
| 85 | + | tb.mu.Unlock() | |
| 86 | + | require.True(t, strings.HasPrefix(tabStableID, "t_"), tabStableID) | |
| 87 | + | require.True(t, strings.HasPrefix(pane1StableID, "p_"), pane1StableID) | |
| 88 | + | require.True(t, strings.HasPrefix(pane2StableID, "p_"), pane2StableID) | |
| 89 | + | require.Contains(t, factory.opens[0].env, "TERM=xterm-direct") | |
| 90 | + | require.Contains(t, factory.opens[0].env, "VEV=session=work,tab="+tabStableID+",pane="+pane1StableID) | |
| 91 | + | require.Contains(t, factory.opens[1].env, "TERM=xterm-direct") | |
| 92 | + | require.Contains(t, factory.opens[1].env, "VEV=session=work,tab="+tabStableID+",pane="+pane2StableID) | |
| 79 | 93 | } | |
| 80 | 94 | ||
| 81 | 95 | func TestRestoreSnapshotsOpensCollapsedStackPanesWithValidPTYSize(t *testing.T) { | |
| @@ -263,12 +277,13 @@ type restorePTYFactory struct { | |||
| 263 | 277 | type restorePTYOpen struct { | |
| 264 | 278 | dir string | |
| 265 | 279 | size domain.Size | |
| 280 | + | env []string | |
| 266 | 281 | } | |
| 267 | 282 | ||
| 268 | - | func (f *restorePTYFactory) Open(_ string, _ []string, _ []string, dir string, sz domain.Size) (ports.PTY, error) { | |
| 283 | + | func (f *restorePTYFactory) Open(_ string, _ []string, env []string, dir string, sz domain.Size) (ports.PTY, error) { | |
| 269 | 284 | f.mu.Lock() | |
| 270 | 285 | defer f.mu.Unlock() | |
| 271 | - | f.opens = append(f.opens, restorePTYOpen{dir: dir, size: sz}) | |
| 286 | + | f.opens = append(f.opens, restorePTYOpen{dir: dir, size: sz, env: append([]string(nil), env...)}) | |
| 272 | 287 | return newRestorePTY(), nil | |
| 273 | 288 | } | |
| 274 | 289 | ||
| @@ -4,6 +4,7 @@ import ( | |||||
| 4 | "context" | 4 | "context" | ||
| 5 | "io" | 5 | "io" | ||
| 6 | "math" | 6 | "math" | ||
| 7 | + | "strings" | |||
| 7 | "sync" | 8 | "sync" | ||
| 8 | "testing" | 9 | "testing" | ||
| 9 | "time" | 10 | "time" | ||
| @@ -76,6 +77,19 @@ func TestRestoreSnapshotsRestoresLayoutCwdAndRows(t *testing.T) { | |||||
| 76 | require.Equal(t, "old2", rowText(copySnap.Rows[0][:4])) | 77 | require.Equal(t, "old2", rowText(copySnap.Rows[0][:4])) | ||
| 77 | require.Equal(t, "vis2", rowText(copySnap.Rows[1][:4])) | 78 | require.Equal(t, "vis2", rowText(copySnap.Rows[1][:4])) | ||
| 78 | p.mu.Unlock() | 79 | p.mu.Unlock() | ||
| 80 | + | ||||
| 81 | + | tb.mu.Lock() | |||
| 82 | + | tabStableID := tb.stableID | |||
| 83 | + | pane1StableID := tb.panes["pane-1"].stableID | |||
| 84 | + | pane2StableID := tb.panes["pane-2"].stableID | |||
| 85 | + | tb.mu.Unlock() | |||
| 86 | + | require.True(t, strings.HasPrefix(tabStableID, "t_"), tabStableID) | |||
| 87 | + | require.True(t, strings.HasPrefix(pane1StableID, "p_"), pane1StableID) | |||
| 88 | + | require.True(t, strings.HasPrefix(pane2StableID, "p_"), pane2StableID) | |||
| 89 | + | require.Contains(t, factory.opens[0].env, "TERM=xterm-direct") | |||
| 90 | + | require.Contains(t, factory.opens[0].env, "VEV=session=work,tab="+tabStableID+",pane="+pane1StableID) | |||
| 91 | + | require.Contains(t, factory.opens[1].env, "TERM=xterm-direct") | |||
| 92 | + | require.Contains(t, factory.opens[1].env, "VEV=session=work,tab="+tabStableID+",pane="+pane2StableID) | |||
| 79 | } | 93 | } | ||
| 80 | 94 | ||||
| 81 | func TestRestoreSnapshotsOpensCollapsedStackPanesWithValidPTYSize(t *testing.T) { | 95 | func TestRestoreSnapshotsOpensCollapsedStackPanesWithValidPTYSize(t *testing.T) { | ||
| @@ -263,12 +277,13 @@ type restorePTYFactory struct { | |||||
| 263 | type restorePTYOpen struct { | 277 | type restorePTYOpen struct { | ||
| 264 | dir string | 278 | dir string | ||
| 265 | size domain.Size | 279 | size domain.Size | ||
| 280 | + | env []string | |||
| 266 | } | 281 | } | ||
| 267 | 282 | ||||
| 268 | - | func (f *restorePTYFactory) Open(_ string, _ []string, _ []string, dir string, sz domain.Size) (ports.PTY, error) { | 283 | + | func (f *restorePTYFactory) Open(_ string, _ []string, env []string, dir string, sz domain.Size) (ports.PTY, error) { |
| 269 | f.mu.Lock() | 284 | f.mu.Lock() | ||
| 270 | defer f.mu.Unlock() | 285 | defer f.mu.Unlock() | ||
| 271 | - | f.opens = append(f.opens, restorePTYOpen{dir: dir, size: sz}) | 286 | + | f.opens = append(f.opens, restorePTYOpen{dir: dir, size: sz, env: append([]string(nil), env...)}) |
| 272 | return newRestorePTY(), nil | 287 | return newRestorePTY(), nil | ||
| 273 | } | 288 | } | ||
| 274 | 289 | ||||