初始提交:OCI 面板后端(含 GenAI 网关一期)
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// TestRealOCITenantUsers 验证用户管理全链路:新增 → 重置密码 →
|
||||
// 清 MFA → 清 API Key → 删除,全部只操作新建的测试用户。
|
||||
func TestRealOCITenantUsers(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(), 5*time.Minute)
|
||||
defer cancel()
|
||||
cred := loadTestCredentials(t, "试用期")
|
||||
client := NewClient()
|
||||
homeRegion := cred.Region
|
||||
|
||||
users, err := client.ListTenantUsers(ctx, cred)
|
||||
if err != nil {
|
||||
t.Fatalf("ListTenantUsers: %v", err)
|
||||
}
|
||||
if len(users) == 0 {
|
||||
t.Fatal("no tenant users, want at least the current one")
|
||||
}
|
||||
currentSeen := false
|
||||
for _, u := range users {
|
||||
if u.IsCurrentUser {
|
||||
currentSeen = true
|
||||
}
|
||||
}
|
||||
if !currentSeen {
|
||||
t.Error("current user not flagged in list")
|
||||
}
|
||||
t.Logf("tenant users: %d", len(users))
|
||||
|
||||
name := fmt.Sprintf("oci-portal-e2e-user-%d", time.Now().Unix())
|
||||
user, err := client.CreateTenantUser(ctx, cred, homeRegion, CreateTenantUserInput{
|
||||
Name: name, Description: "e2e temp user", Email: name + "@example.com",
|
||||
GivenName: "E2E", FamilyName: "Temp",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateTenantUser: %v", err)
|
||||
}
|
||||
t.Logf("created user %s (%s)", user.Name, user.ID)
|
||||
defer func() {
|
||||
if err := client.DeleteTenantUser(ctx, cred, homeRegion, user.ID); err != nil {
|
||||
t.Errorf("cleanup: DeleteTenantUser: %v", err)
|
||||
} else {
|
||||
t.Log("cleanup: user deleted")
|
||||
}
|
||||
}()
|
||||
|
||||
newDesc, newEmail, newGiven := "e2e updated", name+"+upd@example.com", "E2E2"
|
||||
updated, err := client.UpdateTenantUser(ctx, cred, homeRegion, user.ID, UpdateTenantUserInput{
|
||||
Description: &newDesc, Email: &newEmail, GivenName: &newGiven,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateTenantUser: %v", err)
|
||||
}
|
||||
if updated.Description != newDesc {
|
||||
t.Errorf("updated description = %q, want %q", updated.Description, newDesc)
|
||||
}
|
||||
t.Logf("updated user desc=%q email=%q", updated.Description, updated.Email)
|
||||
|
||||
password, err := client.ResetTenantUserPassword(ctx, cred, homeRegion, user.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("ResetTenantUserPassword: %v", err)
|
||||
}
|
||||
if password == "" {
|
||||
t.Error("reset password returned empty password")
|
||||
}
|
||||
t.Logf("password reset ok (len=%d)", len(password))
|
||||
|
||||
mfaDeleted, err := client.DeleteTenantUserMfaDevices(ctx, cred, homeRegion, user.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteTenantUserMfaDevices: %v", err)
|
||||
}
|
||||
t.Logf("mfa devices deleted: %d", mfaDeleted)
|
||||
|
||||
keysDeleted, err := client.DeleteTenantUserApiKeys(ctx, cred, homeRegion, user.ID, false)
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteTenantUserApiKeys: %v", err)
|
||||
}
|
||||
t.Logf("api keys deleted: %d", keysDeleted)
|
||||
}
|
||||
|
||||
// TestRealOCIDomainSettings 验证通知收件人与密码策略的改后恢复。
|
||||
func TestRealOCIDomainSettings(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(), 5*time.Minute)
|
||||
defer cancel()
|
||||
cred := loadTestCredentials(t, "试用期")
|
||||
client := NewClient()
|
||||
region := cred.Region
|
||||
|
||||
original, err := client.GetNotificationRecipients(ctx, cred, region)
|
||||
if err != nil {
|
||||
t.Fatalf("GetNotificationRecipients: %v", err)
|
||||
}
|
||||
t.Logf("original recipients=%v testMode=%v", original.Recipients, original.TestModeEnabled)
|
||||
updated, err := client.UpdateNotificationRecipients(ctx, cred, region, []string{"e2e@example.com"})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateNotificationRecipients: %v", err)
|
||||
}
|
||||
if !updated.TestModeEnabled || len(updated.Recipients) != 1 {
|
||||
t.Errorf("updated = %+v, want test mode with 1 recipient", updated)
|
||||
}
|
||||
restoreRecipients := original.Recipients
|
||||
if !original.TestModeEnabled {
|
||||
restoreRecipients = nil
|
||||
}
|
||||
if _, err := client.UpdateNotificationRecipients(ctx, cred, region, restoreRecipients); err != nil {
|
||||
t.Errorf("restore recipients: %v", err)
|
||||
} else {
|
||||
t.Log("recipients restored")
|
||||
}
|
||||
|
||||
policies, err := client.ListPasswordPolicies(ctx, cred, region)
|
||||
if err != nil {
|
||||
t.Fatalf("ListPasswordPolicies: %v", err)
|
||||
}
|
||||
target, ok := pickCustomPolicy(policies)
|
||||
if !ok {
|
||||
t.Fatalf("no Custom password policy among %d policies (built-ins are read-only)", len(policies))
|
||||
}
|
||||
t.Logf("policies: %d, patch target %s strength=%s expires=%v", len(policies), target.Name, target.PasswordStrength, target.PasswordExpiresAfter)
|
||||
days := 350
|
||||
patched, err := client.UpdatePasswordPolicy(ctx, cred, region, target.ID, UpdatePasswordPolicyInput{PasswordExpiresAfter: &days})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdatePasswordPolicy: %v", err)
|
||||
}
|
||||
if patched.PasswordExpiresAfter == nil || *patched.PasswordExpiresAfter != days {
|
||||
t.Errorf("passwordExpiresAfter = %v, want %d", patched.PasswordExpiresAfter, days)
|
||||
}
|
||||
restore := 0
|
||||
if target.PasswordExpiresAfter != nil {
|
||||
restore = *target.PasswordExpiresAfter
|
||||
}
|
||||
if _, err := client.UpdatePasswordPolicy(ctx, cred, region, target.ID, UpdatePasswordPolicyInput{PasswordExpiresAfter: &restore}); err != nil {
|
||||
t.Errorf("restore password policy: %v", err)
|
||||
} else {
|
||||
t.Logf("policy restored to %d", restore)
|
||||
}
|
||||
}
|
||||
|
||||
// pickCustomPolicy 返回 Custom 强度的密码策略;Simple/Standard 内置策略只读不可改。
|
||||
func pickCustomPolicy(policies []PasswordPolicyInfo) (PasswordPolicyInfo, bool) {
|
||||
for _, p := range policies {
|
||||
if strings.EqualFold(p.PasswordStrength, "custom") {
|
||||
return p, true
|
||||
}
|
||||
}
|
||||
return PasswordPolicyInfo{}, false
|
||||
}
|
||||
|
||||
// TestRealOCIUsageAndTraffic 验证成本分析与实例流量统计:
|
||||
// 成本查最近 7 天;流量需要真实 VNIC,无实例时创建一台临时 Micro 实例。
|
||||
func TestRealOCIUsageAndTraffic(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(), 15*time.Minute)
|
||||
defer cancel()
|
||||
cred := loadTestCredentials(t, "试用期")
|
||||
client := NewClient()
|
||||
|
||||
end := time.Now().UTC().Truncate(24*time.Hour).AddDate(0, 0, 1)
|
||||
items, err := client.SummarizeCosts(ctx, cred, CostQuery{
|
||||
StartTime: end.AddDate(0, 0, -7),
|
||||
EndTime: end,
|
||||
Granularity: "DAILY",
|
||||
QueryType: "COST",
|
||||
GroupBy: "service",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SummarizeCosts: %v", err)
|
||||
}
|
||||
t.Logf("cost items: %d", len(items))
|
||||
for i, item := range items {
|
||||
if i >= 3 {
|
||||
break
|
||||
}
|
||||
t.Logf(" %s %s %.4f %s", item.TimeStart, item.GroupValue, item.ComputedAmount, item.Currency)
|
||||
}
|
||||
|
||||
instanceID := ensureTrafficInstance(ctx, t, client, cred)
|
||||
traffic, err := client.SummarizeInstanceTraffic(ctx, cred, TrafficQuery{
|
||||
InstanceID: instanceID,
|
||||
StartTime: end.AddDate(0, 0, -7),
|
||||
EndTime: end,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("SummarizeInstanceTraffic: %v", err)
|
||||
}
|
||||
t.Logf("traffic vnics=%d in=%.0fB out=%.0fB", len(traffic.Vnics), traffic.InboundBytes, traffic.OutboundBytes)
|
||||
if len(traffic.Vnics) == 0 {
|
||||
t.Error("no vnic in traffic result, want at least one")
|
||||
}
|
||||
}
|
||||
|
||||
// ensureTrafficInstance 返回一个可查流量的实例:已有实例直接用,
|
||||
// 否则创建一台临时 E2 Micro(测试结束删除)。
|
||||
func ensureTrafficInstance(ctx context.Context, t *testing.T, client *RealClient, cred Credentials) string {
|
||||
t.Helper()
|
||||
instances, err := client.ListInstances(ctx, cred, "")
|
||||
if err != nil {
|
||||
t.Fatalf("ListInstances: %v", err)
|
||||
}
|
||||
for _, in := range instances {
|
||||
if in.LifecycleState == "RUNNING" || in.LifecycleState == "STOPPED" {
|
||||
t.Logf("using existing instance %s", in.ID)
|
||||
return in.ID
|
||||
}
|
||||
}
|
||||
images, err := client.ListImages(ctx, cred, ImagesQuery{OperatingSystem: "Canonical Ubuntu", Shape: "VM.Standard.E2.1.Micro"})
|
||||
if err != nil || len(images) == 0 {
|
||||
t.Skipf("no micro image available: %v", err)
|
||||
}
|
||||
instance, err := client.LaunchInstance(ctx, cred, CreateInstanceInput{
|
||||
DisplayName: fmt.Sprintf("oci-portal-e2e-traffic-%d", time.Now().Unix()),
|
||||
Shape: "VM.Standard.E2.1.Micro",
|
||||
ImageID: images[0].ID,
|
||||
AssignPublicIP: false,
|
||||
SSHPublicKey: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEfake+e2e+key oci-portal-e2e",
|
||||
})
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "capacity") || strings.Contains(err.Error(), "LimitExceeded") {
|
||||
t.Skipf("micro capacity unavailable: %v", err)
|
||||
}
|
||||
t.Fatalf("LaunchInstance: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
terminateAndWait(t, client, cred, instance.ID)
|
||||
cleanupAutoVCNIfAny(t, client, cred)
|
||||
})
|
||||
// VNIC attachment 在实例 RUNNING 后才就绪,否则流量查询列不到 VNIC
|
||||
if err := waitInstanceState(ctx, client, cred, instance.ID, "RUNNING"); err != nil {
|
||||
t.Fatalf("wait instance running: %v", err)
|
||||
}
|
||||
return instance.ID
|
||||
}
|
||||
|
||||
// cleanupAutoVCNIfAny 清理临时实例触发自动建网留下的 auto-vcn。
|
||||
func cleanupAutoVCNIfAny(t *testing.T, client *RealClient, cred Credentials) {
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
|
||||
defer cancel()
|
||||
vcns, err := client.ListVCNs(ctx, cred, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
for _, v := range vcns {
|
||||
if v.DisplayName == autoVcnName {
|
||||
cleanupVCN(t, client, cred, v.ID)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user