diff --git a/internal/e2e/e2e_test.go b/internal/e2e/e2e_test.go new file mode 100644 index 0000000..ae1e66b --- /dev/null +++ b/internal/e2e/e2e_test.go @@ -0,0 +1,135 @@ +// Package e2e cross-validates the emitted Zeta artifacts against the REAL +// Zeta Lua loaders: the arch-binary package.lua through ZETA/lib/manifest.lua +// and the arch-src .recipe through zeta-toolchain/toolchain/lib/recipe.lua. +// +// The converters are driven as packages (not via the CLI), so this runs +// under plain `go test` with no built binary. Everything lands in +// t.TempDir(); the only absolute paths are the two loader library roots, +// which live in sibling repositories and cannot be expressed relative to +// this module. +package e2e + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "regexp" + "strings" + "testing" + + "git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/arch/binary" + "git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/arch/src" + "git.spectoria.dev/huntedbytheirs/zeta-reconstruct/internal/registry" +) + +const ( + // manifestLibRoot lets a Lua probe require("manifest") from the ZETA + // library checkout. + manifestLibRoot = "/home/specter/Public/ZETA/lib/?.lua" + // recipeLibRoot lets a Lua probe require("recipe") from the + // zeta-toolchain library checkout. + recipeLibRoot = "/home/specter/Public/zeta-toolchain/toolchain/lib/?.lua" +) + +// runLua executes script with the `lua` interpreter and returns its output. +// The whole suite is skipped when lua is not installed. +func runLua(t *testing.T, script string) (string, error) { + t.Helper() + lua, err := exec.LookPath("lua") + if err != nil { + t.Skipf("lua not installed: %v", err) + } + out, err := exec.Command(lua, "-e", script).Output() + return string(out), err +} + +// manifestProbe returns a Lua script that loads the manifest at path through +// the real ZETA manifest.load. assert makes a nil,nil-err result a hard +// failure (non-zero exit), so exit code 0 proves load+normalize succeeded. +// The trailing ";" separates our component from the default package.path. +func manifestProbe(path string) string { + return fmt.Sprintf( + `package.path=%q..package.path; local m=require("manifest"); assert(m.load(%q))`, + manifestLibRoot+";", path, + ) +} + +// recipeProbe returns a Lua script that loads the recipe at path through the +// real zeta-makepkg recipe.load, with the same assert semantics. +func recipeProbe(path string) string { + return fmt.Sprintf( + `package.path=%q..package.path; local r=require("recipe"); assert(r.load(%q))`, + recipeLibRoot+";", path, + ) +} + +// TestEndToEnd runs both converters into one temp dir and validates every +// emitted artifact through the real Lua loaders. +func TestEndToEnd(t *testing.T) { + out := t.TempDir() + + // arch-binary: committed fixture -> packages/hello/package.lua. + if err := binary.Convert( + filepath.Join("..", "..", "testdata", "hello-1.0-1-x86_64.pkg.tar.zst"), + registry.Options{Output: out, Repo: "https://example.org/repo"}, + ); err != nil { + t.Fatalf("binary.Convert: %v", err) + } + manifestPath := filepath.Join(out, "packages", "hello", "package.lua") + if _, err := os.Stat(manifestPath); err != nil { + t.Fatalf("expected manifest at %s: %v", manifestPath, err) + } + if stdout, err := runLua(t, manifestProbe(manifestPath)); err != nil { + t.Fatalf("manifest probe failed: %v\nlua output: %s", err, stdout) + } + + // arch-src: committed fixture -> /mytool.recipe. + if err := src.Convert( + filepath.Join("..", "..", "testdata", "srcinfo", "mytool"), + registry.Options{Output: out, Arch: "x86_64"}, + ); err != nil { + t.Fatalf("src.Convert: %v", err) + } + recipePath := filepath.Join(out, "mytool.recipe") + if _, err := os.Stat(recipePath); err != nil { + t.Fatalf("expected recipe at %s: %v", recipePath, err) + } + if stdout, err := runLua(t, recipeProbe(recipePath)); err != nil { + t.Fatalf("recipe probe failed: %v\nlua output: %s", err, stdout) + } +} + +// TestEndToEndRejectsBrokenManifest proves the manifest probe is real, not a +// no-op: corrupting the emitted sha256 must make manifest.load (and thus the +// probe) fail. A probe that ignored the file would let this test fail. +func TestEndToEndRejectsBrokenManifest(t *testing.T) { + out := t.TempDir() + + if err := binary.Convert( + filepath.Join("..", "..", "testdata", "hello-1.0-1-x86_64.pkg.tar.zst"), + registry.Options{Output: out, Repo: "https://example.org/repo"}, + ); err != nil { + t.Fatalf("binary.Convert: %v", err) + } + manifestPath := filepath.Join(out, "packages", "hello", "package.lua") + content, err := os.ReadFile(manifestPath) + if err != nil { + t.Fatalf("read manifest: %v", err) + } + + sha256Re := regexp.MustCompile(`[0-9a-fA-F]{64}`) + broken := sha256Re.ReplaceAllString(string(content), "zzz") + if broken == string(content) || !strings.Contains(broken, "zzz") { + t.Fatal("could not corrupt the manifest: no 64-hex sha256 found") + } + brokenPath := filepath.Join(out, "broken-package.lua") + if err := os.WriteFile(brokenPath, []byte(broken), 0o644); err != nil { + t.Fatalf("write broken manifest: %v", err) + } + + stdout, err := runLua(t, manifestProbe(brokenPath)) + if err == nil { + t.Fatalf("probe unexpectedly accepted broken manifest\nlua output: %s", stdout) + } +}