160 lines
4.7 KiB
Go
160 lines
4.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"oci-portal/internal/oci"
|
|
)
|
|
|
|
// consoleFakeClient 在 fakeClient 上覆写控制台连接三方法:
|
|
// 首次 List 返回预置连接,删除后的后续 List 全部报告 DELETED。
|
|
type consoleFakeClient struct {
|
|
fakeClient
|
|
|
|
conns []oci.ConsoleConnection
|
|
listed int
|
|
deleted []string
|
|
created int
|
|
}
|
|
|
|
func (f *consoleFakeClient) ListConsoleConnections(ctx context.Context, cred oci.Credentials, region, instanceID string) ([]oci.ConsoleConnection, error) {
|
|
f.listed++
|
|
if f.listed == 1 {
|
|
return f.conns, nil
|
|
}
|
|
gone := make([]oci.ConsoleConnection, len(f.conns))
|
|
for i, c := range f.conns {
|
|
c.LifecycleState = "DELETED"
|
|
gone[i] = c
|
|
}
|
|
return gone, nil
|
|
}
|
|
|
|
func (f *consoleFakeClient) DeleteConsoleConnection(ctx context.Context, cred oci.Credentials, region, connectionID string) error {
|
|
f.deleted = append(f.deleted, connectionID)
|
|
return nil
|
|
}
|
|
|
|
func (f *consoleFakeClient) CreateConsoleConnection(ctx context.Context, cred oci.Credentials, region, instanceID, sshPublicKey string) (oci.ConsoleConnection, error) {
|
|
f.created++
|
|
return oci.ConsoleConnection{
|
|
ID: "ocid1.icc.new",
|
|
InstanceID: instanceID,
|
|
LifecycleState: "ACTIVE",
|
|
VncConnectionString: "ssh -o ProxyCommand='ssh -W %h:%p -p 443 " +
|
|
"ocid1.icc.new@instance-console.eu-frankfurt-1.oci.oraclecloud.com' -N -L ...",
|
|
}, nil
|
|
}
|
|
|
|
func newConsoleTestService(t *testing.T, client *consoleFakeClient) (*ConsoleService, uint) {
|
|
t.Helper()
|
|
client.tenancy = oci.TenancyInfo{Name: "t"}
|
|
svc := newTestService(t, client)
|
|
cfg, err := svc.Import(context.Background(), trialImportInput())
|
|
if err != nil {
|
|
t.Fatalf("import config: %v", err)
|
|
}
|
|
console := NewConsoleService(svc)
|
|
console.pollInterval = time.Millisecond
|
|
return console, cfg.ID
|
|
}
|
|
|
|
func TestCreateConsoleSession(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
typ string
|
|
conns []oci.ConsoleConnection
|
|
wantErr bool
|
|
wantDeleted int
|
|
}{
|
|
{name: "非法类型拒绝", typ: "rdp", wantErr: true},
|
|
{name: "无残留直接创建", typ: ConsoleTypeSerial},
|
|
{
|
|
name: "ACTIVE 残留先删再建",
|
|
typ: ConsoleTypeVnc,
|
|
conns: []oci.ConsoleConnection{
|
|
{ID: "ocid1.icc.stale", LifecycleState: "ACTIVE"},
|
|
},
|
|
wantDeleted: 1,
|
|
},
|
|
{
|
|
name: "DELETED 终态记录跳过",
|
|
typ: ConsoleTypeSerial,
|
|
conns: []oci.ConsoleConnection{
|
|
{ID: "ocid1.icc.gone", LifecycleState: "DELETED"},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := &consoleFakeClient{conns: tt.conns}
|
|
console, cfgID := newConsoleTestService(t, client)
|
|
sess, err := console.CreateSession(context.Background(), cfgID, "ocid1.instance.i", "", tt.typ)
|
|
if tt.wantErr {
|
|
if err == nil {
|
|
t.Fatal("expect error, got nil")
|
|
}
|
|
return
|
|
}
|
|
if err != nil {
|
|
t.Fatalf("CreateSession: %v", err)
|
|
}
|
|
if sess.Type != tt.typ {
|
|
t.Errorf("session type = %s, want %s", sess.Type, tt.typ)
|
|
}
|
|
if len(client.deleted) != tt.wantDeleted {
|
|
t.Errorf("deleted %d stale connections, want %d", len(client.deleted), tt.wantDeleted)
|
|
}
|
|
if client.created != 1 {
|
|
t.Errorf("created %d connections, want 1", client.created)
|
|
}
|
|
if console.Get(sess.ID) == nil {
|
|
t.Error("session not retrievable after create")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestConsoleSessionExpire 验证过期回收:在用会话跳过本轮,断开后下一轮回收。
|
|
func TestConsoleSessionExpire(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
inUse bool
|
|
wantAlive bool
|
|
}{
|
|
{name: "在用会话跳过回收", inUse: true, wantAlive: true},
|
|
{name: "空闲会话回收并删云端连接", inUse: false, wantAlive: false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
client := &consoleFakeClient{}
|
|
console, cfgID := newConsoleTestService(t, client)
|
|
sess, err := console.CreateSession(context.Background(), cfgID, "ocid1.instance.i", "", ConsoleTypeSerial)
|
|
if err != nil {
|
|
t.Fatalf("CreateSession: %v", err)
|
|
}
|
|
sess.MarkUse(tt.inUse)
|
|
console.expire(sess.ID)
|
|
if alive := console.Get(sess.ID) != nil; alive != tt.wantAlive {
|
|
t.Fatalf("session alive = %v, want %v", alive, tt.wantAlive)
|
|
}
|
|
if wantDel := !tt.wantAlive; (len(client.deleted) == 1) != wantDel {
|
|
t.Errorf("cloud connection deleted %v, want %v", client.deleted, wantDel)
|
|
}
|
|
if tt.inUse {
|
|
// 断开后下一轮回收(此前的缺陷:跳过后不再检查,会话常驻)
|
|
sess.MarkUse(false)
|
|
console.expire(sess.ID)
|
|
if console.Get(sess.ID) != nil {
|
|
t.Fatal("session still alive after idle expire round")
|
|
}
|
|
if len(client.deleted) != 1 {
|
|
t.Errorf("cloud connection not deleted after idle expire: %v", client.deleted)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|