mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-22 11:10: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,254 @@
|
||||
//go:build linux
|
||||
|
||||
package scenario
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/agent"
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/client"
|
||||
"github.com/nezhahq/nezha/integration/agentcompat/internal/dashboard"
|
||||
)
|
||||
|
||||
func TestNewHeldLegacyFMSessionRejectsInvalidTypedInputs(t *testing.T) {
|
||||
// Given
|
||||
plan := heldFMTestPlan(t, StressSessionFM)
|
||||
agentInstance := newHeldReadinessTestAgent(t, "00000000-0000-0000-0000-000000000305")
|
||||
readiness := completeHeldReadiness(agentInstance.UUID())
|
||||
valid := heldLegacyFMInput{Dashboard: &dashboard.Dashboard{}, PATClient: &client.Client{}, Agent: agentInstance, Readiness: readiness, Plan: plan}
|
||||
cases := []struct {
|
||||
name string
|
||||
input heldLegacyFMInput
|
||||
}{
|
||||
{name: "nil dashboard", input: heldLegacyFMInput{Agent: valid.Agent, Readiness: readiness, Plan: plan}},
|
||||
{name: "nil agent", input: heldLegacyFMInput{Dashboard: valid.Dashboard, Readiness: readiness, Plan: plan}},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
// When
|
||||
_, err := newHeldLegacyFMSession(context.Background(), testCase.input)
|
||||
|
||||
// Then
|
||||
require.ErrorIs(t, err, ErrInvalidHeldLegacyFMInput)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewHeldLegacyFMSessionReturnsPreciseReadinessErrorForZeroServerID(t *testing.T) {
|
||||
agentInstance := newHeldReadinessTestAgent(t, "00000000-0000-0000-0000-000000000306")
|
||||
input := heldLegacyFMInput{Dashboard: &dashboard.Dashboard{}, PATClient: &client.Client{}, Agent: agentInstance, Readiness: completeHeldReadiness(agentInstance.UUID()), Plan: heldFMTestPlan(t, StressSessionFM)}
|
||||
input.Readiness.ServerID = 0
|
||||
|
||||
_, err := newHeldLegacyFMSession(context.Background(), input)
|
||||
|
||||
require.ErrorIs(t, err, ErrHeldReadinessServerID)
|
||||
require.NotErrorIs(t, err, ErrInvalidHeldLegacyFMInput)
|
||||
}
|
||||
|
||||
func TestNewHeldLegacyFMSessionReturnsPreciseReadinessErrorForUUIDMismatch(t *testing.T) {
|
||||
agentInstance := newHeldReadinessTestAgent(t, "00000000-0000-0000-0000-000000000307")
|
||||
input := heldLegacyFMInput{Dashboard: &dashboard.Dashboard{}, PATClient: &client.Client{}, Agent: agentInstance, Readiness: completeHeldReadiness("00000000-0000-0000-0000-000000000308"), Plan: heldFMTestPlan(t, StressSessionFM)}
|
||||
|
||||
_, err := newHeldLegacyFMSession(context.Background(), input)
|
||||
|
||||
require.ErrorIs(t, err, ErrHeldReadinessAgentMismatch)
|
||||
require.NotErrorIs(t, err, ErrInvalidHeldLegacyFMInput)
|
||||
}
|
||||
|
||||
func TestHeldLegacyFMSessionImplementsHeldSession(t *testing.T) {
|
||||
var _ heldSession = (*heldLegacyFMSession)(nil)
|
||||
}
|
||||
|
||||
func TestNewHeldLegacyFMSessionRejectsNilContext(t *testing.T) {
|
||||
// Given
|
||||
input := heldLegacyFMInput{
|
||||
Dashboard: &dashboard.Dashboard{}, PATClient: &client.Client{}, Agent: &agent.Agent{},
|
||||
Readiness: agent.Readiness{ServerID: 9, UUID: "agent-uuid", Online: true}, Plan: heldFMTestPlan(t, StressSessionFM),
|
||||
}
|
||||
|
||||
// When
|
||||
_, err := newHeldLegacyFMSession(nil, input)
|
||||
|
||||
// Then
|
||||
require.ErrorIs(t, err, ErrInvalidHeldLegacyFMInput)
|
||||
}
|
||||
|
||||
func TestNewHeldLegacyFMSessionRejectsNilPATBeforeMutation(t *testing.T) {
|
||||
// Given
|
||||
agentInstance := newHeldReadinessTestAgent(t, "00000000-0000-0000-0000-000000000303")
|
||||
input := heldLegacyFMInput{
|
||||
Dashboard: &dashboard.Dashboard{},
|
||||
PATClient: nil,
|
||||
Agent: agentInstance,
|
||||
Readiness: completeHeldReadiness(agentInstance.UUID()),
|
||||
Plan: heldFMTestPlan(t, StressSessionFM),
|
||||
}
|
||||
workspaceRoot := agentInstance.WorkspaceRoot()
|
||||
before, err := os.ReadDir(workspaceRoot)
|
||||
require.NoError(t, err)
|
||||
|
||||
// When
|
||||
var recovered any
|
||||
func() {
|
||||
defer func() { recovered = recover() }()
|
||||
_, err = newHeldLegacyFMSession(context.Background(), input)
|
||||
}()
|
||||
|
||||
// Then
|
||||
require.Nil(t, recovered)
|
||||
require.ErrorIs(t, err, ErrInvalidHeldPATClient)
|
||||
after, readErr := os.ReadDir(workspaceRoot)
|
||||
require.NoError(t, readErr)
|
||||
require.Equal(t, before, after)
|
||||
_, statErr := os.Stat(filepath.Join(workspaceRoot, "held-fm-held-fm-session"))
|
||||
require.ErrorIs(t, statErr, os.ErrNotExist)
|
||||
}
|
||||
|
||||
func TestHeldLegacyFMSessionRejectsNonFMPlanIdentity(t *testing.T) {
|
||||
agentInstance := newHeldReadinessTestAgent(t, "00000000-0000-0000-0000-000000000309")
|
||||
plan := heldFMTestPlan(t, StressSessionTerminal)
|
||||
input := heldLegacyFMInput{
|
||||
Dashboard: &dashboard.Dashboard{}, PATClient: &client.Client{}, Agent: agentInstance,
|
||||
Readiness: completeHeldReadiness(agentInstance.UUID()), Plan: plan,
|
||||
}
|
||||
|
||||
// When
|
||||
_, err := newHeldLegacyFMSession(context.Background(), input)
|
||||
|
||||
// Then
|
||||
require.Error(t, err)
|
||||
require.ErrorIs(t, err, ErrInvalidHeldLegacyFMInput)
|
||||
}
|
||||
|
||||
func TestHeldLegacyFMInputRejectsPATClientBeforeOtherValidation(t *testing.T) {
|
||||
// Given
|
||||
agentInstance := newHeldReadinessTestAgent(t, "00000000-0000-0000-0000-000000000304")
|
||||
input := heldLegacyFMInput{
|
||||
Dashboard: &dashboard.Dashboard{},
|
||||
Agent: agentInstance,
|
||||
Readiness: completeHeldReadiness(agentInstance.UUID()),
|
||||
Plan: heldFMTestPlan(t, StressSessionFM),
|
||||
}
|
||||
|
||||
// When
|
||||
err := validateHeldLegacyFMInput(context.Background(), input)
|
||||
|
||||
// Then
|
||||
require.ErrorIs(t, err, ErrInvalidHeldPATClient)
|
||||
}
|
||||
|
||||
func TestHeldLegacyFMStreamMismatchErrorDoesNotExposeIdentifiers(t *testing.T) {
|
||||
// Given
|
||||
responseID := "response-secret-session"
|
||||
capabilityID := "capability-secret-stream"
|
||||
|
||||
// When
|
||||
err := heldLegacyFMStreamMismatchError()
|
||||
message := err.Error()
|
||||
|
||||
// Then
|
||||
require.ErrorIs(t, err, ErrHeldLegacyFMProtocol)
|
||||
require.NotContains(t, message, responseID)
|
||||
require.NotContains(t, message, capabilityID)
|
||||
require.NotContains(t, message, "Authorization")
|
||||
}
|
||||
|
||||
func TestHeldLegacyFMListProofRequiresBinaryExactNZFNPathAndEntry(t *testing.T) {
|
||||
// Given
|
||||
root := "/workspace/held-fm/list"
|
||||
valid := heldLegacyFMListFrame(root, "entry.txt", false)
|
||||
cases := []struct {
|
||||
name string
|
||||
frame client.Frame
|
||||
}{
|
||||
{name: "valid", frame: client.Frame{Type: client.FrameBinary, Payload: valid}},
|
||||
{name: "text", frame: client.Frame{Type: client.FrameText, Payload: valid}},
|
||||
{name: "wrong path", frame: client.Frame{Type: client.FrameBinary, Payload: heldLegacyFMListFrame("/other", "entry.txt", false)}},
|
||||
{name: "directory entry", frame: client.Frame{Type: client.FrameBinary, Payload: heldLegacyFMListFrame(root, "entry.txt", true)}},
|
||||
{name: "remote error", frame: client.Frame{Type: client.FrameBinary, Payload: []byte("NERRdenied")}},
|
||||
}
|
||||
for _, testCase := range cases {
|
||||
t.Run(testCase.name, func(t *testing.T) {
|
||||
release := make(chan struct{})
|
||||
server := heldPumpServer(t, func(connection *websocket.Conn) {
|
||||
messageType := websocket.BinaryMessage
|
||||
if testCase.frame.Type == client.FrameText {
|
||||
messageType = websocket.TextMessage
|
||||
}
|
||||
require.NoError(t, connection.WriteMessage(messageType, testCase.frame.Payload))
|
||||
<-release
|
||||
})
|
||||
connection := heldPumpConnection(t, server)
|
||||
pump, err := newHeldWebSocketPump(context.Background(), connection, 1)
|
||||
require.NoError(t, err)
|
||||
err = proveHeldLegacyFMList(context.Background(), pump, root)
|
||||
if testCase.name == "valid" {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.NotContains(t, err.Error(), root)
|
||||
require.NotContains(t, err.Error(), "entry.txt")
|
||||
}
|
||||
require.NoError(t, pump.Stop(context.Background()))
|
||||
close(release)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHeldLegacyFMCanceledCloseWaiterRetainsCleanupResult(t *testing.T) {
|
||||
// Given
|
||||
lifecycle, err := newHeldSessionLifecycle(context.Background(), heldFMTestPlan(t, StressSessionFM), "held-fm-stream", time.Second)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, lifecycle.markLive(nil))
|
||||
started := make(chan struct{})
|
||||
release := make(chan struct{})
|
||||
stack := newHeldCleanupStack()
|
||||
require.NoError(t, stack.Push(heldCleanupAction{name: "blocked cleanup", cleanup: func(context.Context) error {
|
||||
close(started)
|
||||
<-release
|
||||
return nil
|
||||
}}))
|
||||
session := &heldLegacyFMSession{lifecycle: lifecycle, stack: stack}
|
||||
canceled, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
// When
|
||||
first := make(chan error, 1)
|
||||
go func() { first <- session.Close(canceled) }()
|
||||
<-started
|
||||
|
||||
// Then
|
||||
require.ErrorIs(t, <-first, context.Canceled)
|
||||
close(release)
|
||||
require.NoError(t, session.Close(context.Background()))
|
||||
}
|
||||
|
||||
func heldLegacyFMListFrame(path, name string, directory bool) []byte {
|
||||
kind := byte(0)
|
||||
if directory {
|
||||
kind = 1
|
||||
}
|
||||
frame := make([]byte, 8, 8+len(path)+2+len(name))
|
||||
copy(frame, []byte("NZFN"))
|
||||
binary.BigEndian.PutUint32(frame[4:], uint32(len(path)))
|
||||
frame = append(frame, []byte(path)...)
|
||||
frame = append(frame, kind, byte(len(name)))
|
||||
return append(frame, []byte(name)...)
|
||||
}
|
||||
|
||||
func heldFMTestPlan(t *testing.T, kind StressSessionKind) StressSessionPlan {
|
||||
t.Helper()
|
||||
id, err := NewStressSessionID("held-fm-session")
|
||||
require.NoError(t, err)
|
||||
ordinal, err := NewStressAgentOrdinal(1)
|
||||
require.NoError(t, err)
|
||||
return StressSessionPlan{ID: id, Kind: kind, Ordinal: 1, Agent: ordinal}
|
||||
}
|
||||
Reference in New Issue
Block a user