feat: complete remaining Go backend modules

This commit is contained in:
zn-admin committed 2026-08-14 00:02:43 +08:00
1 parent cea2751dc5
commit aef5a97165
145 files changed
+18376 -199

No files matched your search

+162
View File
@@ -0,0 +1,162 @@
package usage
import (
"context"
"errors"
"fmt"
"reflect"
"testing"
"time"
)
func TestPersonalReportFiltersDuplicateAPIAndMockEvents(t *testing.T) {
repo := &reportRepository{events: []Event{{ID: "1", JobID: "job", OwnerID: "user", Source: "platform", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-08-13T01:00:00Z"}, {ID: "duplicate", JobID: "job", OwnerID: "user", Source: "platform", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-08-13T00:00:00Z"}, {ID: "api", JobID: "api", OwnerID: "user", Source: "api", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-08-13T00:00:00Z"}, {ID: "mock", JobID: "mock", OwnerID: "user", Source: "platform", Capability: "image.generate", Provider: "mock", CreatedAt: "2026-08-13T00:00:00Z"}}}
report, err := (Service{Repository: repo}).Personal(context.Background(), PersonalRequest{AccountID: "user", Preset: PresetToday, Now: time.Date(2026, 8, 13, 1, 0, 0, 0, time.UTC)})
if err != nil {
t.Fatal(err)
}
if report.Total != 1 || len(report.Recent) != 1 || report.ByCapability[0].Count != 1 {
t.Fatalf("report=%+v", report)
}
}
func TestAdminReportEmitsEmptyOptionArraysInsteadOfNull(t *testing.T) {
report, err := (Service{Repository: &reportRepository{}}).Admin(context.Background(), AdminRequest{Now: time.Date(2026, 8, 13, 1, 0, 0, 0, time.UTC)})
if err != nil {
t.Fatal(err)
}
if report.Options.Organizations == nil || report.Options.Accounts == nil || report.Options.Providers == nil || report.Trend == nil {
t.Fatalf("report contains nil collection: %#v", report)
}
}
func TestAdminReportBuildsAccountOptionsFromBaseRangeBeforeReportFilters(t *testing.T) {
repo := &reportRepository{events: []Event{
{ID: "selected", JobID: "job-selected", OwnerID: "account-a", Source: "platform", Capability: "image.generate", Provider: "bailian", OrganizationID: "org-a", AccountDisplayName: "甲账号", AccountUsername: "13800000001", CreatedAt: "2026-08-12T01:00:00Z"},
{ID: "same-org", JobID: "job-same-org", OwnerID: "account-b", Source: "platform", Capability: "video.generate", Provider: "seedance", OrganizationID: "org-a", AccountDisplayName: "乙账号", CreatedAt: "2026-08-12T00:00:00Z"},
{ID: "other-org", JobID: "job-other-org", OwnerID: "account-c", Source: "platform", Capability: "image.generate", Provider: "evolink", OrganizationID: "org-b", AccountDisplayName: "丙账号", CreatedAt: "2026-08-11T00:00:00Z"},
}}
report, err := (Service{Repository: repo}).Admin(context.Background(), AdminRequest{
Requester: Requester{OrganizationID: "org-a"},
OwnerID: "account-a",
Capability: "image.generate",
Provider: "bailian",
Now: time.Date(2026, 8, 13, 1, 0, 0, 0, time.UTC),
})
if err != nil {
t.Fatal(err)
}
if report.Summary.Total != 1 {
t.Fatalf("filtered report total = %d, want 1", report.Summary.Total)
}
want := []Option{{Value: "account-b", Label: "乙账号"}, {Value: "account-a", Label: "甲账号(13800000001)"}}
if !reflect.DeepEqual(report.Options.Accounts, want) {
t.Fatalf("account options = %#v, want %#v", report.Options.Accounts, want)
}
}
func TestAdminReportIncludesInjectedOrganizationsWithoutUsage(t *testing.T) {
source := organizationOptionSourceStub{options: []Option{
{Value: "org-no-usage", Label: "零用量组织"},
{Value: "org-used", Label: "组织目录名称"},
}}
repo := &reportRepository{events: []Event{{ID: "used", JobID: "job-used", OwnerID: "account", Source: "platform", Capability: "image.generate", Provider: "bailian", OrganizationID: "org-used", CreatedAt: "2026-08-12T01:00:00Z"}}}
report, err := (Service{Repository: repo, OrganizationOptions: source}).Admin(context.Background(), AdminRequest{Now: time.Date(2026, 8, 13, 1, 0, 0, 0, time.UTC)})
if err != nil {
t.Fatal(err)
}
want := []Option{{Value: "org-used", Label: "组织目录名称"}, {Value: "org-no-usage", Label: "零用量组织"}}
if !reflect.DeepEqual(report.Options.Organizations, want) {
t.Fatalf("organization options = %#v, want %#v", report.Options.Organizations, want)
}
recent := report.Recent.([]Record)
if len(recent) != 1 || recent[0].OrganizationName != "组织目录名称" {
t.Fatalf("recent records = %#v", recent)
}
}
func TestAdminReportAggregatesLongRangesByMonth(t *testing.T) {
repo := &reportRepository{events: []Event{
{ID: "jan", JobID: "job-jan", OwnerID: "account", Source: "platform", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-01-31T16:30:00Z"},
{ID: "apr", JobID: "job-apr", OwnerID: "account", Source: "platform", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-04-01T00:00:00Z"},
}}
report, err := (Service{Repository: repo}).Admin(context.Background(), AdminRequest{StartDate: "2026-01-01", EndDate: "2026-04-30"})
if err != nil {
t.Fatal(err)
}
want := []TrendPoint{{Date: "2026-02", Label: "2026-02", Count: 1}, {Date: "2026-04", Label: "2026-04", Count: 1}}
if !reflect.DeepEqual(report.Trend, want) {
t.Fatalf("trend = %#v, want %#v", report.Trend, want)
}
}
func TestAdminReportRejectsRangesLongerThan3660Days(t *testing.T) {
_, err := (Service{Repository: &reportRepository{}}).Admin(context.Background(), AdminRequest{StartDate: "2016-01-01", EndDate: "2026-08-13"})
if !errors.Is(err, ErrDateRangeTooLong) {
t.Fatalf("error = %v, want ErrDateRangeTooLong", err)
}
}
func TestAdminReportLimitsRecentAndUsesCanonicalLabels(t *testing.T) {
events := make([]Event, 101)
for i := range events {
provider := "evolink"
if i == 0 {
provider = "volcengine-visual"
}
events[i] = Event{ID: fmt.Sprintf("event-%03d", i), JobID: fmt.Sprintf("job-%03d", i), OwnerID: "account", Source: "platform", Capability: "image.generate", Provider: provider, CreatedAt: fmt.Sprintf("2026-08-12T%02d:%02d:00Z", i/60, i%60)}
}
report, err := (Service{Repository: &reportRepository{events: events}}).Admin(context.Background(), AdminRequest{Now: time.Date(2026, 8, 13, 1, 0, 0, 0, time.UTC)})
if err != nil {
t.Fatal(err)
}
recent := report.Recent.([]Record)
if len(recent) != 100 {
t.Fatalf("recent length = %d, want 100", len(recent))
}
if recent[99].ID != "event-001" {
t.Fatalf("last recent id = %q, want event-001", recent[99].ID)
}
if recent[0].OrganizationID != UnassignedOrganizationID {
t.Fatalf("organization id = %q, want %q", recent[0].OrganizationID, UnassignedOrganizationID)
}
if recent[0].ProviderLabel != "EvoLink" || report.Options.Providers[1] != (Option{Value: "volcengine-visual", Label: "即梦图片"}) {
t.Fatalf("provider labels: record=%q options=%#v", recent[0].ProviderLabel, report.Options.Providers)
}
}
func TestAdminReportIncludesRangeLabelAndDeterministicBreakdowns(t *testing.T) {
repo := &reportRepository{events: []Event{
{ID: "unassigned", JobID: "job-unassigned", OwnerID: "account-z", AccountDisplayName: "乙账号", Source: "platform", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-08-12T03:00:00Z"},
{ID: "org-b", JobID: "job-b", OwnerID: "account-b", AccountDisplayName: "甲账号", OrganizationID: "org-b", OrganizationName: "乙组织", Source: "platform", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-08-12T02:00:00Z"},
{ID: "org-a", JobID: "job-a", OwnerID: "account-a", AccountDisplayName: "丙账号", OrganizationID: "org-a", OrganizationName: "甲组织", Source: "platform", Capability: "image.generate", Provider: "bailian", CreatedAt: "2026-08-12T01:00:00Z"},
}}
report, err := (Service{Repository: repo}).Admin(context.Background(), AdminRequest{StartDate: "2026-08-11", EndDate: "2026-08-12"})
if err != nil {
t.Fatal(err)
}
if report.Range.Label != "2026-08-11 至 2026-08-12" {
t.Fatalf("range label = %q", report.Range.Label)
}
organizations := report.Organizations.([]organizationRow)
if got := []string{organizations[0].OrganizationID, organizations[1].OrganizationID, organizations[2].OrganizationID}; !reflect.DeepEqual(got, []string{"org-a", "org-b", UnassignedOrganizationID}) {
t.Fatalf("organization order = %#v", got)
}
accounts := report.Accounts.([]accountRow)
if got := []string{accounts[0].OwnerID, accounts[1].OwnerID, accounts[2].OwnerID}; !reflect.DeepEqual(got, []string{"account-z", "account-b", "account-a"}) {
t.Fatalf("account order = %#v", got)
}
}
type reportRepository struct{ events []Event }
func (r *reportRepository) Insert(e Event) (Event, bool, error) { return e, true, nil }
func (r *reportRepository) List(Filters) ([]Event, error) { return r.events, nil }
type organizationOptionSourceStub struct{ options []Option }
func (s organizationOptionSourceStub) ListOrganizationOptions(context.Context, Requester) ([]Option, error) {
return s.options, nil
}