191 lines
6.4 KiB
Go
191 lines
6.4 KiB
Go
package controller
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"ezops/model"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/glebarez/sqlite"
|
|
"gorm.io/gorm"
|
|
gormlogger "gorm.io/gorm/logger"
|
|
)
|
|
|
|
func TestReceiveReportMatchesExistingAssetByIP(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db, err := gorm.Open(sqlite.Open("file:agent_report_match?mode=memory&cache=shared"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("打开测试数据库失败: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(&model.Server{}, &model.AgentReport{}, &model.HealthProbe{}); err != nil {
|
|
t.Fatalf("迁移测试表失败: %v", err)
|
|
}
|
|
|
|
server := model.Server{Hostname: "c-redis-cluster-3", IP: "172.19.16.59", Status: "running"}
|
|
if err := db.Create(&server).Error; err != nil {
|
|
t.Fatalf("创建测试资产失败: %v", err)
|
|
}
|
|
|
|
body := []byte(`{
|
|
"hostname":"VM-16-59-tencentos",
|
|
"ip":"172.19.16.59",
|
|
"agent_version":"v2.1.0",
|
|
"cpu":{"usage_percent":12.5,"iowait":0.5,"core_count":4},
|
|
"memory":{"total_gb":7.8,"used_gb":3.1,"available_gb":4.7,"usage_percent":39.7},
|
|
"disks":[{"mount":"/","total_gb":100,"used_gb":25,"usage_percent":25}],
|
|
"system":{"os_version":"TencentOS Server 4","kernel":"6.6.0","uptime":3600}
|
|
}`)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/agent/report", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
recorder := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(recorder)
|
|
c.Request = req
|
|
|
|
NewAgentHandler(db, nil, nil).ReceiveReport(c)
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("上报返回码 = %d, body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
|
|
var gotServer model.Server
|
|
if err := db.Take(&gotServer, server.ID).Error; err != nil {
|
|
t.Fatalf("读取资产失败: %v", err)
|
|
}
|
|
if gotServer.Hostname != server.Hostname || gotServer.AgentStatus != "online" {
|
|
t.Fatalf("资产关联错误: hostname=%q status=%q", gotServer.Hostname, gotServer.AgentStatus)
|
|
}
|
|
if gotServer.OS != "TencentOS Server 4" || gotServer.Kernel != "6.6.0" || gotServer.Uptime != 3600 {
|
|
t.Fatalf("系统字段更新错误: os=%q kernel=%q uptime=%d", gotServer.OS, gotServer.Kernel, gotServer.Uptime)
|
|
}
|
|
|
|
var report model.AgentReport
|
|
if err := db.Order("id DESC").Take(&report).Error; err != nil {
|
|
t.Fatalf("读取 Agent 报告失败: %v", err)
|
|
}
|
|
if report.Hostname != server.Hostname {
|
|
t.Fatalf("报告主机名未归一: got=%q want=%q", report.Hostname, server.Hostname)
|
|
}
|
|
|
|
var serverCount int64
|
|
db.Model(&model.Server{}).Count(&serverCount)
|
|
if serverCount != 1 {
|
|
t.Fatalf("上报后产生重复资产: count=%d", serverCount)
|
|
}
|
|
}
|
|
|
|
func TestReceiveReportUsesHeartbeatUptimeWithoutSystemPayload(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db, err := gorm.Open(sqlite.Open("file:agent_report_heartbeat_uptime?mode=memory&cache=shared"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("打开测试数据库失败: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(&model.Server{}, &model.AgentReport{}, &model.HealthProbe{}); err != nil {
|
|
t.Fatalf("迁移测试表失败: %v", err)
|
|
}
|
|
|
|
server := model.Server{Hostname: "c-monitor", IP: "172.19.16.113", Status: "running"}
|
|
if err := db.Create(&server).Error; err != nil {
|
|
t.Fatalf("创建测试资产失败: %v", err)
|
|
}
|
|
|
|
body := []byte(`{
|
|
"hostname":"c-monitor",
|
|
"ip":"172.19.16.113",
|
|
"agent_version":"v2.1.0",
|
|
"cpu":{"usage_percent":1.5,"core_count":4},
|
|
"memory":{"total_gb":15.1,"used_gb":1.8,"available_gb":13.3,"usage_percent":11.9},
|
|
"heartbeat":{"uptime_sec":7200}
|
|
}`)
|
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/agent/report", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
recorder := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(recorder)
|
|
c.Request = req
|
|
|
|
NewAgentHandler(db, nil, nil).ReceiveReport(c)
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("上报返回码 = %d, body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
|
|
var gotServer model.Server
|
|
if err := db.Take(&gotServer, server.ID).Error; err != nil {
|
|
t.Fatalf("读取资产失败: %v", err)
|
|
}
|
|
if gotServer.Uptime != 7200 {
|
|
t.Fatalf("心跳运行时长未写入资产: got=%d want=7200", gotServer.Uptime)
|
|
}
|
|
}
|
|
|
|
func TestGetLatestReportsUsesUTCTimeBoundary(t *testing.T) {
|
|
gin.SetMode(gin.TestMode)
|
|
db, err := gorm.Open(sqlite.Open("file:agent_latest_utc?mode=memory&cache=shared"), &gorm.Config{
|
|
Logger: gormlogger.Default.LogMode(gormlogger.Silent),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("打开测试数据库失败: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(&model.Server{}, &model.AgentReport{}); err != nil {
|
|
t.Fatalf("迁移测试表失败: %v", err)
|
|
}
|
|
if err := db.Exec("CREATE TABLE asset_regions (id INTEGER PRIMARY KEY, name TEXT)").Error; err != nil {
|
|
t.Fatalf("创建区域测试表失败: %v", err)
|
|
}
|
|
|
|
server := model.Server{
|
|
Hostname: "c-redis-cluster-3",
|
|
IP: "172.19.16.59",
|
|
AgentVersion: "v2.1.0",
|
|
AgentStatus: "online",
|
|
DiskUsage: 5.56,
|
|
}
|
|
if err := db.Create(&server).Error; err != nil {
|
|
t.Fatalf("创建测试资产失败: %v", err)
|
|
}
|
|
// 模拟 Agent 以 UTC 上报,同时应用运行在 UTC+8 时区。 // Logan.Li
|
|
report := model.AgentReport{
|
|
Hostname: server.Hostname,
|
|
CPUUsage: 12.5,
|
|
MemUsage: 39.7,
|
|
CPUCores: 4,
|
|
ReportTime: time.Now().UTC().Add(-time.Minute),
|
|
}
|
|
if err := db.Create(&report).Error; err != nil {
|
|
t.Fatalf("创建 Agent 报告失败: %v", err)
|
|
}
|
|
|
|
originalLocal := time.Local
|
|
time.Local = time.FixedZone("UTC+8", 8*60*60)
|
|
defer func() { time.Local = originalLocal }()
|
|
|
|
recorder := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(recorder)
|
|
c.Request = httptest.NewRequest(http.MethodGet, "/api/v1/agent/latest", nil)
|
|
NewAgentHandler(db, nil, nil).GetLatestReports(c)
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("查询返回码 = %d, body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
|
|
var response struct {
|
|
Code int `json:"code"`
|
|
Data []model.AgentLatestResponse `json:"data"`
|
|
}
|
|
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
|
|
t.Fatalf("解析响应失败: %v", err)
|
|
}
|
|
if len(response.Data) != 1 {
|
|
t.Fatalf("主机数量 = %d, body=%s", len(response.Data), recorder.Body.String())
|
|
}
|
|
got := response.Data[0]
|
|
if got.AgentStatus != "online" || got.CPUUsage != 12.5 || got.MemUsage != 39.7 {
|
|
t.Fatalf("最新指标未命中: status=%q cpu=%.1f mem=%.1f", got.AgentStatus, got.CPUUsage, got.MemUsage)
|
|
}
|
|
}
|