package daemon import ( "context" "encoding/json" "os" "path/filepath" "strings" "testing" "banger/internal/api" "banger/internal/buildinfo" "banger/internal/model" "banger/internal/paths" "banger/internal/rpc" "banger/internal/system" ) func TestBuildImageRequiresFromImage(t *testing.T) { d := &Daemon{ layout: paths.Layout{ImagesDir: t.TempDir(), StateDir: t.TempDir()}, store: openDaemonStore(t), runner: system.NewRunner(), } _, err := d.BuildImage(context.Background(), api.ImageBuildParams{Name: "missing-base"}) if err == nil || !strings.Contains(err.Error(), "from-image is required") { t.Fatalf("BuildImage() error = %v", err) } } func TestRegisterImageRequiresKernel(t *testing.T) { rootfs := filepath.Join(t.TempDir(), "rootfs.ext4") if err := os.WriteFile(rootfs, []byte("rootfs"), 0o644); err != nil { t.Fatalf("write rootfs: %v", err) } d := &Daemon{store: openDaemonStore(t)} _, err := d.RegisterImage(context.Background(), api.ImageRegisterParams{ Name: "missing-kernel", RootfsPath: rootfs, }) if err == nil || !strings.Contains(err.Error(), "kernel path is required") { t.Fatalf("RegisterImage() error = %v", err) } } func TestDispatchPingIncludesBuildInfo(t *testing.T) { d := &Daemon{pid: 42, webURL: "http://127.0.0.1:7777"} resp := d.dispatch(context.Background(), rpc.Request{Version: rpc.Version, Method: "ping"}) if !resp.OK { t.Fatalf("dispatch(ping) = %+v, want ok", resp) } var got api.PingResult if err := json.Unmarshal(resp.Result, &got); err != nil { t.Fatalf("Unmarshal(PingResult): %v", err) } info := buildinfo.Current() if got.Status != "ok" || got.PID != 42 || got.WebURL != "http://127.0.0.1:7777" { t.Fatalf("PingResult = %+v, want status/pid/weburl populated", got) } if got.Version != info.Version || got.Commit != info.Commit || got.BuiltAt != info.BuiltAt { t.Fatalf("PingResult build info = %+v, want %+v", got, info) } } func TestPromoteImageCopiesBootArtifactsIntoArtifactDir(t *testing.T) { dir := t.TempDir() rootfs := filepath.Join(dir, "rootfs.ext4") kernel := filepath.Join(dir, "vmlinux") initrd := filepath.Join(dir, "initrd.img") modulesDir := filepath.Join(dir, "modules") if err := os.MkdirAll(modulesDir, 0o755); err != nil { t.Fatalf("mkdir modules: %v", err) } for path, data := range map[string]string{ rootfs: "rootfs", kernel: "kernel", initrd: "initrd", filepath.Join(modulesDir, "depmod"): "modules", } { if err := os.WriteFile(path, []byte(data), 0o644); err != nil { t.Fatalf("write %s: %v", path, err) } } db := openDaemonStore(t) image := model.Image{ ID: "img-promote", Name: "void-exp", Managed: false, RootfsPath: rootfs, KernelPath: kernel, InitrdPath: initrd, ModulesDir: modulesDir, CreatedAt: model.Now(), UpdatedAt: model.Now(), } if err := db.UpsertImage(context.Background(), image); err != nil { t.Fatalf("UpsertImage: %v", err) } imagesDir := filepath.Join(dir, "images") if err := os.MkdirAll(imagesDir, 0o755); err != nil { t.Fatalf("mkdir images dir: %v", err) } d := &Daemon{ layout: paths.Layout{ImagesDir: imagesDir}, store: db, runner: system.NewRunner(), } got, err := d.PromoteImage(context.Background(), image.Name) if err != nil { t.Fatalf("PromoteImage: %v", err) } if !got.Managed { t.Fatal("promoted image should be managed") } for _, path := range []string{got.RootfsPath, got.KernelPath, got.InitrdPath, got.ModulesDir} { if !strings.HasPrefix(path, got.ArtifactDir) { t.Fatalf("artifact path %q does not live under %q", path, got.ArtifactDir) } if _, err := os.Stat(path); err != nil { t.Fatalf("stat %s: %v", path, err) } } }