package model import ( "strings" "testing" ) func TestResolveVMDefaultsBuiltinFallback(t *testing.T) { // No config override, no host info → every field is "builtin". d := ResolveVMDefaults(VMDefaultsOverride{}, 0, 0) if d.VCPUCount != DefaultVCPUCount || d.VCPUSource != "builtin" { t.Errorf("vcpu = %d (%s), want %d (builtin)", d.VCPUCount, d.VCPUSource, DefaultVCPUCount) } if d.MemoryMiB != DefaultMemoryMiB || d.MemorySource != "builtin" { t.Errorf("memory = %d (%s), want %d (builtin)", d.MemoryMiB, d.MemorySource, DefaultMemoryMiB) } if d.WorkDiskSizeBytes != DefaultWorkDiskSize || d.WorkDiskSource != "builtin" { t.Errorf("disk = %d (%s), want %d (builtin)", d.WorkDiskSizeBytes, d.WorkDiskSource, DefaultWorkDiskSize) } } func TestResolveVMDefaultsAutoFromHost(t *testing.T) { // 8 host cores, 16 GiB RAM → 2 vcpus, 2 GiB memory. d := ResolveVMDefaults(VMDefaultsOverride{}, 8, 16*gib) if d.VCPUCount != 2 || d.VCPUSource != "auto" { t.Errorf("vcpu = %d (%s), want 2 (auto)", d.VCPUCount, d.VCPUSource) } if d.MemoryMiB != 2048 || d.MemorySource != "auto" { t.Errorf("memory = %d (%s), want 2048 (auto)", d.MemoryMiB, d.MemorySource) } // Disk has no auto policy — still builtin. if d.WorkDiskSource != "builtin" { t.Errorf("disk source = %s, want builtin", d.WorkDiskSource) } } func TestResolveVMDefaultsConfigWinsOverAuto(t *testing.T) { override := VMDefaultsOverride{VCPUCount: 6, MemoryMiB: 4096, WorkDiskSizeBytes: 16 * gib} d := ResolveVMDefaults(override, 8, 16*gib) if d.VCPUCount != 6 || d.VCPUSource != "config" { t.Errorf("vcpu = %d (%s), want 6 (config)", d.VCPUCount, d.VCPUSource) } if d.MemoryMiB != 4096 || d.MemorySource != "config" { t.Errorf("memory = %d (%s), want 4096 (config)", d.MemoryMiB, d.MemorySource) } if d.WorkDiskSizeBytes != 16*gib || d.WorkDiskSource != "config" { t.Errorf("disk = %d (%s), want 16*gib (config)", d.WorkDiskSizeBytes, d.WorkDiskSource) } } func TestAutoVCPUClamps(t *testing.T) { cases := []struct { host, want int }{ {1, 1}, // floor {2, 1}, {4, 1}, {5, 1}, {7, 1}, {8, 2}, {16, 4}, {32, 4}, // ceiling {128, 4}, // ceiling sticks } for _, tc := range cases { if got := autoVCPU(tc.host); got != tc.want { t.Errorf("autoVCPU(%d) = %d, want %d", tc.host, got, tc.want) } } } func TestAutoMemoryCappedAndFloor(t *testing.T) { // 4 GiB host → floor 1024 MiB. if got := autoMemoryMiB(4 * gib); got != 1024 { t.Errorf("4 GiB → got %d, want 1024", got) } // 32 GiB host → 32/8 = 4 GiB = 4096 MiB. if got := autoMemoryMiB(32 * gib); got != 4096 { t.Errorf("32 GiB → got %d, want 4096", got) } // 128 GiB host → 128/8 = 16 GiB, capped at 8 GiB = 8192 MiB. if got := autoMemoryMiB(128 * gib); got != 8192 { t.Errorf("128 GiB → got %d, want 8192", got) } } func TestAutoMemoryRoundsTo256MiB(t *testing.T) { // 17 GiB host → 17/8 = 2.125 GiB ≈ 2176 MiB → rounded to 2048. if got := autoMemoryMiB(17 * gib); got%256 != 0 { t.Errorf("%d MiB not a 256 multiple", got) } } func TestFormatSpecLine(t *testing.T) { d := VMDefaults{VCPUCount: 2, MemoryMiB: 2048, WorkDiskSizeBytes: 8 * gib} line := d.FormatSpecLine() for _, want := range []string{"2 vcpu", "2048 MiB", "disk"} { if !strings.Contains(line, want) { t.Errorf("line %q missing %q", line, want) } } } const gib = int64(1024 * 1024 * 1024)