From 682c0f89554c3fba99bed5e4aff0491a7c603811 Mon Sep 17 00:00:00 2001 From: Bruno Carvalho Date: Sat, 13 Jun 2026 09:26:42 -0300 Subject: [PATCH] test(appgen): cover generated-app module loud-failure path PR #350 made moduleSource fail loudly when go list -m cannot determine the main module and the generated app imports app-owned packages, but only the decision heuristic (isLocalModuleImportPath / appHasLocalModuleImports) was unit-tested; the error-propagation path itself was not. Add two integration tests that run outside any Go module (t.Chdir to an empty temp dir, so go list -m fails with a real 'no go.mod' reason): - with an app-owned inline go {} import, moduleSource surfaces 'cannot determine the app Go module' - with no app-owned imports, moduleSource tolerates the failure and still emits a valid generated go.mod Full go test ./... (55 non-example packages) passes. --- CHANGELOG.md | 4 ++++ internal/appgen/appgen_test.go | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22bbcf9..19d5fa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,10 @@ packages, and tooling contracts may change before a stable release. generated app imports app-owned packages it now fails with the real `go list` error instead of producing a go.mod that fails to build with an opaque "cannot find package". +- The generated-app module loud-failure path is now covered by an integration + test: with `go list -m` unable to determine the main module, `moduleSource` + fails when the app imports app-owned packages and stays silent when it does + not, so the fail-loudly contract for app-module resolution is regression-proof. ### Known Gaps diff --git a/internal/appgen/appgen_test.go b/internal/appgen/appgen_test.go index e48c925..acbf102 100644 --- a/internal/appgen/appgen_test.go +++ b/internal/appgen/appgen_test.go @@ -3125,6 +3125,50 @@ func HomeTitle() string { return local.Suffix() }`, } } +func TestModuleSourceFailsLoudlyWhenAppModuleUndeterminedWithLocalImports(t *testing.T) { + // Run outside any Go module so `go list -m -json` fails with a clear reason + // (no go.mod). When the generated app imports an app-owned package we cannot + // emit its require/replace without the module path, so that failure must + // surface here instead of producing an app that later fails to build with an + // opaque "cannot find package" error. + t.Chdir(t.TempDir()) + + ir := gwdkir.Program{ + Pages: []gwdkir.Page{{ + Package: "pages", + ID: "home", + Route: "/", + Blocks: gwdkir.Blocks{GoBlocks: []gwdkir.GoBlock{{ + Body: `import local "example.com/site/local" + +func HomeTitle() string { return local.Suffix() }`, + }}}, + }}, + } + + if _, err := moduleSource(Options{IR: &ir}); err == nil { + t.Fatal("expected moduleSource to fail when the app module is undetermined and the app imports app-owned packages") + } else if !strings.Contains(err.Error(), "cannot determine the app Go module") { + t.Fatalf("expected the app-module determination failure to be surfaced, got: %v", err) + } +} + +func TestModuleSourceToleratesUndeterminedAppModuleWithoutLocalImports(t *testing.T) { + // The same `go list -m` failure is non-fatal when the generated app imports + // nothing app-owned: there is no require/replace to add, so a missing main + // module is irrelevant and module generation must still succeed. + t.Chdir(t.TempDir()) + + empty := gwdkir.Program{Pages: []gwdkir.Page{{Package: "pages", ID: "home", Route: "/"}}} + source, err := moduleSource(Options{IR: &empty}) + if err != nil { + t.Fatalf("expected moduleSource to tolerate an undetermined app module without app-owned imports, got: %v", err) + } + if !strings.Contains(source, "module gowdk-generated-app") { + t.Fatalf("expected a valid generated go.mod, got:\n%s", source) + } +} + func TestGenerateWritesAddonGoBlockConsumerFiles(t *testing.T) { root := t.TempDir() outputDir := filepath.Join(root, "dist")