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/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 + `', 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) + } +}