forked from niuniu/llm-intelligence
169 lines
4.6 KiB
Go
169 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestSubscriptionPlansHandlerReturnsEnvelope(t *testing.T) {
|
|
mux := newMux(
|
|
&sql.DB{},
|
|
func(context.Context, *sql.DB) ([]modelResponse, error) {
|
|
return nil, nil
|
|
},
|
|
func(context.Context, *sql.DB) ([]subscriptionPlanResponse, error) {
|
|
return []subscriptionPlanResponse{
|
|
{
|
|
PlanFamily: "token_plan",
|
|
PlanCode: "token-plan-lite",
|
|
PlanName: "General Token Plan Lite",
|
|
Tier: "Lite",
|
|
Provider: "Tencent",
|
|
ProviderCN: "腾讯",
|
|
Operator: "Tencent Cloud",
|
|
OperatorCN: "腾讯云",
|
|
Currency: "CNY",
|
|
ListPrice: 39,
|
|
PriceUnit: "CNY/month",
|
|
QuotaValue: 35000000,
|
|
QuotaUnit: "tokens/month",
|
|
ContextWindow: 0,
|
|
ModelScope: []string{"tc-code-latest", "glm-5", "glm-5.1"},
|
|
SourceURL: "https://cloud.tencent.com/document/product/1823/130060",
|
|
EffectiveDate: "2026-04-27",
|
|
},
|
|
}, nil
|
|
},
|
|
"",
|
|
)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/subscription-plans", nil)
|
|
rec := httptest.NewRecorder()
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status 200, got %d", rec.Code)
|
|
}
|
|
|
|
var payload struct {
|
|
Data []subscriptionPlanResponse `json:"data"`
|
|
}
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil {
|
|
t.Fatalf("unmarshal response: %v", err)
|
|
}
|
|
|
|
if len(payload.Data) != 1 {
|
|
t.Fatalf("expected 1 plan, got %d", len(payload.Data))
|
|
}
|
|
|
|
got := payload.Data[0]
|
|
if got.PlanCode != "token-plan-lite" {
|
|
t.Fatalf("unexpected plan code: %q", got.PlanCode)
|
|
}
|
|
if got.ProviderCN != "腾讯" {
|
|
t.Fatalf("unexpected providerCN: %q", got.ProviderCN)
|
|
}
|
|
if got.OperatorCN != "腾讯云" {
|
|
t.Fatalf("unexpected operatorCN: %q", got.OperatorCN)
|
|
}
|
|
if got.ListPrice != 39 {
|
|
t.Fatalf("unexpected list price: %v", got.ListPrice)
|
|
}
|
|
if len(got.ModelScope) != 3 {
|
|
t.Fatalf("unexpected model scope length: %d", len(got.ModelScope))
|
|
}
|
|
}
|
|
|
|
func TestFrontendHandlerServesIndexAssetsAndSpaFallback(t *testing.T) {
|
|
distDir := t.TempDir()
|
|
writeTestFile(t, filepath.Join(distDir, "index.html"), "<html>dashboard</html>")
|
|
writeTestFile(t, filepath.Join(distDir, "assets", "app.js"), "console.log('ok');")
|
|
|
|
mux := newMux(&sql.DB{}, noOpModelsFetcher, noOpPlansFetcher, distDir)
|
|
|
|
t.Run("root serves index", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status 200, got %d", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "dashboard") {
|
|
t.Fatalf("expected index response, got %q", rec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("asset serves file", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/assets/app.js", nil)
|
|
rec := httptest.NewRecorder()
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status 200, got %d", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "console.log") {
|
|
t.Fatalf("expected asset response, got %q", rec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("spa route falls back to index", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/explorer/detail", nil)
|
|
rec := httptest.NewRecorder()
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status 200, got %d", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Body.String(), "dashboard") {
|
|
t.Fatalf("expected SPA fallback, got %q", rec.Body.String())
|
|
}
|
|
})
|
|
|
|
t.Run("missing asset returns not found", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/assets/missing.js", nil)
|
|
rec := httptest.NewRecorder()
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusNotFound {
|
|
t.Fatalf("expected status 404, got %d", rec.Code)
|
|
}
|
|
})
|
|
|
|
t.Run("api routes keep precedence", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/models", nil)
|
|
rec := httptest.NewRecorder()
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("expected status 200, got %d", rec.Code)
|
|
}
|
|
})
|
|
}
|
|
|
|
func noOpModelsFetcher(context.Context, *sql.DB) ([]modelResponse, error) {
|
|
return []modelResponse{}, nil
|
|
}
|
|
|
|
func noOpPlansFetcher(context.Context, *sql.DB) ([]subscriptionPlanResponse, error) {
|
|
return []subscriptionPlanResponse{}, nil
|
|
}
|
|
|
|
func writeTestFile(t *testing.T, path string, contents string) {
|
|
t.Helper()
|
|
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
t.Fatalf("mkdir %s: %v", path, err)
|
|
}
|
|
if err := os.WriteFile(path, []byte(contents), 0o644); err != nil {
|
|
t.Fatalf("write %s: %v", path, err)
|
|
}
|
|
}
|