fix(session): clear invalid toggle targets
- commit
- 391596fd8fc92966bf722297d2d4d47028c1ce52
- author
- brice · · 2026-07-11 11:47 UTC
- parent
- 316c07f
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
4 files · +32 -0
M internal/usecase/daemon/client.go +1 -0
| @@ -644,6 +644,7 @@ func (d *Daemon) detachOnSendError(sess *session, ac *attachedClient, failed por | |||
| 644 | 644 | return | |
| 645 | 645 | } | |
| 646 | 646 | d.resetScreenDefaultColors(sess) | |
| 647 | + | ac.clearPreviousSession() | |
| 647 | 648 | _ = ac.closeCapturedTransport(failed) | |
| 648 | 649 | d.log.Warn("detached client after send error", "session", sess.name) | |
| 649 | 650 | } | |
| @@ -644,6 +644,7 @@ func (d *Daemon) detachOnSendError(sess *session, ac *attachedClient, failed por | |||||
| 644 | return | 644 | return | ||
| 645 | } | 645 | } | ||
| 646 | d.resetScreenDefaultColors(sess) | 646 | d.resetScreenDefaultColors(sess) | ||
| 647 | + | ac.clearPreviousSession() | |||
| 647 | _ = ac.closeCapturedTransport(failed) | 648 | _ = ac.closeCapturedTransport(failed) | ||
| 648 | d.log.Warn("detached client after send error", "session", sess.name) | 649 | d.log.Warn("detached client after send error", "session", sess.name) | ||
| 649 | } | 650 | } | ||
M internal/usecase/daemon/input_test.go +25 -0
| @@ -445,6 +445,31 @@ func TestPaletteBackSessionDoesNotMoveWithoutValidTarget(t *testing.T) { | |||
| 445 | 445 | tc.prepare() | |
| 446 | 446 | runPaletteCommand(t, d, current, ac, "BSK") | |
| 447 | 447 | require.Same(t, current, ac.currentSession()) | |
| 448 | + | if tc.name != "no target" { | |
| 449 | + | require.Nil(t, ac.previousSession.Get(), "invalid target must not remain toggleable") | |
| 450 | + | } | |
| 451 | + | }) | |
| 452 | + | } | |
| 453 | + | } | |
| 454 | + | ||
| 455 | + | func TestTerminalTeardownClearsPreviousSession(t *testing.T) { | |
| 456 | + | for _, tc := range []struct { | |
| 457 | + | name string | |
| 458 | + | teardown func(*Daemon, *session, *attachedClient) | |
| 459 | + | }{ | |
| 460 | + | {name: "send error", teardown: func(d *Daemon, sess *session, ac *attachedClient) { d.detachOnSendError(sess, ac, ac.transport()) }}, | |
| 461 | + | {name: "session kill", teardown: func(d *Daemon, sess *session, _ *attachedClient) { | |
| 462 | + | require.NoError(t, d.killSession(sess, ports.ReasonSessionKilled, false)) | |
| 463 | + | }}, | |
| 464 | + | } { | |
| 465 | + | t.Run(tc.name, func(t *testing.T) { | |
| 466 | + | d, current, ac, _, releases := newRecentNavigationTestSessions(t) | |
| 467 | + | defer releaseAll(releases) | |
| 468 | + | ||
| 469 | + | ac.previousSession.Set(d.sessions[domain.SessionID("recent")]) | |
| 470 | + | tc.teardown(d, current, ac) | |
| 471 | + | ||
| 472 | + | require.Nil(t, ac.previousSession.Get()) | |
| 448 | 473 | }) | |
| 449 | 474 | } | |
| 450 | 475 | } | |
| @@ -445,6 +445,31 @@ func TestPaletteBackSessionDoesNotMoveWithoutValidTarget(t *testing.T) { | |||||
| 445 | tc.prepare() | 445 | tc.prepare() | ||
| 446 | runPaletteCommand(t, d, current, ac, "BSK") | 446 | runPaletteCommand(t, d, current, ac, "BSK") | ||
| 447 | require.Same(t, current, ac.currentSession()) | 447 | require.Same(t, current, ac.currentSession()) | ||
| 448 | + | if tc.name != "no target" { | |||
| 449 | + | require.Nil(t, ac.previousSession.Get(), "invalid target must not remain toggleable") | |||
| 450 | + | } | |||
| 451 | + | }) | |||
| 452 | + | } | |||
| 453 | + | } | |||
| 454 | + | ||||
| 455 | + | func TestTerminalTeardownClearsPreviousSession(t *testing.T) { | |||
| 456 | + | for _, tc := range []struct { | |||
| 457 | + | name string | |||
| 458 | + | teardown func(*Daemon, *session, *attachedClient) | |||
| 459 | + | }{ | |||
| 460 | + | {name: "send error", teardown: func(d *Daemon, sess *session, ac *attachedClient) { d.detachOnSendError(sess, ac, ac.transport()) }}, | |||
| 461 | + | {name: "session kill", teardown: func(d *Daemon, sess *session, _ *attachedClient) { | |||
| 462 | + | require.NoError(t, d.killSession(sess, ports.ReasonSessionKilled, false)) | |||
| 463 | + | }}, | |||
| 464 | + | } { | |||
| 465 | + | t.Run(tc.name, func(t *testing.T) { | |||
| 466 | + | d, current, ac, _, releases := newRecentNavigationTestSessions(t) | |||
| 467 | + | defer releaseAll(releases) | |||
| 468 | + | ||||
| 469 | + | ac.previousSession.Set(d.sessions[domain.SessionID("recent")]) | |||
| 470 | + | tc.teardown(d, current, ac) | |||
| 471 | + | ||||
| 472 | + | require.Nil(t, ac.previousSession.Get()) | |||
| 448 | }) | 473 | }) | ||
| 449 | } | 474 | } | ||
| 450 | } | 475 | } | ||
M internal/usecase/daemon/session.go +1 -0
| @@ -734,6 +734,7 @@ func (d *Daemon) killSession(sess *session, reason uint8, purge bool) error { | |||
| 734 | 734 | if ac != nil { | |
| 735 | 735 | ac.cancelResizePaint() | |
| 736 | 736 | d.unregisterPreview(ac) | |
| 737 | + | ac.clearPreviousSession() | |
| 737 | 738 | ac.setSession(nil) | |
| 738 | 739 | } | |
| 739 | 740 | ||
| @@ -734,6 +734,7 @@ func (d *Daemon) killSession(sess *session, reason uint8, purge bool) error { | |||||
| 734 | if ac != nil { | 734 | if ac != nil { | ||
| 735 | ac.cancelResizePaint() | 735 | ac.cancelResizePaint() | ||
| 736 | d.unregisterPreview(ac) | 736 | d.unregisterPreview(ac) | ||
| 737 | + | ac.clearPreviousSession() | |||
| 737 | ac.setSession(nil) | 738 | ac.setSession(nil) | ||
| 738 | } | 739 | } | ||
| 739 | 740 | ||||
M internal/usecase/daemon/session_back.go +5 -0
| @@ -10,6 +10,11 @@ func (d *Daemon) backSession(current *session, ac *attachedClient) { | |||
| 10 | 10 | } | |
| 11 | 11 | target := ac.previousSession.Get() | |
| 12 | 12 | if target == nil || target == current || d.sessionByID(target.id) != target { | |
| 13 | + | ac.previousSession.With(func(previous **session) { | |
| 14 | + | if *previous == target { | |
| 15 | + | *previous = nil | |
| 16 | + | } | |
| 17 | + | }) | |
| 13 | 18 | d.paint(current, ac, true) | |
| 14 | 19 | return | |
| 15 | 20 | } | |
| @@ -10,6 +10,11 @@ func (d *Daemon) backSession(current *session, ac *attachedClient) { | |||||
| 10 | } | 10 | } | ||
| 11 | target := ac.previousSession.Get() | 11 | target := ac.previousSession.Get() | ||
| 12 | if target == nil || target == current || d.sessionByID(target.id) != target { | 12 | if target == nil || target == current || d.sessionByID(target.id) != target { | ||
| 13 | + | ac.previousSession.With(func(previous **session) { | |||
| 14 | + | if *previous == target { | |||
| 15 | + | *previous = nil | |||
| 16 | + | } | |||
| 17 | + | }) | |||
| 13 | d.paint(current, ac, true) | 18 | d.paint(current, ac, true) | ||
| 14 | return | 19 | return | ||
| 15 | } | 20 | } | ||