230 lines
7.1 KiB
Go
230 lines
7.1 KiB
Go
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)
|
||
}
|
||
})
|
||
}
|
||
}
|