Summary
Run() in cmd/mxcli/testrunner/runner.go always executes ALTER PROJECT SECURITY LEVEL OFF before building, then restores it to PRODUCTION afterward (hardcoded, regardless of what the original level actually was). This is unnecessary: the after-startup microflow the test runner relies on executes in an administrative/system context and is not subject to the project's Security Level -- confirmed empirically, it runs identically with security ON.
Forcing security OFF is actively harmful for a certain class of project: any project with a published REST or OData service configured for custom authentication fails to build at all while security is OFF, with an opaque error that has nothing to do with the test runner itself, e.g.:
ERROR at API, Published REST service 'TaskAPI', -: App security is off, but custom authentication is enabled for this service
ERROR at Reporting, Published REST service 'reporting', -: App security is off, but custom authentication is enabled for this service
For such a project, mxcli test cannot produce a passing run at all, even though nothing about the project's own microflow logic or test setup is actually broken.
There's a secondary correctness issue in the same area: cleanup() restores security to a hardcoded PRODUCTION, rather than the project's actual original level -- so a project that legitimately runs with security OFF, or at some other level, gets silently changed to PRODUCTION after every test run.
Steps to reproduce
mxcli new ProbeProj --version 11.12.0 --skip-init
cd ProbeProj
./mxcli -c "ALTER PROJECT SECURITY LEVEL PRODUCTION" -p ProbeProj.mpr
cat > smoke.test.mdl << 'EOF'
/**
* @test Basic arithmetic sanity check
* @expect $result = 4
*/
DECLARE $result Integer = 2 + 2;
/
EOF
./mxcli test smoke.test.mdl -p ProbeProj.mpr
Inspecting the container logs during the run shows the after-startup microflow executing and passing normally with security left at Production throughout -- forcing it OFF was never necessary for the test pattern to work.
The security/custom-auth conflict itself can't be reproduced in a blank project (mxcli has no way to author a published REST service with custom authentication via MDL), but it's straightforward to see by inspection: any project with such a service configured will hit exactly the error above the moment mxcli test forces security OFF, independent of anything about the tests themselves.
Root cause
cmd/mxcli/testrunner/runner.go:
// Set security OFF for testing
if err := execMxcliCmd(opts.ProjectPath, "ALTER PROJECT SECURITY LEVEL OFF"); err != nil {
fmt.Fprintf(w, " Warning: could not set security OFF: %v\n", err)
}
and in cleanup():
// Restore security level
if err := execMxcliCmd(projectPath, "ALTER PROJECT SECURITY LEVEL PRODUCTION"); err != nil {
fmt.Fprintf(w, " Warning: could not restore security: %v\n", err)
}
Suggested fix
Don't touch Security Level at all. The after-startup pattern the test runner depends on works identically with security left at whatever level the project already has -- there's no need to force it off, and therefore nothing to restore afterward either. This removes both the custom-auth-service conflict and the "restores to the wrong level" correctness issue in one step.
If there's a reason for the OFF-then-restore behavior that isn't apparent from the code (e.g. some other test scenario it was meant to unblock), a safer middle ground would be to capture the actual original security level (the same way getAfterStartup() captures the original after-startup value) and restore that, rather than hardcoding PRODUCTION -- and to make forcing it OFF at all opt-in via a flag, rather than the default.
I have a working fix (verified: after-startup executes and tests pass with security left untouched throughout, confirmed on both a blank project and a production project with published REST services using custom authentication) and am happy to open a PR once this issue is approved.
Environment
- mxcli: built from
main @ ead892672f82fdc5f54ed8a944e98026845700a2 (2026-07-28)
- Mendix version: 11.12.0
Summary
Run()incmd/mxcli/testrunner/runner.goalways executesALTER PROJECT SECURITY LEVEL OFFbefore building, then restores it toPRODUCTIONafterward (hardcoded, regardless of what the original level actually was). This is unnecessary: the after-startup microflow the test runner relies on executes in an administrative/system context and is not subject to the project's Security Level -- confirmed empirically, it runs identically with security ON.Forcing security OFF is actively harmful for a certain class of project: any project with a published REST or OData service configured for custom authentication fails to build at all while security is OFF, with an opaque error that has nothing to do with the test runner itself, e.g.:
For such a project,
mxcli testcannot produce a passing run at all, even though nothing about the project's own microflow logic or test setup is actually broken.There's a secondary correctness issue in the same area:
cleanup()restores security to a hardcodedPRODUCTION, rather than the project's actual original level -- so a project that legitimately runs with security OFF, or at some other level, gets silently changed toPRODUCTIONafter every test run.Steps to reproduce
Inspecting the container logs during the run shows the after-startup microflow executing and passing normally with security left at Production throughout -- forcing it OFF was never necessary for the test pattern to work.
The security/custom-auth conflict itself can't be reproduced in a blank project (
mxclihas no way to author a published REST service with custom authentication via MDL), but it's straightforward to see by inspection: any project with such a service configured will hit exactly the error above the momentmxcli testforces security OFF, independent of anything about the tests themselves.Root cause
cmd/mxcli/testrunner/runner.go:and in
cleanup():Suggested fix
Don't touch Security Level at all. The after-startup pattern the test runner depends on works identically with security left at whatever level the project already has -- there's no need to force it off, and therefore nothing to restore afterward either. This removes both the custom-auth-service conflict and the "restores to the wrong level" correctness issue in one step.
If there's a reason for the OFF-then-restore behavior that isn't apparent from the code (e.g. some other test scenario it was meant to unblock), a safer middle ground would be to capture the actual original security level (the same way
getAfterStartup()captures the original after-startup value) and restore that, rather than hardcodingPRODUCTION-- and to make forcing it OFF at all opt-in via a flag, rather than the default.I have a working fix (verified: after-startup executes and tests pass with security left untouched throughout, confirmed on both a blank project and a production project with published REST services using custom authentication) and am happy to open a PR once this issue is approved.
Environment
main@ead892672f82fdc5f54ed8a944e98026845700a2(2026-07-28)