Files
heicode-win/heicode/controller/agnet_control_plane_test.go
T

222 lines
7.2 KiB
Go

package controller
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/gin-gonic/gin"
"github.com/heicode/manager/common"
"github.com/stretchr/testify/require"
)
type agnetCreateTestEnvelope struct {
Success bool `json:"success"`
Message string `json:"message"`
Error agnetTestError `json:"error"`
}
type agnetTestError struct {
Code string `json:"code"`
Message string `json:"message"`
}
func resetAgnetControlPlaneState(t *testing.T) {
t.Helper()
agnetMu.Lock()
defer agnetMu.Unlock()
agnetDeployments = make(map[string]agnetDeploymentRecord)
agnetEvents = make(map[string][]agnetEvent)
agnetSnapshots = make(map[string][]agnetSKSnapshot)
}
func baseAgnetResourceGrantPlan() agnetOrchestrationPlan {
return agnetOrchestrationPlan{
IntentID: "intent-resource-grant",
TemplateHint: "manager-resource-binding",
Objective: "Grant bounded resources to a child Agnet role",
RiskLevel: agnetRiskLow,
Budget: agnetBudget{
MaxTokens: 10000,
MaxCostUSD: 10,
MaxDurationSec: 3600,
},
Agents: []agnetAgentPlan{
{
RoleTemplate: "builder",
Goal: "Use granted resources without receiving plaintext credentials",
DefaultModelID: "gpt-resource-test",
ResourceGrants: []agnetResourceGrant{
{
GrantID: "grant-git-builder",
ResourceID: "res-git-main",
ResourceType: agnetResourceGit,
TenantID: "tenant-p1",
ProjectID: "project-p1",
TargetRole: "builder",
TargetAgentRef: "agent-builder-1",
PermissionScope: []string{"repo:read", "repo:write:feature-branches"},
Constraints: map[string]string{
"allowed_paths": "src/**,docs/**",
"ref": "main",
},
Metadata: map[string]string{
"provider": "gitee",
"repo_url": "https://example.invalid/acme/project.git",
},
Status: agnetGrantStatusActive,
SecretRef: "vault://tenant-p1/project-p1/git/res-git-main",
Audit: map[string]string{
"created_by": "manager-test",
},
},
{
GrantID: "grant-doc-builder",
ResourceID: "res-doc-plan",
ResourceType: agnetResourceProjectDoc,
TenantID: "tenant-p1",
ProjectID: "project-p1",
TargetRole: "builder",
TargetAgentRef: "agent-builder-1",
PermissionScope: []string{"doc:read"},
Metadata: map[string]string{
"doc_ref": "docs/heicode.md",
},
Status: agnetGrantStatusActive,
},
},
},
},
Constraints: agnetConstraints{AllowedModelIDs: []string{"gpt-resource-test"}},
Metadata: agnetMetadata{
TenantID: "tenant-p1",
ProjectID: "project-p1",
CorrelationID: "corr-p1-resource-grant",
},
}
}
func postAgnetCreateDeployment(t *testing.T, plan agnetOrchestrationPlan) (*httptest.ResponseRecorder, agnetCreateTestEnvelope) {
t.Helper()
gin.SetMode(gin.TestMode)
body, err := common.Marshal(agnetDeploymentRequest{Plan: plan})
require.NoError(t, err)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments", strings.NewReader(string(body)))
ctx.Request.Header.Set("Content-Type", "application/json")
ctx.Request.Header.Set("X-Tenant-Id", plan.Metadata.TenantID)
AgnetCreateDeployment(ctx)
var envelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope))
return recorder, envelope
}
func TestAgnetCreateDeploymentAcceptsP1ResourceGrantModel(t *testing.T) {
resetAgnetControlPlaneState(t)
recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan())
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
agnetMu.RLock()
require.Len(t, agnetDeployments, 1)
var stored agnetDeploymentRecord
for _, record := range agnetDeployments {
stored = record
}
agnetMu.RUnlock()
grants := stored.Plan.Agents[0].ResourceGrants
require.Len(t, grants, 2)
require.Equal(t, agnetResourceGit, grants[0].ResourceType)
require.Equal(t, "tenant-p1", grants[0].TenantID)
require.Equal(t, "project-p1", grants[0].ProjectID)
require.Equal(t, "builder", grants[0].TargetRole)
require.Equal(t, "agent-builder-1", grants[0].TargetAgentRef)
require.Equal(t, "vault://tenant-p1/project-p1/git/res-git-main", grants[0].SecretRef)
require.Empty(t, grants[1].SecretRef, "project document grants should not require credential material")
}
func TestAgnetCreateDeploymentRejectsPlaintextResourceGrantCredentialFields(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.Agents[0].ResourceGrants[0].Metadata["access_token"] = "must-not-be-stored"
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "RESOURCE_GRANT_SECRET_REJECTED", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetCreateDeploymentRejectsResourceGrantWithoutSecretRef(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.Agents[0].ResourceGrants[0].SecretRef = ""
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "RESOURCE_GRANT_SECRET_REF_REQUIRED", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetCreateDeploymentRejectsCrossTenantResourceGrant(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.Agents[0].ResourceGrants[0].TenantID = "tenant-other"
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "RESOURCE_GRANT_INVALID", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetDeploymentLogsAndMetricsExposeRedactedReadiness(t *testing.T) {
resetAgnetControlPlaneState(t)
_, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan())
require.True(t, envelope.Success)
agnetMu.RLock()
var deploymentID string
for id := range agnetDeployments {
deploymentID = id
break
}
agnetMu.RUnlock()
require.NotEmpty(t, deploymentID)
logRecorder := httptest.NewRecorder()
logCtx, _ := gin.CreateTestContext(logRecorder)
logCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID+"/logs", nil)
logCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
AgnetListDeploymentLogs(logCtx)
require.Equal(t, http.StatusOK, logRecorder.Code)
require.Contains(t, logRecorder.Body.String(), `"redacted":true`)
require.NotContains(t, strings.ToLower(logRecorder.Body.String()), "password")
require.NotContains(t, strings.ToLower(logRecorder.Body.String()), "token")
metricRecorder := httptest.NewRecorder()
metricCtx, _ := gin.CreateTestContext(metricRecorder)
metricCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID+"/metrics?window=15m&step=60s", nil)
metricCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
AgnetGetDeploymentMetrics(metricCtx)
require.Equal(t, http.StatusOK, metricRecorder.Code)
require.Contains(t, metricRecorder.Body.String(), `"deployment_id":"`+deploymentID+`"`)
require.Contains(t, metricRecorder.Body.String(), `"platform_estimated":true`)
}