266 lines
8.9 KiB
Go
266 lines
8.9 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|