Skip to content

Commit 8766bd3

Browse files
akoclaude
andcommitted
fix: emit structured reference errors and add JSON output tests (#134)
Route check --references validation errors through the linter formatter so --format json produces machine-readable output instead of plain text. Add tests for showAccessOnMicroflow/showAccessOnPage JSON output and empty- result JSON output for showConstants and showPublishedRestServices. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent bbff3da commit 8766bd3

2 files changed

Lines changed: 89 additions & 4 deletions

File tree

cmd/mxcli/cmd_check.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,23 @@ Examples:
160160
// Validate the program (considers objects defined within the script)
161161
validationErrors := exec.ValidateProgram(prog)
162162
if len(validationErrors) > 0 {
163-
fmt.Fprintf(os.Stderr, "Reference errors:\n")
164-
for _, err := range validationErrors {
165-
fmt.Fprintf(os.Stderr, " %v\n", err)
163+
if isStructured {
164+
var refViolations []linter.Violation
165+
for _, err := range validationErrors {
166+
refViolations = append(refViolations, linter.Violation{
167+
RuleID: "MDL-REF",
168+
Severity: linter.SeverityError,
169+
Message: err.Error(),
170+
})
171+
}
172+
formatter.Format(refViolations, os.Stderr)
173+
} else {
174+
fmt.Fprintf(os.Stderr, "Reference errors:\n")
175+
for _, err := range validationErrors {
176+
fmt.Fprintf(os.Stderr, " %v\n", err)
177+
}
178+
fmt.Fprintf(os.Stderr, "\n✗ %d reference error(s) found\n", len(validationErrors))
166179
}
167-
fmt.Fprintf(os.Stderr, "\n✗ %d reference error(s) found\n", len(validationErrors))
168180
os.Exit(1)
169181
}
170182
if !isStructured {

mdl/executor/cmd_json_mock_test.go

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package executor
55
import (
66
"testing"
77

8+
"github.com/mendixlabs/mxcli/mdl/ast"
89
"github.com/mendixlabs/mxcli/mdl/backend/mock"
910
"github.com/mendixlabs/mxcli/mdl/types"
1011
"github.com/mendixlabs/mxcli/model"
@@ -526,3 +527,75 @@ func TestListDataTransformers_Mock_JSON(t *testing.T) {
526527
assertValidJSON(t, buf.String())
527528
assertContainsStr(t, buf.String(), "Transform1")
528529
}
530+
531+
func TestShowAccessOnMicroflow_Mock_JSON(t *testing.T) {
532+
mod := mkModule("MyModule")
533+
h := mkHierarchy(mod)
534+
mf := mkMicroflow(mod.ID, "ACT_DoStuff")
535+
mf.AllowedModuleRoles = []model.ID{"MyModule.User", "MyModule.Admin"}
536+
withContainer(h, mf.ContainerID, mod.ID)
537+
538+
mb := &mock.MockBackend{
539+
IsConnectedFunc: func() bool { return true },
540+
ListMicroflowsFunc: func() ([]*microflows.Microflow, error) { return []*microflows.Microflow{mf}, nil },
541+
}
542+
543+
name := &ast.QualifiedName{Module: "MyModule", Name: "ACT_DoStuff"}
544+
ctx, buf := newMockCtx(t, withBackend(mb), withFormat(FormatJSON), withHierarchy(h))
545+
assertNoError(t, showAccessOnMicroflow(ctx, name))
546+
assertValidJSON(t, buf.String())
547+
assertContainsStr(t, buf.String(), "User")
548+
}
549+
550+
func TestShowAccessOnPage_Mock_JSON(t *testing.T) {
551+
mod := mkModule("MyModule")
552+
h := mkHierarchy(mod)
553+
pg := mkPage(mod.ID, "Page_Home")
554+
pg.AllowedRoles = []model.ID{"MyModule.User"}
555+
withContainer(h, pg.ContainerID, mod.ID)
556+
557+
mb := &mock.MockBackend{
558+
IsConnectedFunc: func() bool { return true },
559+
ListPagesFunc: func() ([]*pages.Page, error) { return []*pages.Page{pg}, nil },
560+
}
561+
562+
name := &ast.QualifiedName{Module: "MyModule", Name: "Page_Home"}
563+
ctx, buf := newMockCtx(t, withBackend(mb), withFormat(FormatJSON), withHierarchy(h))
564+
assertNoError(t, showAccessOnPage(ctx, name))
565+
assertValidJSON(t, buf.String())
566+
assertContainsStr(t, buf.String(), "User")
567+
}
568+
569+
// TestShowConstants_Mock_JSON_EmptyResult verifies that an empty result still
570+
// produces valid JSON (not the "No ... found." plain-text message).
571+
func TestShowConstants_Mock_JSON_EmptyResult(t *testing.T) {
572+
mod := mkModule("MyModule")
573+
h := mkHierarchy(mod)
574+
575+
mb := &mock.MockBackend{
576+
IsConnectedFunc: func() bool { return true },
577+
ListConstantsFunc: func() ([]*model.Constant, error) { return nil, nil },
578+
}
579+
580+
ctx, buf := newMockCtx(t, withBackend(mb), withFormat(FormatJSON), withHierarchy(h))
581+
assertNoError(t, showConstants(ctx, ""))
582+
assertValidJSON(t, buf.String())
583+
assertNotContainsStr(t, buf.String(), "No constants found")
584+
}
585+
586+
// TestShowPublishedRestServices_Mock_JSON_EmptyResult verifies that an empty
587+
// result still produces valid JSON in JSON mode.
588+
func TestShowPublishedRestServices_Mock_JSON_EmptyResult(t *testing.T) {
589+
mod := mkModule("MyModule")
590+
h := mkHierarchy(mod)
591+
592+
mb := &mock.MockBackend{
593+
IsConnectedFunc: func() bool { return true },
594+
ListPublishedRestServicesFunc: func() ([]*model.PublishedRestService, error) { return nil, nil },
595+
}
596+
597+
ctx, buf := newMockCtx(t, withBackend(mb), withFormat(FormatJSON), withHierarchy(h))
598+
assertNoError(t, showPublishedRestServices(ctx, ""))
599+
assertValidJSON(t, buf.String())
600+
assertNotContainsStr(t, buf.String(), "No published REST services found")
601+
}

0 commit comments

Comments
 (0)