From 5a4856612ffe2924d20ffc7d399c4fe689243eda Mon Sep 17 00:00:00 2001 From: argonui <92067588+argonui@users.noreply.github.com> Date: Sun, 2 Aug 2026 19:03:09 -0500 Subject: [PATCH] tests: enable bundled_core and bundled_lua e2e fixtures The e2e round-trip test skipped bundled_core and bundled_lua on the premise that they "depend on a src file the test doesn't know about." That is no longer accurate: the reverse step already unbundles each script's modules to src/ and the build step resolves the require() back in from the same fake filesystem, so no external src fixture is needed. bundled_core passes as-is once un-denied. bundled_lua nests its bundled script inside an ObjectStates entry, where the whole savegame is compared byte-for-byte. Genuine luabundle output preserves a module's trailing newline that the unbundle/re-bundle round trip normalizes away, so the byte comparison fails on formatting alone. The harness already sidesteps this for the top-level LuaScript by comparing bundled scripts as a set of module names rather than bytes. Generalize that same rule to bundled scripts at any nesting depth so a nested object script is held to the same standard as the root. Only bundled scripts are normalized; non-bundled scripts are still compared exactly, so no existing coverage is weakened. Co-Authored-By: Claude Opus 5 --- bundler/AGENTS.md | 8 +++++--- tests/AGENTS.md | 6 ++++-- tests/e2e_test.go | 40 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/bundler/AGENTS.md b/bundler/AGENTS.md index 1dacfdc..55061f3 100644 --- a/bundler/AGENTS.md +++ b/bundler/AGENTS.md @@ -52,6 +52,8 @@ root back with `scripts[bundler.Rootname]` rather than a hardcoded string, and round trips — bundle then unbundle, or unbundle then bundle, and compare — since that is the property the rest of the tool depends on. -Two end-to-end fixtures (`bundled_core`, `bundled_lua`) are currently on the deny list in -[tests/e2e_test.go](../tests/e2e_test.go) because they require `src/` files the harness doesn't -supply. Wiring those up would give bundling real integration coverage. +Two end-to-end fixtures (`bundled_core` and `bundled_lua`) give bundling real integration coverage +through [tests/e2e_test.go](../tests/e2e_test.go): each is reversed (unbundled to `src/`) and built +back (re-bundled by resolving `require`), exercising the full round trip. `bundled_lua` nests its +bundled script inside an object, so the harness compares every bundled `LuaScript` by module-name +set at any depth, since bundling is formatting-sensitive. diff --git a/tests/AGENTS.md b/tests/AGENTS.md index 35fad3a..e056371 100644 --- a/tests/AGENTS.md +++ b/tests/AGENTS.md @@ -47,8 +47,10 @@ What the comparison covers: - `Date` and `EpochTime` are excluded — they are regenerated on every build by design. - Any `float64` value is excluded. Numeric behavior is asserted in [objects/numbersmoother_test.go](../objects/numbersmoother_test.go) instead. -- `bundled_core` and `bundled_lua` are on a deny list; they depend on `src/` files the harness - doesn't provide. +- Every bundled `LuaScript` is compared by module-name set, not just the top-level one. A bundled + script found at any nesting depth (e.g. inside an object in `ObjectStates`) is normalized the + same way, so nested scripts are not held to a stricter byte-for-byte standard than the root. + Non-bundled scripts are still compared exactly. ## Fixture locations diff --git a/tests/e2e_test.go b/tests/e2e_test.go index 5957235..c352900 100644 --- a/tests/e2e_test.go +++ b/tests/e2e_test.go @@ -26,10 +26,7 @@ func TestAllReverseThenBuild(t *testing.T) { for _, path := range paths { _, filename := filepath.Split(path) testname := filename[:len(filename)-len(filepath.Ext(path))] - denyList := []string{ - "bundled_core", // currently depends on src file that test doesn't know about - "bundled_lua", // currently depends on src file that test doesn't know about - } + denyList := []string{} t.Run(testname, func(t *testing.T) { for _, f := range denyList { @@ -129,6 +126,13 @@ func TestAllReverseThenBuild(t *testing.T) { delete(got, "LuaScript") } + // Object-level LuaScript (e.g. inside ObjectStates) is compared the + // same formatting-insensitive way as the top-level script above: + // bundling is order- and formatting-sensitive, so a bundled script + // is compared by its set of module names rather than byte-for-byte. + normalizeBundledLua(want) + normalizeBundledLua(got) + if diff := cmp.Diff(want, got, cmpopts.IgnoreMapEntries(ignoreUnpredictable)); diff != "" { t.Errorf("want != got:\n%v\n", diff) } @@ -137,6 +141,34 @@ func TestAllReverseThenBuild(t *testing.T) { } } +// normalizeBundledLua walks a decoded savegame and replaces every bundled +// LuaScript string it finds (at any nesting depth) with the set of module +// names that unbundle out of it. This mirrors how the top-level LuaScript is +// compared, so nested object scripts are not held to a stricter byte-for-byte +// standard than the root while still verifying that require resolution rebuilt +// the same set of modules. Non-bundled scripts are left untouched so their +// contents are still compared exactly. +func normalizeBundledLua(v interface{}) { + switch t := v.(type) { + case map[string]interface{}: + for k, val := range t { + if k == "LuaScript" { + if s, ok := val.(string); ok && bundler.IsBundled(s) { + if modules, err := bundler.UnbundleAll(s); err == nil { + t[k] = mapOfKeys(modules) + continue + } + } + } + normalizeBundledLua(val) + } + case []interface{}: + for _, val := range t { + normalizeBundledLua(val) + } + } +} + func mapOfKeys(m map[string]string) map[string]interface{} { r := map[string]interface{}{} for k := range m {