Files

179 lines
6.4 KiB
Go

package service
import (
"context"
"strings"
"testing"
"oci-portal/internal/oci"
)
func TestCreateInstanceValidation(t *testing.T) {
valid := oci.CreateInstanceInput{
AvailabilityDomain: "AD-1",
DisplayName: "vm1",
Shape: "VM.Standard.A1.Flex",
ImageID: "ocid1.image..a",
SubnetID: "ocid1.subnet..s",
}
tests := []struct {
name string
mutate func(*oci.CreateInstanceInput)
wantErr string
}{
{name: "合法输入", mutate: func(*oci.CreateInstanceInput) {}},
{name: "缺名称", mutate: func(in *oci.CreateInstanceInput) { in.DisplayName = "" }, wantErr: "displayName"},
{name: "可用域可空(默认 ad-1)", mutate: func(in *oci.CreateInstanceInput) { in.AvailabilityDomain = "" }},
{name: "缺 shape", mutate: func(in *oci.CreateInstanceInput) { in.Shape = "" }, wantErr: "shape"},
{name: "缺启动源", mutate: func(in *oci.CreateInstanceInput) { in.ImageID = "" }, wantErr: "imageId or bootVolumeId"},
{name: "启动源互斥", mutate: func(in *oci.CreateInstanceInput) { in.BootVolumeID = "bv" }, wantErr: "mutually exclusive"},
{name: "子网可空(自动建网)", mutate: func(in *oci.CreateInstanceInput) { in.SubnetID = "" }},
{name: "密钥与密码互斥", mutate: func(in *oci.CreateInstanceInput) {
in.SSHPublicKey = "ssh-ed25519 AAA"
in.RootPassword = "pw"
}, wantErr: "sshPublicKey and rootPassword"},
{name: "随机密码与密钥互斥", mutate: func(in *oci.CreateInstanceInput) {
in.GenerateRootPassword = true
in.SSHPublicKey = "ssh-ed25519 AAA"
}, wantErr: "generateRootPassword"},
{name: "随机密码与固定密码互斥", mutate: func(in *oci.CreateInstanceInput) {
in.GenerateRootPassword = true
in.RootPassword = "pw"
}, wantErr: "generateRootPassword"},
}
svc := newTestService(t, &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}})
cfg := importAliveConfig(t, svc)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
in := valid
tt.mutate(&in)
_, _, err := svc.CreateInstances(context.Background(), cfg.ID, in, 1)
if tt.wantErr == "" {
if err != nil {
t.Errorf("CreateInstances: %v, want success", err)
}
return
}
if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
t.Errorf("CreateInstances error = %v, want contains %q", err, tt.wantErr)
}
})
}
}
func TestCreateInstancesBatch(t *testing.T) {
client := &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}}
svc := newTestService(t, client)
cfg := importAliveConfig(t, svc)
in := oci.CreateInstanceInput{DisplayName: "vm", Shape: "VM.Standard.A1.Flex", ImageID: "img"}
for _, count := range []int{0, 11} {
if _, _, err := svc.CreateInstances(context.Background(), cfg.ID, in, count); err == nil {
t.Errorf("count=%d: got nil error, want out-of-range failure", count)
}
}
instances, failures, err := svc.CreateInstances(context.Background(), cfg.ID, in, 3)
if err != nil {
t.Fatalf("CreateInstances: %v", err)
}
if len(failures) != 0 {
t.Errorf("failures = %v, want none", failures)
}
got := make([]string, 0, len(instances))
for _, inst := range instances {
got = append(got, inst.DisplayName)
}
want := []string{"vm-1", "vm-2", "vm-3"}
if len(got) != len(want) {
t.Fatalf("created %d instances %v, want %v", len(got), got, want)
}
for i := range want {
if got[i] != want[i] {
t.Errorf("instance %d name = %q, want %q", i, got[i], want[i])
}
}
}
func TestCreateInstancesGeneratedRootPassword(t *testing.T) {
client := &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}}
svc := newTestService(t, client)
cfg := importAliveConfig(t, svc)
in := oci.CreateInstanceInput{
DisplayName: "vm",
Shape: "VM.Standard.A1.Flex",
ImageID: "img",
GenerateRootPassword: true,
FreeformTags: map[string]string{"env": "prod"},
}
if _, _, err := svc.CreateInstances(context.Background(), cfg.ID, in, 2); err != nil {
t.Fatalf("CreateInstances: %v", err)
}
if len(client.launched) != 2 {
t.Fatalf("launched %d instances, want 2", len(client.launched))
}
pwds := map[string]bool{}
for i, got := range client.launched {
if got.RootPassword == "" {
t.Errorf("instance %d: RootPassword empty, want generated", i)
}
if got.FreeformTags["RootPassword"] != got.RootPassword {
t.Errorf("instance %d: tag RootPassword = %q, want %q", i, got.FreeformTags["RootPassword"], got.RootPassword)
}
if got.FreeformTags["env"] != "prod" {
t.Errorf("instance %d: tag env = %q, want prod (原有标签须保留)", i, got.FreeformTags["env"])
}
pwds[got.RootPassword] = true
}
if len(pwds) != 2 {
t.Errorf("批量创建密码应各台独立, got %d 个不同密码", len(pwds))
}
if in.FreeformTags["RootPassword"] != "" {
t.Error("原始输入 FreeformTags 被修改, 应复制后再写入")
}
}
func TestUpdateInstanceRequiresChange(t *testing.T) {
svc := newTestService(t, &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}})
cfg := importAliveConfig(t, svc)
if _, err := svc.UpdateInstance(context.Background(), cfg.ID, "ocid1.instance..i", oci.UpdateInstanceInput{}); err == nil {
t.Error("UpdateInstance with empty input: got nil error, want failure")
}
}
func TestCreateConsoleConnectionValidation(t *testing.T) {
svc := newTestService(t, &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}})
cfg := importAliveConfig(t, svc)
tests := []struct {
name string
key string
}{
{name: "空公钥", key: ""},
{name: "非 RSA 公钥", key: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA test"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if _, err := svc.CreateConsoleConnection(context.Background(), cfg.ID, "", "ocid1.instance..i", tt.key); err == nil {
t.Error("CreateConsoleConnection: got nil error, want failure")
}
})
}
}
func TestDeleteInstanceIpv6RequiresAddress(t *testing.T) {
svc := newTestService(t, &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}})
cfg := importAliveConfig(t, svc)
if err := svc.DeleteInstanceIpv6(context.Background(), cfg.ID, "", "ocid1.instance..i", ""); err == nil {
t.Error("DeleteInstanceIpv6 with empty address: got nil error, want failure")
}
}
func TestUpdateBootVolumeRequiresChange(t *testing.T) {
svc := newTestService(t, &fakeClient{tenancy: oci.TenancyInfo{Name: "t"}})
cfg := importAliveConfig(t, svc)
if _, err := svc.UpdateBootVolume(context.Background(), cfg.ID, "ocid1.bootvolume..b", oci.UpdateBootVolumeInput{}); err == nil {
t.Error("UpdateBootVolume with empty input: got nil error, want failure")
}
}