新增对象存储/账单/保留IP模块,删桶并发提速与成本增强
CI / test (push) Successful in 33s

This commit is contained in:
2026-07-21 12:11:05 +08:00
parent 9cfde8b702
commit f40f2a20e8
44 changed files with 7788 additions and 54 deletions
+109
View File
@@ -0,0 +1,109 @@
package service
import (
"context"
"strings"
"testing"
"oci-portal/internal/oci"
)
// billingStubClient 只桩账单方法,其余经内嵌 fakeClient 兜底。
type billingStubClient struct {
*fakeClient
invoices []oci.Invoice
lines []oci.InvoiceLine
methods []oci.PaymentMethod
pdf []byte
listYear int
paidID string
paidEmail string
}
func (f *billingStubClient) ListInvoices(ctx context.Context, cred oci.Credentials, year int) ([]oci.Invoice, error) {
f.listYear = year
return f.invoices, nil
}
func (f *billingStubClient) ListInvoiceLines(ctx context.Context, cred oci.Credentials, internalInvoiceID string) ([]oci.InvoiceLine, error) {
return f.lines, nil
}
func (f *billingStubClient) DownloadInvoicePdf(ctx context.Context, cred oci.Credentials, internalInvoiceID string, maxBytes int64) ([]byte, error) {
return f.pdf, nil
}
func (f *billingStubClient) PayInvoice(ctx context.Context, cred oci.Credentials, internalInvoiceID, email string) error {
f.paidID, f.paidEmail = internalInvoiceID, email
return nil
}
func (f *billingStubClient) ListPaymentMethods(ctx context.Context, cred oci.Credentials) ([]oci.PaymentMethod, error) {
return f.methods, nil
}
func TestInvoicesAndPaymentMethods(t *testing.T) {
client := &billingStubClient{
fakeClient: &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}},
invoices: []oci.Invoice{{ID: "inv1", Number: "100001", Status: "OPEN"}},
lines: []oci.InvoiceLine{{Product: "Compute", Total: 12.5}},
methods: []oci.PaymentMethod{{Method: "CREDIT_CARD", LastDigits: "4242"}},
pdf: []byte("%PDF-1.4 fake"),
}
svc := newTestService(t, client)
cfg := importAliveConfig(t, svc)
ctx := context.Background()
if list, err := svc.Invoices(ctx, cfg.ID, 0); err != nil || len(list) != 1 || list[0].Number != "100001" {
t.Fatalf("Invoices = %v, %v", list, err)
}
if _, err := svc.Invoices(ctx, cfg.ID, 2026); err != nil || client.listYear != 2026 {
t.Fatalf("Invoices(year) 透传 = %d, %v, want 2026", client.listYear, err)
}
if _, err := svc.Invoices(ctx, cfg.ID, 26); err == nil {
t.Fatal("Invoices(26) 应拒绝非法年份")
}
if lines, err := svc.InvoiceLines(ctx, cfg.ID, "6100"); err != nil || len(lines) != 1 {
t.Fatalf("InvoiceLines = %v, %v", lines, err)
}
if data, err := svc.InvoicePdf(ctx, cfg.ID, "6100"); err != nil || len(data) == 0 {
t.Fatalf("InvoicePdf = %d bytes, %v", len(data), err)
}
if ms, err := svc.PaymentMethods(ctx, cfg.ID); err != nil || len(ms) != 1 || ms[0].LastDigits != "4242" {
t.Fatalf("PaymentMethods = %v, %v", ms, err)
}
}
func TestPayInvoiceValidation(t *testing.T) {
client := &billingStubClient{fakeClient: &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}}}
svc := newTestService(t, client)
cfg := importAliveConfig(t, svc)
ctx := context.Background()
tests := []struct {
name, internalID, email, wantErr string
}{
{"内部 ID 为空", "", "a@b.c", "internal invoice id is empty"},
{"邮箱非法", "6100", "not-an-email", "invalid receipt email"},
{"合法请求", "6100", "billing@example.com", ""},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := svc.PayInvoice(ctx, cfg.ID, tt.internalID, tt.email)
if tt.wantErr == "" {
if err != nil {
t.Fatalf("err = %v, want nil", err)
}
if client.paidID != tt.internalID || client.paidEmail != tt.email {
t.Fatalf("透传 = (%s, %s), want (%s, %s)", client.paidID, client.paidEmail, tt.internalID, tt.email)
}
return
}
if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
t.Fatalf("err = %v, want contains %q", err, tt.wantErr)
}
})
}
}