test(daemon): cover invalid terminal dimensions
- commit
- 8421039c00e455e62904f591e666688f0726b9b8
- author
- brice · · 2026-08-04 06:53 UTC
- parent
- 8f34248
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
1 file · +37 -26
M internal/usecase/daemon/daemon_test.go +37 -26
| @@ -575,32 +575,43 @@ func tabCount(sess *session) int { | |||
| 575 | 575 | // --- handshake -------------------------------------------------------------- | |
| 576 | 576 | ||
| 577 | 577 | func TestRouteRejectsInvalidTerminalSizeBeforeResumeMutation(t *testing.T) { | |
| 578 | - | pty, release := newBlockingPTY(t) | |
| 579 | - | defer release() | |
| 580 | - | d := newTestDaemon(t, newFactory(t, pty), stubClock{}) | |
| 581 | - | ||
| 582 | - | transport := &closeTrackingTransport{} | |
| 583 | - | sess, ac, err := d.route(helloResumeCapable(ports.IntentNew, "work", 0), transport) | |
| 584 | - | require.NoError(t, err) | |
| 585 | - | token := ac.resumeToken | |
| 586 | - | d.clientGone(sess, ac, transport, false) | |
| 587 | - | ||
| 588 | - | d.mu.Lock() | |
| 589 | - | parked := d.parked[token] | |
| 590 | - | d.mu.Unlock() | |
| 591 | - | require.NotNil(t, parked) | |
| 592 | - | ||
| 593 | - | invalid := helloResumeCapable(ports.IntentResume, "work", token) | |
| 594 | - | invalid.Size = domain.Size{Cols: 0, Rows: 24} | |
| 595 | - | _, _, err = d.route(invalid, &closeTrackingTransport{}) | |
| 596 | - | var protocolErr *protoErr | |
| 597 | - | require.ErrorAs(t, err, &protocolErr) | |
| 598 | - | require.Equal(t, ports.ErrInternal, protocolErr.code) | |
| 599 | - | ||
| 600 | - | d.mu.Lock() | |
| 601 | - | require.Same(t, parked, d.parked[token], "invalid resume must not consume the parked attachment") | |
| 602 | - | d.mu.Unlock() | |
| 603 | - | require.Same(t, transport, ac.transport(), "invalid resume must not bind a replacement transport") | |
| 578 | + | tests := []struct { | |
| 579 | + | name string | |
| 580 | + | size domain.Size | |
| 581 | + | }{ | |
| 582 | + | {name: "zero columns", size: domain.Size{Cols: 0, Rows: 24}}, | |
| 583 | + | {name: "zero rows", size: domain.Size{Cols: 80, Rows: 0}}, | |
| 584 | + | } | |
| 585 | + | for _, tt := range tests { | |
| 586 | + | t.Run(tt.name, func(t *testing.T) { | |
| 587 | + | pty, release := newBlockingPTY(t) | |
| 588 | + | defer release() | |
| 589 | + | d := newTestDaemon(t, newFactory(t, pty), stubClock{}) | |
| 590 | + | ||
| 591 | + | transport := &closeTrackingTransport{} | |
| 592 | + | sess, ac, err := d.route(helloResumeCapable(ports.IntentNew, "work", 0), transport) | |
| 593 | + | require.NoError(t, err) | |
| 594 | + | token := ac.resumeToken | |
| 595 | + | d.clientGone(sess, ac, transport, false) | |
| 596 | + | ||
| 597 | + | d.mu.Lock() | |
| 598 | + | parked := d.parked[token] | |
| 599 | + | d.mu.Unlock() | |
| 600 | + | require.NotNil(t, parked) | |
| 601 | + | ||
| 602 | + | invalid := helloResumeCapable(ports.IntentResume, "work", token) | |
| 603 | + | invalid.Size = tt.size | |
| 604 | + | _, _, err = d.route(invalid, &closeTrackingTransport{}) | |
| 605 | + | var protocolErr *protoErr | |
| 606 | + | require.ErrorAs(t, err, &protocolErr) | |
| 607 | + | require.Equal(t, ports.ErrInternal, protocolErr.code) | |
| 608 | + | ||
| 609 | + | d.mu.Lock() | |
| 610 | + | require.Same(t, parked, d.parked[token], "invalid resume must not consume the parked attachment") | |
| 611 | + | d.mu.Unlock() | |
| 612 | + | require.Same(t, transport, ac.transport(), "invalid resume must not bind a replacement transport") | |
| 613 | + | }) | |
| 614 | + | } | |
| 604 | 615 | } | |
| 605 | 616 | ||
| 606 | 617 | func waitGroupDone(wg *sync.WaitGroup) <-chan struct{} { | |
| @@ -575,32 +575,43 @@ func tabCount(sess *session) int { | |||||
| 575 | // --- handshake -------------------------------------------------------------- | 575 | // --- handshake -------------------------------------------------------------- | ||
| 576 | 576 | ||||
| 577 | func TestRouteRejectsInvalidTerminalSizeBeforeResumeMutation(t *testing.T) { | 577 | func TestRouteRejectsInvalidTerminalSizeBeforeResumeMutation(t *testing.T) { | ||
| 578 | - | pty, release := newBlockingPTY(t) | 578 | + | tests := []struct { |
| 579 | - | defer release() | 579 | + | name string |
| 580 | - | d := newTestDaemon(t, newFactory(t, pty), stubClock{}) | 580 | + | size domain.Size |
| 581 | - | 581 | + | }{ | |
| 582 | - | transport := &closeTrackingTransport{} | 582 | + | {name: "zero columns", size: domain.Size{Cols: 0, Rows: 24}}, |
| 583 | - | sess, ac, err := d.route(helloResumeCapable(ports.IntentNew, "work", 0), transport) | 583 | + | {name: "zero rows", size: domain.Size{Cols: 80, Rows: 0}}, |
| 584 | - | require.NoError(t, err) | 584 | + | } |
| 585 | - | token := ac.resumeToken | 585 | + | for _, tt := range tests { |
| 586 | - | d.clientGone(sess, ac, transport, false) | 586 | + | t.Run(tt.name, func(t *testing.T) { |
| 587 | - | 587 | + | pty, release := newBlockingPTY(t) | |
| 588 | - | d.mu.Lock() | 588 | + | defer release() |
| 589 | - | parked := d.parked[token] | 589 | + | d := newTestDaemon(t, newFactory(t, pty), stubClock{}) |
| 590 | - | d.mu.Unlock() | 590 | + | |
| 591 | - | require.NotNil(t, parked) | 591 | + | transport := &closeTrackingTransport{} |
| 592 | - | 592 | + | sess, ac, err := d.route(helloResumeCapable(ports.IntentNew, "work", 0), transport) | |
| 593 | - | invalid := helloResumeCapable(ports.IntentResume, "work", token) | 593 | + | require.NoError(t, err) |
| 594 | - | invalid.Size = domain.Size{Cols: 0, Rows: 24} | 594 | + | token := ac.resumeToken |
| 595 | - | _, _, err = d.route(invalid, &closeTrackingTransport{}) | 595 | + | d.clientGone(sess, ac, transport, false) |
| 596 | - | var protocolErr *protoErr | 596 | + | |
| 597 | - | require.ErrorAs(t, err, &protocolErr) | 597 | + | d.mu.Lock() |
| 598 | - | require.Equal(t, ports.ErrInternal, protocolErr.code) | 598 | + | parked := d.parked[token] |
| 599 | - | 599 | + | d.mu.Unlock() | |
| 600 | - | d.mu.Lock() | 600 | + | require.NotNil(t, parked) |
| 601 | - | require.Same(t, parked, d.parked[token], "invalid resume must not consume the parked attachment") | 601 | + | |
| 602 | - | d.mu.Unlock() | 602 | + | invalid := helloResumeCapable(ports.IntentResume, "work", token) |
| 603 | - | require.Same(t, transport, ac.transport(), "invalid resume must not bind a replacement transport") | 603 | + | invalid.Size = tt.size |
| 604 | + | _, _, err = d.route(invalid, &closeTrackingTransport{}) | |||
| 605 | + | var protocolErr *protoErr | |||
| 606 | + | require.ErrorAs(t, err, &protocolErr) | |||
| 607 | + | require.Equal(t, ports.ErrInternal, protocolErr.code) | |||
| 608 | + | ||||
| 609 | + | d.mu.Lock() | |||
| 610 | + | require.Same(t, parked, d.parked[token], "invalid resume must not consume the parked attachment") | |||
| 611 | + | d.mu.Unlock() | |||
| 612 | + | require.Same(t, transport, ac.transport(), "invalid resume must not bind a replacement transport") | |||
| 613 | + | }) | |||
| 614 | + | } | |||
| 604 | } | 615 | } | ||
| 605 | 616 | ||||
| 606 | func waitGroupDone(wg *sync.WaitGroup) <-chan struct{} { | 617 | func waitGroupDone(wg *sync.WaitGroup) <-chan struct{} { | ||