Files
oci-portal/internal/oci/instance_test.go
T

182 lines
5.3 KiB
Go

package oci
import (
"encoding/base64"
"strings"
"testing"
"github.com/oracle/oci-go-sdk/v65/core"
)
func TestShellSingleQuote(t *testing.T) {
tests := []struct {
name string
in string
want string
}{
{name: "普通", in: "Passw0rd", want: "'Passw0rd'"},
{name: "含单引号", in: "a'b", want: `'a'\''b'`},
{name: "含空格特殊符", in: "p @ss!", want: "'p @ss!'"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := shellSingleQuote(tt.in); got != tt.want {
t.Errorf("shellSingleQuote(%q) = %q, want %q", tt.in, got, tt.want)
}
})
}
}
func TestRootPasswordUserData(t *testing.T) {
encoded := rootPasswordUserData("Secret123")
raw, err := base64.StdEncoding.DecodeString(encoded)
if err != nil {
t.Fatalf("result is not base64: %v", err)
}
script := string(raw)
for _, want := range []string{
"#!/bin/bash",
"echo root:'Secret123' | chpasswd",
"PermitRootLogin yes",
"PasswordAuthentication yes",
"systemctl restart sshd",
} {
if !strings.Contains(script, want) {
t.Errorf("script missing %q\n---\n%s", want, script)
}
}
}
func TestBuildInstanceMetadataRootPasswordOverridesUserData(t *testing.T) {
md := buildInstanceMetadata(CreateInstanceInput{
RootPassword: "pw",
UserData: "ZXhwbGljaXQ=", // "explicit"
})
decoded, _ := base64.StdEncoding.DecodeString(md["user_data"])
if !strings.Contains(string(decoded), "chpasswd") {
t.Errorf("user_data = %q, want root password script (not explicit userData)", md["user_data"])
}
}
func TestBuildInstanceSourceVpusPerGB(t *testing.T) {
src := buildInstanceSource(CreateInstanceInput{ImageID: "img", BootVolumeSizeGBs: 100, BootVolumeVpusPerGB: 20})
img, ok := src.(core.InstanceSourceViaImageDetails)
if !ok {
t.Fatalf("source type = %T, want image", src)
}
if img.BootVolumeVpusPerGB == nil || *img.BootVolumeVpusPerGB != 20 {
t.Errorf("BootVolumeVpusPerGB = %v, want 20", img.BootVolumeVpusPerGB)
}
}
func TestBuildInstanceSource(t *testing.T) {
tests := []struct {
name string
in CreateInstanceInput
wantType string
}{
{
name: "从镜像启动",
in: CreateInstanceInput{ImageID: "ocid1.image..a", BootVolumeSizeGBs: 100},
wantType: "image",
},
{
name: "从引导卷启动",
in: CreateInstanceInput{BootVolumeID: "ocid1.bootvolume..b"},
wantType: "bootVolume",
},
{
name: "引导卷优先于镜像",
in: CreateInstanceInput{ImageID: "ocid1.image..a", BootVolumeID: "ocid1.bootvolume..b"},
wantType: "bootVolume",
},
{
name: "两者皆无返回 nil",
in: CreateInstanceInput{},
wantType: "nil",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
src := buildInstanceSource(tt.in)
switch tt.wantType {
case "image":
img, ok := src.(core.InstanceSourceViaImageDetails)
if !ok {
t.Fatalf("source type = %T, want InstanceSourceViaImageDetails", src)
}
if got, want := *img.BootVolumeSizeInGBs, tt.in.BootVolumeSizeGBs; got != want {
t.Errorf("BootVolumeSizeInGBs = %d, want %d", got, want)
}
case "bootVolume":
if _, ok := src.(core.InstanceSourceViaBootVolumeDetails); !ok {
t.Fatalf("source type = %T, want InstanceSourceViaBootVolumeDetails", src)
}
case "nil":
if src != nil {
t.Fatalf("source = %v, want nil", src)
}
}
})
}
}
func TestBuildLaunchDetails(t *testing.T) {
in := CreateInstanceInput{
AvailabilityDomain: "AD-1",
DisplayName: "vm1",
Shape: "VM.Standard.A1.Flex",
Ocpus: 2,
MemoryInGBs: 12,
ImageID: "ocid1.image..a",
SubnetID: "ocid1.subnet..s",
AssignPublicIP: true,
AssignIpv6: true,
SSHPublicKey: "ssh-ed25519 AAA",
}
details, err := buildLaunchDetails("ocid1.tenancy..t", in)
if err != nil {
t.Fatalf("buildLaunchDetails: %v", err)
}
if got, want := *details.ShapeConfig.Ocpus, float32(2); got != want {
t.Errorf("Ocpus = %v, want %v", got, want)
}
if got, want := *details.ShapeConfig.MemoryInGBs, float32(12); got != want {
t.Errorf("MemoryInGBs = %v, want %v", got, want)
}
if got, want := *details.CreateVnicDetails.SubnetId, in.SubnetID; got != want {
t.Errorf("SubnetId = %v, want %v", got, want)
}
if details.CreateVnicDetails.AssignIpv6Ip == nil || !*details.CreateVnicDetails.AssignIpv6Ip {
t.Error("AssignIpv6Ip not set, want true")
}
if got, want := details.Metadata["ssh_authorized_keys"], in.SSHPublicKey; got != want {
t.Errorf("ssh_authorized_keys = %q, want %q", got, want)
}
}
func TestBuildLaunchDetailsRequiresSource(t *testing.T) {
_, err := buildLaunchDetails("ocid1.tenancy..t", CreateInstanceInput{
AvailabilityDomain: "AD-1", DisplayName: "vm1", Shape: "shape", SubnetID: "sub",
})
if err == nil {
t.Error("buildLaunchDetails without source: got nil error, want failure")
}
}
func TestBuildLaunchDetailsNoShapeConfig(t *testing.T) {
details, err := buildLaunchDetails("t", CreateInstanceInput{
AvailabilityDomain: "AD-1", DisplayName: "vm1", Shape: "VM.Standard.E2.1.Micro",
ImageID: "img", SubnetID: "sub",
})
if err != nil {
t.Fatalf("buildLaunchDetails: %v", err)
}
if details.ShapeConfig != nil {
t.Errorf("ShapeConfig = %+v, want nil for fixed shape", details.ShapeConfig)
}
if details.Metadata != nil {
t.Errorf("Metadata = %v, want nil when no key and user data", details.Metadata)
}
}