Files
nezha_domains/integration/agentcompat/internal/scenario/stress_evidence_test.go
T
2026-07-20 04:54:03 +00:00

148 lines
5.8 KiB
Go

//go:build linux
package scenario
import (
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/nezhahq/nezha/integration/agentcompat/internal/contract"
)
func TestStressEvidence_AcceptsCompleteSuccessContract(t *testing.T) {
profile, err := contract.ProfileByName(string(contract.ProfilePRFull))
require.NoError(t, err)
evidence := validStressEvidence(t, profile)
err = evidence.ValidateSuccess(profile)
require.NoError(t, err)
}
func TestStressEvidence_AcceptsOnlyExactStressWorkerFault(t *testing.T) {
profile, err := contract.ProfileByName(string(contract.ProfilePRFull))
require.NoError(t, err)
evidence := validStressEvidence(t, profile)
target := StressWorkerFaultTarget()
evidence.FaultTarget = &target
faultOperation := findStressFaultOperation(evidence.Plan)
for roundIndex := range evidence.Iterations[0].Rounds {
for operationIndex := range evidence.Iterations[0].Rounds[roundIndex].Operations {
operation := &evidence.Iterations[0].Rounds[roundIndex].Operations[operationIndex]
if operation.ID == faultOperation.ID {
operation.Succeeded = false
operation.Error = "injected stress worker fault"
}
}
}
err = evidence.ValidateStressWorker(profile)
require.NoError(t, err)
}
func TestStressEvidence_RejectsSecondFailedOperationForStressWorker(t *testing.T) {
profile, err := contract.ProfileByName(string(contract.ProfilePRFull))
require.NoError(t, err)
evidence := validStressEvidence(t, profile)
target := StressWorkerFaultTarget()
evidence.FaultTarget = &target
evidence.Iterations[0].Rounds[1].Operations[0].Succeeded = false
evidence.Iterations[0].Rounds[1].Operations[0].Error = "injected stress worker fault"
evidence.Iterations[0].Rounds[1].Operations[1].Succeeded = false
evidence.Iterations[0].Rounds[1].Operations[1].Error = "unexpected failure"
err = evidence.ValidateStressWorker(profile)
require.ErrorIs(t, err, ErrStressFault)
}
func TestStressEvidence_RejectsSelfConsistentTruncatedPlan(t *testing.T) {
profile, err := contract.ProfileByName(string(contract.ProfilePRFull))
require.NoError(t, err)
evidence := validStressEvidence(t, profile)
evidence.Plan.Rounds = evidence.Plan.Rounds[:1]
evidence.Iterations[0].Rounds = evidence.Iterations[0].Rounds[:1]
err = evidence.ValidateSuccess(profile)
require.ErrorIs(t, err, ErrStressEvidence)
}
func TestStressEvidence_RejectsDuplicateDashboardResources(t *testing.T) {
profile, err := contract.ProfileByName(string(contract.ProfilePRFull))
require.NoError(t, err)
evidence := validStressEvidence(t, profile)
evidence.Iterations[0].Resources[1] = evidence.Iterations[0].Resources[0]
err = evidence.ValidateSuccess(profile)
require.ErrorIs(t, err, ErrStressEvidence)
}
func validStressEvidence(t *testing.T, profile contract.Profile) StressEvidence {
t.Helper()
plan, err := GenerateStressPlan(profile, contract.DefaultSeed)
require.NoError(t, err)
warmups := make([]StressWarmupEvidence, profile.AgentCount())
for index := range warmups {
agent, agentErr := NewStressAgentOrdinal(index + 1)
require.NoError(t, agentErr)
warmups[index] = StressWarmupEvidence{Agent: agent, Exec: true, Filesystem: true, Terminal: true, NAT: true, FM: true}
}
sessions := make([]StressSessionEvidence, len(plan.Sessions))
for index, session := range plan.Sessions {
sessions[index] = StressSessionEvidence{ID: session.ID, Kind: session.Kind, Succeeded: true}
}
rounds := make([]StressRoundEvidence, len(plan.Rounds))
started := time.Unix(100, 0)
for roundIndex, round := range plan.Rounds {
operations := make([]StressOperationEvidence, len(round.Operations))
for operationIndex, operation := range round.Operations {
operations[operationIndex] = StressOperationEvidence{ID: operation.ID, Round: operation.Round, Agent: operation.Agent, PAT: operation.PAT, Kind: operation.Kind, LaunchedAt: started, CompletedAt: started.Add(time.Millisecond), Succeeded: true, SuccessProof: "ok"}
}
rounds[roundIndex] = StressRoundEvidence{Round: round.Round, Operations: operations}
}
resources := make([]StressProcessWindows, 0, profile.AgentCount()+1)
resources = append(resources, stressDashboardResourceFixture(100))
for index := 1; index <= profile.AgentCount(); index++ {
agent, agentErr := NewStressAgentOrdinal(index)
require.NoError(t, agentErr)
process, processErr := NewStressAgentProcess(agent, 200+index)
require.NoError(t, processErr)
resources = append(resources, StressProcessWindows{Process: process, Baseline: stressWindow(200+index, 100), End: stressWindow(200+index, 100)})
}
iterations := make([]StressIterationEvidence, profile.Iterations())
for index := range iterations {
iterations[index] = StressIterationEvidence{Iteration: index + 1, Rounds: rounds, Resources: resources}
}
return StressEvidence{
Version: 1, Profile: profile.Name(), Seed: contract.DefaultSeed, Plan: plan,
PreparedBinaries: StressPreparedBinaries{DashboardBuildCount: 1, DashboardPathReused: true, AgentBuildCount: 1, AgentPathReused: true},
Quotas: StressQuotaEvidence{
PATSecond: quotaBoundary(10, 11), PATMinute: quotaBoundary(120, 121),
UserStreams: quotaBoundary(20, 21), ServerStreams: quotaBoundary(40, 41), PathLockStripes: 1024,
},
Warmups: warmups, Sessions: sessions, Iterations: iterations,
Cleanup: StressCleanupSummary{Passed: true, ReceiptCount: 9},
}
}
func quotaBoundary(allowed, rejected int) StressQuotaBoundary {
return StressQuotaBoundary{Allowed: allowed, Rejected: rejected, AllowedAccepted: true, RejectedDenied: true}
}
func findStressFaultOperation(plan StressPlan) StressOperationPlan {
target := StressWorkerFaultTarget()
for _, round := range plan.Rounds {
for _, operation := range round.Operations {
if round.Round == target.Round && operation.Agent == target.Agent && operation.Kind == target.Kind {
return operation
}
}
}
panic("stress fault operation missing")
}