Files
heicode-mananger/heicode/controller/agent_role_template_test.go
T
chenchenandClaude Opus 4.8 0fe1d20d67 feat(agent): unify agnet→agent and implement client/runtime unification spec v0.1 core
按桌面客户端统一方案 v0.1 + agent_management Sub Mode Runtime 对接,强制全量统一,不留兼容。

命名统一(强制,无兼容):
- 全仓 agnet/Agnet/AGNET → agent/Agent/AGENT:后端 Go(路由 /api/agent/*、env AGENT_*、
  结构体/函数、19 个文件改名)、前端(agent-console/agent-hub、/api/agent 调用、i18n)、
  DB(表 agent_*、列 agent_id)、compose/.env、文档、脚本。
- DB 加幂等迁移 renameAgnetTablesToAgent():启动时 rename 老 agnet_* 表/列,保住生产数据。

统一方案核心(10 项):
- callback 统一 /api/agent/callbacks/runtime-events(路由/广播URL/函数名)。
- artifact 兜底判定改用 Runtime 权威信号 metadata.synthesized(§7.2)+ 结构化 artifact_type。
- Manager→Runtime 路径对齐 /api/agent/sub-agile/deployments(§2.2),{deployment_id} 回退 swarm_id。
- 状态裁决 display_status:Manager 唯一裁判,completed 无有效产物→needs_codegen/
  completed_without_deliverable(§10.6),接入 detail/timeline/workflow。
- GET /api/heicode/capabilities 能力发现(§6)。
- 模型策略 per_role(role_models)+ 收集 allowed_model_ids(§9)。
- resource_binding_id→secret_ref 服务端解析,客户端不再 inline secret_ref(§17.6)。
- 客户端统一路由层 /api/heicode/sub-agile|swarm/*(task≡deployment,复用控制面)+ workflow 投影。
- 日志分层 user_logs/debug_logs(§13)。

验证:go build ./... + go test(controller/router/model/middleware)全绿;前端 tsc -b + rsbuild build 通过。
待部署:VM .env 的 AGNET_*→AGENT_*;启动迁移自动 rename 表;其他三仓库需同步切到 /api/agent。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 23:45:10 +08:00

101 lines
3.2 KiB
Go

package controller
import (
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
"github.com/heicode/manager/common"
)
func TestAgentRoleTemplates_CanonicalSetCovered(t *testing.T) {
// Pins the six canonical role keys from docs §13.3.3. Any code
// change that adds, removes or renames a key MUST update this
// list — guards against accidental drift between Manager and
// the product spec.
want := []string{"product", "architect", "frontend", "backend", "reviewer", "ops"}
got := agentRoleTemplates()
if len(got) != len(want) {
t.Fatalf("expected %d roles, got %d", len(want), len(got))
}
for i, key := range want {
if got[i].Key != key {
t.Errorf("position %d: want key %q, got %q", i, key, got[i].Key)
}
// Display + summary populated; never returning a blank-row
// in the picker.
if strings.TrimSpace(got[i].DisplayName) == "" {
t.Errorf("role %q has empty DisplayName", key)
}
if strings.TrimSpace(got[i].Summary) == "" {
t.Errorf("role %q has empty Summary", key)
}
if len(got[i].DefaultPermissions) == 0 {
t.Errorf("role %q has empty DefaultPermissions", key)
}
}
}
func TestAgentRoleTemplates_RiskLevels(t *testing.T) {
// Ops is the only canonical role with high risk (production
// deployment intent). Reviewer + Product + Architect stay low
// (read-mostly). Frontend + Backend land at medium. Pins the
// product-doc default risk classification so we don't silently
// flip an ops role to "low" and skip the high-risk approval
// gating downstream.
wantRisk := map[string]string{
"product": agentRiskLow,
"architect": agentRiskLow,
"reviewer": agentRiskLow,
"frontend": agentRiskMedium,
"backend": agentRiskMedium,
"ops": agentRiskHigh,
}
for _, tpl := range agentRoleTemplates() {
if want, ok := wantRisk[tpl.Key]; ok && tpl.RiskLevel != want {
t.Errorf("role %q: want risk %q, got %q", tpl.Key, want, tpl.RiskLevel)
}
}
}
func TestAgentListRoleTemplates_HTTPShape(t *testing.T) {
gin.SetMode(gin.TestMode)
rec := httptest.NewRecorder()
c, _ := gin.CreateTestContext(rec)
c.Request = httptest.NewRequest("GET", "/api/agent/role-templates", nil)
c.Set("id", 1)
c.Set("role", common.RoleCommonUser)
AgentListRoleTemplates(c)
if rec.Code != 200 {
t.Fatalf("expected 200, got %d body=%s", rec.Code, rec.Body.String())
}
body := rec.Body.String()
// Stable keys the frontend depends on — checked via substring
// rather than parsing so we don't have to spin up a typed
// envelope just for this guard.
for _, want := range []string{`"items"`, `"total"`, `"product"`, `"ops"`, `"display_name"`} {
if !strings.Contains(body, want) {
t.Errorf("response missing %q. body=%s", want, body)
}
}
}
func TestAgentIsCanonicalRoleKey(t *testing.T) {
// Defensive helper currently used as advisory — pins the closed
// set so the future tighten-up to closed-set validation is one
// flip instead of an open-ended audit.
for _, ok := range []string{"product", "architect", "frontend", "backend", "reviewer", "ops"} {
if !agentIsCanonicalRoleKey(ok) {
t.Errorf("%q should be canonical", ok)
}
}
for _, bad := range []string{"", "debugger", "executor", "random_string"} {
if agentIsCanonicalRoleKey(bad) {
t.Errorf("%q should NOT be canonical", bad)
}
}
}