mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
258 lines
8.2 KiB
Go
258 lines
8.2 KiB
Go
//go:build linux
|
|
|
|
package scenario
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/nezhahq/nezha/integration/agentcompat/internal/agent"
|
|
"github.com/nezhahq/nezha/integration/agentcompat/internal/client"
|
|
)
|
|
|
|
func TestHeldTerminalCommandSubmitsWithLineFeed(t *testing.T) {
|
|
command := heldTerminalCommand("marker-session")
|
|
|
|
require.Equal(t, byte('\n'), command[len(command)-1])
|
|
require.NotContains(t, command, "exit\\n")
|
|
}
|
|
|
|
func TestHeldTerminalProofRejectsEchoOnlyExactTokens(t *testing.T) {
|
|
proof := newHeldTerminalProof("marker-session")
|
|
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: []byte("printf 'compat-size='; stty size; printf 'marker-session\\n'; read -r held_terminal_release; exit\\n\r\ncompat-size=43 132\r\nmarker-session\r\n")})
|
|
|
|
require.False(t, proof.Complete())
|
|
}
|
|
|
|
func TestHeldTerminalProofAcceptsMarkerAndExactSizeAcrossFrames(t *testing.T) {
|
|
// Given
|
|
proof := newHeldTerminalProof("marker-session")
|
|
|
|
// When
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: []byte("prefix\r\n\x1emarker-session|43 ")})
|
|
proof.Consume(client.Frame{Type: client.FrameBinary, Payload: []byte("132\x1f\r\n")})
|
|
|
|
// Then
|
|
require.True(t, proof.Complete())
|
|
require.Equal(t, uint32(43), proof.Rows())
|
|
require.Equal(t, uint32(132), proof.Columns())
|
|
}
|
|
|
|
func TestHeldTerminalProofIgnoresMarkerInEchoedCommand(t *testing.T) {
|
|
// Given
|
|
proof := newHeldTerminalProof("marker-session")
|
|
|
|
// When
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: []byte("printf '\\036%s%s|' 'marker-' 'session'; stty size; printf '\\037'\r\n")})
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: []byte("\x1emarker-session|43 132\x1f\r\n")})
|
|
|
|
// Then
|
|
require.True(t, proof.Complete())
|
|
require.Equal(t, uint32(43), proof.Rows())
|
|
require.Equal(t, uint32(132), proof.Columns())
|
|
}
|
|
|
|
func TestHeldTerminalProofRejectsWrongMarkerAndWrongSize(t *testing.T) {
|
|
// Given
|
|
wrongMarker := newHeldTerminalProof("marker-session")
|
|
wrongSize := newHeldTerminalProof("marker-session")
|
|
|
|
// When
|
|
wrongMarker.Consume(client.Frame{Type: client.FrameText, Payload: []byte("\x1emarker-other|43 132\x1f\r\n")})
|
|
wrongSize.Consume(client.Frame{Type: client.FrameText, Payload: []byte("\x1emarker-session|42 132\x1f\r\n")})
|
|
|
|
// Then
|
|
require.False(t, wrongMarker.Complete())
|
|
require.False(t, wrongSize.Complete())
|
|
}
|
|
|
|
func TestHeldTerminalProofAcceptsValidRecordAfterWrongMarkerInSameFrame(t *testing.T) {
|
|
// Given
|
|
proof := newHeldTerminalProof("marker-session")
|
|
frame := []byte("\x1emarker-other|43 132\x1f\x1emarker-session|43 132\x1f")
|
|
|
|
// When
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: frame})
|
|
|
|
// Then
|
|
require.True(t, proof.Complete())
|
|
}
|
|
|
|
func TestHeldTerminalProofAcceptsValidRecordAfterMalformedRecordInSameFrame(t *testing.T) {
|
|
// Given
|
|
proof := newHeldTerminalProof("marker-session")
|
|
frame := []byte("\x1emarker-session|43 nope\x1f\x1emarker-session|43 132\x1f")
|
|
|
|
// When
|
|
proof.Consume(client.Frame{Type: client.FrameBinary, Payload: frame})
|
|
|
|
// Then
|
|
require.True(t, proof.Complete())
|
|
}
|
|
|
|
func TestHeldTerminalProofScansMultipleInvalidRecordsBeforeValidRecord(t *testing.T) {
|
|
// Given
|
|
proof := newHeldTerminalProof("marker-session")
|
|
frame := []byte("noise\x1emarker-other|43 132\x1f\x1emarker-session|43 nope\x1f\x1emarker-session|43 132\x1f")
|
|
|
|
// When
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: frame})
|
|
|
|
// Then
|
|
require.True(t, proof.Complete())
|
|
}
|
|
|
|
func TestHeldTerminalProofAcceptsFramedRecordAcrossFrames(t *testing.T) {
|
|
proof := newHeldTerminalProof("marker-session")
|
|
|
|
proof.Consume(client.Frame{Type: client.FrameBinary, Payload: []byte("prefix\x1emarker-session|43 ")})
|
|
proof.Consume(client.Frame{Type: client.FrameBinary, Payload: []byte("132\x1f\r\n")})
|
|
|
|
require.True(t, proof.Complete())
|
|
}
|
|
|
|
func TestHeldTerminalProofRejectsMalformedOrUnclosedRecord(t *testing.T) {
|
|
malformed := newHeldTerminalProof("marker-session")
|
|
unclosed := newHeldTerminalProof("marker-session")
|
|
|
|
malformed.Consume(client.Frame{Type: client.FrameBinary, Payload: []byte("\x1emarker-session|43 nope\x1f")})
|
|
unclosed.Consume(client.Frame{Type: client.FrameBinary, Payload: []byte("\x1emarker-session|43 132")})
|
|
|
|
require.False(t, malformed.Complete())
|
|
require.False(t, unclosed.Complete())
|
|
require.Equal(t, []byte("\x1emarker-session|43 132"), unclosed.buffer)
|
|
}
|
|
|
|
func TestHeldTerminalProofAcceptsEchoThenFramedRecord(t *testing.T) {
|
|
proof := newHeldTerminalProof("marker-session")
|
|
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: []byte("printf '\\036%s%s|' 'marker-' 'session'; stty size; printf '\\037' exit\\n\r\n")})
|
|
require.False(t, proof.Complete())
|
|
proof.Consume(client.Frame{Type: client.FrameBinary, Payload: []byte("\x1emarker-session|43 132\x1f")})
|
|
|
|
require.True(t, proof.Complete())
|
|
}
|
|
|
|
func TestHeldTerminalProofRejectsClosedPumpBeforeProof(t *testing.T) {
|
|
// Given
|
|
proof := newHeldTerminalProof("marker-session")
|
|
|
|
// When
|
|
err := proof.Closed(errors.New("pump closed"))
|
|
|
|
// Then
|
|
require.Error(t, err)
|
|
require.ErrorContains(t, err, "pump closed")
|
|
}
|
|
|
|
func TestHeldTerminalProofBoundsAccumulator(t *testing.T) {
|
|
// Given
|
|
proof := newHeldTerminalProof("marker-session")
|
|
|
|
// When
|
|
proof.Consume(client.Frame{Type: client.FrameText, Payload: make([]byte, heldTerminalProofLimit+1)})
|
|
|
|
// Then
|
|
require.LessOrEqual(t, len(proof.buffer), heldTerminalProofLimit)
|
|
}
|
|
|
|
func TestHeldTerminalResponseRequiresExactSessionServerIdentity(t *testing.T) {
|
|
// Given
|
|
response := terminalCreateResponse{SessionID: "session", ServerID: 9}
|
|
|
|
// When
|
|
err := validateHeldTerminalResponse(response, 8)
|
|
|
|
// Then
|
|
require.ErrorIs(t, err, ErrHeldTerminalProtocol)
|
|
require.NoError(t, validateHeldTerminalResponse(response, 9))
|
|
}
|
|
|
|
func TestHeldTerminalInputRejectsMissingResourcesAndMismatchedReadiness(t *testing.T) {
|
|
// Given
|
|
plan := heldTestPlan(t)
|
|
input := heldTerminalInput{Plan: plan, Readiness: agent.Readiness{ServerID: 7, UUID: "agent"}}
|
|
|
|
// When
|
|
err := validateHeldTerminalInput(context.Background(), input)
|
|
|
|
// Then
|
|
require.ErrorIs(t, err, ErrInvalidHeldTerminalInput)
|
|
}
|
|
|
|
func TestHeldTerminalInputRejectsMissingPATClientBeforeRemoteMutation(t *testing.T) {
|
|
err := validateHeldPATClient(nil)
|
|
|
|
require.ErrorIs(t, err, ErrInvalidHeldPATClient)
|
|
}
|
|
|
|
func TestHeldTerminalCommandKeepsShellHeldUntilInput(t *testing.T) {
|
|
// Given
|
|
command := heldTerminalCommand("marker-session")
|
|
|
|
// Then
|
|
require.Contains(t, command, "marker-")
|
|
require.Contains(t, command, "session")
|
|
require.Contains(t, command, "stty size")
|
|
require.Contains(t, command, "read -r")
|
|
require.Contains(t, command, "exit")
|
|
require.Contains(t, command, "\\036")
|
|
require.Contains(t, command, "\\037")
|
|
require.NotEqual(t, "\n", command)
|
|
}
|
|
|
|
func TestHeldTerminalCleanupOwnerDoesNotUseCanceledWaiter(t *testing.T) {
|
|
// Given
|
|
cleanupStarted := make(chan struct{})
|
|
cleanupRelease := make(chan struct{})
|
|
lifecycle := heldTestLifecycle(t, "held-terminal-session")
|
|
stack := newHeldCleanupStack()
|
|
require.NoError(t, stack.Push(heldCleanupAction{name: "blocked", cleanup: func(cleanupContext context.Context) error {
|
|
require.NoError(t, cleanupContext.Err())
|
|
close(cleanupStarted)
|
|
<-cleanupRelease
|
|
return nil
|
|
}}))
|
|
session := newHeldTerminalSessionForTest(lifecycle, stack)
|
|
|
|
// When
|
|
canceled, cancel := context.WithCancel(context.Background())
|
|
cancel()
|
|
first := make(chan error, 1)
|
|
go func() { first <- session.Close(canceled) }()
|
|
<-cleanupStarted
|
|
|
|
// Then
|
|
require.ErrorIs(t, <-first, context.Canceled)
|
|
close(cleanupRelease)
|
|
require.NoError(t, session.Close(context.Background()))
|
|
}
|
|
|
|
func TestHeldTerminalCleanupStackReleasesHoldBeforeTransport(t *testing.T) {
|
|
// Given
|
|
stack := newHeldCleanupStack()
|
|
var order []string
|
|
require.NoError(t, stack.Push(heldCleanupAction{name: "absence", cleanup: func(context.Context) error {
|
|
order = append(order, "absence")
|
|
return nil
|
|
}}))
|
|
require.NoError(t, stack.Push(heldCleanupAction{name: "transport", cleanup: func(context.Context) error {
|
|
order = append(order, "transport")
|
|
return nil
|
|
}}))
|
|
require.NoError(t, stack.Push(heldCleanupAction{name: "release", cleanup: func(context.Context) error {
|
|
order = append(order, "release")
|
|
return nil
|
|
}}))
|
|
|
|
// When
|
|
require.NoError(t, stack.Run(context.Background()))
|
|
|
|
// Then
|
|
require.Equal(t, []string{"release", "transport", "absence"}, order)
|
|
}
|