Skip to content

Commit ea6603f

Browse files
committed
test(linter): add unit tests for all 14 untested linter rules
Add 12 new test files (~1470 lines) covering all previously untested linter rules in mdl/linter/rules/: - SQLite-backed Check() tests for MPR002, MPR003, SEC001 - Walker function tests for CONV011-014, MPR004 - Metadata + nil-reader guard tests for SEC002, SEC003, MPR007, MPR008 - Pure unit tests for naming helpers and empty container detection - Shared test helpers extracted into helpers_test.go Also fixes a misleading comment in cmd_javaactions_mock_test.go clarifying the execShow connected-guard dependency.
1 parent dccb33d commit ea6603f

13 files changed

Lines changed: 1476 additions & 3 deletions

mdl/executor/cmd_javaactions_mock_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ func TestDescribeJavaAction_Mock(t *testing.T) {
5959
}
6060

6161
// NOTE: listJavaActions has no explicit not-connected guard. It calls
62-
// getHierarchy (returns nil when disconnected) then proceeds to call
63-
// ListJavaActions on the backend. The handler degrades gracefully —
64-
// with an empty result set it succeeds with a nil hierarchy.
62+
// getHierarchy (which returns nil when disconnected) and is intended to
63+
// be reached through execShow, which enforces a connected backend first.
64+
// A nil hierarchy is only harmless when the backend returns no Java
65+
// actions; if Java actions are returned while disconnected, dereferencing
66+
// the nil hierarchy would panic.
6567

6668
func TestShowJavaActions_BackendError(t *testing.T) {
6769
mod := mkModule("MyModule")
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package rules
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mendixlabs/mxcli/mdl/linter"
9+
"github.com/mendixlabs/mxcli/sdk/microflows"
10+
)
11+
12+
func TestFindUnhandledCalls_RestCallNoCustom(t *testing.T) {
13+
objects := []microflows.MicroflowObject{
14+
&microflows.ActionActivity{
15+
BaseActivity: microflows.BaseActivity{
16+
ErrorHandlingType: microflows.ErrorHandlingTypeAbort,
17+
},
18+
Action: &microflows.RestCallAction{},
19+
},
20+
}
21+
22+
var violations []linter.Violation
23+
r := NewErrorHandlingOnCallsRule()
24+
findUnhandledCalls(objects, testMicroflow(), r, &violations)
25+
26+
if len(violations) != 1 {
27+
t.Fatalf("expected 1 violation, got %d", len(violations))
28+
}
29+
if violations[0].RuleID != "CONV013" {
30+
t.Errorf("expected CONV013, got %s", violations[0].RuleID)
31+
}
32+
}
33+
34+
func TestFindUnhandledCalls_RestCallCustom(t *testing.T) {
35+
objects := []microflows.MicroflowObject{
36+
&microflows.ActionActivity{
37+
BaseActivity: microflows.BaseActivity{
38+
ErrorHandlingType: microflows.ErrorHandlingTypeCustom,
39+
},
40+
Action: &microflows.RestCallAction{},
41+
},
42+
}
43+
44+
var violations []linter.Violation
45+
r := NewErrorHandlingOnCallsRule()
46+
findUnhandledCalls(objects, testMicroflow(), r, &violations)
47+
48+
if len(violations) != 0 {
49+
t.Errorf("expected 0 violations with Custom handling, got %d", len(violations))
50+
}
51+
}
52+
53+
func TestFindUnhandledCalls_CustomWithoutRollback(t *testing.T) {
54+
objects := []microflows.MicroflowObject{
55+
&microflows.ActionActivity{
56+
BaseActivity: microflows.BaseActivity{
57+
ErrorHandlingType: microflows.ErrorHandlingTypeCustomWithoutRollback,
58+
},
59+
Action: &microflows.RestCallAction{},
60+
},
61+
}
62+
63+
var violations []linter.Violation
64+
r := NewErrorHandlingOnCallsRule()
65+
findUnhandledCalls(objects, testMicroflow(), r, &violations)
66+
67+
if len(violations) != 0 {
68+
t.Errorf("expected 0 violations with CustomWithoutRollback, got %d", len(violations))
69+
}
70+
}
71+
72+
func TestFindUnhandledCalls_JavaAction(t *testing.T) {
73+
objects := []microflows.MicroflowObject{
74+
&microflows.ActionActivity{
75+
BaseActivity: microflows.BaseActivity{
76+
ErrorHandlingType: microflows.ErrorHandlingTypeAbort,
77+
},
78+
Action: &microflows.JavaActionCallAction{},
79+
},
80+
}
81+
82+
var violations []linter.Violation
83+
r := NewErrorHandlingOnCallsRule()
84+
findUnhandledCalls(objects, testMicroflow(), r, &violations)
85+
86+
if len(violations) != 1 {
87+
t.Fatalf("expected 1 violation for Java action, got %d", len(violations))
88+
}
89+
}
90+
91+
func TestFindUnhandledCalls_WebServiceCall(t *testing.T) {
92+
objects := []microflows.MicroflowObject{
93+
&microflows.ActionActivity{
94+
BaseActivity: microflows.BaseActivity{
95+
ErrorHandlingType: microflows.ErrorHandlingTypeContinue,
96+
},
97+
Action: &microflows.WebServiceCallAction{},
98+
},
99+
}
100+
101+
var violations []linter.Violation
102+
r := NewErrorHandlingOnCallsRule()
103+
findUnhandledCalls(objects, testMicroflow(), r, &violations)
104+
105+
if len(violations) != 1 {
106+
t.Fatalf("expected 1 violation for WS call, got %d", len(violations))
107+
}
108+
if violations[0].RuleID != "CONV013" {
109+
t.Errorf("expected CONV013, got %s", violations[0].RuleID)
110+
}
111+
}
112+
113+
func TestFindUnhandledCalls_NonExternalAction(t *testing.T) {
114+
objects := []microflows.MicroflowObject{
115+
&microflows.ActionActivity{
116+
BaseActivity: microflows.BaseActivity{
117+
ErrorHandlingType: microflows.ErrorHandlingTypeAbort,
118+
},
119+
Action: &microflows.CommitObjectsAction{},
120+
},
121+
}
122+
123+
var violations []linter.Violation
124+
r := NewErrorHandlingOnCallsRule()
125+
findUnhandledCalls(objects, testMicroflow(), r, &violations)
126+
127+
if len(violations) != 0 {
128+
t.Errorf("expected 0 violations for non-external action, got %d", len(violations))
129+
}
130+
}
131+
132+
func TestFindUnhandledCalls_InsideLoop(t *testing.T) {
133+
loopBody := &microflows.MicroflowObjectCollection{
134+
Objects: []microflows.MicroflowObject{
135+
&microflows.ActionActivity{
136+
BaseActivity: microflows.BaseActivity{
137+
ErrorHandlingType: microflows.ErrorHandlingTypeAbort,
138+
},
139+
Action: &microflows.RestCallAction{},
140+
},
141+
},
142+
}
143+
objects := []microflows.MicroflowObject{
144+
&microflows.LoopedActivity{
145+
ObjectCollection: loopBody,
146+
},
147+
}
148+
149+
var violations []linter.Violation
150+
r := NewErrorHandlingOnCallsRule()
151+
findUnhandledCalls(objects, testMicroflow(), r, &violations)
152+
153+
if len(violations) != 1 {
154+
t.Errorf("expected 1 violation inside loop, got %d", len(violations))
155+
}
156+
}
157+
158+
// --- CONV014 tests ---
159+
160+
func TestFindContinueErrorHandling_Activity(t *testing.T) {
161+
objects := []microflows.MicroflowObject{
162+
&microflows.ActionActivity{
163+
BaseActivity: microflows.BaseActivity{
164+
Caption: "Do something",
165+
ErrorHandlingType: microflows.ErrorHandlingTypeContinue,
166+
},
167+
Action: &microflows.CommitObjectsAction{},
168+
},
169+
}
170+
171+
var violations []linter.Violation
172+
r := NewNoContinueErrorHandlingRule()
173+
findContinueErrorHandling(objects, testMicroflow(), r, &violations)
174+
175+
if len(violations) != 1 {
176+
t.Fatalf("expected 1 violation, got %d", len(violations))
177+
}
178+
if violations[0].RuleID != "CONV014" {
179+
t.Errorf("expected CONV014, got %s", violations[0].RuleID)
180+
}
181+
}
182+
183+
func TestFindContinueErrorHandling_Loop(t *testing.T) {
184+
objects := []microflows.MicroflowObject{
185+
&microflows.LoopedActivity{
186+
Caption: "Process items",
187+
ErrorHandlingType: microflows.ErrorHandlingTypeContinue,
188+
},
189+
}
190+
191+
var violations []linter.Violation
192+
r := NewNoContinueErrorHandlingRule()
193+
findContinueErrorHandling(objects, testMicroflow(), r, &violations)
194+
195+
if len(violations) != 1 {
196+
t.Fatalf("expected 1 violation for loop, got %d", len(violations))
197+
}
198+
}
199+
200+
func TestFindContinueErrorHandling_AbortIsOk(t *testing.T) {
201+
objects := []microflows.MicroflowObject{
202+
&microflows.ActionActivity{
203+
BaseActivity: microflows.BaseActivity{
204+
ErrorHandlingType: microflows.ErrorHandlingTypeAbort,
205+
},
206+
Action: &microflows.CommitObjectsAction{},
207+
},
208+
}
209+
210+
var violations []linter.Violation
211+
r := NewNoContinueErrorHandlingRule()
212+
findContinueErrorHandling(objects, testMicroflow(), r, &violations)
213+
214+
if len(violations) != 0 {
215+
t.Errorf("expected 0 violations for Abort, got %d", len(violations))
216+
}
217+
}
218+
219+
func TestErrorHandlingOnCallsRule_Metadata(t *testing.T) {
220+
r := NewErrorHandlingOnCallsRule()
221+
if r.ID() != "CONV013" {
222+
t.Errorf("ID = %q, want CONV013", r.ID())
223+
}
224+
}
225+
226+
func TestNoContinueErrorHandlingRule_Metadata(t *testing.T) {
227+
r := NewNoContinueErrorHandlingRule()
228+
if r.ID() != "CONV014" {
229+
t.Errorf("ID = %q, want CONV014", r.ID())
230+
}
231+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package rules
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mendixlabs/mxcli/mdl/linter"
9+
"github.com/mendixlabs/mxcli/sdk/microflows"
10+
)
11+
12+
func TestFindCommitsInLoops_NoLoop(t *testing.T) {
13+
objects := []microflows.MicroflowObject{
14+
&microflows.ActionActivity{
15+
BaseActivity: microflows.BaseActivity{},
16+
Action: &microflows.CommitObjectsAction{},
17+
},
18+
}
19+
20+
var violations []linter.Violation
21+
r := NewNoCommitInLoopRule()
22+
findCommitsInLoops(objects, testMicroflow(), r, &violations, false)
23+
24+
if len(violations) != 0 {
25+
t.Errorf("expected 0 violations outside loop, got %d", len(violations))
26+
}
27+
}
28+
29+
func TestFindCommitsInLoops_CommitInsideLoop(t *testing.T) {
30+
loopBody := &microflows.MicroflowObjectCollection{
31+
Objects: []microflows.MicroflowObject{
32+
&microflows.ActionActivity{
33+
BaseActivity: microflows.BaseActivity{},
34+
Action: &microflows.CommitObjectsAction{},
35+
},
36+
},
37+
}
38+
objects := []microflows.MicroflowObject{
39+
&microflows.LoopedActivity{
40+
ObjectCollection: loopBody,
41+
},
42+
}
43+
44+
var violations []linter.Violation
45+
r := NewNoCommitInLoopRule()
46+
findCommitsInLoops(objects, testMicroflow(), r, &violations, false)
47+
48+
if len(violations) != 1 {
49+
t.Fatalf("expected 1 violation, got %d", len(violations))
50+
}
51+
if violations[0].RuleID != "CONV011" {
52+
t.Errorf("expected CONV011, got %s", violations[0].RuleID)
53+
}
54+
}
55+
56+
func TestFindCommitsInLoops_NilAction(t *testing.T) {
57+
objects := []microflows.MicroflowObject{
58+
&microflows.ActionActivity{
59+
BaseActivity: microflows.BaseActivity{},
60+
Action: nil,
61+
},
62+
}
63+
64+
var violations []linter.Violation
65+
r := NewNoCommitInLoopRule()
66+
findCommitsInLoops(objects, testMicroflow(), r, &violations, true)
67+
68+
if len(violations) != 0 {
69+
t.Errorf("expected 0 violations for nil action, got %d", len(violations))
70+
}
71+
}
72+
73+
func TestNoCommitInLoopRule_NilReader(t *testing.T) {
74+
r := NewNoCommitInLoopRule()
75+
ctx := linter.NewLintContextFromDB(nil)
76+
// Reader() is nil, should return nil
77+
violations := r.Check(ctx)
78+
if violations != nil {
79+
t.Errorf("expected nil with nil reader, got %v", violations)
80+
}
81+
}
82+
83+
func TestNoCommitInLoopRule_Metadata(t *testing.T) {
84+
r := NewNoCommitInLoopRule()
85+
if r.ID() != "CONV011" {
86+
t.Errorf("ID = %q, want CONV011", r.ID())
87+
}
88+
if r.Category() != "performance" {
89+
t.Errorf("Category = %q, want performance", r.Category())
90+
}
91+
}

0 commit comments

Comments
 (0)