Download internal/panel/timerange_test.go from a3216/gcli2api: direct link, hf CLI and curl.
- Browser
- Download file 4.79 kB
-
https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/panel/timerange_test.go
- Command line
-
hf download hf://spaces/a3216/gcli2api/internal/panel/timerange_test.go
-
curl -L -o timerange_test.go https://huggingface.co/spaces/a3216/gcli2api/resolve/main/internal/panel/timerange_test.go
4.79 kB
| package panel | |
| import ( | |
| "encoding/json" | |
| "net/http/httptest" | |
| "strconv" | |
| "testing" | |
| "time" | |
| "github.com/linguo2625469/workbuddy2api-panel/internal/pool" | |
| "github.com/linguo2625469/workbuddy2api-panel/internal/reqlog" | |
| "github.com/linguo2625469/workbuddy2api-panel/internal/usage" | |
| ) | |
| // parseTimeParam 是「今天 / 自定义」区间的唯一入口:前端默认发 unix 秒,手工 | |
| // 调接口时可以用 RFC3339 / datetime-local;非法值与空串一律回落"不设界", | |
| // 不能让一个拼错的参数把整页用量打成 500。 | |
| func TestParseTimeParam(t *testing.T) { | |
| want := time.Date(2026, 9, 30, 14, 5, 0, 0, time.Local) | |
| cases := []struct { | |
| name string | |
| in string | |
| want time.Time | |
| }{ | |
| {"空串", "", time.Time{}}, | |
| {"空白", " ", time.Time{}}, | |
| {"unix 秒", "1786000000", time.Unix(1786000000, 0)}, | |
| {"unix 毫秒", "1786000000000", time.UnixMilli(1786000000000)}, | |
| {"零值按不设界", "0", time.Time{}}, | |
| {"负数按不设界", "-5", time.Time{}}, | |
| {"RFC3339", want.Format(time.RFC3339), want}, | |
| {"本地 datetime-local", "2026-09-30T14:05", want}, | |
| {"非法", "not-a-time", time.Time{}}, | |
| } | |
| for _, c := range cases { | |
| t.Run(c.name, func(t *testing.T) { | |
| got := parseTimeParam(c.in) | |
| if !got.Equal(c.want) { | |
| t.Fatalf("parseTimeParam(%q) = %v, want %v", c.in, got, c.want) | |
| } | |
| }) | |
| } | |
| } | |
| // usage 接口的区间参数必须真的作用到聚合上:from/to 生效时 hours 被忽略, | |
| // 且响应回显 window_from/window_to 供面板确认口径。 | |
| func TestUsageHandlerAcceptsExplicitRange(t *testing.T) { | |
| rec := usage.New("") | |
| base := time.Now().Truncate(time.Hour).Add(-5 * time.Hour) | |
| for i := 0; i < 6; i++ { | |
| rec.Add(base.Add(time.Duration(i)*time.Hour), "cn", "u1", "glm-5.2", | |
| usage.Delta{PromptTokens: 10, HasPromptTokens: true}, true) | |
| } | |
| p := New(Config{Version: "test", APIKey: "k", Usage: rec, Pool: pool.New("")}) | |
| get := func(query string) map[string]any { | |
| t.Helper() | |
| req := httptest.NewRequest("GET", "/panel/api/usage"+query, nil) | |
| req.Header.Set("Authorization", "Bearer k") | |
| rr := httptest.NewRecorder() | |
| p.ServeHTTP(rr, req) | |
| if rr.Code != 200 { | |
| t.Fatalf("usage%s -> %d %s", query, rr.Code, rr.Body) | |
| } | |
| var out map[string]any | |
| if err := json.Unmarshal(rr.Body.Bytes(), &out); err != nil { | |
| t.Fatal(err) | |
| } | |
| return out | |
| } | |
| from := base.Add(2 * time.Hour).Unix() | |
| to := base.Add(3 * time.Hour).Unix() | |
| // 同时带上 hours=720:区间参数应优先,否则会被算成 30 天全量。 | |
| got := get("?hours=720&from=" + strconv.FormatInt(from, 10) + "&to=" + strconv.FormatInt(to, 10)) | |
| totals := got["totals"].(map[string]any) | |
| if reqs := totals["requests"].(float64); reqs != 2 { | |
| t.Fatalf("区间请求数 = %v, want 2(from/to 应优先于 hours)", reqs) | |
| } | |
| if got["window_from"] == nil || got["window_to"] == nil { | |
| t.Fatalf("响应应回显 window_from/window_to: %+v", got) | |
| } | |
| // 无区间参数时回落默认 72h,且不回显区间。 | |
| def := get("") | |
| if def["window_from"] != nil || def["window_to"] != nil { | |
| t.Fatalf("默认窗口不应回显区间: %+v", def) | |
| } | |
| if r := def["totals"].(map[string]any)["requests"].(float64); r != 6 { | |
| t.Fatalf("默认窗口请求数 = %v, want 6", r) | |
| } | |
| // hours=0(全部历史)同样不回显区间。 | |
| all := get("?hours=0") | |
| if r := all["totals"].(map[string]any)["requests"].(float64); r != 6 { | |
| t.Fatalf("全部历史请求数 = %v, want 6", r) | |
| } | |
| } | |
| // 归档为空时接口必须回 [],而不是 JSON null:前端把 null 与"归档关闭"混在一起 | |
| // 会走错分支,把不满足时间区间的最近请求显示出来。 | |
| func TestRequestLogsEmptyRangeReturnsArray(t *testing.T) { | |
| rec := reqlog.New(reqlog.Config{Enabled: true, Dir: t.TempDir(), MaxBytes: 1 << 20, RetentionDays: 7}) | |
| rec.Record(reqlog.Event{Time: time.Now().Add(-48 * time.Hour), RequestID: "old", | |
| Status: 200, OK: true, Outcome: reqlog.OutcomeSuccess}) | |
| rec.Close() | |
| p := New(Config{Version: "test", APIKey: "k", Pool: pool.New(""), RequestLog: rec}) | |
| future := strconv.FormatInt(time.Now().Add(time.Hour).Unix(), 10) | |
| req := httptest.NewRequest("GET", "/panel/api/request_logs?from="+future, nil) | |
| req.Header.Set("Authorization", "Bearer k") | |
| rr := httptest.NewRecorder() | |
| p.ServeHTTP(rr, req) | |
| if rr.Code != 200 { | |
| t.Fatalf("code=%d body=%s", rr.Code, rr.Body) | |
| } | |
| var out struct { | |
| Entries []reqlog.Event `json:"entries"` | |
| } | |
| if err := json.Unmarshal(rr.Body.Bytes(), &out); err != nil { | |
| t.Fatal(err) | |
| } | |
| if out.Entries == nil { | |
| t.Fatalf("空区间应回 [],得到 null: %s", rr.Body) | |
| } | |
| if len(out.Entries) != 0 { | |
| t.Fatalf("未来区间不该命中任何记录: %+v", out.Entries) | |
| } | |
| } | |