Files
heicode-mananger/heicode/controller/agent_template_test.go
T
chenchenandClaude Opus 4.8 cdbc43baeb feat(agent): template-agent deploy/list/stop API (AM contract isolated)
HM-side logic for the template-agent model, written against a proposed AM
contract that is fully isolated in agent_template_runtime.go — when AM ships its
real API, only the default paths + response field names in that one file change.

- agent_template_runtime.go: AM adapter (list templates, start template agent
  with env, stop/delete) reusing the existing runtime config/url/envelope helpers.
- agent_template_handlers.go: GET /api/heicode/agent-templates; POST /api/heicode/
  agents (resolve bindings -> env, start via AM, persist subdomain+token);
  GET /agents, GET/:id, POST/:id/stop, DELETE/:id. Owner-scoped; env never logged.
- reuses AgentDeployment as the agent record (TemplateID/Subdomain/AccessToken/
  BindingIDsJSON) and buildAgentEnvFromBindings for env assembly.
- routes wired under /api/heicode (UserOrV2DeviceAuth), verified no registration
  panic (router tests pass).
- agent_template_test.go: 7 independent unit tests (env assembly metadata-only,
  ownership, secret-without-ref, unknown-source, empty; path substitution;
  response mapping) — all pass without AM/KV.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:55:24 +08:00

97 lines
3.0 KiB
Go

package controller
import (
"testing"
"github.com/heicode/manager/model"
"github.com/stretchr/testify/require"
)
func TestBuildAgentEnvFromBindings_MetadataOnly(t *testing.T) {
setupResourceControllerTestDB(t)
b := model.ResourceBinding{
UserId: 7,
Name: "blob1",
ResourceType: "blob",
Metadata: `{"account":"myacct","container":"uploads"}`,
EnvMap: `{"AZURE_BLOB_ACCOUNT":{"source":"metadata","key":"account"},"AZURE_BLOB_CONTAINER":{"source":"metadata","key":"container"}}`,
}
require.NoError(t, model.DB.Create(&b).Error)
env, err := buildAgentEnvFromBindings(7, []int{b.Id})
require.NoError(t, err)
require.Equal(t, "myacct", env["AZURE_BLOB_ACCOUNT"])
require.Equal(t, "uploads", env["AZURE_BLOB_CONTAINER"])
}
func TestBuildAgentEnvFromBindings_OwnershipEnforced(t *testing.T) {
setupResourceControllerTestDB(t)
b := model.ResourceBinding{UserId: 7, Name: "x", ResourceType: "blob", Metadata: `{}`, EnvMap: `{}`}
require.NoError(t, model.DB.Create(&b).Error)
_, err := buildAgentEnvFromBindings(99, []int{b.Id}) // different user
require.Error(t, err)
}
func TestBuildAgentEnvFromBindings_SecretWithoutRefFailsBeforeKV(t *testing.T) {
setupResourceControllerTestDB(t)
b := model.ResourceBinding{
UserId: 7,
Name: "vm1",
ResourceType: "vm",
Metadata: `{"host":"1.2.3.4"}`,
EnvMap: `{"VM_PASSWORD":{"source":"secret","key":"password"}}`,
// SecretRef intentionally empty -> must error before any Key Vault call
}
require.NoError(t, model.DB.Create(&b).Error)
_, err := buildAgentEnvFromBindings(7, []int{b.Id})
require.Error(t, err)
require.Contains(t, err.Error(), "secret_ref")
}
func TestBuildAgentEnvFromBindings_UnknownSource(t *testing.T) {
setupResourceControllerTestDB(t)
b := model.ResourceBinding{
UserId: 7,
Name: "x",
ResourceType: "blob",
Metadata: `{}`,
EnvMap: `{"FOO":{"source":"bogus","key":"x"}}`,
}
require.NoError(t, model.DB.Create(&b).Error)
_, err := buildAgentEnvFromBindings(7, []int{b.Id})
require.Error(t, err)
}
func TestBuildAgentEnvFromBindings_Empty(t *testing.T) {
setupResourceControllerTestDB(t)
env, err := buildAgentEnvFromBindings(7, nil)
require.NoError(t, err)
require.Empty(t, env)
}
func TestAgentTemplatePathSubstitution(t *testing.T) {
require.Contains(t, agentTemplateStartPath("tpl1"), "/templates/tpl1/start")
require.Contains(t, agentTemplateAgentStopPath("rt-9"), "/agents/rt-9/stop")
require.Contains(t, agentTemplateAgentPath("rt-9"), "/agents/rt-9")
}
func TestTemplateAgentResponse(t *testing.T) {
row := model.AgentDeployment{
DeploymentID: "dep_abc",
TemplateID: "tpl1",
Subdomain: "https://abc.agents.example",
AccessToken: "tok",
BindingIDsJSON: "[1,2]",
Status: "running",
}
resp := templateAgentResponse(row)
require.Equal(t, "dep_abc", resp["agent_id"])
require.Equal(t, "tpl1", resp["template_id"])
require.Equal(t, []int{1, 2}, resp["binding_ids"])
require.Equal(t, "running", resp["status"])
}