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

1568 lines
62 KiB
Go

package controller
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"net/http"
"net/http/httptest"
"strconv"
"strings"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"github.com/heicode/manager/common"
"github.com/heicode/manager/model"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
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)
agnetSnapshots = make(map[string][]agnetSKSnapshot)
// Audit rows live in DB now; tests use a fresh SQLite per case
// (setupTokenControllerTestDB style) so there is no global map
// to flush here.
}
func setupAgnetControlPlaneTestDB(t *testing.T) *gorm.DB {
t.Helper()
gin.SetMode(gin.TestMode)
common.UsingSQLite = true
common.UsingMySQL = false
common.UsingPostgreSQL = false
common.RedisEnabled = false
dsn := fmt.Sprintf("file:%s?mode=memory&cache=shared", strings.ReplaceAll(t.Name(), "/", "_"))
db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
require.NoError(t, err)
model.DB = db
model.LOG_DB = db
require.NoError(t, db.AutoMigrate(
&model.ResourceBinding{},
&model.ResourceGrant{},
&model.AgnetAuditEvent{},
&model.AgnetDeployment{},
&model.AgnetCallbackEvent{},
&model.AgnetArtifact{},
&model.AgnetSKSnapshot{},
&model.AgnetApprovalRequest{},
))
t.Cleanup(func() {
if model.DB == db {
model.DB = nil
}
if model.LOG_DB == db {
model.LOG_DB = nil
}
sqlDB, err := db.DB()
if err == nil {
_ = sqlDB.Close()
}
})
return db
}
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,
},
UserContext: agnetUserContext{
UserID: "user-p1",
Email: "builder@example.invalid",
Role: "user",
ChannelID: "channel-p1",
},
BillingContext: agnetBillingContext{
Provider: "newapi",
NewAPIUserRef: "newapi-user-p1",
NewAPIGroup: "development",
QuotaRef: "newapi-quota-ref-p1",
},
AgentRuntime: agnetAgentRuntime{
Platform: "agnet",
Agents: []agnetRuntimeAgent{
{
Role: "builder",
ModelRef: "agnet-model-profile-builder",
InstanceCount: 1,
},
},
},
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,
UserID: "user-p1",
BindingScope: "https://example.invalid/acme/project.git#main",
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: "azkv://heicode-kv.vault.azure.net/secrets/user-p1-project-main-res-git-main",
Audit: map[string]string{
"created_by": "manager-test",
},
},
{
GrantID: "grant-doc-builder",
ResourceID: "res-doc-plan",
ResourceType: agnetResourceProjectDoc,
UserID: "user-p1",
BindingScope: "docs/heicode.md",
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{
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")
AgnetCreateDeployment(ctx)
var envelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope))
return recorder, envelope
}
func postAgnetCreateUserDeployment(t *testing.T, userID int, 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.Set("id", userID)
ctx.Set("group", "development")
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments", strings.NewReader(string(body)))
ctx.Request.Header.Set("Content-Type", "application/json")
AgnetCreateUserDeployment(ctx)
var envelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope))
return recorder, envelope
}
func TestAgnetDeploymentSurvivesInProcessStateReset(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan())
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
require.NotEmpty(t, deploymentID)
var stored model.AgnetDeployment
require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error)
require.Equal(t, "user-p1", stored.UserID)
require.Equal(t, "channel-p1", stored.ChannelID)
require.Contains(t, stored.PayloadJSON, `"orchestration_plan"`)
require.Contains(t, stored.PermissionManifestJSON, `"grant-git-builder"`)
require.Contains(t, stored.PermissionManifestJSON, `"secret_ref"`)
require.NotContains(t, strings.ToLower(stored.PayloadJSON), "password")
require.NotContains(t, strings.ToLower(stored.PayloadJSON), "private_key")
require.NotContains(t, strings.ToLower(stored.PermissionManifestJSON), "access_token")
resetAgnetControlPlaneState(t)
getRecorder := httptest.NewRecorder()
getCtx, _ := gin.CreateTestContext(getRecorder)
getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil)
AgnetGetDeployment(getCtx)
var getBody map[string]any
require.NoError(t, common.Unmarshal(getRecorder.Body.Bytes(), &getBody))
require.Equal(t, true, getBody["success"])
data := getBody["data"].(map[string]any)
require.Equal(t, deploymentID, data["deployment_id"])
require.Equal(t, "accepted", data["status"])
listRecorder := httptest.NewRecorder()
listCtx, _ := gin.CreateTestContext(listRecorder)
listCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments?user_id=user-p1", nil)
AgnetListDeployments(listCtx)
var listBody map[string]any
require.NoError(t, common.Unmarshal(listRecorder.Body.Bytes(), &listBody))
require.Equal(t, true, listBody["success"])
listData := listBody["data"].(map[string]any)
require.Equal(t, float64(1), listData["total"])
}
func TestAgnetDeploymentPersistsSubMode(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.SubMode = "waterfall"
recorder, envelope := postAgnetCreateDeployment(t, plan)
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
require.Contains(t, recorder.Body.String(), `"sub_mode":"waterfall"`)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
var stored model.AgnetDeployment
require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error)
require.Equal(t, "waterfall", stored.SubMode)
resetAgnetControlPlaneState(t)
getRecorder := httptest.NewRecorder()
getCtx, _ := gin.CreateTestContext(getRecorder)
getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil)
AgnetGetDeployment(getCtx)
require.Equal(t, http.StatusOK, getRecorder.Code)
require.Contains(t, getRecorder.Body.String(), `"sub_mode":"waterfall"`)
}
func TestAgnetDeploymentDefaultsSubModeToAgile(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.SubMode = ""
recorder, envelope := postAgnetCreateDeployment(t, plan)
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
require.Contains(t, recorder.Body.String(), `"sub_mode":"agile"`)
}
func TestAgnetRuntimeHealthNotConfigured(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
t.Setenv("AGNET_RUNTIME_BASE_URL", "")
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/runtime/health", nil)
AgnetRuntimeHealth(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.Contains(t, recorder.Body.String(), `"status":"not_configured"`)
require.Contains(t, recorder.Body.String(), `"configured":false`)
}
func TestAgnetRuntimeHealthProxy(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/api/agnet/health", r.URL.Path)
require.Equal(t, "Bearer service-token", r.Header.Get("Authorization"))
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"success":true,"data":{"status":"healthy"}}`))
}))
defer server.Close()
t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL)
t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token")
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/runtime/health", nil)
AgnetRuntimeHealth(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.Contains(t, recorder.Body.String(), `"status":"healthy"`)
require.Contains(t, recorder.Body.String(), `"http_status":200`)
require.Contains(t, recorder.Body.String(), `"remote"`)
}
func TestAgnetRuntimeShadowCreateStoresRuntimeMapping(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "/api/agnet/deployments", r.URL.Path)
require.Equal(t, http.MethodPost, r.Method)
require.Equal(t, "Bearer service-token", r.Header.Get("Authorization"))
require.NotEmpty(t, r.Header.Get("X-Correlation-ID"))
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
require.Contains(t, string(body), `"sub_mode":"agile"`)
require.Contains(t, string(body), `"manager_deployment_id"`)
var runtimeBody map[string]any
require.NoError(t, common.Unmarshal(body, &runtimeBody))
agents := runtimeBody["agents"].([]any)
require.Equal(t, "builder", agents[0].(map[string]any)["role"])
metadata := runtimeBody["metadata"].(map[string]any)
require.NotEmpty(t, metadata["manager_deployment_id"])
grants := runtimeBody["resource_grants"].([]any)
require.NotEmpty(t, grants)
require.NotEmpty(t, grants[0].(map[string]any)["ref"])
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"success":true,"data":{"deployment_id":"runtime-dep-1","swarm_id":"swarm-1","runtime_status":"accepted"}}`))
}))
defer server.Close()
t.Setenv("AGNET_RUNTIME_ENABLED", "true")
t.Setenv("AGNET_RUNTIME_ASYNC", "false")
t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL)
t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token")
plan := baseAgnetResourceGrantPlan()
plan.SubMode = "agile"
recorder, envelope := postAgnetCreateDeployment(t, plan)
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
require.Contains(t, recorder.Body.String(), `"runtime_deployment_id":"runtime-dep-1"`)
require.Contains(t, recorder.Body.String(), `"runtime_swarm_id":"swarm-1"`)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
var stored model.AgnetDeployment
require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error)
require.Equal(t, "runtime-dep-1", stored.RuntimeDeploymentID)
require.Equal(t, "swarm-1", stored.RuntimeSwarmID)
require.Equal(t, "accepted", stored.RuntimeState)
}
func TestAgnetRuntimeShadowCreateFailureDoesNotFailLocalDeployment(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "runtime unavailable", http.StatusBadGateway)
}))
defer server.Close()
t.Setenv("AGNET_RUNTIME_ENABLED", "true")
t.Setenv("AGNET_RUNTIME_ASYNC", "false")
t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL)
recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan())
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
require.Contains(t, recorder.Body.String(), `"runtime_state":"runtime_sync_failed"`)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
var stored model.AgnetDeployment
require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error)
require.Equal(t, agnetRuntimeStateFailed, stored.RuntimeState)
require.Contains(t, stored.FailureReason, "HTTP 502")
}
func TestAgnetRuntimeShadowCreateTreatsSuccessFalseAsFailure(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"success":false,"error":{"code":"POLICY_REJECTED","message":"sub mode rejected"}}`))
}))
defer server.Close()
t.Setenv("AGNET_RUNTIME_ENABLED", "true")
t.Setenv("AGNET_RUNTIME_ASYNC", "false")
t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL)
recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan())
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
var stored model.AgnetDeployment
require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error)
require.Equal(t, agnetRuntimeStateFailed, stored.RuntimeState)
require.Contains(t, stored.FailureReason, "sub mode rejected")
}
func TestAgnetCreateDeploymentRejectsInvalidSubMode(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.SubMode = "scrum"
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "POLICY_REJECTED", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetCreateDeploymentRejectsNonAzureSecretRef(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.Agents[0].ResourceGrants[0].SecretRef = "vault:heicode/repo-main"
recorder, envelope := postAgnetCreateDeployment(t, plan)
require.Equal(t, http.StatusOK, recorder.Code)
require.False(t, envelope.Success)
require.Equal(t, "SECRET_REF_INVALID", envelope.Error.Code)
}
func TestAgnetCreateDeploymentAcceptsTopLevelResourceGrants(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.ResourceGrants = plan.Agents[0].ResourceGrants
plan.Agents[0].ResourceGrants = nil
recorder, envelope := postAgnetCreateDeployment(t, plan)
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
require.Contains(t, recorder.Body.String(), `"resource_grants":[`)
require.Contains(t, recorder.Body.String(), `"grant_id":"grant-git-builder"`)
require.Contains(t, recorder.Body.String(), `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/user-p1-project-main-res-git-main"`)
}
func TestAgnetUserDeploymentForcesAuthenticatedUserScope(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = ""
plan.UserContext.ChannelID = ""
for agentIdx := range plan.Agents {
for grantIdx := range plan.Agents[agentIdx].ResourceGrants {
plan.Agents[agentIdx].ResourceGrants[grantIdx].UserID = ""
}
}
recorder, envelope := postAgnetCreateUserDeployment(t, 7, plan)
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
var stored model.AgnetDeployment
require.NoError(t, db.Where("deployment_id = ?", deploymentID).First(&stored).Error)
require.Equal(t, "7", stored.UserID)
require.Equal(t, "development", stored.ChannelID)
require.Contains(t, stored.PlanJSON, `"user_id":"7"`)
require.NotContains(t, stored.PlanJSON, `"user_id":"user-p1"`)
}
func TestAgnetUserDeploymentRejectsMismatchedUserScope(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "8"
_, envelope := postAgnetCreateUserDeployment(t, 7, plan)
require.False(t, envelope.Success)
require.Equal(t, "USER_CONTEXT_FORBIDDEN", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetUserDeploymentListIsScopedToAuthenticatedUser(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
user7Plan := baseAgnetResourceGrantPlan()
user7Plan.UserContext.UserID = "7"
user7Plan.Metadata.CorrelationID = "corr-user-7"
for idx := range user7Plan.Agents[0].ResourceGrants {
user7Plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
_, user7Envelope := postAgnetCreateDeployment(t, user7Plan)
require.True(t, user7Envelope.Success)
user8Plan := baseAgnetResourceGrantPlan()
user8Plan.UserContext.UserID = "8"
user8Plan.Metadata.CorrelationID = "corr-user-8"
for idx := range user8Plan.Agents[0].ResourceGrants {
user8Plan.Agents[0].ResourceGrants[idx].UserID = "8"
}
_, user8Envelope := postAgnetCreateDeployment(t, user8Plan)
require.True(t, user8Envelope.Success)
listRecorder := httptest.NewRecorder()
listCtx, _ := gin.CreateTestContext(listRecorder)
listCtx.Set("id", 7)
listCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments", nil)
AgnetListUserDeployments(listCtx)
require.Equal(t, http.StatusOK, listRecorder.Code)
var listBody map[string]any
require.NoError(t, common.Unmarshal(listRecorder.Body.Bytes(), &listBody))
require.Equal(t, true, listBody["success"])
listData := listBody["data"].(map[string]any)
require.Equal(t, float64(1), listData["total"])
items := listData["items"].([]any)
item := items[0].(map[string]any)
planData := item["orchestration_plan"].(map[string]any)
userContext := planData["user_context"].(map[string]any)
require.Equal(t, "7", userContext["user_id"])
}
func TestAgnetTaskDeploymentDraftBuildsSafePlan(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
body := `{
"task":{"id":"task_bridge_1","name":"Ship Manager sub mode","intent":"Need a scoped implementation","status":"running","card":{"goal":"Implement the Manager-side sub task flow"}},
"sub_mode":"waterfall",
"binding_scope":"repo-main",
"role_templates":["backend"],
"default_model_id":"agnet-model-builder",
"budget":{"max_tokens":10000,"max_cost_usd":2,"max_duration_sec":900}
}`
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Set("id", 7)
ctx.Set("group", "development")
ctx.Params = gin.Params{{Key: "task_id", Value: "task_bridge_1"}}
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/tasks/task_bridge_1/deployment-draft", strings.NewReader(body))
ctx.Request.Header.Set("Content-Type", "application/json")
AgnetCreateTaskDeploymentDraft(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.NotContains(t, strings.ToLower(recorder.Body.String()), "password")
require.NotContains(t, strings.ToLower(recorder.Body.String()), "access_token")
var env map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &env))
require.Equal(t, true, env["success"])
data := env["data"].(map[string]any)
plan := data["orchestration_plan"].(map[string]any)
require.Equal(t, "task_bridge_1", plan["intent_id"])
require.Equal(t, "waterfall", plan["sub_mode"])
require.Equal(t, "Implement the Manager-side sub task flow", plan["objective"])
userContext := plan["user_context"].(map[string]any)
require.Equal(t, "7", userContext["user_id"])
agents := plan["agents"].([]any)
agent := agents[0].(map[string]any)
grants := agent["resource_grants"].([]any)
grant := grants[0].(map[string]any)
require.Equal(t, "project_doc", grant["resource_type"])
require.Equal(t, "7", grant["user_id"])
}
func TestAgnetTaskDeploymentDraftRequiresTaskSnapshot(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Set("id", 7)
ctx.Params = gin.Params{{Key: "task_id", Value: "task_missing"}}
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/tasks/task_missing/deployment-draft", strings.NewReader(`{"sub_mode":"agile"}`))
ctx.Request.Header.Set("Content-Type", "application/json")
AgnetCreateTaskDeploymentDraft(ctx)
var envelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope))
require.False(t, envelope.Success)
require.Equal(t, "TASK_NOT_FOUND", envelope.Error.Code)
}
func TestAgnetTaskDeploymentDraftRejectsCredentialGrantWithoutSecretRef(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
body := `{
"task":{"id":"task_bridge_git","name":"Use a git repo","intent":"Need repo access","status":"running"},
"sub_mode":"agile",
"binding_scope":"repo-main",
"role_templates":["backend"],
"default_model_id":"agnet-model-builder",
"resource_grants":[{
"resource_id":"repo-1",
"resource_type":"git",
"permission_scope":["repo:read"]
}]
}`
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Set("id", 7)
ctx.Params = gin.Params{{Key: "task_id", Value: "task_bridge_git"}}
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/tasks/task_bridge_git/deployment-draft", strings.NewReader(body))
ctx.Request.Header.Set("Content-Type", "application/json")
AgnetCreateTaskDeploymentDraft(ctx)
var envelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &envelope))
require.False(t, envelope.Success)
require.Equal(t, "RESOURCE_GRANT_SECRET_REF_REQUIRED", envelope.Error.Code)
}
func TestAgnetUserDeploymentSimulatedEventsArePersistedAndPrefixed(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.Equal(t, http.StatusOK, createRecorder.Code)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
simRecorder := httptest.NewRecorder()
simCtx, _ := gin.CreateTestContext(simRecorder)
simCtx.Set("id", 7)
simCtx.Set("role", common.RoleAdminUser)
simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{"events":["deployment.started","simulation.agent.done"]}`))
simCtx.Request.Header.Set("Content-Type", "application/json")
AgnetSimulateUserDeploymentEvents(simCtx)
require.Equal(t, http.StatusOK, simRecorder.Code)
require.Contains(t, simRecorder.Body.String(), `"simulated":true`)
require.Contains(t, simRecorder.Body.String(), `"simulation.deployment.started"`)
require.Contains(t, simRecorder.Body.String(), `"simulation.agent.done"`)
resetAgnetControlPlaneState(t)
eventsRecorder := httptest.NewRecorder()
eventsCtx, _ := gin.CreateTestContext(eventsRecorder)
eventsCtx.Set("id", 7)
eventsCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
eventsCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/events", nil)
AgnetListUserDeploymentEvents(eventsCtx)
require.Equal(t, http.StatusOK, eventsRecorder.Code)
require.Contains(t, eventsRecorder.Body.String(), `"simulation.deployment.started"`)
require.Contains(t, eventsRecorder.Body.String(), `"simulation.agent.done"`)
}
func TestAgnetUserDeploymentSimulationRejectsOtherUsersDeployment(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.Equal(t, http.StatusOK, createRecorder.Code)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
simRecorder := httptest.NewRecorder()
simCtx, _ := gin.CreateTestContext(simRecorder)
simCtx.Set("id", 8)
simCtx.Set("role", common.RoleAdminUser)
simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`))
simCtx.Request.Header.Set("Content-Type", "application/json")
AgnetSimulateUserDeploymentEvents(simCtx)
var envelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(simRecorder.Body.Bytes(), &envelope))
require.False(t, envelope.Success)
require.Equal(t, "DEPLOYMENT_FORBIDDEN", envelope.Error.Code)
}
func TestAgnetUserDeploymentSimulationIsDisabledForRegularUserByDefault(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.Equal(t, http.StatusOK, createRecorder.Code)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
simRecorder := httptest.NewRecorder()
simCtx, _ := gin.CreateTestContext(simRecorder)
simCtx.Set("id", 7)
simCtx.Set("role", common.RoleCommonUser)
simCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
simCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/user/deployments/"+deploymentID+"/simulate-events", strings.NewReader(`{}`))
simCtx.Request.Header.Set("Content-Type", "application/json")
AgnetSimulateUserDeploymentEvents(simCtx)
var envelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(simRecorder.Body.Bytes(), &envelope))
require.False(t, envelope.Success)
require.Equal(t, "SIMULATION_DISABLED", envelope.Error.Code)
}
func TestAgnetUserSwarmsAdapterCreatesScopedDeployment(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = ""
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = ""
}
body, err := common.Marshal(agnetDeploymentRequest{Plan: plan})
require.NoError(t, err)
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Set("id", 7)
ctx.Set("group", "development")
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/swarms", strings.NewReader(string(body)))
ctx.Request.Header.Set("Content-Type", "application/json")
AgnetCreateUserSwarm(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.Contains(t, recorder.Body.String(), `"swarm_id"`)
require.Contains(t, recorder.Body.String(), `"deployment_id"`)
require.Contains(t, recorder.Body.String(), `"user_id":"7"`)
}
func TestAgnetCallbackStoresEventArtifactAndIsIdempotent(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token")
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
body := fmt.Sprintf(`{
"event_id":"evt-callback-1",
"idempotency_key":"idem-callback-1",
"event_type":"artifact.created",
"deployment_id":%q,
"task_id":"task-1",
"correlation_id":"corr-p1-resource-grant",
"artifact":{
"artifact_id":"art-1",
"artifact_type":"summary",
"title":"Build summary",
"summary":"Created backend scaffold",
"uri":"artifact://task-1/summary"
}
}`, deploymentID)
for i := 0; i < 2; i++ {
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body))
ctx.Request.Header.Set("Content-Type", "application/json")
ctx.Request.Header.Set("X-Agnet-Service-Token", "callback-token")
AgnetReceiveSwarmEventCallback(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.Contains(t, recorder.Body.String(), `"success":true`)
}
var callbackCount int64
require.NoError(t, db.Model(&model.AgnetCallbackEvent{}).Where("event_id = ?", "evt-callback-1").Count(&callbackCount).Error)
require.Equal(t, int64(1), callbackCount)
var artifactCount int64
require.NoError(t, db.Model(&model.AgnetArtifact{}).Where("artifact_id = ?", "art-1").Count(&artifactCount).Error)
require.Equal(t, int64(1), artifactCount)
}
func TestAgnetCallbackAcceptsDocumentedHMACPayloadArtifact(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
t.Setenv("AGNET_CALLBACK_SIGNING_SECRET", "callback-signing-secret")
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
body := fmt.Sprintf(`{
"event_type":"artifact.created",
"deployment_id":%q,
"swarm_id":"swarm-doc-1",
"agent_instance_id":"agi-backend-1",
"occurred_at":"2026-05-26T10:40:00Z",
"payload":{
"artifact_id":"art-doc-payload-1",
"artifact_type":"code_patch",
"title":"Backend API patch",
"summary":"Patch created",
"uri":"azblob://heicode-artifacts/task-123/backend.patch",
"checksum":"sha256:abc",
"stage":"development",
"checkpoint":"artifact_ready"
}
}`, deploymentID)
eventID := "evt-doc-hmac-1"
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
mac := hmac.New(sha256.New, []byte("callback-signing-secret"))
mac.Write([]byte(timestamp + "." + eventID + "." + body))
signature := "sha256=" + hex.EncodeToString(mac.Sum(nil))
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body))
ctx.Request.Header.Set("Content-Type", "application/json")
ctx.Request.Header.Set("X-Agnet-Event-Id", eventID)
ctx.Request.Header.Set("X-Agnet-Timestamp", timestamp)
ctx.Request.Header.Set("X-Agnet-Signature", signature)
ctx.Request.Header.Set("X-Correlation-ID", "corr-doc-hmac")
AgnetReceiveSwarmEventCallback(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.Contains(t, recorder.Body.String(), `"success":true`)
var callback model.AgnetCallbackEvent
require.NoError(t, db.Where("event_id = ?", eventID).First(&callback).Error)
require.Equal(t, "swarm-doc-1", callback.SwarmID)
require.Equal(t, "agi-backend-1", callback.AgentInstanceID)
require.Equal(t, "2026-05-26T10:40:00Z", callback.OccurredAt)
var artifact model.AgnetArtifact
require.NoError(t, db.Where("artifact_id = ?", "art-doc-payload-1").First(&artifact).Error)
require.Equal(t, "code_patch", artifact.ArtifactType)
require.Contains(t, artifact.MetadataJSON, "artifact_ready")
}
func TestAgnetCallbackHMACCanUseAzureKeyVaultSigningSecretRef(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
tokenServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, "true", r.Header.Get("Metadata"))
_, _ = w.Write([]byte(`{"access_token":"manager-token","expires_in":"3600"}`))
}))
defer tokenServer.Close()
vaultServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
require.Equal(t, http.MethodGet, r.Method)
require.Equal(t, "Bearer manager-token", r.Header.Get("Authorization"))
require.Equal(t, "/secrets/callback-signing", r.URL.Path)
_, _ = w.Write([]byte(`{"value":"{\"callback_signing_secret\":\"from-kv-secret\"}"}`))
}))
defer vaultServer.Close()
t.Setenv("AZURE_KEY_VAULT_URL", vaultServer.URL)
t.Setenv("AZURE_MANAGED_IDENTITY_TOKEN_URL", tokenServer.URL)
t.Setenv("AGNET_CALLBACK_SIGNING_SECRET_REF", fmt.Sprintf("azkv://%s/secrets/callback-signing", strings.TrimPrefix(vaultServer.URL, "http://")))
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
body := fmt.Sprintf(`{"event_type":"timeline.updated","deployment_id":%q,"payload":{"title":"Ready"}}`, deploymentID)
eventID := "evt-kv-hmac-1"
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
mac := hmac.New(sha256.New, []byte("from-kv-secret"))
mac.Write([]byte(timestamp + "." + eventID + "." + body))
signature := "sha256=" + hex.EncodeToString(mac.Sum(nil))
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body))
ctx.Request.Header.Set("Content-Type", "application/json")
ctx.Request.Header.Set("X-Agnet-Event-Id", eventID)
ctx.Request.Header.Set("X-Agnet-Timestamp", timestamp)
ctx.Request.Header.Set("X-Agnet-Signature", signature)
AgnetReceiveSwarmEventCallback(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.Contains(t, recorder.Body.String(), `"success":true`)
var callback model.AgnetCallbackEvent
require.NoError(t, db.Where("event_id = ?", eventID).First(&callback).Error)
}
func TestAgnetCallbackUsesSwarmIDFallbackAndCreatesApproval(t *testing.T) {
db := setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token")
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
swarmID := "runtime-swarm-only-1"
require.NoError(t, db.Model(&model.AgnetDeployment{}).
Where("deployment_id = ?", deploymentID).
Update("runtime_swarm_id", swarmID).Error)
resetAgnetControlPlaneState(t)
body := `{
"event_id":"evt-approval-swarm-only",
"event_type":"approval.requested",
"swarm_id":"runtime-swarm-only-1",
"agent_instance_id":"agi-backend-approval",
"occurred_at":"2026-05-27T10:41:00Z",
"payload":{
"approval_id":"runtime-approval-1",
"operation":"repo.write",
"resource_id":"res-git-main",
"resource_type":"git",
"resource_scope":"heicode/",
"target_role":"builder",
"risk_level":"high",
"requires_credential":true,
"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/repo-main",
"ttl_seconds":600,
"reason":"Runtime needs write access for backend patch"
}
}`
for i := 0; i < 2; i++ {
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(body))
ctx.Request.Header.Set("Content-Type", "application/json")
ctx.Request.Header.Set("X-Agnet-Service-Token", "callback-token")
AgnetReceiveSwarmEventCallback(ctx)
require.Equal(t, http.StatusOK, recorder.Code)
require.Contains(t, recorder.Body.String(), `"success":true`)
if i == 0 {
require.Contains(t, recorder.Body.String(), `"deduplicated":false`)
} else {
require.Contains(t, recorder.Body.String(), `"deduplicated":true`)
}
}
var callback model.AgnetCallbackEvent
require.NoError(t, db.Where("event_id = ?", "evt-approval-swarm-only").First(&callback).Error)
require.Equal(t, deploymentID, callback.DeploymentID)
require.Equal(t, swarmID, callback.SwarmID)
require.Equal(t, "agi-backend-approval", callback.AgentInstanceID)
var approval model.AgnetApprovalRequest
require.NoError(t, db.Where("approval_id = ?", "runtime-approval-1").First(&approval).Error)
require.Equal(t, 7, approval.UserId)
require.Equal(t, deploymentID, approval.DeploymentID)
require.Equal(t, "pending", approval.Status)
require.Equal(t, "repo.write", approval.Operation)
require.Equal(t, "azkv://heicode-kv.vault.azure.net/secrets/repo-main", approval.SecretRef)
}
func TestAgnetCallbackRejectsPlaintextSecretsAndMissingToken(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token")
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(`{"event_id":"evt-no-token","event_type":"status.updated"}`))
ctx.Request.Header.Set("Content-Type", "application/json")
AgnetReceiveSwarmEventCallback(ctx)
var missingToken agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &missingToken))
require.False(t, missingToken.Success)
require.Equal(t, "CALLBACK_UNAUTHORIZED", missingToken.Error.Code)
secretRecorder := httptest.NewRecorder()
secretCtx, _ := gin.CreateTestContext(secretRecorder)
secretCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(`{
"event_id":"evt-secret",
"event_type":"status.updated",
"metadata":{"access_token":"must-not-store"}
}`))
secretCtx.Request.Header.Set("Content-Type", "application/json")
secretCtx.Request.Header.Set("X-Agnet-Service-Token", "callback-token")
AgnetReceiveSwarmEventCallback(secretCtx)
var secretEnvelope agnetCreateTestEnvelope
require.NoError(t, common.Unmarshal(secretRecorder.Body.Bytes(), &secretEnvelope))
require.False(t, secretEnvelope.Success)
require.Equal(t, "CALLBACK_SECRET_REJECTED", secretEnvelope.Error.Code)
}
func TestAgnetArtifactsAndTimelineReturnPersistedRelatedRecords(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
t.Setenv("AGNET_CALLBACK_TOKEN", "callback-token")
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
plan.SubMode = "waterfall"
for idx := range plan.Agents[0].ResourceGrants {
plan.Agents[0].ResourceGrants[idx].UserID = "7"
}
createRecorder, createEnvelope := postAgnetCreateUserDeployment(t, 7, plan)
require.True(t, createEnvelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(createRecorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
callbackBody := fmt.Sprintf(`{
"event_id":"evt-artifact-timeline",
"idempotency_key":"idem-artifact-timeline",
"event_type":"artifact.created",
"deployment_id":%q,
"task_id":"task-timeline",
"correlation_id":"corr-p1-resource-grant",
"artifact":{"artifact_id":"art-timeline","artifact_type":"summary","title":"Timeline artifact","summary":"Done","uri":"artifact://task-timeline/summary"}
}`, deploymentID)
callbackRecorder := httptest.NewRecorder()
callbackCtx, _ := gin.CreateTestContext(callbackRecorder)
callbackCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/callbacks/swarm-events", strings.NewReader(callbackBody))
callbackCtx.Request.Header.Set("Content-Type", "application/json")
callbackCtx.Request.Header.Set("X-Agnet-Service-Token", "callback-token")
AgnetReceiveSwarmEventCallback(callbackCtx)
require.Contains(t, callbackRecorder.Body.String(), `"success":true`)
snapshotRecorder := httptest.NewRecorder()
snapshotCtx, _ := gin.CreateTestContext(snapshotRecorder)
snapshotCtx.Set("id", 7)
snapshotCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/sk-snapshots/resolve", strings.NewReader(fmt.Sprintf(`{"deployment_id":%q}`, deploymentID)))
snapshotCtx.Request.Header.Set("Content-Type", "application/json")
AgnetResolveSKSnapshots(snapshotCtx)
require.Equal(t, http.StatusOK, snapshotRecorder.Code)
resetAgnetControlPlaneState(t)
artifactRecorder := httptest.NewRecorder()
artifactCtx, _ := gin.CreateTestContext(artifactRecorder)
artifactCtx.Set("id", 7)
artifactCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
artifactCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/artifacts", nil)
AgnetListUserDeploymentArtifacts(artifactCtx)
require.Equal(t, http.StatusOK, artifactRecorder.Code)
require.Contains(t, artifactRecorder.Body.String(), `"art-timeline"`)
require.Contains(t, artifactRecorder.Body.String(), `"deployment_id":"`+deploymentID+`"`)
require.Contains(t, artifactRecorder.Body.String(), `"artifacts":[`)
snapshotListRecorder := httptest.NewRecorder()
snapshotListCtx, _ := gin.CreateTestContext(snapshotListRecorder)
snapshotListCtx.Set("id", 7)
snapshotListCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
snapshotListCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/sk-snapshots", nil)
AgnetListUserSKSnapshots(snapshotListCtx)
require.Equal(t, http.StatusOK, snapshotListRecorder.Code)
require.Contains(t, snapshotListRecorder.Body.String(), `"deployment_id":"`+deploymentID+`"`)
require.Contains(t, snapshotListRecorder.Body.String(), `"sk_snapshots":[`)
timelineRecorder := httptest.NewRecorder()
timelineCtx, _ := gin.CreateTestContext(timelineRecorder)
timelineCtx.Set("id", 7)
timelineCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
timelineCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/user/deployments/"+deploymentID+"/timeline", nil)
AgnetGetUserDeploymentTimeline(timelineCtx)
require.Equal(t, http.StatusOK, timelineRecorder.Code)
require.Contains(t, timelineRecorder.Body.String(), `"callbacks"`)
require.Contains(t, timelineRecorder.Body.String(), `"artifacts"`)
require.Contains(t, timelineRecorder.Body.String(), `"sk_snapshots"`)
require.Contains(t, timelineRecorder.Body.String(), `"timeline"`)
require.Contains(t, timelineRecorder.Body.String(), `"deployment_id":"`+deploymentID+`"`)
require.Contains(t, timelineRecorder.Body.String(), `"event_id":"evt-artifact-timeline"`)
require.Contains(t, timelineRecorder.Body.String(), `"event_type":"artifact.created"`)
}
func TestAgnetStopDeploymentPersistsState(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan())
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
stopRecorder := httptest.NewRecorder()
stopCtx, _ := gin.CreateTestContext(stopRecorder)
stopCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments/"+deploymentID+"/stop", nil)
AgnetStopDeployment(stopCtx)
require.Equal(t, http.StatusOK, stopRecorder.Code)
require.Contains(t, stopRecorder.Body.String(), `"status":"stopped"`)
resetAgnetControlPlaneState(t)
getRecorder := httptest.NewRecorder()
getCtx, _ := gin.CreateTestContext(getRecorder)
getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil)
AgnetGetDeployment(getCtx)
require.Equal(t, http.StatusOK, getRecorder.Code)
require.Contains(t, getRecorder.Body.String(), `"status":"stopped"`)
require.Contains(t, getRecorder.Body.String(), `"runtime_state":"stopped"`)
}
func TestAgnetStopDeploymentPropagatesToRuntime(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
stopCalled := false
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch {
case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments":
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"success":true,"data":{"deployment_id":"runtime-dep-stop","status":"pending"}}`))
case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments/runtime-dep-stop/stop":
stopCalled = true
require.Equal(t, "Bearer service-token", r.Header.Get("Authorization"))
body, err := io.ReadAll(r.Body)
require.NoError(t, err)
require.Contains(t, string(body), `"reason":"runtime stop smoke"`)
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"success":true,"deployment_id":"runtime-dep-stop","status":"stopped"}`))
default:
http.NotFound(w, r)
}
}))
defer server.Close()
t.Setenv("AGNET_RUNTIME_ENABLED", "true")
t.Setenv("AGNET_RUNTIME_ASYNC", "false")
t.Setenv("AGNET_RUNTIME_BASE_URL", server.URL)
t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "service-token")
recorder, envelope := postAgnetCreateDeployment(t, baseAgnetResourceGrantPlan())
require.Equal(t, http.StatusOK, recorder.Code)
require.True(t, envelope.Success)
var createBody map[string]any
require.NoError(t, common.Unmarshal(recorder.Body.Bytes(), &createBody))
deploymentID := createBody["data"].(map[string]any)["deployment_id"].(string)
stopRecorder := httptest.NewRecorder()
stopCtx, _ := gin.CreateTestContext(stopRecorder)
stopCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments/"+deploymentID+"/stop", strings.NewReader(`{"reason":"runtime stop smoke"}`))
stopCtx.Request.Header.Set("Content-Type", "application/json")
AgnetStopDeployment(stopCtx)
require.Equal(t, http.StatusOK, stopRecorder.Code)
require.Contains(t, stopRecorder.Body.String(), `"success":true`)
require.Contains(t, stopRecorder.Body.String(), `"runtime_state":"stopped"`)
require.True(t, stopCalled)
}
func TestManagerOnlyAgnetSmokeFlow(t *testing.T) {
setupAgnetControlPlaneTestDB(t)
resetAgnetControlPlaneState(t)
resourceBody := `{
"binding_scope":"project-smoke",
"name":"Smoke project repo",
"resource_type":"git",
"provider":"gitee",
"external_id":"https://example.invalid/acme/smoke.git",
"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/project-smoke",
"metadata":{"repo_url":"https://example.invalid/acme/smoke.git","ref":"main"},
"permission_scope":{"actions":["repo:read"]},
"constraints":{"allowed_paths":"src/**,docs/**"}
}`
resourceRecorder := performResourceRequest(CreateResource, 7, http.MethodPost, "/", resourceBody)
require.Equal(t, http.StatusOK, resourceRecorder.Code)
require.Contains(t, resourceRecorder.Body.String(), `"success":true`)
require.NotContains(t, strings.ToLower(resourceRecorder.Body.String()), "password")
var resourceEnv map[string]any
require.NoError(t, common.Unmarshal(resourceRecorder.Body.Bytes(), &resourceEnv))
resource := resourceEnv["data"].(map[string]any)
resourceID := int(resource["id"].(float64))
grantBody := fmt.Sprintf(`{
"binding_scope":"project-smoke",
"resource_id":%d,
"role":"builder",
"agnet_id":"agent-builder-1",
"permission_scope":{"actions":["repo:read","repo:write:feature-branches"]},
"constraints":{"allowed_paths":"src/**,docs/**","ref":"main"}
}`, resourceID)
grantRecorder := performResourceRequest(CreateResourceGrant, 7, http.MethodPost, "/", grantBody)
require.Equal(t, http.StatusOK, grantRecorder.Code)
require.Contains(t, grantRecorder.Body.String(), `"success":true`)
var grantEnv map[string]any
require.NoError(t, common.Unmarshal(grantRecorder.Body.Bytes(), &grantEnv))
grant := grantEnv["data"].(map[string]any)
grantID := int(grant["id"].(float64))
manifestRecorder := performResourceRequestWithRoute(
GenerateResourceGrantManifest,
7,
http.MethodGet,
"/manifest",
"/manifest?binding_scope=project-smoke&role=builder&agnet_id=agent-builder-1",
"",
)
require.Equal(t, http.StatusOK, manifestRecorder.Code)
require.Contains(t, manifestRecorder.Body.String(), `"resource_grants":[`)
require.Contains(t, manifestRecorder.Body.String(), `"secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/project-smoke"`)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = "7"
plan.UserContext.ChannelID = "smoke-channel"
plan.BillingContext.NewAPIUserRef = "newapi-user-smoke"
plan.BillingContext.NewAPIGroup = "vip"
plan.BillingContext.QuotaRef = "quota-smoke"
plan.Metadata.CorrelationID = "corr-manager-smoke"
plan.Agents[0].ResourceGrants = []agnetResourceGrant{
{
GrantID: fmt.Sprintf("grant-%d", grantID),
ResourceID: fmt.Sprintf("%d", resourceID),
ResourceType: agnetResourceGit,
UserID: "7",
BindingScope: "project-smoke",
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",
"resource_ref": "https://example.invalid/acme/smoke.git",
},
Status: agnetGrantStatusActive,
SecretRef: "azkv://heicode-kv.vault.azure.net/secrets/project-smoke",
Audit: map[string]string{"confirmed_by": "7"},
},
}
deployRecorder, envelope := postAgnetCreateDeployment(t, plan)
require.Equal(t, http.StatusOK, deployRecorder.Code)
require.True(t, envelope.Success)
var deployEnv map[string]any
require.NoError(t, common.Unmarshal(deployRecorder.Body.Bytes(), &deployEnv))
deploymentID := deployEnv["data"].(map[string]any)["deployment_id"].(string)
require.NotEmpty(t, deploymentID)
logRecorder := httptest.NewRecorder()
logCtx, _ := gin.CreateTestContext(logRecorder)
logCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
logCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID+"/logs", nil)
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()), "access_token")
stopRecorder := httptest.NewRecorder()
stopCtx, _ := gin.CreateTestContext(stopRecorder)
stopCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
stopCtx.Request = httptest.NewRequest(http.MethodPost, "/api/agnet/deployments/"+deploymentID+"/stop", nil)
AgnetStopDeployment(stopCtx)
require.Equal(t, http.StatusOK, stopRecorder.Code)
require.Contains(t, stopRecorder.Body.String(), `"status":"stopped"`)
resetAgnetControlPlaneState(t)
getRecorder := httptest.NewRecorder()
getCtx, _ := gin.CreateTestContext(getRecorder)
getCtx.Params = gin.Params{{Key: "deployment_id", Value: deploymentID}}
getCtx.Request = httptest.NewRequest(http.MethodGet, "/api/agnet/deployments/"+deploymentID, nil)
AgnetGetDeployment(getCtx)
require.Equal(t, http.StatusOK, getRecorder.Code)
require.Contains(t, getRecorder.Body.String(), `"status":"stopped"`)
revokeGrantRecorder := performResourceRequestWithRoute(
DeleteResourceGrant,
7,
http.MethodDelete,
"/:id",
fmt.Sprintf("/%d", grantID),
"",
)
require.Equal(t, http.StatusOK, revokeGrantRecorder.Code)
require.Contains(t, revokeGrantRecorder.Body.String(), `"status":"revoked"`)
emptyManifestRecorder := performResourceRequestWithRoute(
GenerateResourceGrantManifest,
7,
http.MethodGet,
"/manifest",
"/manifest?binding_scope=project-smoke&role=builder&agnet_id=agent-builder-1",
"",
)
require.Equal(t, http.StatusOK, emptyManifestRecorder.Code)
require.Contains(t, emptyManifestRecorder.Body.String(), `"resource_grants":[]`)
}
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, "user-p1", grants[0].UserID)
require.Equal(t, "https://example.invalid/acme/project.git#main", grants[0].BindingScope)
require.Equal(t, "builder", grants[0].TargetRole)
require.Equal(t, "agent-builder-1", grants[0].TargetAgentRef)
require.Equal(t, "azkv://heicode-kv.vault.azure.net/secrets/user-p1-project-main-res-git-main", grants[0].SecretRef)
require.Empty(t, grants[1].SecretRef, "project document grants should not require credential material")
require.Equal(t, "newapi", stored.Plan.BillingContext.Provider)
require.Equal(t, "channel-p1", stored.Plan.UserContext.ChannelID)
require.Equal(t, "agnet", stored.Plan.AgentRuntime.Platform)
require.Equal(t, "agnet-model-profile-builder", stored.Plan.AgentRuntime.Agents[0].ModelRef)
require.Equal(t, "queued", stored.RuntimeState)
require.Empty(t, stored.FailureReason)
require.Len(t, stored.AgentInstances, 1)
require.Equal(t, "queued", stored.AgentInstances[0].RuntimeState)
require.Len(t, stored.ResourceGrantManifest.ResourceGrants, 2)
require.Equal(t, "user-p1", stored.ResourceGrantManifest.UserID)
require.Equal(t, "builder", stored.ResourceGrantManifest.AgentRole)
require.Equal(t, "agent-builder-1", stored.ResourceGrantManifest.TargetAgentRef)
require.Equal(t, "azkv://heicode-kv.vault.azure.net/secrets/user-p1-project-main-res-git-main", stored.ResourceGrantManifest.ResourceGrants[0].SecretRef)
}
func TestAgnetPermissionManifestExcludesInactiveResourceGrants(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.Agents[0].ResourceGrants[1].Status = agnetGrantStatusRevoked
_, envelope := postAgnetCreateDeployment(t, plan)
require.True(t, envelope.Success)
agnetMu.RLock()
var stored agnetDeploymentRecord
for _, record := range agnetDeployments {
stored = record
}
agnetMu.RUnlock()
require.Len(t, stored.ResourceGrantManifest.ResourceGrants, 1)
require.Equal(t, "grant-git-builder", stored.ResourceGrantManifest.ResourceGrants[0].GrantID)
}
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 TestAgnetCreateDeploymentRejectsMissingUserContext(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.UserID = ""
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "POLICY_REJECTED", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetCreateDeploymentRejectsResourceGrantUserMismatch(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.Agents[0].ResourceGrants[0].UserID = "user-other"
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "RESOURCE_GRANT_INVALID", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetCreateDeploymentRejectsResourceGrantWithoutBindingScope(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.Agents[0].ResourceGrants[0].BindingScope = ""
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "RESOURCE_GRANT_INVALID", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetCreateDeploymentRejectsNewAPIBillingWithoutUserMapping(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.UserContext.ChannelID = ""
plan.BillingContext.NewAPIUserRef = ""
plan.BillingContext.NewAPIGroup = ""
plan.BillingContext.QuotaRef = ""
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "BILLING_CONTEXT_INVALID", envelope.Error.Code)
require.Empty(t, agnetDeployments)
}
func TestAgnetCreateDeploymentRejectsRuntimeRoleOutsideAgentPlan(t *testing.T) {
resetAgnetControlPlaneState(t)
plan := baseAgnetResourceGrantPlan()
plan.AgentRuntime.Agents[0].Role = "ops"
_, envelope := postAgnetCreateDeployment(t, plan)
require.False(t, envelope.Success)
require.Equal(t, "AGENT_RUNTIME_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`)
require.Contains(t, metricRecorder.Body.String(), `"runtime_state":"queued"`)
require.Contains(t, metricRecorder.Body.String(), `"failure_reason":""`)
}