mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-22 03:00:12 +00:00
test(agentcompat): add integration scenarios
Co-authored-by: naiba/CloudCode <hi+cloudcode@nai.ba>
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
//go:build linux && agentcompat
|
||||
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/client"
|
||||
)
|
||||
|
||||
type terminalDeadlineProbe struct {
|
||||
writeDeadline time.Time
|
||||
}
|
||||
|
||||
func (probe *terminalDeadlineProbe) WriteFrame(ctx context.Context, _ client.Frame) error {
|
||||
probe.writeDeadline, _ = ctx.Deadline()
|
||||
return context.DeadlineExceeded
|
||||
}
|
||||
|
||||
func (*terminalDeadlineProbe) ReadFrame(context.Context) (client.Frame, error) {
|
||||
return client.Frame{}, errors.New("read must not run after blocked write")
|
||||
}
|
||||
|
||||
func TestTerminalOutputReader_ReturnsMarkerAndCloseEvidence(t *testing.T) {
|
||||
frames := make(chan client.Frame, 1)
|
||||
frames <- client.Frame{Type: client.FrameBinary, Payload: []byte("shell prompt\r\ncompat-size=43 132\r\ncompat-terminal\r\n")}
|
||||
close(frames)
|
||||
|
||||
result, err := readTerminalOutput(context.Background(), terminalOutputReadInput{ExitSentAt: time.Unix(0, 0), Now: func() time.Time { return time.Unix(0, int64(time.Second)) }}, func(context.Context) (client.Frame, error) {
|
||||
frame, ok := <-frames
|
||||
if !ok {
|
||||
return client.Frame{}, &client.WebSocketCloseError{Code: 1000, Text: "normal closure"}
|
||||
}
|
||||
return frame, nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, result.MarkerObserved)
|
||||
require.True(t, result.StreamClosed)
|
||||
require.Equal(t, 1000, result.CloseCode)
|
||||
require.Equal(t, time.Second, result.CloseElapsed)
|
||||
require.Contains(t, result.Output, "compat-terminal")
|
||||
}
|
||||
|
||||
func TestTerminalOutputReader_ObservesRequestedPTYSize(t *testing.T) {
|
||||
frames := make(chan client.Frame, 1)
|
||||
frames <- client.Frame{Type: client.FrameBinary, Payload: []byte("compat-size=43 132\r\ncompat-terminal\r\n")}
|
||||
close(frames)
|
||||
|
||||
result, err := readTerminalOutput(context.Background(), terminalOutputReadInput{ExitSentAt: time.Unix(0, 0), Now: func() time.Time { return time.Unix(0, int64(time.Second)) }}, func(context.Context) (client.Frame, error) {
|
||||
frame, ok := <-frames
|
||||
if !ok {
|
||||
return client.Frame{}, &client.WebSocketCloseError{Code: 1000, Text: "normal closure"}
|
||||
}
|
||||
return frame, nil
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.True(t, result.SizeObserved)
|
||||
require.Equal(t, uint32(43), result.Rows)
|
||||
require.Equal(t, uint32(132), result.Cols)
|
||||
}
|
||||
|
||||
func TestTerminalCommand_ReportsSizeBeforeMarkerAndExit(t *testing.T) {
|
||||
require.Equal(t, "printf 'compat-size='; stty size; printf 'compat-terminal\\n'; exit\n", terminalCommand)
|
||||
}
|
||||
|
||||
func TestTerminalCloseContract_AllowsAgentTimeoutPlusHarnessMargin(t *testing.T) {
|
||||
require.True(t, terminalCloseWithinContract(terminalShutdownContract+terminalShutdownHarnessMargin))
|
||||
require.False(t, terminalCloseWithinContract(terminalShutdownContract+terminalShutdownHarnessMargin+time.Nanosecond))
|
||||
}
|
||||
|
||||
func TestTerminalExit_UsesOneAbsoluteDeadlineForCommandWriteAndCloseRead(t *testing.T) {
|
||||
exitSentAt := time.Unix(100, 0)
|
||||
probe := &terminalDeadlineProbe{}
|
||||
|
||||
_, err := executeTerminalExit(context.Background(), terminalExitInput{ExitSentAt: exitSentAt, Now: func() time.Time { return exitSentAt }}, probe)
|
||||
|
||||
require.ErrorIs(t, err, context.DeadlineExceeded)
|
||||
require.Equal(t, exitSentAt.Add(terminalShutdownContract+terminalShutdownHarnessMargin), probe.writeDeadline)
|
||||
}
|
||||
|
||||
func TestForeignTerminalPATScopes_UseMinimumAttachScope(t *testing.T) {
|
||||
require.Equal(t, "nezha:server:exec", terminalAttachPATScope)
|
||||
}
|
||||
|
||||
func TestTerminalOutputReader_RejectsNonCloseErrorAfterMarker(t *testing.T) {
|
||||
reads := 0
|
||||
|
||||
result, err := readTerminalOutput(context.Background(), terminalOutputReadInput{ExitSentAt: time.Unix(0, 0), Now: func() time.Time { return time.Unix(0, int64(time.Second)) }}, func(context.Context) (client.Frame, error) {
|
||||
reads++
|
||||
if reads == 1 {
|
||||
return client.Frame{Type: client.FrameBinary, Payload: []byte("compat-size=43 132\r\ncompat-terminal\r\n")}, nil
|
||||
}
|
||||
return client.Frame{}, context.DeadlineExceeded
|
||||
})
|
||||
|
||||
require.ErrorIs(t, err, context.DeadlineExceeded)
|
||||
require.True(t, result.MarkerObserved)
|
||||
require.False(t, result.StreamClosed)
|
||||
}
|
||||
|
||||
func TestTerminalOutputReader_RejectsProtocolCloseAfterMarker(t *testing.T) {
|
||||
reads := 0
|
||||
|
||||
result, err := readTerminalOutput(context.Background(), terminalOutputReadInput{ExitSentAt: time.Unix(0, 0), Now: func() time.Time { return time.Unix(0, int64(time.Second)) }}, func(context.Context) (client.Frame, error) {
|
||||
reads++
|
||||
if reads == 1 {
|
||||
return client.Frame{Type: client.FrameBinary, Payload: []byte("compat-size=43 132\r\ncompat-terminal\r\n")}, nil
|
||||
}
|
||||
return client.Frame{}, &client.WebSocketCloseError{Code: 1002, Text: "protocol error"}
|
||||
})
|
||||
|
||||
var closeError *client.WebSocketCloseError
|
||||
require.ErrorAs(t, err, &closeError)
|
||||
require.Equal(t, 1002, result.CloseCode)
|
||||
require.True(t, result.MarkerObserved)
|
||||
require.False(t, result.StreamClosed)
|
||||
}
|
||||
|
||||
func TestTerminalResizeFrame_UsesAgentWireContract(t *testing.T) {
|
||||
frame, err := terminalResizeFrame(132, 43)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, client.FrameBinary, frame.Type)
|
||||
require.Equal(t, byte(1), frame.Payload[0])
|
||||
require.JSONEq(t, `{"Cols":132,"Rows":43}`, string(frame.Payload[1:]))
|
||||
}
|
||||
Reference in New Issue
Block a user