mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
178 lines
7.1 KiB
Go
178 lines
7.1 KiB
Go
//go:build linux && agentcompat
|
|
|
|
package dashboard
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nezhahq/nezha/model"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestMCPReceiptSet_MatchesUnorderedExactServerTaskIdentities(t *testing.T) {
|
|
expectations := []MCPReceiptExpectation{
|
|
{DashboardGeneration: 2, GateGeneration: 9, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec},
|
|
{DashboardGeneration: 2, GateGeneration: 9, ServerID: 8, TaskID: 102, TaskType: model.TaskTypeFsRead},
|
|
}
|
|
events := []MCPReceiptEvent{
|
|
mcpReceiptEvent(1, 2, 9, 8, 102, model.TaskTypeFsRead, MCPReceiptTask),
|
|
mcpReceiptEvent(2, 2, 9, 8, 102, model.TaskTypeFsRead, MCPReceiptResult),
|
|
mcpReceiptEvent(3, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptTask),
|
|
mcpReceiptEvent(4, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptResult),
|
|
}
|
|
|
|
pairs, complete, err := matchMCPReceiptSet(events, MCPReceiptCursor{}, expectations)
|
|
|
|
require.NoError(t, err)
|
|
require.True(t, complete)
|
|
require.Equal(t, uint64(7), pairs[0].Task.ServerID)
|
|
require.Equal(t, uint64(101), pairs[0].Result.TaskID)
|
|
require.Equal(t, uint64(8), pairs[1].Task.ServerID)
|
|
require.Equal(t, uint64(102), pairs[1].Result.TaskID)
|
|
}
|
|
|
|
func TestMCPReceiptSet_RejectsMissingDuplicateAndMismatchedReceipts(t *testing.T) {
|
|
expectation := []MCPReceiptExpectation{{DashboardGeneration: 2, GateGeneration: 9, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec}}
|
|
task := mcpReceiptEvent(1, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptTask)
|
|
|
|
tests := map[string][]MCPReceiptEvent{
|
|
"missing result": {task},
|
|
"duplicate task identity": {task, mcpReceiptEvent(2, 2, 9, 7, 102, model.TaskTypeExec, MCPReceiptTask)},
|
|
"duplicate task ID": {task, mcpReceiptEvent(2, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptTask)},
|
|
"mismatched server": {mcpReceiptEvent(1, 2, 9, 8, 101, model.TaskTypeExec, MCPReceiptTask)},
|
|
"result gate generation mismatch": {task, mcpReceiptEvent(2, 2, 10, 7, 101, model.TaskTypeExec, MCPReceiptResult)},
|
|
"result dashboard generation mismatch": {task, mcpReceiptEvent(2, 3, 9, 7, 101, model.TaskTypeExec, MCPReceiptResult)},
|
|
}
|
|
for name, events := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
_, complete, err := matchMCPReceiptSet(events, MCPReceiptCursor{}, expectation)
|
|
if name == "missing result" {
|
|
require.NoError(t, err)
|
|
require.False(t, complete)
|
|
return
|
|
}
|
|
require.Error(t, err)
|
|
require.False(t, complete)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestMCPReceiptSet_WaitsForEventAndPreservesCursorGeneration(t *testing.T) {
|
|
dashboard := newWaiterDashboard()
|
|
dashboard.eventGeneration = 2
|
|
dashboard.mcpReceiptEvents = []MCPReceiptEvent{
|
|
mcpReceiptEvent(1, 1, 8, 7, 100, model.TaskTypeExec, MCPReceiptTask),
|
|
mcpReceiptEvent(2, 1, 8, 7, 100, model.TaskTypeExec, MCPReceiptResult),
|
|
}
|
|
dashboard.mcpReceiptSequence = 2
|
|
cursor := dashboard.MCPReceiptCursor()
|
|
result := make(chan struct {
|
|
pairs []MCPReceiptPair
|
|
err error
|
|
}, 1)
|
|
ctx, cancel := context.WithTimeout(t.Context(), time.Second)
|
|
defer cancel()
|
|
go func() {
|
|
pairs, err := dashboard.WaitForMCPReceiptSet(ctx, cursor, []MCPReceiptExpectation{{DashboardGeneration: 2, GateGeneration: 9, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec}})
|
|
result <- struct {
|
|
pairs []MCPReceiptPair
|
|
err error
|
|
}{pairs: pairs, err: err}
|
|
}()
|
|
|
|
dashboard.eventMu.Lock()
|
|
dashboard.mcpReceiptEvents = append(dashboard.mcpReceiptEvents,
|
|
mcpReceiptEvent(3, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptTask),
|
|
mcpReceiptEvent(4, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptResult),
|
|
)
|
|
dashboard.mcpReceiptSequence = 4
|
|
close(dashboard.eventNotify)
|
|
dashboard.eventNotify = make(chan struct{})
|
|
dashboard.eventMu.Unlock()
|
|
|
|
received := <-result
|
|
require.NoError(t, received.err)
|
|
require.Len(t, received.pairs, 1)
|
|
require.Equal(t, uint64(101), received.pairs[0].Task.TaskID)
|
|
require.Equal(t, uint64(2), received.pairs[0].Task.DashboardGeneration)
|
|
require.Equal(t, uint64(9), received.pairs[0].Task.GateGeneration)
|
|
}
|
|
|
|
func TestMCPReceiptSet_RespectsCancellationAndDeadline(t *testing.T) {
|
|
dashboard := newWaiterDashboard()
|
|
expectations := []MCPReceiptExpectation{{DashboardGeneration: 2, GateGeneration: 9, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec}}
|
|
|
|
cancelled, cancel := context.WithCancel(t.Context())
|
|
cancel()
|
|
_, err := dashboard.WaitForMCPReceiptSet(cancelled, MCPReceiptCursor{}, expectations)
|
|
require.ErrorIs(t, err, context.Canceled)
|
|
|
|
expired, expire := context.WithDeadline(t.Context(), time.Now())
|
|
defer expire()
|
|
_, err = dashboard.WaitForMCPReceiptSet(expired, MCPReceiptCursor{}, expectations)
|
|
require.ErrorIs(t, err, context.DeadlineExceeded)
|
|
}
|
|
|
|
func TestMCPReceiptSet_RejectsDuplicateExpectations(t *testing.T) {
|
|
_, err := (&Dashboard{}).WaitForMCPReceiptSet(t.Context(), MCPReceiptCursor{}, []MCPReceiptExpectation{
|
|
{DashboardGeneration: 2, GateGeneration: 9, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec},
|
|
{DashboardGeneration: 2, GateGeneration: 9, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec},
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMCPReceiptSet_RejectsWrongExpectedGenerationAndTaskID(t *testing.T) {
|
|
// Given
|
|
events := []MCPReceiptEvent{
|
|
mcpReceiptEvent(1, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptTask),
|
|
mcpReceiptEvent(2, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptResult),
|
|
}
|
|
expectation := MCPReceiptExpectation{DashboardGeneration: 3, GateGeneration: 9, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec}
|
|
|
|
// When
|
|
_, _, err := matchMCPReceiptSet(events, MCPReceiptCursor{}, []MCPReceiptExpectation{expectation})
|
|
|
|
// Then
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMCPReceiptSet_RejectsZeroGateGenerationExpectation(t *testing.T) {
|
|
// Given
|
|
dashboard := newWaiterDashboard()
|
|
dashboard.eventGeneration = 2
|
|
dashboard.mcpReceiptEvents = []MCPReceiptEvent{
|
|
mcpReceiptEvent(1, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptTask),
|
|
mcpReceiptEvent(2, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptResult),
|
|
}
|
|
expectation := []MCPReceiptExpectation{{DashboardGeneration: 2, GateGeneration: 0, ServerID: 7, TaskID: 101, TaskType: model.TaskTypeExec}}
|
|
|
|
// When
|
|
_, err := dashboard.WaitForMCPReceiptSet(t.Context(), MCPReceiptCursor{}, expectation)
|
|
|
|
// Then
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestMCPReceiptSet_RejectsZeroTaskIDExpectation(t *testing.T) {
|
|
// Given
|
|
dashboard := newWaiterDashboard()
|
|
dashboard.eventGeneration = 2
|
|
dashboard.mcpReceiptEvents = []MCPReceiptEvent{
|
|
mcpReceiptEvent(1, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptTask),
|
|
mcpReceiptEvent(2, 2, 9, 7, 101, model.TaskTypeExec, MCPReceiptResult),
|
|
}
|
|
expectation := []MCPReceiptExpectation{{DashboardGeneration: 2, GateGeneration: 9, ServerID: 7, TaskID: 0, TaskType: model.TaskTypeExec}}
|
|
|
|
// When
|
|
_, err := dashboard.WaitForMCPReceiptSet(t.Context(), MCPReceiptCursor{}, expectation)
|
|
|
|
// Then
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func mcpReceiptEvent(sequence, dashboardGeneration, gateGeneration, serverID, taskID, taskType uint64, kind MCPReceiptKind) MCPReceiptEvent {
|
|
return MCPReceiptEvent{Sequence: sequence, DashboardGeneration: dashboardGeneration, GateGeneration: gateGeneration, ServerID: serverID, TaskID: taskID, TaskType: taskType, Kind: kind}
|
|
}
|