Files
LokiStack/backend/routers/router_test.go
T
opsx d4422f1541
CI / Backend (go vet + test) (push) Canceled after 0s
CI / Frontend (lint + build + test) (push) Canceled after 0s
refactor(analysis): 去掉排行 10/30/50 选择器,改为一次取 50 + 前端分页;零点结算昨天
=== 1. 为什么去掉「显示条数」选择器 ===
topk 的 N 不影响查询成本(扫描的 chunk 集合完全相同),实测:
  topk(10)  17833ms / 11 条
  topk(30)  18019ms / 34 条
  topk(50)   7394ms / 54 条
  topk(100) 17553ms / 74 条
波动来自集群负载,与 N 无关 —— 它只决定最后返回多少序列。
所以"选 30"既不更快也不更慢,本质是个翻页开关;
而选它还要重新等一次十几秒的查询,纯亏。
改为:一次 topk(50),前端 10 条/页 → 5 页只需 1 次查询。
附带收益:占比的分母从"前 10 合计"变成"前 50 合计",更接近真实占比。
副标题明确写清"一次取前 50 个应用(占比按这 50 个合计计算)"。

=== 2. 零点结算昨天(daily volume)===
原来只有 30 分钟 ticker 回补,且启动时间不对齐整点 →
昨天最早的一段可能已经超出 Loki 24h 保留期被删,
结算结果系统性偏低,图上却标"完整一天",属静默错数。
新增 nextMidnightFinalize():每天 00:01 触发 FinalizeYesterdayVolume()
  - 昨天定稿 partial=false
  - 补一次条数(index/stats,一天一次可接受)
  - 顺带刷新今天 partial=true 的柱子
ticker 仍保留,负责当天数据的 30 分钟刷新。

=== 3. 顺带 ===
- 去掉每日量"其中 N 天平台没有留存记录"的提示文案(按用户要求)
- 修 QuickQueryTemplates.tsx 的未使用变量导致 tsc -b 失败
- 工作区里上一轮中断的"排行体积列"改动一并纳入(app-ranking 返回 bytes,
  TopAppsTable 增加体积列,快照记录 per-app 字节数)

Pod = Hub latest = a16978d2f88b
2026-10-30 17:13:18 +00:00

89 lines
2.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package routers
import (
"testing"
"github.com/gin-gonic/gin"
)
// 回归:接口的 **HTTP 方法与路径** 必须和前端调用一致。
//
// 线上事故:一次模糊匹配的 patch 把
//
// logs.POST("/histogram", ...) → logs.GET("/histogram", ...)
//
// 前端 logsApi.histogram 用的是 POST(body 里带 sourceId/query/start/end),
// 于是所有直方图请求 404:
// - 日志查询页:直方图不显示
// - 日志分析页:直接报 "API endpoint not found",整页不可用
//
// 这类"方法写错"编译期完全查不出来,只能靠路由表断言。
func TestCriticalRoutes(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := SetupRouter()
// 期望的 method + path(注意 gin 会把 :param 保留为 :id)
expect := []struct{ method, path string }{
// 日志查询 / 分析页的数据来源:必须是 POST(前端传 body)
{"POST", "/api/v1/logs/histogram"},
{"POST", "/api/v1/logs/stats"},
{"POST", "/api/v1/logs/metrics"},
{"POST", "/api/v1/logs/query"},
{"POST", "/api/v1/logs/context"},
// 查询辅助接口:GET + query string
{"GET", "/api/v1/logs/sources"},
{"GET", "/api/v1/logs/labels"},
{"GET", "/api/v1/logs/label/:name/values"},
// 批量 label 值(下拉联动一次请求)—— 路由写错就是前端全部下拉空
{"GET", "/api/v1/logs/label-values"},
{"GET", "/api/v1/logs/my-scope"},
{"GET", "/api/v1/logs/daily-volume"},
{"GET", "/api/v1/logs/health"},
// 告警
{"GET", "/api/v1/alerts/rules"},
{"POST", "/api/v1/alerts/rules"},
{"GET", "/api/v1/alerts/channels"},
{"POST", "/api/v1/alerts/check"},
{"GET", "/api/v1/alerts/deliveries"},
// 审计 / 自检
{"GET", "/api/v1/audit-logs"},
{"GET", "/api/v1/system/self-check"},
// 设置类:保留期必须同时有读和写(写要管理员)
{"GET", "/api/v1/settings/retention"},
{"PUT", "/api/v1/settings/retention"},
// 备份恢复接口已在上游被移除(commit f87fb4b),这里不再断言
}
registered := map[string]bool{}
for _, route := range engine.Routes() {
registered[route.Method+" "+route.Path] = true
}
for _, item := range expect {
key := item.method + " " + item.path
if !registered[key] {
t.Errorf("缺少路由: %s(前端按这个方法和路径调用,缺了就是 404)", key)
}
}
}
// 回归:前端用 POST 的接口不能只有 GET 版本。
// 单独断言一次,避免以后又有人"顺手"把 POST 改成 GET。
func TestHistogramAcceptsPost(t *testing.T) {
gin.SetMode(gin.TestMode)
engine := SetupRouter()
methods := map[string]bool{}
for _, route := range engine.Routes() {
if route.Path == "/api/v1/logs/histogram" {
methods[route.Method] = true
}
}
if !methods["POST"] {
t.Fatal("POST /api/v1/logs/histogram 未注册:前端 logsApi.histogram 用 POST,会 404")
}
if !methods["GET"] {
t.Fatal("GET /api/v1/logs/histogram 未注册:curl 调试入口丢了")
}
}