From 4818651e21b388d69b0f57497a768d847be92739 Mon Sep 17 00:00:00 2001 From: Ako Date: Wed, 16 Sep 2026 08:09:43 +0000 Subject: [PATCH 1/2] test(workflow): gate the task-page fixtures on Mendix 11 The nightly's Mendix 10.24 leg has failed since 2026-09-15 with four workflow round-trips dying in their fixture: create page RoundtripTest.ReviewPage: create page with parameters requires Mendix 11.0.0+ (project is 10.24.24.119349) db61b949 gave the task pages `Params: { $WorkflowUserTask: ... }` because exec now checks a user task's page signature and a parameterless page is CE7410 on 11.13. That fix is right, and it is mutually exclusive with Mendix 10: a valid task page NEEDS a parameter and CREATE PAGE cannot write one below 11.0 (mendixlabs/mxcli#294). So these fixtures cannot exist on Mendix 10 and the tests have to skip there. The gate goes in createTaskPages rather than at the four call sites, so a fifth caller cannot forget it -- the constraint belongs to the fixture, not to the tests that happen to use it. db61b949's own message says it was "verified locally on both engines with a real 11.13 project". 11.13 only; 10.24 is the leg that broke. Measured now on both: with mxbuild 10.24.24.119349 all four SKIP ("Requires Mendix 11.0+"), and on 11.12.0 all four still RUN and PASS. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BNDe35kDNsMX5cz4Ahn4rk --- mdl/executor/roundtrip_workflow_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mdl/executor/roundtrip_workflow_test.go b/mdl/executor/roundtrip_workflow_test.go index 53193a43e..85e54ff59 100644 --- a/mdl/executor/roundtrip_workflow_test.go +++ b/mdl/executor/roundtrip_workflow_test.go @@ -35,6 +35,14 @@ import ( // page's signature and refused them, so the fixture is a valid task page. func createTaskPages(t *testing.T, env *testEnv, mod string, names ...string) { t.Helper() + // A user task's page must take $WorkflowUserTask or exec refuses it (CE7410 + // on 11.13, which is why db61b949 added the parameter), and CREATE PAGE + // cannot write a parameter below 11.0 (mendixlabs/mxcli#294). The two + // requirements are mutually exclusive, so these fixtures cannot exist on + // Mendix 10 at all -- the nightly's 10.24 leg failed here for two days. + // The gate lives with the fixture rather than at the four call sites so a + // fifth caller cannot forget it. + env.requireMinVersion(t, 11, 0) for _, name := range names { mdl := `create page ` + mod + `.` + name + ` ( Title: '` + name + `', From 7e36724706de39460bf2589e80cedd3bafa10c2c Mon Sep 17 00:00:00 2001 From: Ako Date: Wed, 16 Sep 2026 08:09:44 +0000 Subject: [PATCH 2/2] test(doctype): a file-level @version floor survives a later `@version: any` The second half of the nightly's 10.24 failure, and a different defect: skipped 531 version-gated lines Execution error: entity 'WFTest.OrderContext' not found for parameter 'OrderContext' mx check passed: 0 errors filterByVersion treats each directive as REPLACING the previous one, so `-- @version: any` resets the constraint to nil. 24-workflow-examples.mdl opens at line 1 with `-- @version: 11.0+`, creates WFTest.OrderContext and WFTest.TaskPage under it (the page takes a parameter, so it is 11.0+ only), and then marks PART H `any` while using both. On Mendix 10 the fixtures were skipped and PART H ran anyway, against a project that did not have them. The model that did get built was valid, which is why mx check passed and only the execution error failed the test. A directive appearing before any statement is now the FILE's floor and later directives narrow it instead of replacing it. That is what `any` plainly means there -- "no constraint beyond this file's" -- and it is precisely scoped: 24-workflow-examples.mdl is the only fixture with a line-1 directive. The other six use `any` mid-file to close a gated section, where there is no baseline and nothing changes. Two tests, and the second is the one that keeps this honest: a mid-file `any` must still reopen for every version. Stubbing the fix back out fails TestFilterByVersion_FileBaselineSurvivesAny with the reported symptom. Measured end to end with mxbuild 10.24.24.119349: the file goes from Execution error to PASS, skipped lines 487 -> 531 (PART H now gated too), mx check 0 errors. On 11.12.0 only 15 lines are skipped, PART H still runs, mx check 0 errors. The whole doctype suite passes on both. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01BNDe35kDNsMX5cz4Ahn4rk --- mdl/executor/roundtrip_doctype_test.go | 21 +++++- mdl/executor/version_filter_baseline_test.go | 74 ++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 mdl/executor/version_filter_baseline_test.go diff --git a/mdl/executor/roundtrip_doctype_test.go b/mdl/executor/roundtrip_doctype_test.go index 95eb8b60c..e42c14cf1 100644 --- a/mdl/executor/roundtrip_doctype_test.go +++ b/mdl/executor/roundtrip_doctype_test.go @@ -552,18 +552,37 @@ func parseMajorMinor(s string) (int, int, bool) { func filterByVersion(content string, pv *types.ProjectVersion) (string, int) { var result strings.Builder var currentConstraint *versionConstraint // nil = no constraint (always include) + // A directive placed before any statement is the FILE's floor, and later + // directives narrow it rather than replace it. Without this, `-- @version: + // any` resets the constraint to nil and re-enables a section whose fixtures + // were skipped: 24-workflow-examples.mdl opens at 11.0+, creates + // WFTest.OrderContext and WFTest.TaskPage (a page with parameters, 11.0+ + // only -- mendixlabs/mxcli#294), and its PART H says `any` while using both. + // On the nightly's 10.24 leg that ran PART H against a project missing them: + // "entity 'WFTest.OrderContext' not found". Only that file has a line-1 + // directive; the other six use `any` mid-file to close a gated section, + // where there is no baseline and nothing changes. + var baseline *versionConstraint + sawStatement := false skippedLines := 0 for _, line := range strings.Split(content, "\n") { trimmed := strings.TrimSpace(line) if strings.HasPrefix(trimmed, "-- @version:") { currentConstraint = parseVersionDirective(trimmed) + if !sawStatement && baseline == nil { + baseline = currentConstraint + } // Keep the directive line as a comment (so line numbers stay close) result.WriteString(line) result.WriteString("\n") continue } - if currentConstraint == nil || currentConstraint.matches(pv) { + if trimmed != "" && !strings.HasPrefix(trimmed, "--") { + sawStatement = true + } + if (baseline == nil || baseline.matches(pv)) && + (currentConstraint == nil || currentConstraint.matches(pv)) { result.WriteString(line) result.WriteString("\n") } else { diff --git a/mdl/executor/version_filter_baseline_test.go b/mdl/executor/version_filter_baseline_test.go new file mode 100644 index 000000000..51b8f3698 --- /dev/null +++ b/mdl/executor/version_filter_baseline_test.go @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: Apache-2.0 + +//go:build integration + +package executor + +import ( + "strings" + "testing" + + "github.com/mendixlabs/mxcli/mdl/types" +) + +// A file-level `-- @version:` floor must survive a later `-- @version: any`. +// Without that, PART H of 24-workflow-examples.mdl ran on the nightly's Mendix +// 10.24 leg against a project whose fixtures had been skipped, and failed with +// "entity 'WFTest.OrderContext' not found for parameter 'OrderContext'". +func TestFilterByVersion_FileBaselineSurvivesAny(t *testing.T) { + const script = `-- @version: 11.0+ +create persistent entity WFTest.OrderContext ( Name: string ) +/ +-- @version: any +create workflow WFTest.CompletionRules parameter $OrderContext: WFTest.OrderContext +/ +` + mx10 := &types.ProjectVersion{ProductVersion: "10.24.24.119349", MajorVersion: 10, MinorVersion: 24} + mx11 := &types.ProjectVersion{ProductVersion: "11.13.0", MajorVersion: 11, MinorVersion: 13} + + got10, skipped10 := filterByVersion(script, mx10) + if strings.Contains(got10, "create workflow") { + t.Errorf("Mendix 10: the `any` section ran although the file floor is 11.0+ "+ + "and its fixtures were skipped.\nfiltered:\n%s", got10) + } + if strings.Contains(got10, "create persistent entity") { + t.Errorf("Mendix 10: the gated fixture should have been skipped too") + } + if skipped10 == 0 { + t.Errorf("Mendix 10: expected skipped lines, got 0") + } + + got11, _ := filterByVersion(script, mx11) + for _, want := range []string{"create persistent entity", "create workflow"} { + if !strings.Contains(got11, want) { + t.Errorf("Mendix 11: %q was dropped; the floor is satisfied so everything must run.\n%s", want, got11) + } + } +} + +// The other six fixtures use `any` MID-file to close a gated section. There is +// no baseline there, so the section must still run on every version -- the +// control that keeps the fix above from over-firing. +func TestFilterByVersion_MidFileAnyStillReopens(t *testing.T) { + const script = `create entity M.Always ( Name: string ) +/ +-- @version: 10.18+ +create view entity M.Gated ( Name: string ) +/ +-- @version: any +create entity M.AfterAny ( Name: string ) +/ +` + mx10 := &types.ProjectVersion{ProductVersion: "10.6.0", MajorVersion: 10, MinorVersion: 6} + + got, _ := filterByVersion(script, mx10) + if !strings.Contains(got, "M.AfterAny") { + t.Errorf("a mid-file `any` must reopen for every version:\n%s", got) + } + if strings.Contains(got, "M.Gated") { + t.Errorf("the 10.18+ section must still be skipped on 10.6:\n%s", got) + } + if !strings.Contains(got, "M.Always") { + t.Errorf("content before any directive must always run:\n%s", got) + } +}