Skip to content

Commit 2be0b81

Browse files
akoclaude
andcommitted
feat: add per-statement version gates for doctype integration tests
The nightly build fails on Mendix 10.24 because some MDL statements use features unavailable in older versions (VIEW ENTITY, page parameters, design properties, REST/DB connector BSON format). Add a -- @Version: directive system that gates individual sections within MDL test scripts by Mendix version: -- @Version: 11.0+ (minimum version) -- @Version: 10.6..10.24 (version range) -- @Version: ..10.24 (maximum version) -- @Version: any (reset to unconditional) The test runner preprocesses MDL content before parsing, stripping lines in sections that don't match the project's Mendix version. Line numbers are preserved (replaced with blank lines) for error attribution. Gated sections in doctype scripts: - 01-domain-model: VIEW ENTITY sections (11.0+) - 02-microflow: page parameter pages/microflows (11.0+) - 03-page: pages with Params blocks (11.0+) - 05-database-connection: entire file (11.0+) - 06-rest-client: entire file (11.0+) - 12-styling: Atlas v3 design properties (11.0+) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 2cf032d commit 2be0b81

File tree

8 files changed

+292
-9
lines changed

8 files changed

+292
-9
lines changed

mdl-examples/doctype-tests/01-domain-model-examples.mdl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -699,8 +699,9 @@ TYPE ReferenceSet
699699
OWNER Both;
700700
/
701701

702+
-- @version: 11.0+
702703
-- ############################################################################
703-
-- PART 7: VIEW ENTITIES (OQL)
704+
-- PART 7: VIEW ENTITIES (OQL) — requires Mendix 11.0+
704705
-- ############################################################################
705706

706707
-- MARK: View Entities
@@ -895,6 +896,7 @@ BEGIN
895896
END;
896897
/
897898

899+
-- @version: any
898900
-- ############################################################################
899901
-- PART 8: NON-PERSISTENT ENTITIES
900902
-- ############################################################################
@@ -1233,8 +1235,9 @@ CREATE OR MODIFY NON-PERSISTENT ENTITY DmTest.FilterParameters (
12331235
);
12341236
/
12351237

1238+
-- @version: 11.0+
12361239
-- ============================================================================
1237-
-- Level 9.5: CREATE OR MODIFY for View Entity
1240+
-- Level 9.5: CREATE OR MODIFY for View Entity — requires Mendix 11.0+
12381241
-- ============================================================================
12391242
--
12401243
-- View entities can also use CREATE OR MODIFY to update their definition.
@@ -1307,6 +1310,7 @@ CREATE OR MODIFY VIEW ENTITY DmTest.ActiveConfigs (
13071310
WHERE c.IsActive = true;
13081311
/
13091312

1313+
-- @version: any
13101314
-- ############################################################################
13111315
-- PART 10: ALTER ENTITY (Incremental Schema Changes)
13121316
-- ############################################################################
@@ -1449,8 +1453,9 @@ ALTER ENTITY DmTest.VATRate
14491453
ALTER ENTITY DmTest.VATRate
14501454
SET POSITION (300, 400);
14511455

1456+
-- @version: 11.0+
14521457
-- ============================================================================
1453-
-- PART 11: COMPLEX VIEW ENTITIES WITH SUBQUERIES
1458+
-- PART 11: COMPLEX VIEW ENTITIES WITH SUBQUERIES — requires Mendix 11.0+
14541459
-- ============================================================================
14551460
-- Demonstrates advanced OQL features including:
14561461
-- - Inline subqueries (derived tables)
@@ -1603,6 +1608,7 @@ move entity DmTest.ActiveConfigs to DmTest3;
16031608
show entities in DmTest2;
16041609
show enumerations in DmTest3;
16051610

1611+
-- @version: any
16061612
-- ############################################################################
16071613
-- PART 12: ENTITY GENERALIZATION (INHERITANCE)
16081614
-- ############################################################################

mdl-examples/doctype-tests/02-microflow-examples.mdl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2575,8 +2575,9 @@ BEGIN
25752575
END;
25762576
/
25772577

2578+
-- @version: 11.0+
25782579
/**
2579-
* Helper page for M090.
2580+
* Helper page for M090 — requires Mendix 11.0+ (page parameters).
25802581
*/
25812582
CREATE PAGE MfTest.ProductDetail
25822583
(
@@ -2619,6 +2620,7 @@ BEGIN
26192620
END;
26202621
/
26212622

2623+
-- @version: any
26222624
/**
26232625
* M091: Annotations on VALIDATION FEEDBACK activity.
26242626
*/

mdl-examples/doctype-tests/03-page-examples.mdl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,8 @@ CREATE PAGE PgTest.P005_OrderEdit
251251
}
252252
}
253253

254-
-- MARK: DATA FORM
254+
-- @version: 11.0+
255+
-- MARK: DATA FORM — requires Mendix 11.0+ (page parameters)
255256

256257
/**
257258
* Edit customer form

mdl-examples/doctype-tests/05-database-connection-examples.mdl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
-- @version: 11.0+
12
-- ============================================================================
2-
-- Database Connection Examples - Formula 1 Database
3+
-- Database Connection Examples - Formula 1 Database (requires Mendix 11.0+)
34
-- ============================================================================
45
--
56
-- This file demonstrates CREATE DATABASE CONNECTION features using

mdl-examples/doctype-tests/06-rest-client-examples.mdl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
-- @version: 11.0+
12
-- ============================================================================
2-
-- REST Client Examples - Consumed REST Service Showcase
3+
-- REST Client Examples - Consumed REST Service Showcase (requires Mendix 11.0+)
34
-- ============================================================================
45
--
56
-- This file demonstrates REST client features supported by the Mendix REPL,

mdl-examples/doctype-tests/12-styling-examples.mdl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ CREATE PAGE StyleTest.P002_Inline_Styles
134134
}
135135
}
136136

137-
-- MARK: DesignProperties
137+
-- @version: 11.0+
138+
-- MARK: DesignProperties — requires Mendix 11.0+ (Atlas v3 design properties)
138139

139140
-- ============================================================================
140141
-- LEVEL 3: Design Properties (Atlas UI structured properties)

mdl/executor/roundtrip_doctype_test.go

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@
55
package executor
66

77
import (
8+
"fmt"
89
"os"
910
"os/exec"
1011
"path/filepath"
1112
"sort"
13+
"strconv"
1214
"strings"
1315
"testing"
1416

1517
"github.com/mendixlabs/mxcli/mdl/ast"
1618
"github.com/mendixlabs/mxcli/mdl/visitor"
19+
"github.com/mendixlabs/mxcli/sdk/mpr/version"
1720
)
1821

1922
// scriptModuleDeps maps script filenames to marketplace module MPKs they require.
@@ -129,8 +132,15 @@ func TestMxCheck_DoctypeScripts(t *testing.T) {
129132
}
130133
}
131134

135+
// Filter out version-gated sections that don't match this project's Mendix version
136+
pv := env.executor.reader.ProjectVersion()
137+
filtered, skippedLines := filterByVersion(string(content), pv)
138+
if skippedLines > 0 {
139+
t.Logf("Mendix %s: skipped %d version-gated lines", pv.ProductVersion, skippedLines)
140+
}
141+
132142
// Execute the script
133-
prog, errs := visitor.Build(string(content))
143+
prog, errs := visitor.Build(filtered)
134144
if len(errs) > 0 {
135145
t.Fatalf("Parse error: %v", errs[0])
136146
}
@@ -192,3 +202,131 @@ func allErrorsKnown(output string, knownCodes []string) bool {
192202
}
193203
return true
194204
}
205+
206+
// versionConstraint represents a min/max Mendix version range for -- @version: directives.
207+
type versionConstraint struct {
208+
minMajor, minMinor int // -1 means no minimum
209+
maxMajor, maxMinor int // -1 means no maximum
210+
}
211+
212+
// matches returns true if the project version satisfies this constraint.
213+
func (vc *versionConstraint) matches(pv *version.ProjectVersion) bool {
214+
if vc.minMajor >= 0 {
215+
if !pv.IsAtLeast(vc.minMajor, vc.minMinor) {
216+
return false
217+
}
218+
}
219+
if vc.maxMajor >= 0 {
220+
// Check that version is at most maxMajor.maxMinor
221+
if pv.MajorVersion > vc.maxMajor || (pv.MajorVersion == vc.maxMajor && pv.MinorVersion > vc.maxMinor) {
222+
return false
223+
}
224+
}
225+
return true
226+
}
227+
228+
func (vc *versionConstraint) String() string {
229+
if vc.minMajor >= 0 && vc.maxMajor >= 0 {
230+
return fmt.Sprintf("%d.%d..%d.%d", vc.minMajor, vc.minMinor, vc.maxMajor, vc.maxMinor)
231+
}
232+
if vc.minMajor >= 0 {
233+
return fmt.Sprintf("%d.%d+", vc.minMajor, vc.minMinor)
234+
}
235+
if vc.maxMajor >= 0 {
236+
return fmt.Sprintf("..%d.%d", vc.maxMajor, vc.maxMinor)
237+
}
238+
return "any"
239+
}
240+
241+
// parseVersionDirective parses a "-- @version: <constraint>" line.
242+
// Returns nil for "any" or unparseable directives.
243+
// Formats: "11.0+", "10.6..10.24", "..10.24", "any"
244+
func parseVersionDirective(line string) *versionConstraint {
245+
s := strings.TrimPrefix(line, "-- @version:")
246+
s = strings.TrimSpace(s)
247+
248+
if s == "" || s == "any" {
249+
return nil
250+
}
251+
252+
// Range: "10.6..10.24"
253+
if parts := strings.SplitN(s, "..", 2); len(parts) == 2 {
254+
vc := &versionConstraint{minMajor: -1, minMinor: -1, maxMajor: -1, maxMinor: -1}
255+
if parts[0] != "" {
256+
major, minor, ok := parseMajorMinor(parts[0])
257+
if !ok {
258+
return nil
259+
}
260+
vc.minMajor, vc.minMinor = major, minor
261+
}
262+
if parts[1] != "" {
263+
major, minor, ok := parseMajorMinor(parts[1])
264+
if !ok {
265+
return nil
266+
}
267+
vc.maxMajor, vc.maxMinor = major, minor
268+
}
269+
return vc
270+
}
271+
272+
// Minimum: "11.0+"
273+
if strings.HasSuffix(s, "+") {
274+
s = strings.TrimSuffix(s, "+")
275+
major, minor, ok := parseMajorMinor(s)
276+
if !ok {
277+
return nil
278+
}
279+
return &versionConstraint{minMajor: major, minMinor: minor, maxMajor: -1, maxMinor: -1}
280+
}
281+
282+
return nil
283+
}
284+
285+
// parseMajorMinor parses "10.24" into (10, 24, true).
286+
func parseMajorMinor(s string) (int, int, bool) {
287+
parts := strings.SplitN(s, ".", 2)
288+
if len(parts) != 2 {
289+
return 0, 0, false
290+
}
291+
major, err := strconv.Atoi(parts[0])
292+
if err != nil {
293+
return 0, 0, false
294+
}
295+
minor, err := strconv.Atoi(parts[1])
296+
if err != nil {
297+
return 0, 0, false
298+
}
299+
return major, minor, true
300+
}
301+
302+
// filterByVersion removes MDL content sections that don't match the project's Mendix version.
303+
// Sections are delimited by "-- @version: <constraint>" directives.
304+
// A directive applies to all following lines until the next directive or end of file.
305+
// "-- @version: any" resets to unconditional inclusion.
306+
func filterByVersion(content string, pv *version.ProjectVersion) (string, int) {
307+
var result strings.Builder
308+
var currentConstraint *versionConstraint // nil = no constraint (always include)
309+
skippedLines := 0
310+
311+
for _, line := range strings.Split(content, "\n") {
312+
trimmed := strings.TrimSpace(line)
313+
if strings.HasPrefix(trimmed, "-- @version:") {
314+
currentConstraint = parseVersionDirective(trimmed)
315+
// Keep the directive line as a comment (so line numbers stay close)
316+
result.WriteString(line)
317+
result.WriteString("\n")
318+
continue
319+
}
320+
if currentConstraint == nil || currentConstraint.matches(pv) {
321+
result.WriteString(line)
322+
result.WriteString("\n")
323+
} else {
324+
// Replace with empty line to preserve line numbering for error messages
325+
result.WriteString("\n")
326+
if strings.TrimSpace(line) != "" && !strings.HasPrefix(trimmed, "--") {
327+
skippedLines++
328+
}
329+
}
330+
}
331+
return result.String(), skippedLines
332+
}

0 commit comments

Comments
 (0)