mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-04-28 19:41:17 +00:00
116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
)
|
|
|
|
func TestHandleExportAICostHistory_JSON(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
cfg := &config.Config{DataPath: tmp}
|
|
persistence := config.NewConfigPersistence(tmp)
|
|
|
|
events := []config.AIUsageEventRecord{
|
|
{
|
|
Timestamp: time.Now().UTC(),
|
|
Provider: "openai",
|
|
RequestModel: "gpt-4o-mini",
|
|
UseCase: "chat",
|
|
InputTokens: 42,
|
|
OutputTokens: 17,
|
|
TargetType: "vm",
|
|
TargetID: "vm-1",
|
|
FindingID: "finding-1",
|
|
},
|
|
}
|
|
if err := persistence.SaveAIUsageHistory(events); err != nil {
|
|
t.Fatalf("SaveAIUsageHistory: %v", err)
|
|
}
|
|
|
|
handler := newTestAISettingsHandler(cfg, persistence, nil)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/ai/cost/export?days=7&format=json", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.HandleExportAICostHistory(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want 200", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Header().Get("Content-Type"), "application/json") {
|
|
t.Fatalf("expected json content type")
|
|
}
|
|
|
|
var resp struct {
|
|
Days int `json:"days"`
|
|
Events []struct {
|
|
Provider string `json:"provider"`
|
|
PricingKnown bool `json:"pricing_known"`
|
|
} `json:"events"`
|
|
}
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("decode response: %v", err)
|
|
}
|
|
if resp.Days != 7 {
|
|
t.Fatalf("days = %d, want 7", resp.Days)
|
|
}
|
|
if len(resp.Events) != 1 {
|
|
t.Fatalf("events = %d, want 1", len(resp.Events))
|
|
}
|
|
if resp.Events[0].Provider != "openai" {
|
|
t.Fatalf("provider = %s, want openai", resp.Events[0].Provider)
|
|
}
|
|
if !resp.Events[0].PricingKnown {
|
|
t.Fatalf("expected pricing known")
|
|
}
|
|
}
|
|
|
|
func TestHandleExportAICostHistory_CSV(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
cfg := &config.Config{DataPath: tmp}
|
|
persistence := config.NewConfigPersistence(tmp)
|
|
|
|
events := []config.AIUsageEventRecord{
|
|
{
|
|
Timestamp: time.Now().UTC(),
|
|
Provider: "openai",
|
|
RequestModel: "gpt-4o-mini",
|
|
UseCase: "chat",
|
|
InputTokens: 5,
|
|
OutputTokens: 3,
|
|
TargetType: "node",
|
|
TargetID: "node-1",
|
|
},
|
|
}
|
|
if err := persistence.SaveAIUsageHistory(events); err != nil {
|
|
t.Fatalf("SaveAIUsageHistory: %v", err)
|
|
}
|
|
|
|
handler := newTestAISettingsHandler(cfg, persistence, nil)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/ai/cost/export?days=1&format=csv", nil)
|
|
rec := httptest.NewRecorder()
|
|
|
|
handler.HandleExportAICostHistory(rec, req)
|
|
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want 200", rec.Code)
|
|
}
|
|
if !strings.Contains(rec.Header().Get("Content-Type"), "text/csv") {
|
|
t.Fatalf("expected csv content type")
|
|
}
|
|
|
|
lines := strings.Split(strings.TrimSpace(rec.Body.String()), "\n")
|
|
if len(lines) < 2 {
|
|
t.Fatalf("expected header and data rows")
|
|
}
|
|
if !strings.HasPrefix(lines[0], "timestamp,provider,request_model") {
|
|
t.Fatalf("unexpected header: %s", lines[0])
|
|
}
|
|
}
|