mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 09:40:12 +00:00
compareSemver fell back to lexical string ordering when the numeric
segments were equal, so an agent reporting "2.1.0" compared as older than
the gate constant "v2.1.0" (0x32 < 0x76). This wrongly flagged every agent
as unsupported_agent, breaking server.exec and fs.* on all online servers.
Drop the fallback: semverParts already strips the v prefix, so equal
numeric segments mean equal versions.
The error was further masked because tool error responses shipped a
structuredContent {error_code,error} that violates the tool outputSchema
(which requires exit_code/stdout/...). Strict MCP clients validated it and
rejected the whole response with -32602, hiding the real isError text.
Omit structuredContent on error; the cause stays in content[].text.
78 lines
3.5 KiB
Go
78 lines
3.5 KiB
Go
package controller
|
||
|
||
import (
|
||
"errors"
|
||
"testing"
|
||
|
||
"github.com/stretchr/testify/require"
|
||
|
||
"github.com/nezhahq/nezha/model"
|
||
)
|
||
|
||
// 旧 agent 不识别 TaskTypeExec/TaskTypeFs* 等新 task type,会走 default
|
||
// 分支不回 TaskResult;dashboard 必须在调 CallAgent 之前依据 Host.Version
|
||
// 快速失败,否则用户要等到 30s/24h timeout 才知道 agent 不支持。
|
||
// MinServerTransferAgentVersion 已为同类问题在 transfer 路径上确立了
|
||
// release-time 必填的版本下限——这里把 MCP 也纳入同一不变量。
|
||
func TestMCPMinAgentVersionIsPinnedToRelease(t *testing.T) {
|
||
require.NotEmpty(t, MCPMinAgentVersion,
|
||
"MCPMinAgentVersion must be set to the lowest agent build that ships MCP handlers; an empty string disables the gate and lets old agents hang dashboard requests until timeout")
|
||
}
|
||
|
||
func TestRequireAgentSupportsMCPRejectsBelowMinVersion(t *testing.T) {
|
||
old := &model.Server{Host: &model.Host{Version: "v0.0.1"}}
|
||
err := requireAgentSupportsMCP(old)
|
||
require.Error(t, err, "agents older than MCPMinAgentVersion must be rejected before CallAgent")
|
||
require.True(t, errors.Is(err, errMCPUnsupported) || err.Error() == errMCPUnsupported.Error(),
|
||
"expected the errMCPUnsupported sentinel, got %v", err)
|
||
}
|
||
|
||
func TestRequireAgentSupportsMCPAcceptsCurrentVersion(t *testing.T) {
|
||
current := &model.Server{Host: &model.Host{Version: MCPMinAgentVersion}}
|
||
require.NoError(t, requireAgentSupportsMCP(current),
|
||
"server reporting exactly MCPMinAgentVersion must be accepted")
|
||
}
|
||
|
||
// 钉住「最近一个不带 MCP handler 的已发布 agent tag (v2.0.4) 必须被拒绝」。
|
||
// v2.0.4 的 model/task.go 还没有 TaskTypeExec/TaskTypeFs* 常量,cmd/agent/
|
||
// mcp_handlers.go 也不存在;如果版本门槛把它放行,dashboard 调 MCP tool
|
||
// 后 agent 会走 default 分支不回 TaskResult,CallAgent 必须等 30s 超时。
|
||
func TestRequireAgentSupportsMCPRejectsLastReleaseWithoutMCP(t *testing.T) {
|
||
noMCP := &model.Server{Host: &model.Host{Version: "v2.0.4"}}
|
||
err := requireAgentSupportsMCP(noMCP)
|
||
require.Error(t, err,
|
||
"v2.0.4 is the latest released agent tag that ships *without* MCP handlers; bumping MCPMinAgentVersion below the first MCP release re-introduces the silent-timeout bug")
|
||
require.True(t, errors.Is(err, errMCPUnsupported) || err.Error() == errMCPUnsupported.Error(),
|
||
"expected the errMCPUnsupported sentinel, got %v", err)
|
||
}
|
||
|
||
func TestRequireAgentSupportsMCPDefersWhenAgentNeverReported(t *testing.T) {
|
||
require.NoError(t, requireAgentSupportsMCP(&model.Server{Host: nil}),
|
||
"Host==nil means agent never reported its build; defer the version decision to the CallAgent timeout layer")
|
||
}
|
||
|
||
func TestCompareSemverIgnoresVPrefixMismatch(t *testing.T) {
|
||
cases := []struct {
|
||
a, b string
|
||
want int
|
||
}{
|
||
{"2.1.0", "v2.1.0", 0},
|
||
{"v2.1.0", "2.1.0", 0},
|
||
{"2.1.0", "2.1.0", 0},
|
||
{"2.1.0", "v2.1.1", -1},
|
||
{"v2.1.2", "2.1.0", 1},
|
||
{"2.2.0", "v2.1.9", 1},
|
||
}
|
||
for _, c := range cases {
|
||
require.Equalf(t, c.want, compareSemver(c.a, c.b),
|
||
"compareSemver(%q,%q): the only difference is the optional 'v' prefix and/or numeric ordering; "+
|
||
"a bare lexical fallback wrongly orders %q < %q", c.a, c.b, c.a, c.b)
|
||
}
|
||
}
|
||
|
||
func TestRequireAgentSupportsMCPAcceptsBarePrefixReport(t *testing.T) {
|
||
agent := &model.Server{Host: &model.Host{Version: "2.1.0"}}
|
||
require.NoError(t, requireAgentSupportsMCP(agent),
|
||
"agents report Host.Version without the 'v' prefix (e.g. \"2.1.0\"); it must compare equal to MCPMinAgentVersion \"v2.1.0\" and be accepted")
|
||
}
|