mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
172 lines
8.7 KiB
Go
172 lines
8.7 KiB
Go
//go:build linux && agentcompat
|
|
|
|
package scenario
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/nezhahq/nezha/integration/agentcompat/internal/agent"
|
|
processharness "github.com/nezhahq/nezha/integration/agentcompat/internal/process"
|
|
)
|
|
|
|
func TestFDDiagnosticEnabled_OnlyAcceptsExactOne(t *testing.T) {
|
|
for _, value := range []string{"", "0", "true", "01", " 1", "1 ", "\t1"} {
|
|
require.False(t, fdDiagnosticEnabled(value), "value=%q", value)
|
|
}
|
|
require.True(t, fdDiagnosticEnabled("1"))
|
|
}
|
|
|
|
func TestFDDiagnosticCandidates_ExcludeDashboardAndMatchOrdinals(t *testing.T) {
|
|
collector := newFDDiagnosticCollector(fdDiagnosticCollectorSpec{Enabled: true, TailResultCapacity: 8, Sample: fdDiagnosticTestSampler})
|
|
agentOne := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 1, PID: 101, BaselineCount: 8, EndCount: 9, Target: "one"})
|
|
agentTwo := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 2, PID: 102, BaselineCount: 8, EndCount: 8, Target: "two"})
|
|
dashboard := stressDiagnosticDashboardWindow(t, stressDiagnosticDashboardWindowSpec{PID: 100, BaselineCount: 8, EndCount: 9})
|
|
collector.RecordBaseline(dashboard.baseline)
|
|
collector.RecordBaseline(agentTwo.baseline)
|
|
collector.RecordBaseline(agentOne.baseline)
|
|
|
|
collector.RecordEnd(t.Context(), agentTwo.end)
|
|
collector.RecordEnd(t.Context(), dashboard.end)
|
|
collector.RecordEnd(t.Context(), agentOne.end)
|
|
records := collector.WaitRecords()
|
|
|
|
require.Len(t, records, 1)
|
|
require.Equal(t, 1, records[0].AgentOrdinal)
|
|
require.Equal(t, 101, records[0].AgentPID)
|
|
}
|
|
|
|
func TestFDDiagnosticCandidates_UseOnlyFinalSampleCounts(t *testing.T) {
|
|
collector := newFDDiagnosticCollector(fdDiagnosticCollectorSpec{Enabled: true, TailResultCapacity: 8, Sample: fdDiagnosticTestSampler})
|
|
pair := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 1, PID: 101, BaselineCount: 8, EndCount: 8, Target: "stable"})
|
|
pair.end.Window.Samples[0].NonStdioFDCount = 9
|
|
collector.RecordBaseline(pair.baseline)
|
|
|
|
collector.RecordEnd(t.Context(), pair.end)
|
|
require.Empty(t, collector.WaitRecords())
|
|
|
|
pair.end.Window.Samples[len(pair.end.Window.Samples)-1].NonStdioFDCount = 9
|
|
collector = newFDDiagnosticCollector(fdDiagnosticCollectorSpec{Enabled: true, TailResultCapacity: 8, Sample: fdDiagnosticTestSampler})
|
|
collector.RecordBaseline(pair.baseline)
|
|
collector.RecordEnd(t.Context(), pair.end)
|
|
require.Len(t, collector.WaitRecords(), 1)
|
|
}
|
|
|
|
func TestFDDiagnosticRecord_ReportsIdentityChangeWithoutTail(t *testing.T) {
|
|
pair := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 1, PID: 101, BaselineCount: 8, EndCount: 9, Target: "changed"})
|
|
pair.end.Identity = agent.ProcessIdentity{Generation: 2, PID: 202}
|
|
candidate := fdDiagnosticCandidate{Baseline: pair.baseline, End: pair.end}
|
|
|
|
record, tail := newFDDiagnosticRecord(candidate, 999)
|
|
|
|
require.Equal(t, "process_identity_changed", record.LifecycleStatus)
|
|
require.False(t, tail)
|
|
require.Empty(t, record.Lifecycle)
|
|
}
|
|
|
|
func TestFDDiagnosticRecord_RejectsStressProcessPIDMismatch(t *testing.T) {
|
|
pair := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 1, PID: 101, BaselineCount: 8, EndCount: 9, Target: "process-pid-mismatch"})
|
|
pair.end.Process.PID = 202
|
|
candidate := fdDiagnosticCandidate{Baseline: pair.baseline, End: pair.end}
|
|
|
|
record, tail := newFDDiagnosticRecord(candidate, 999)
|
|
|
|
require.Equal(t, "process_identity_changed", record.LifecycleStatus)
|
|
require.False(t, tail)
|
|
}
|
|
|
|
func TestFDDiagnosticRecord_RejectsZeroRuntimeGeneration(t *testing.T) {
|
|
pair := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 1, PID: 101, BaselineCount: 8, EndCount: 9, Target: "zero-generation"})
|
|
pair.end.Identity.Generation = 0
|
|
candidate := fdDiagnosticCandidate{Baseline: pair.baseline, End: pair.end}
|
|
|
|
record, tail := newFDDiagnosticRecord(candidate, 999)
|
|
|
|
require.Equal(t, "process_identity_changed", record.LifecycleStatus)
|
|
require.False(t, tail)
|
|
}
|
|
|
|
func TestFDDiagnosticRecord_EncodesStableObservedAtForEveryWindowSample(t *testing.T) {
|
|
pair := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 1, PID: 101, BaselineCount: 8, EndCount: 9, Target: "timestamps"})
|
|
baselineTime := time.Date(2025, time.January, 2, 3, 4, 5, 0, time.UTC)
|
|
endTime := baselineTime.Add(time.Minute)
|
|
for index := range pair.baseline.Window.Samples {
|
|
pair.baseline.Window.Samples[index].SampledAt = baselineTime.Add(time.Duration(index) * time.Second)
|
|
pair.end.Window.Samples[index].SampledAt = endTime.Add(time.Duration(index) * time.Second)
|
|
}
|
|
candidate := fdDiagnosticCandidate{Baseline: pair.baseline, End: pair.end}
|
|
|
|
record, tail := newFDDiagnosticRecord(candidate, 999)
|
|
encoded, err := json.Marshal(record)
|
|
|
|
require.True(t, tail)
|
|
require.NoError(t, err)
|
|
require.Equal(t, baselineTime, record.Baseline.Samples[0].ObservedAt)
|
|
require.Equal(t, endTime, record.End.Samples[0].ObservedAt)
|
|
require.Contains(t, string(encoded), `"observed_at":"2025-01-02T03:04:05Z"`)
|
|
}
|
|
|
|
func TestFDDiagnosticRecord_DiffsFinalObservationsByExactIdentity(t *testing.T) {
|
|
pair := stressDiagnosticAgentWindow(t, stressDiagnosticAgentWindowSpec{Ordinal: 1, PID: 101, BaselineCount: 8, EndCount: 9, Target: "diff"})
|
|
pair.baseline.Window.Samples[4].FDObservations = []processharness.FDObservation{{Number: 5, Target: "beta"}, {Number: 4, Target: "alpha"}}
|
|
pair.end.Window.Samples[4].FDObservations = []processharness.FDObservation{{Number: 6, Target: "gamma"}, {Number: 5, Target: "beta"}}
|
|
candidate := fdDiagnosticCandidate{Baseline: pair.baseline, End: pair.end}
|
|
|
|
record, tail := newFDDiagnosticRecord(candidate, 999)
|
|
|
|
require.True(t, tail)
|
|
require.Equal(t, []processharness.FDObservation{{Number: 6, Target: "gamma"}}, record.AddedFinal)
|
|
require.Equal(t, []processharness.FDObservation{{Number: 4, Target: "alpha"}}, record.RemovedFinal)
|
|
}
|
|
|
|
func TestFDDiagnosticLifecycle_ClassifiesEachFinalAddition(t *testing.T) {
|
|
added := []processharness.FDObservation{{Number: 3, Target: "before"}, {Number: 4, Target: "during"}, {Number: 5, Target: "through"}, {Number: 6, Target: "intermittent"}, {Number: 7, Target: "reused"}}
|
|
tail := []fdDiagnosticSample{
|
|
stressDiagnosticSample(1, []processharness.FDObservation{{Number: 4, Target: "during"}, {Number: 5, Target: "through"}, {Number: 6, Target: "intermittent"}, {Number: 7, Target: "reused"}}),
|
|
stressDiagnosticSample(2, []processharness.FDObservation{{Number: 5, Target: "through"}, {Number: 7, Target: "other"}}),
|
|
stressDiagnosticSample(3, []processharness.FDObservation{{Number: 5, Target: "through"}, {Number: 6, Target: "intermittent"}, {Number: 7, Target: "reused"}}),
|
|
}
|
|
|
|
lifecycle := classifyFDDiagnosticLifecycle(added, tail)
|
|
|
|
require.Equal(t, []fdDiagnosticLifecycle{
|
|
{Observation: processharness.FDObservation{Number: 3, Target: "before"}, Status: "cleared_before_tail"},
|
|
{Observation: processharness.FDObservation{Number: 4, Target: "during"}, Status: "cleared_during_tail"},
|
|
{Observation: processharness.FDObservation{Number: 5, Target: "through"}, Status: "observed_through_tail"},
|
|
{Observation: processharness.FDObservation{Number: 6, Target: "intermittent"}, Status: "intermittent_or_reused"},
|
|
{Observation: processharness.FDObservation{Number: 7, Target: "reused"}, Status: "intermittent_or_reused", NumberReused: true},
|
|
}, lifecycle)
|
|
}
|
|
|
|
func TestFDDiagnosticFields_DoNotChangeStressEvaluationOrEvidenceJSON(t *testing.T) {
|
|
resource := stressDashboardResourceFixture(100)
|
|
beforeEvaluation, err := EvaluateStressResource(resource)
|
|
require.NoError(t, err)
|
|
for sampleIndex := range resource.Baseline.Samples {
|
|
resource.Baseline.Samples[sampleIndex].FDObservations = []processharness.FDObservation{{Number: 9, Target: "diagnostic"}}
|
|
resource.End.Samples[sampleIndex].FDObservations = []processharness.FDObservation{{Number: 9, Target: "diagnostic"}}
|
|
}
|
|
afterEvaluation, err := EvaluateStressResource(resource)
|
|
require.NoError(t, err)
|
|
profile := mustStressPRFullProfile(t)
|
|
evidence := validStressEvidence(t, profile)
|
|
beforeEvidence, err := json.Marshal(evidence)
|
|
require.NoError(t, err)
|
|
for iteration := range evidence.Iterations {
|
|
for resourceIndex := range evidence.Iterations[iteration].Resources {
|
|
for sampleIndex := range evidence.Iterations[iteration].Resources[resourceIndex].Baseline.Samples {
|
|
evidence.Iterations[iteration].Resources[resourceIndex].Baseline.Samples[sampleIndex].FDObservations = []processharness.FDObservation{{Number: 9, Target: "diagnostic"}}
|
|
evidence.Iterations[iteration].Resources[resourceIndex].End.Samples[sampleIndex].FDObservations = []processharness.FDObservation{{Number: 9, Target: "diagnostic"}}
|
|
}
|
|
}
|
|
}
|
|
afterEvidence, err := json.Marshal(evidence)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, beforeEvaluation, afterEvaluation)
|
|
require.Equal(t, string(beforeEvidence), string(afterEvidence))
|
|
}
|