Merge remote-tracking branch 'upstream/main' into feat/base-service-delivery
# Conflicts: # server/.env.example # server/internal/config/config.go # server/internal/service/delivery.go # server/internal/service/delivery_test.go
This commit is contained in:
@@ -3,59 +3,123 @@ package service
|
||||
import "testing"
|
||||
|
||||
func TestValidateDeliveryInput(t *testing.T) {
|
||||
dataDisks := []string{"/data", "/disk1"}
|
||||
valid := MySQLDeliveryInput{BusinessLineID: 1, TargetID: 1, Namespace: "team-a", InstanceName: "mysql-01", CPUCores: 2, MemoryGB: 4, StorageGB: 50}
|
||||
normalizeMySQLDeliveryInput(&valid)
|
||||
if err := validateDeliveryInput(valid); err != nil {
|
||||
if err := validateDeliveryInput(valid, dataDisks); err != nil {
|
||||
t.Fatalf("valid input rejected: %v", err)
|
||||
}
|
||||
withVersion := valid
|
||||
withVersion.MySQLVersion = "8.0"
|
||||
if err := validateDeliveryInput(withVersion); err != nil {
|
||||
t.Fatalf("valid input with version 8.0 rejected: %v", err)
|
||||
full := valid
|
||||
full.MySQLVersion = "8.0"
|
||||
full.Topology = "standalone"
|
||||
full.MySQLPort = 13306
|
||||
full.DataDisk = "/disk1"
|
||||
full.TargetHost = "k8s-server-03"
|
||||
full.CPUMilli = 2000
|
||||
full.MemoryMi = 8192
|
||||
full.StorageGi = 2000
|
||||
full.TimeZone = "+08:00"
|
||||
full.LowerCaseTableNames = 0
|
||||
full.CharacterSet = "utf8mb4"
|
||||
full.Collation = "utf8mb4_general_ci"
|
||||
full.MaxConnections = "auto"
|
||||
full.InnoDBRedoLogCapacity = "256M"
|
||||
full.InnoDBFlushLogAtTrxCommit = 2
|
||||
full.SyncBinlog = 0
|
||||
full.InnoDBIOCapacity = 2000
|
||||
full.LongQueryTime = 0.5
|
||||
full.BinlogExpireLogsSeconds = 604800
|
||||
full.MaxBinlogSize = "512M"
|
||||
if err := validateDeliveryInput(full, dataDisks); err != nil {
|
||||
t.Fatalf("valid full input rejected: %v", err)
|
||||
}
|
||||
for name, input := range map[string]MySQLDeliveryInput{
|
||||
"uppercase namespace": valid,
|
||||
"bad instance": valid,
|
||||
"bad cpu cores": valid,
|
||||
"too much memory": valid,
|
||||
"too little storage": valid,
|
||||
"too much storage": valid,
|
||||
"unsupported version": valid,
|
||||
"unsupported topology": valid,
|
||||
"bad mysql port": valid,
|
||||
"bad data disk": valid,
|
||||
"unsupported charset": valid,
|
||||
"bad collation": valid,
|
||||
namedZone := valid
|
||||
namedZone.TimeZone = "Asia/Shanghai"
|
||||
if err := validateDeliveryInput(namedZone, dataDisks); err != nil {
|
||||
t.Fatalf("named timezone rejected: %v", err)
|
||||
}
|
||||
lts := valid
|
||||
lts.MySQLVersion = "8.4"
|
||||
if err := validateDeliveryInput(lts, dataDisks); err != nil {
|
||||
t.Fatalf("8.4 LTS rejected: %v", err)
|
||||
}
|
||||
for name, mutate := range map[string]func(*MySQLDeliveryInput){
|
||||
"uppercase namespace": func(in *MySQLDeliveryInput) { in.Namespace = "Team-A" },
|
||||
"bad instance": func(in *MySQLDeliveryInput) { in.InstanceName = "mysql_01" },
|
||||
"too little cpu": func(in *MySQLDeliveryInput) { in.CPUMilli = 50 },
|
||||
"too much cpu": func(in *MySQLDeliveryInput) { in.CPUMilli = 65000 },
|
||||
"too little memory": func(in *MySQLDeliveryInput) { in.MemoryMi = 1024 },
|
||||
"too much memory": func(in *MySQLDeliveryInput) { in.MemoryMi = 131072 },
|
||||
"too little storage": func(in *MySQLDeliveryInput) { in.StorageGi = 10 },
|
||||
"too much storage": func(in *MySQLDeliveryInput) { in.StorageGi = 4000 },
|
||||
"unsupported version": func(in *MySQLDeliveryInput) { in.MySQLVersion = "5.7" },
|
||||
"eol version": func(in *MySQLDeliveryInput) { in.MySQLVersion = "5.6" },
|
||||
"unsupported topology": func(in *MySQLDeliveryInput) { in.Topology = "mgr_3" },
|
||||
"port below pool": func(in *MySQLDeliveryInput) { in.MySQLPort = 3307 },
|
||||
"port above pool": func(in *MySQLDeliveryInput) { in.MySQLPort = 14000 },
|
||||
"data disk not in list": func(in *MySQLDeliveryInput) { in.DataDisk = "/mnt/other" },
|
||||
"bad target host": func(in *MySQLDeliveryInput) { in.TargetHost = "-bad-host" },
|
||||
"bad timezone": func(in *MySQLDeliveryInput) { in.TimeZone = "UTC+8" },
|
||||
"bad lower case": func(in *MySQLDeliveryInput) { in.LowerCaseTableNames = 2 },
|
||||
"bad charset": func(in *MySQLDeliveryInput) { in.CharacterSet = "big5" },
|
||||
"collation mismatch": func(in *MySQLDeliveryInput) { in.CharacterSet = "gbk"; in.Collation = "utf8mb4_general_ci" },
|
||||
"bad max connections": func(in *MySQLDeliveryInput) { in.MaxConnections = "300" },
|
||||
"bad redo capacity": func(in *MySQLDeliveryInput) { in.InnoDBRedoLogCapacity = "2G" },
|
||||
"bad flush log": func(in *MySQLDeliveryInput) { in.InnoDBFlushLogAtTrxCommit = 3 },
|
||||
"bad sync binlog": func(in *MySQLDeliveryInput) { in.SyncBinlog = 2 },
|
||||
"bad io capacity": func(in *MySQLDeliveryInput) { in.InnoDBIOCapacity = 500 },
|
||||
"bad long query time": func(in *MySQLDeliveryInput) { in.LongQueryTime = 3 },
|
||||
"bad binlog expire": func(in *MySQLDeliveryInput) { in.BinlogExpireLogsSeconds = 3600 },
|
||||
"bad max binlog size": func(in *MySQLDeliveryInput) { in.MaxBinlogSize = "64M" },
|
||||
} {
|
||||
switch name {
|
||||
case "uppercase namespace":
|
||||
input.Namespace = "Team-A"
|
||||
case "bad instance":
|
||||
input.InstanceName = "mysql_01"
|
||||
case "bad cpu cores":
|
||||
input.CPUCores = 3
|
||||
case "too much memory":
|
||||
input.MemoryGB = 128
|
||||
case "too little storage":
|
||||
input.StorageGB = 10
|
||||
case "too much storage":
|
||||
input.StorageGB = 3000
|
||||
case "unsupported version":
|
||||
input.MySQLVersion = "5.7"
|
||||
case "unsupported topology":
|
||||
input.Topology = "mgr"
|
||||
case "bad mysql port":
|
||||
input.MySQLPort = 3306
|
||||
case "bad data disk":
|
||||
input.DataDisk = "/"
|
||||
case "unsupported charset":
|
||||
input.CharacterSet = "sjis"
|
||||
case "bad collation":
|
||||
input.CharacterSet = "utf8"
|
||||
input.Collation = "utf8mb4_general_ci"
|
||||
}
|
||||
if err := validateDeliveryInput(input); err == nil {
|
||||
input := valid
|
||||
mutate(&input)
|
||||
if err := validateDeliveryInput(input, dataDisks); err == nil {
|
||||
t.Errorf("%s was accepted", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFirstFreeHost(t *testing.T) {
|
||||
hosts := []targetHost{{Name: "node-a"}, {Name: "node-b"}}
|
||||
if h := firstFreeHost(hosts, nil, 1); h == nil || h.Name != "node-a" {
|
||||
t.Fatalf("expected node-a on empty occupancy, got %+v", h)
|
||||
}
|
||||
if h := firstFreeHost(hosts, []string{"node-a"}, 1); h == nil || h.Name != "node-b" {
|
||||
t.Fatalf("expected node-b when node-a is full at limit 1, got %+v", h)
|
||||
}
|
||||
if h := firstFreeHost(hosts, []string{"node-a", "node-b"}, 1); h != nil {
|
||||
t.Fatalf("limit 1 with all hosts taken should return nil, got %+v", h)
|
||||
}
|
||||
if h := firstFreeHost(hosts, []string{"node-a", "node-b"}, 2); h == nil || h.Name != "node-a" {
|
||||
t.Fatalf("expected node-a for second round at limit 2, got %+v", h)
|
||||
}
|
||||
if h := firstFreeHost(hosts, []string{"node-a", "node-a", "node-b", "node-b"}, 2); h != nil {
|
||||
t.Fatalf("limit 2 with all hosts saturated should return nil, got %+v", h)
|
||||
}
|
||||
if h := firstFreeHost(hosts, []string{"node-a"}, 0); h == nil || h.Name != "node-b" {
|
||||
t.Fatalf("limit 0 should degrade to 1, got %+v", h)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAllocatePort(t *testing.T) {
|
||||
if port, err := allocatePort(0, nil); err != nil || port != mysqlPortPoolStart {
|
||||
t.Fatalf("expected first pool port %d, got %d err=%v", mysqlPortPoolStart, port, err)
|
||||
}
|
||||
if port, err := allocatePort(0, []int{13306, 13307}); err != nil || port != 13308 {
|
||||
t.Fatalf("expected 13308 skipping occupied, got %d err=%v", port, err)
|
||||
}
|
||||
if port, err := allocatePort(13400, []int{13306}); err != nil || port != 13400 {
|
||||
t.Fatalf("expected requested port 13400, got %d err=%v", port, err)
|
||||
}
|
||||
if _, err := allocatePort(13306, []int{13306}); err == nil {
|
||||
t.Fatal("requested occupied port was accepted")
|
||||
}
|
||||
used := make([]int, 0, mysqlPortPoolEnd-mysqlPortPoolStart+1)
|
||||
for p := mysqlPortPoolStart; p <= mysqlPortPoolEnd; p++ {
|
||||
used = append(used, p)
|
||||
}
|
||||
if _, err := allocatePort(0, used); err == nil {
|
||||
t.Fatal("exhausted pool still allocated a port")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user