banger/internal/daemon/daemon_test.go
Thales Maciel f798e1db33
Stamp shared build metadata into banger binaries
Treat `banger`, `bangerd`, and `banger-vsock-agent` as one release by
stamping the same version, commit SHA, and build timestamp into every
binary through a shared ldflag-backed `internal/buildinfo` package.

Add `banger version`, extend daemon ping/status to report the running
daemon's build tuple, and keep the guest helper linked to the same build
metadata without adding a new public version surface for it.

Validate with `GOCACHE=/tmp/banger-gocache go test ./...`, `make build`,
`./build/bin/banger version`, and `./build/bin/banger daemon status`
after the daemon restarts onto the new binary.
2026-03-22 17:14:06 -03:00

131 lines
3.7 KiB
Go

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)
}
}
}