feat: align agnet runtime v2.1 integration
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"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"
|
||||
)
|
||||
|
||||
func setupAgnetRuntimeHTTPSmokeDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
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.User{},
|
||||
&model.AgnetDeployment{},
|
||||
&model.AgnetAuditEvent{},
|
||||
))
|
||||
adminToken := "runtime-smoke-admin-token"
|
||||
require.NoError(t, db.Create(&model.User{
|
||||
Id: 101,
|
||||
Username: "runtime-smoke-admin",
|
||||
Password: "not-used",
|
||||
Role: common.RoleAdminUser,
|
||||
Status: common.UserStatusEnabled,
|
||||
DisplayName: "Runtime Smoke Admin",
|
||||
AccessToken: &adminToken,
|
||||
Group: "default",
|
||||
}).Error)
|
||||
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 startAgnetRuntimeManagerSmokeServer(t *testing.T) string {
|
||||
t.Helper()
|
||||
gin.SetMode(gin.TestMode)
|
||||
engine := gin.New()
|
||||
engine.Use(sessions.Sessions("runtime-smoke", cookie.NewStore([]byte("runtime-smoke-secret"))))
|
||||
SetApiRouter(engine)
|
||||
listener, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
require.NoError(t, err)
|
||||
server := &http.Server{Handler: engine}
|
||||
go func() {
|
||||
_ = server.Serve(listener)
|
||||
}()
|
||||
t.Cleanup(func() {
|
||||
_ = server.Close()
|
||||
})
|
||||
return "http://" + listener.Addr().String()
|
||||
}
|
||||
|
||||
func agnetRuntimeAdminRequest(t *testing.T, method string, url string, body string) *http.Response {
|
||||
t.Helper()
|
||||
req, err := http.NewRequest(method, url, bytes.NewBufferString(body))
|
||||
require.NoError(t, err)
|
||||
req.Header.Set("Authorization", "Bearer runtime-smoke-admin-token")
|
||||
req.Header.Set("New-Api-User", "101")
|
||||
if body != "" {
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
}
|
||||
resp, err := (&http.Client{Timeout: 5 * time.Second}).Do(req)
|
||||
require.NoError(t, err)
|
||||
return resp
|
||||
}
|
||||
|
||||
func readAgnetRuntimeSmokeBody(t *testing.T, resp *http.Response) string {
|
||||
t.Helper()
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
return string(body)
|
||||
}
|
||||
|
||||
func TestAgnetRuntimeRealHTTPHealthAndShadowCreateSmoke(t *testing.T) {
|
||||
db := setupAgnetRuntimeHTTPSmokeDB(t)
|
||||
runtimeCreateCalled := false
|
||||
runtime := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch {
|
||||
case r.Method == http.MethodGet && r.URL.Path == "/api/agnet/health":
|
||||
require.Equal(t, "Bearer runtime-service-token", r.Header.Get("Authorization"))
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"success":true,"data":{"status":"healthy"}}`))
|
||||
case r.Method == http.MethodPost && r.URL.Path == "/api/agnet/deployments":
|
||||
runtimeCreateCalled = true
|
||||
require.Equal(t, "Bearer runtime-service-token", r.Header.Get("Authorization"))
|
||||
require.Equal(t, "corr-http-smoke", 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), `"agile_context"`)
|
||||
require.Contains(t, string(body), `"checkpoint":"ready_for_test"`)
|
||||
require.Contains(t, string(body), `"manager_deployment_id"`)
|
||||
require.Contains(t, string(body), `"signing_secret_ref":"azkv://heicode-kv.vault.azure.net/secrets/callback-signing"`)
|
||||
require.Contains(t, string(body), `"subscribed_events"`)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"success":true,"data":{"deployment_id":"runtime-http-dep","swarm_id":"runtime-http-swarm","runtime_status":"accepted"}}`))
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
}))
|
||||
defer runtime.Close()
|
||||
|
||||
t.Setenv("AGNET_RUNTIME_ENABLED", "true")
|
||||
t.Setenv("AGNET_RUNTIME_ASYNC", "false")
|
||||
t.Setenv("AGNET_RUNTIME_BASE_URL", runtime.URL)
|
||||
t.Setenv("AGNET_RUNTIME_SERVICE_TOKEN", "runtime-service-token")
|
||||
t.Setenv("AGNET_RUNTIME_CALLBACK_SIGNING_SECRET_REF", "azkv://heicode-kv.vault.azure.net/secrets/callback-signing")
|
||||
|
||||
managerURL := startAgnetRuntimeManagerSmokeServer(t)
|
||||
|
||||
healthResp := agnetRuntimeAdminRequest(t, http.MethodGet, managerURL+"/api/agnet/runtime/health", "")
|
||||
healthBody := readAgnetRuntimeSmokeBody(t, healthResp)
|
||||
require.Equal(t, http.StatusOK, healthResp.StatusCode)
|
||||
require.Contains(t, healthBody, `"success":true`)
|
||||
require.Contains(t, healthBody, `"status":"healthy"`)
|
||||
|
||||
createBody := `{
|
||||
"orchestration_plan":{
|
||||
"intent_id":"intent-http-smoke",
|
||||
"template_hint":"heicode-task",
|
||||
"objective":"real HTTP runtime smoke",
|
||||
"sub_mode":"agile",
|
||||
"risk_level":"low",
|
||||
"budget":{"max_tokens":10000,"max_cost_usd":1,"max_duration_sec":600},
|
||||
"user_context":{"user_id":"101","channel_id":"default"},
|
||||
"billing_context":{"provider":"newapi","newapi_user_ref":"newapi-http-smoke"},
|
||||
"agile_context":{"iteration":"2026-05-27~2026-05-28","stage":"development","checkpoint":"ready_for_test","acceptance_criteria":["接口返回成功"],"next_action":"submit_test_result","requires_user_approval":false},
|
||||
"agent_runtime":{"platform":"agnet","agents":[{"role":"builder","model_ref":"model-http-smoke","instance_count":1}]},
|
||||
"agents":[{"role_template":"builder","goal":"smoke","default_model_id":"model-http-smoke","resource_grants":[{"grant_id":"grant-http-doc","resource_id":"doc-http","resource_type":"project_doc","user_id":"101","binding_scope":"task-http-smoke","target_role":"builder","target_agent_ref":"agent-builder-1","permission_scope":["doc:read"],"status":"active"}]}],
|
||||
"constraints":{"allowed_model_ids":["model-http-smoke"]},
|
||||
"metadata":{"correlation_id":"corr-http-smoke"}
|
||||
}
|
||||
}`
|
||||
createResp := agnetRuntimeAdminRequest(t, http.MethodPost, managerURL+"/api/agnet/deployments", createBody)
|
||||
responseBody := readAgnetRuntimeSmokeBody(t, createResp)
|
||||
require.Equal(t, http.StatusOK, createResp.StatusCode)
|
||||
require.Contains(t, responseBody, `"success":true`)
|
||||
require.Contains(t, responseBody, `"runtime_deployment_id":"runtime-http-dep"`)
|
||||
require.Contains(t, responseBody, `"runtime_swarm_id":"runtime-http-swarm"`)
|
||||
require.True(t, runtimeCreateCalled)
|
||||
|
||||
var stored model.AgnetDeployment
|
||||
require.NoError(t, db.Where("runtime_swarm_id = ?", "runtime-http-swarm").First(&stored).Error)
|
||||
require.Equal(t, "accepted", stored.RuntimeState)
|
||||
}
|
||||
@@ -529,6 +529,7 @@ func SetApiRouter(router *gin.Engine) {
|
||||
agnetRoute.GET("/deployments/:deployment_id/events", controller.AgnetListDeploymentEvents)
|
||||
agnetRoute.GET("/deployments/:deployment_id/sk-snapshots", controller.AgnetListSKSnapshots)
|
||||
agnetRoute.POST("/sk-snapshots/resolve", controller.AgnetResolveSKSnapshots)
|
||||
agnetRoute.GET("/runtime/health", controller.AgnetRuntimeHealth)
|
||||
agnetRoute.GET("/projects/:project_id/dashboard-snapshot", controller.AgnetProjectDashboardSnapshot)
|
||||
agnetRoute.GET("/audit-logs", controller.AgnetListAuditLogs)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestSetApiRouterDoesNotRegisterDuplicateRoutes(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
require.NotPanics(t, func() {
|
||||
SetApiRouter(gin.New())
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user