Files
OpenFlare/internal/apps/openflare/integration/agent_protocol_test.go
T
ryan 943818f7d4 refactor(repository): 收敛 model/repository 分层为唯一持久化入口
将 OpenFlare 与平台业务的数据访问从 model 与 apps 直连迁入 repository,
model 仅保留实体与无 IO 规则;补充 code-check 架构守卫与开发规范。
2026-07-24 17:00:17 +08:00

201 lines
6.2 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package integration
import (
"context"
"net/http"
"testing"
"github.com/Rain-kl/Wavelet/internal/repository"
"github.com/Rain-kl/Wavelet/internal/apps/openflare/agent"
ofnode "github.com/Rain-kl/Wavelet/internal/apps/openflare/node"
db "github.com/Rain-kl/Wavelet/internal/infra/persistence"
"github.com/Rain-kl/Wavelet/internal/model"
"github.com/Rain-kl/Wavelet/internal/testhelper"
"github.com/gin-gonic/gin"
"github.com/glebarez/sqlite"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gorm.io/gorm"
)
func setupProtocolTestEnv(t *testing.T) (*gin.Engine, func()) {
t.Helper()
sqliteDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
require.NoError(t, err)
require.NoError(t, sqliteDB.AutoMigrate(
&model.OpenFlareNode{},
&model.SystemConfig{},
&model.OpenFlareApplyLog{},
&model.OpenFlareNodeSystemProfile{},
&model.OpenFlareHealthEvent{},
&model.ConfigVersion{},
))
db.SetDB(sqliteDB)
agent.ResetAuthCacheForTest()
resetAccessLogStore := repository.SetAccessLogStoreForTest(repository.NewMemoryAccessLogStore())
resetObservabilityStore := repository.SetObservabilityStoreForTest(repository.NewMemoryObservabilityStore())
engine := testhelper.NewTestGinEngine()
mountOpenFlareTestRoutes(engine)
cleanup := func() {
resetObservabilityStore()
resetAccessLogStore()
db.SetDB(nil)
agent.ResetAuthCacheForTest()
}
return engine, cleanup
}
func TestAgentRelayFlaredProtocol(t *testing.T) {
engine, cleanup := setupProtocolTestEnv(t)
defer cleanup()
ctx := context.Background()
t.Run("create edge node and heartbeat with X-Agent-Token", func(t *testing.T) {
edge, err := ofnode.CreateNode(ctx, ofnode.Input{
Name: "edge-1",
IP: "10.0.0.1",
})
require.NoError(t, err)
require.NotEmpty(t, edge.AccessToken)
assert.Equal(t, "edge_node", edge.NodeType)
rec := performJSONRequest(t, engine, http.MethodPost, "/api/v1/agent/nodes/heartbeat", map[string]any{
"name": "edge-1",
"ip": "203.0.113.10",
"version": "0.1.0",
}, map[string]string{
"X-Agent-Token": edge.AccessToken,
})
assert.Equal(t, http.StatusOK, rec.Code)
resp := requireAPIOK(t, rec)
data := unmarshalAPIMap(t, resp.Data)
assert.NotNil(t, data["agent_settings"])
})
t.Run("create tunnel_relay node and relay heartbeat", func(t *testing.T) {
relayNode, err := ofnode.CreateNode(ctx, ofnode.Input{
Name: "relay-1",
NodeType: "tunnel_relay",
})
require.NoError(t, err)
require.NotEmpty(t, relayNode.AccessToken)
rec := performJSONRequest(t, engine, http.MethodPost, "/api/v1/relay/heartbeat", map[string]any{
"version": "v0.1.0",
"frp_version": "0.61.0",
"relay_status": "healthy",
"name": "relay-1",
"ip": "203.0.113.20",
}, map[string]string{
"X-Agent-Token": relayNode.AccessToken,
})
assert.Equal(t, http.StatusOK, rec.Code)
resp := requireAPIOK(t, rec)
var heartbeatData struct {
RelayConfig map[string]any `json:"relay_config"`
RelaySettings map[string]any `json:"relay_settings"`
}
unmarshalAPIData(t, resp.Data, &heartbeatData)
assert.NotNil(t, heartbeatData.RelayConfig)
assert.NotNil(t, heartbeatData.RelaySettings)
stored, err := repository.GetOpenFlareNodeByNodeID(ctx, relayNode.NodeID)
require.NoError(t, err)
assert.Equal(t, "online", stored.Status)
assert.Equal(t, "healthy", stored.RelayStatus)
})
t.Run("create tunnel_client node and flared heartbeat with X-Tunnel-Token", func(t *testing.T) {
clientNode, err := ofnode.CreateNode(ctx, ofnode.Input{
Name: "client-1",
NodeType: "tunnel_client",
})
require.NoError(t, err)
require.NotEmpty(t, clientNode.AccessToken)
rec := performJSONRequest(t, engine, http.MethodPost, "/api/v1/tunnel/heartbeat", map[string]any{
"client_version": "v0.2.0",
"frp_version": "0.61.0",
"tunnel_status": "running",
}, map[string]string{
"X-Tunnel-Token": clientNode.AccessToken,
})
assert.Equal(t, http.StatusOK, rec.Code)
requireAPIOK(t, rec)
stored, err := repository.GetOpenFlareNodeByNodeID(ctx, clientNode.NodeID)
require.NoError(t, err)
assert.Equal(t, "online", stored.Status)
assert.Equal(t, "v0.2.0", stored.Version)
})
t.Run("agent register with discovery token from options", func(t *testing.T) {
bootstrap, err := ofnode.GetBootstrapToken(ctx)
require.NoError(t, err)
require.NotEmpty(t, bootstrap.DiscoveryToken)
rec := performJSONRequest(t, engine, http.MethodPost, "/api/v1/agent/nodes/register", map[string]any{
"name": "discovered-edge",
"ip": "203.0.113.30",
"version": "0.2.0",
}, map[string]string{
"X-Agent-Token": bootstrap.DiscoveryToken,
})
assert.Equal(t, http.StatusOK, rec.Code)
resp := requireAPIOK(t, rec)
var registration agent.RegistrationResponse
unmarshalAPIData(t, resp.Data, &registration)
assert.NotEmpty(t, registration.NodeID)
assert.NotEmpty(t, registration.AccessToken)
assert.Equal(t, "discovered-edge", registration.Name)
stored, err := repository.GetOpenFlareNodeByNodeID(ctx, registration.NodeID)
require.NoError(t, err)
assert.Equal(t, "online", stored.Status)
assert.Equal(t, registration.AccessToken, stored.AccessToken)
})
t.Run("POST agent apply-logs", func(t *testing.T) {
edge, err := ofnode.CreateNode(ctx, ofnode.Input{
Name: "edge-apply",
IP: "10.0.0.2",
})
require.NoError(t, err)
rec := performJSONRequest(t, engine, http.MethodPost, "/api/v1/agent/apply-logs", map[string]any{
"version": "20260618-001",
"result": "success",
"message": "apply ok",
}, map[string]string{
"X-Agent-Token": edge.AccessToken,
})
assert.Equal(t, http.StatusOK, rec.Code)
resp := requireAPIOK(t, rec)
var applyLog model.OpenFlareApplyLog
unmarshalAPIData(t, resp.Data, &applyLog)
assert.Equal(t, edge.NodeID, applyLog.NodeID)
assert.Equal(t, "success", applyLog.Result)
assert.Equal(t, "20260618-001", applyLog.Version)
stored, err := repository.GetOpenFlareNodeByNodeID(ctx, edge.NodeID)
require.NoError(t, err)
assert.Equal(t, "online", stored.Status)
assert.Equal(t, "20260618-001", stored.CurrentVersion)
})
}