Files

177 lines
6.0 KiB
Go

package repository_test
import (
"context"
"path/filepath"
"testing"
"time"
managedaccess "billing/internal/access"
"billing/internal/collection"
"billing/internal/repository"
)
func TestManagedKeysBootstrapLifecycleAndHistoricalUsage(t *testing.T) {
store, err := repository.OpenSQLiteUsage(filepath.Join(t.TempDir(), "usage.db"))
if err != nil {
t.Fatal(err)
}
defer store.Close()
if err := store.Insert(context.Background(), collection.Record{
APIKey: "000000", Model: "gpt-5", RequestedAt: time.Now(), TotalTokens: 10,
}); err != nil {
t.Fatal(err)
}
key, err := store.BootstrapManagedKey(context.Background(), "default", "000000")
if err != nil {
t.Fatal(err)
}
if key.ID != "key_default" || key.Status != managedaccess.StatusActive || !key.AllModels {
t.Fatalf("bootstrap key = %+v", key)
}
records, err := store.ListRecent(context.Background(), 10)
if err != nil {
t.Fatal(err)
}
if len(records) != 1 || records[0].ManagedKeyID != key.ID || records[0].KeyAlias != "default" {
t.Fatalf("historical records = %+v", records)
}
key.Status = managedaccess.StatusDisabled
key.AllModels = false
key.Models = []string{"gpt-5"}
if err := store.UpdateManagedKey(context.Background(), key); err != nil {
t.Fatal(err)
}
if err := store.ArchiveManagedKey(context.Background(), key.ID); err != nil {
t.Fatal(err)
}
dashboard, err := store.UsageDashboard(context.Background(), time.Now(), 7)
if err != nil {
t.Fatal(err)
}
if dashboard.Today.Requests != 1 {
t.Fatalf("archived key usage should remain in dashboard totals: %+v", dashboard)
}
for _, user := range dashboard.Users {
if user.KeyID == key.ID || user.KeyAlias == key.Name {
t.Fatalf("archived key should not remain in user dashboard: %+v", dashboard)
}
}
archived, err := store.ManagedKeyByID(context.Background(), key.ID)
if err != nil || archived.Status != managedaccess.StatusArchived {
t.Fatalf("archived key = %+v, err=%v", archived, err)
}
archived.Status = managedaccess.StatusActive
if err := store.UpdateManagedKey(context.Background(), archived); err == nil {
t.Fatal("archived key was restored")
}
}
func TestManagedKeyStatsUseShanghaiDayBoundary(t *testing.T) {
store, err := repository.OpenSQLiteUsage(filepath.Join(t.TempDir(), "usage.db"))
if err != nil {
t.Fatal(err)
}
defer store.Close()
if _, err := store.BootstrapManagedKey(context.Background(), "default", "000000"); err != nil {
t.Fatal(err)
}
cost := int64(25)
for _, requestedAt := range []time.Time{
time.Date(2026, 8, 14, 15, 59, 59, 0, time.UTC),
time.Date(2026, 8, 14, 16, 0, 1, 0, time.UTC),
} {
if err := store.Insert(context.Background(), collection.Record{
ManagedKeyID: "key_default", RequestedAt: requestedAt, TotalTokens: 10, CostMicros: &cost,
}); err != nil {
t.Fatal(err)
}
}
shanghai, _ := time.LoadLocation("Asia/Shanghai")
today := time.Date(2026, 8, 15, 0, 0, 0, 0, shanghai)
stats, err := store.KeyStats(context.Background(), "key_default", today)
if err != nil {
t.Fatal(err)
}
if stats.Total.Requests != 2 || stats.Today.Requests != 1 || stats.Today.TotalTokens != 10 || stats.Total.CostMicros != 50 {
t.Fatalf("stats = %+v", stats)
}
}
func TestReconcileUpstreamAccountsListsOnlyCurrentSnapshot(t *testing.T) {
store, err := repository.OpenSQLiteUsage(filepath.Join(t.TempDir(), "usage.db"))
if err != nil {
t.Fatal(err)
}
defer store.Close()
ctx := context.Background()
old := managedaccess.UpstreamAccount{
CPAAuthID: "codex-user-prolite.json", CPAAuthIndex: "old-index",
Provider: "codex", DisplayName: "user@example.com", LastSeenAt: time.Now().Add(-time.Hour),
}
current := managedaccess.UpstreamAccount{
CPAAuthID: "codex-user-pro.json", CPAAuthIndex: "current-index",
Provider: "codex", DisplayName: "user@example.com", LastSeenAt: time.Now(),
}
if err := store.ReconcileUpstreamAccounts(ctx, []managedaccess.UpstreamAccount{old, current}); err != nil {
t.Fatal(err)
}
initial, err := store.ListUpstreamAccounts(ctx)
if err != nil || len(initial) != 2 {
t.Fatalf("initial accounts = %+v, err=%v", initial, err)
}
var oldID string
for _, account := range initial {
if account.CPAAuthID == old.CPAAuthID {
oldID = account.ID
}
}
if err := store.ReconcileUpstreamAccounts(ctx, []managedaccess.UpstreamAccount{current}); err != nil {
t.Fatal(err)
}
listed, err := store.ListUpstreamAccounts(ctx)
if err != nil || len(listed) != 1 || listed[0].CPAAuthID != current.CPAAuthID {
t.Fatalf("current accounts = %+v, err=%v", listed, err)
}
if historical, err := store.UpstreamAccountByID(ctx, oldID); err != nil || historical.CPAAuthID != old.CPAAuthID {
t.Fatalf("historical account = %+v, err=%v", historical, err)
}
}
func TestManagedKeyStatsCountsOrphanUsageAndAliasLifecycleOnce(t *testing.T) {
store, err := repository.OpenSQLiteUsage(filepath.Join(t.TempDir(), "usage.db"))
if err != nil {
t.Fatal(err)
}
defer store.Close()
if _, err := store.BootstrapManagedKey(context.Background(), "default", "000000"); err != nil {
t.Fatal(err)
}
startedAt := time.Date(2026, 8, 14, 17, 41, 30, 0, time.UTC)
cost := int64(25)
if err := store.Insert(context.Background(), collection.Record{
ManagedKeyID: "key_default", RequestedAt: startedAt.Add(4 * time.Millisecond),
APIKey: "000000", Model: "deepseek-v4-flash", InputTokens: 90,
OutputTokens: 7, TotalTokens: 97, CostMicros: &cost,
}); err != nil {
t.Fatal(err)
}
if err := store.UpsertRequest(context.Background(), collection.RequestRecord{
ManagedKeyID: "key_default", RequestID: "request-alias", RequestedAt: startedAt,
CompletedAt: startedAt.Add(time.Second), Model: "deepseek-source-a",
Outcome: "succeeded", StatusCode: 200,
}); err != nil {
t.Fatal(err)
}
stats, err := store.KeyStats(context.Background(), "key_default", startedAt.Add(-time.Hour))
if err != nil {
t.Fatal(err)
}
if stats.Total.Requests != 1 || stats.Total.InputTokens != 90 || stats.Total.OutputTokens != 7 ||
stats.Total.TotalTokens != 97 || stats.Total.CostMicros != 25 {
t.Fatalf("stats = %+v", stats)
}
}