Files
ThothII/tools/tht/internal/preflight/preflight_test.go
T

119 lines
4.9 KiB
Go

package preflight
import (
"context"
"encoding/json"
"errors"
"io"
"strings"
"testing"
"github.com/aritmolab/thothii/tools/tht/internal/compose"
"github.com/aritmolab/thothii/tools/tht/internal/config"
)
type fakeDocker struct {
calls [][]string
fail string
effective string
}
func (f *fakeDocker) Run(_ context.Context, args []string, _ io.Reader) (compose.Result, error) {
f.calls = append(f.calls, args)
if strings.Contains(strings.Join(args, " "), f.fail) && f.fail != "" {
return compose.Result{Stderr: "PRIVATE_SENTINEL"}, errors.New("PRIVATE_SENTINEL")
}
switch args[0] {
case "info":
return compose.Result{Stdout: `{"OSType":"linux","Architecture":"x86_64","NCPU":4,"MemTotal":17179869184}`}, nil
case "compose":
if args[len(args)-1] == "json" {
if f.effective != "" {
return compose.Result{Stdout: f.effective}, nil
}
return compose.Result{Stdout: `{"services":{"core":{"image":"example/core:latest"}}}`}, nil
}
return compose.Result{Stdout: "2.39.0"}, nil
case "manifest":
return compose.Result{Stdout: `{"Descriptor":{"digest":"sha256:` + strings.Repeat("a", 64) + `","platform":{"os":"linux","architecture":"amd64"}}}`}, nil
}
return compose.Result{}, errors.New("unexpected command")
}
func TestComposeRejectsIncompleteMutableServiceSet(t *testing.T) {
r := CheckCompose(context.Background(), &fakeDocker{}, config.Installation{ProjectDirectory: "/private", EnvFile: "/private/operator.env"}, Manifest{Compose: []string{"compose.yaml"}}, "/release/manifest.json", "linux/amd64")
if r.OK {
t.Fatal("unreleased service set accepted")
}
}
func TestComposeRejectsPlatformOverrideAgainstSelectedImage(t *testing.T) {
m := Manifest{Images: map[string]map[string]string{}, Compose: []string{"compose.yaml"}}
services := map[string]map[string]string{}
for service, role := range map[string]string{"core": "core", "frontend": "frontend", "catalog-db": "catalog", "catalog-migrate": "core", "workspace-maintenance": "core", "qdrant": "qdrant", "embedding": "embedding", "embedding-model-init": "embedding"} {
m.Images[role] = map[string]string{"linux/amd64": "docker.io/example/" + role + "@sha256:" + strings.Repeat("a", 64)}
services[service] = map[string]string{"image": m.Images[role]["linux/amd64"]}
}
services["core"]["platform"] = "linux/arm64"
data, _ := json.Marshal(map[string]any{"services": services})
r := CheckCompose(context.Background(), &fakeDocker{effective: string(data)}, config.Installation{}, m, "/release/manifest.json", "linux/amd64")
if r.OK {
t.Fatal("incompatible Compose platform accepted")
}
}
func TestHostChecksAreReadOnlyAndRejectUnavailableDocker(t *testing.T) {
f := &fakeDocker{}
host := Host{OS: "linux", Arch: "amd64", Kernel: "6.6-microsoft-standard-WSL2", Distribution: "ubuntu", FreeBytes: 30 << 30}
r := CheckHost(context.Background(), f, host, Requirements{CPUs: 2, MemoryBytes: 4 << 30, DiskBytes: 10 << 30})
if !r.OK {
t.Fatalf("host rejected: %+v", r)
}
for _, args := range f.calls {
if args[0] != "info" && !(args[0] == "compose" && args[1] == "version") {
t.Fatalf("mutating call: %v", args)
}
}
f.fail = "info"
r = CheckHost(context.Background(), f, host, Requirements{CPUs: 2, MemoryBytes: 4 << 30, DiskBytes: 10 << 30})
if r.OK || strings.Contains(r.JSON(), "PRIVATE_SENTINEL") {
t.Fatalf("unsafe success/report: %s", r.JSON())
}
host.Kernel = "4.4-microsoft"
if CheckHost(context.Background(), &fakeDocker{}, host, Requirements{CPUs: 2, MemoryBytes: 4 << 30, DiskBytes: 10 << 30}).OK {
t.Fatal("WSL1 accepted")
}
host.OS = "windows"
if CheckHost(context.Background(), &fakeDocker{}, host, Requirements{}).OK {
t.Fatal("native Windows accepted instead of WSL2")
}
}
func TestReleaseChecksEveryImmutableImageAndPlatform(t *testing.T) {
m := Manifest{SchemaVersion: 1, Version: "1.0.0", Revision: strings.Repeat("b", 40), ValidatorProtocol: 1, Requirements: Requirements{CPUs: 2, MemoryBytes: 4 << 30, DiskBytes: 10 << 30}, Components: []string{"pi", "catalog-migrations", "workspace-maintenance"}, Images: map[string]map[string]string{}}
for _, service := range []string{"core", "frontend", "catalog", "qdrant", "embedding"} {
m.Images[service] = map[string]string{"linux/amd64": "docker.io/example/" + service + "@sha256:" + strings.Repeat("a", 64)}
}
m.Files = map[string]string{"deploy/compose.yaml": strings.Repeat("c", 64)}
m.Compose = []string{"deploy/compose.yaml"}
if err := m.Validate(); err != nil {
t.Fatal(err)
}
f := &fakeDocker{}
r := CheckImages(context.Background(), f, m, "linux/amd64")
if !r.OK || len(f.calls) != 5 {
t.Fatalf("images not checked: %s calls=%d", r.JSON(), len(f.calls))
}
if CheckImages(context.Background(), f, m, "linux/arm64").OK {
t.Fatal("unsupported release architecture accepted")
}
f.fail = "frontend"
if CheckImages(context.Background(), f, m, "linux/amd64").OK {
t.Fatal("missing image accepted")
}
m.Images["core"]["linux/amd64"] = "example/core:latest"
if m.Validate() == nil {
t.Fatal("mutable tag accepted")
}
}