初始提交:OCI 面板后端(含 GenAI 网关一期)

This commit is contained in:
Wang Defa
2026-07-09 15:31:04 +08:00
commit b9a3e97e84
168 changed files with 31794 additions and 0 deletions
+229
View File
@@ -0,0 +1,229 @@
package oci
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
"oci-portal/internal/model"
)
// testKeyDir 指向仓库根的真实 API Key 测试资料目录。
const testKeyDir = "../../../test-api-key"
// keyCase 把测试 key 文件名映射到期望账户类别;expect 为空表示只打印不断言。
type keyCase struct {
name string
expect string
}
func integrationKeyCases(t *testing.T) []keyCase {
t.Helper()
all := []keyCase{
{name: "试用期", expect: model.AccountTypeTrial},
{name: "付费", expect: model.AccountTypePaid},
{name: "免费01", expect: model.AccountTypeFree},
{name: "免费02", expect: model.AccountTypeFree},
{name: "未知01"},
{name: "未知02"},
}
if name := os.Getenv("OCI_TEST_KEY"); name != "" {
for _, k := range all {
if k.name == name {
return []keyCase{k}
}
}
t.Fatalf("OCI_TEST_KEY=%q not found in test-api-key", name)
}
if os.Getenv("OCI_TEST_ALL_KEYS") == "1" {
return all
}
// 默认只使用试用期 key,避免不必要地触碰其他真实账号
return all[:1]
}
func loadTestCredentials(t *testing.T, name string) Credentials {
t.Helper()
iniPath := filepath.Join(testKeyDir, name+"-api-key.ini")
pemPath := filepath.Join(testKeyDir, name+"-api-key.pem")
iniText, err := os.ReadFile(iniPath)
if err != nil {
t.Fatalf("read %s: %v", iniPath, err)
}
pem, err := os.ReadFile(pemPath)
if err != nil {
t.Fatalf("read %s: %v", pemPath, err)
}
cred, err := ParseConfigINI(string(iniText))
if err != nil {
t.Fatalf("parse %s: %v", iniPath, err)
}
cred.PrivateKey = string(pem)
if err := cred.Validate(); err != nil {
t.Fatalf("validate credentials of %s: %v", name, err)
}
return cred
}
// TestRealOCITenantRegionsAndLimits 用试用期 key 验证区域订阅列表和
// 配额查询(均为只读调用)。订阅新区域不可撤销,不做真实测试。
func TestRealOCITenantRegionsAndLimits(t *testing.T) {
if os.Getenv("OCI_INTEGRATION_TEST") != "1" {
t.Skip("set OCI_INTEGRATION_TEST=1 to run real OCI tests")
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
cred := loadTestCredentials(t, "试用期")
client := NewClient()
subs, err := client.ListRegionSubscriptions(ctx, cred)
if err != nil {
t.Fatalf("ListRegionSubscriptions: %v", err)
}
if len(subs) == 0 {
t.Fatal("ListRegionSubscriptions returned empty list, want at least home region")
}
hasHome := false
for _, sub := range subs {
t.Logf("region key=%s name=%s status=%s home=%v", sub.Key, sub.Name, sub.Status, sub.IsHomeRegion)
hasHome = hasHome || sub.IsHomeRegion
}
if !hasHome {
t.Error("no home region in subscriptions")
}
values, err := client.ListLimits(ctx, cred, LimitsQuery{
Service: "compute",
Name: "standard-a1-core-count",
WithAvailability: true,
})
if err != nil {
t.Fatalf("ListLimits: %v", err)
}
if len(values) == 0 {
t.Fatal("ListLimits returned empty list for standard-a1-core-count")
}
for _, v := range values {
used, available := int64(-1), int64(-1)
if v.Used != nil {
used = *v.Used
}
if v.Available != nil {
available = *v.Available
}
t.Logf("limit name=%s scope=%s ad=%s value=%d used=%d available=%d",
v.Name, v.ScopeType, v.AvailabilityDomain, v.Value, used, available)
}
// 模糊过滤应比精确名命中更多配额(a1 与 e2 等系列都含 core-count
fuzzy, err := client.ListLimits(ctx, cred, LimitsQuery{Service: "compute", Name: "core-count"})
if err != nil {
t.Fatalf("ListLimits fuzzy: %v", err)
}
t.Logf("fuzzy core-count matched %d entries", len(fuzzy))
if len(fuzzy) <= len(values) {
t.Errorf("fuzzy matches = %d, want more than exact matches %d", len(fuzzy), len(values))
}
services, err := client.ListLimitServices(ctx, cred, "")
if err != nil {
t.Fatalf("ListLimitServices: %v", err)
}
if len(services) == 0 {
t.Fatal("ListLimitServices returned empty list")
}
hasCompute := false
for _, s := range services {
hasCompute = hasCompute || s.Name == "compute"
}
if !hasCompute {
t.Errorf("services (%d) missing compute", len(services))
}
t.Logf("limit services: %d entries, first=%+v", len(services), services[0])
}
// TestRealOCISubscriptions 用试用期 key 验证订阅列表与订阅详情(只读)。
func TestRealOCISubscriptions(t *testing.T) {
if os.Getenv("OCI_INTEGRATION_TEST") != "1" {
t.Skip("set OCI_INTEGRATION_TEST=1 to run real OCI tests")
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
cred := loadTestCredentials(t, "试用期")
client := NewClient()
subs, err := client.ListSubscriptions(ctx, cred)
if err != nil {
t.Fatalf("ListSubscriptions: %v", err)
}
if len(subs) == 0 {
t.Fatal("ListSubscriptions returned empty list")
}
cloudcmID := ""
for _, sub := range subs {
t.Logf("subscription id=%s service=%s created=%v", sub.ID, sub.ServiceName, sub.TimeCreated)
if strings.EqualFold(sub.ServiceName, cloudcmServiceName) {
cloudcmID = sub.ID
}
}
if cloudcmID == "" {
t.Fatal("no CLOUDCM subscription in list")
}
detail, err := client.GetSubscription(ctx, cred, cloudcmID)
if err != nil {
t.Fatalf("GetSubscription: %v", err)
}
if detail.ID != cloudcmID {
t.Errorf("detail.ID = %q, want %q", detail.ID, cloudcmID)
}
t.Logf("detail service=%s paymentModel=%q tier=%q state=%s currency=%s region=%s promotions=%d",
detail.ServiceName, detail.PaymentModel, detail.SubscriptionTier,
detail.LifecycleState, detail.CloudAmountCurrency, detail.RegionAssignment, len(detail.Promotions))
for _, p := range detail.Promotions {
t.Logf("promotion status=%s amount=%.2f %s expires=%v intentToPay=%v",
p.Status, p.Amount, p.CurrencyUnit, p.TimeExpired, p.IsIntentToPay)
}
if len(detail.Promotions) == 0 {
t.Error("trial subscription has no promotions, want at least one")
}
}
// TestRealOCIVerifyAndClassify 走真实 OCI 验证测活与账户类别判定,
// 必须显式设置 OCI_INTEGRATION_TEST=1 才会执行。
func TestRealOCIVerifyAndClassify(t *testing.T) {
if os.Getenv("OCI_INTEGRATION_TEST") != "1" {
t.Skip("set OCI_INTEGRATION_TEST=1 to run real OCI tests")
}
client := NewClient()
for _, tc := range integrationKeyCases(t) {
t.Run(tc.name, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancel()
cred := loadTestCredentials(t, tc.name)
info, err := client.ValidateKey(ctx, cred)
if err != nil {
t.Fatalf("ValidateKey: %v", err)
}
t.Logf("tenancy=%s homeRegion=%s", info.Name, info.HomeRegionKey)
profile, err := client.FetchAccountProfile(ctx, cred)
if err != nil {
t.Fatalf("FetchAccountProfile: %v", err)
}
t.Logf("type=%s paymentModel=%q tier=%q promo=%q amount=%.2f service=%s",
profile.AccountType, profile.PaymentModel, profile.SubscriptionTier,
profile.PromotionStatus, profile.PromotionAmount, profile.ServiceName)
if !strings.EqualFold(profile.ServiceName, cloudcmServiceName) {
t.Errorf("ServiceName = %q, want %q", profile.ServiceName, cloudcmServiceName)
}
if tc.expect != "" && profile.AccountType != tc.expect {
t.Errorf("AccountType = %q, want %q", profile.AccountType, tc.expect)
}
})
}
}