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