Skip to content

Commit 70ec21e

Browse files
committed
feat: add comprehensive mock-based handler tests (85 tests, 30 files)
Add mock-based unit tests for all testable handler functions in mdl/executor/, covering show/list/describe/create/drop commands across enumerations, constants, entities, associations, modules, microflows, pages, security, navigation, settings, OData, REST, business events, workflows, java actions, JS actions, fragments, data transformers, DB connections, image collections, JSON structures, import/export mappings, agent editor, misc, mermaid, and write handlers. Test infrastructure uses functional options pattern with model factories and a MockBackend with function-field injection. All tests use stdlib only (no testify). Thread-safe ID generation via sync/atomic.
1 parent 24574fd commit 70ec21e

30 files changed

Lines changed: 2615 additions & 98 deletions
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mendixlabs/mxcli/mdl/ast"
9+
"github.com/mendixlabs/mxcli/mdl/backend/mock"
10+
"github.com/mendixlabs/mxcli/model"
11+
"github.com/mendixlabs/mxcli/sdk/agenteditor"
12+
)
13+
14+
func TestShowAgentEditorModels_Mock(t *testing.T) {
15+
mod := mkModule("M")
16+
m1 := &agenteditor.Model{
17+
BaseElement: model.BaseElement{ID: nextID("aem")},
18+
ContainerID: mod.ID,
19+
Name: "GPT4",
20+
Provider: "MxCloudGenAI",
21+
DisplayName: "GPT-4 Turbo",
22+
Key: &agenteditor.ConstantRef{QualifiedName: "M.APIKey"},
23+
}
24+
25+
h := mkHierarchy(mod)
26+
withContainer(h, m1.ContainerID, mod.ID)
27+
28+
mb := &mock.MockBackend{
29+
IsConnectedFunc: func() bool { return true },
30+
ListAgentEditorModelsFunc: func() ([]*agenteditor.Model, error) { return []*agenteditor.Model{m1}, nil },
31+
}
32+
33+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
34+
assertNoError(t, showAgentEditorModels(ctx, ""))
35+
36+
out := buf.String()
37+
assertContainsStr(t, out, "Qualified Name")
38+
assertContainsStr(t, out, "Module")
39+
assertContainsStr(t, out, "Provider")
40+
assertContainsStr(t, out, "Key Constant")
41+
assertContainsStr(t, out, "Display Name")
42+
assertContainsStr(t, out, "M.GPT4")
43+
assertContainsStr(t, out, "MxCloudGenAI")
44+
assertContainsStr(t, out, "M.APIKey")
45+
assertContainsStr(t, out, "GPT-4 Turbo")
46+
}
47+
48+
func TestDescribeAgentEditorModel_Mock(t *testing.T) {
49+
mod := mkModule("M")
50+
m1 := &agenteditor.Model{
51+
BaseElement: model.BaseElement{ID: nextID("aem")},
52+
ContainerID: mod.ID,
53+
Name: "GPT4",
54+
Provider: "MxCloudGenAI",
55+
Key: &agenteditor.ConstantRef{QualifiedName: "M.APIKey"},
56+
}
57+
58+
h := mkHierarchy(mod)
59+
withContainer(h, m1.ContainerID, mod.ID)
60+
61+
mb := &mock.MockBackend{
62+
IsConnectedFunc: func() bool { return true },
63+
ListAgentEditorModelsFunc: func() ([]*agenteditor.Model, error) { return []*agenteditor.Model{m1}, nil },
64+
}
65+
66+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
67+
assertNoError(t, describeAgentEditorModel(ctx, ast.QualifiedName{Module: "M", Name: "GPT4"}))
68+
69+
out := buf.String()
70+
assertContainsStr(t, out, "CREATE MODEL")
71+
assertContainsStr(t, out, "Provider")
72+
assertContainsStr(t, out, "Key")
73+
}
74+
75+
func TestShowAgentEditorAgents_Mock(t *testing.T) {
76+
mod := mkModule("M")
77+
a1 := &agenteditor.Agent{
78+
BaseElement: model.BaseElement{ID: nextID("aea")},
79+
ContainerID: mod.ID,
80+
Name: "MyAgent",
81+
UsageType: "Chat",
82+
Model: &agenteditor.DocRef{QualifiedName: "M.GPT4"},
83+
Tools: []agenteditor.AgentTool{{ID: "t1", Enabled: true}},
84+
KBTools: []agenteditor.AgentKBTool{{ID: "kb1", Enabled: true}},
85+
}
86+
87+
h := mkHierarchy(mod)
88+
withContainer(h, a1.ContainerID, mod.ID)
89+
90+
mb := &mock.MockBackend{
91+
IsConnectedFunc: func() bool { return true },
92+
ListAgentEditorAgentsFunc: func() ([]*agenteditor.Agent, error) { return []*agenteditor.Agent{a1}, nil },
93+
}
94+
95+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
96+
assertNoError(t, showAgentEditorAgents(ctx, ""))
97+
98+
out := buf.String()
99+
assertContainsStr(t, out, "Qualified Name")
100+
assertContainsStr(t, out, "Usage")
101+
assertContainsStr(t, out, "Model")
102+
assertContainsStr(t, out, "Tools")
103+
assertContainsStr(t, out, "KBs")
104+
assertContainsStr(t, out, "M.MyAgent")
105+
assertContainsStr(t, out, "Chat")
106+
assertContainsStr(t, out, "M.GPT4")
107+
}
108+
109+
func TestDescribeAgentEditorAgent_Mock(t *testing.T) {
110+
mod := mkModule("M")
111+
a1 := &agenteditor.Agent{
112+
BaseElement: model.BaseElement{ID: nextID("aea")},
113+
ContainerID: mod.ID,
114+
Name: "MyAgent",
115+
UsageType: "Chat",
116+
Model: &agenteditor.DocRef{QualifiedName: "M.GPT4"},
117+
}
118+
119+
h := mkHierarchy(mod)
120+
withContainer(h, a1.ContainerID, mod.ID)
121+
122+
mb := &mock.MockBackend{
123+
IsConnectedFunc: func() bool { return true },
124+
ListAgentEditorAgentsFunc: func() ([]*agenteditor.Agent, error) { return []*agenteditor.Agent{a1}, nil },
125+
}
126+
127+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
128+
assertNoError(t, describeAgentEditorAgent(ctx, ast.QualifiedName{Module: "M", Name: "MyAgent"}))
129+
130+
out := buf.String()
131+
assertContainsStr(t, out, "CREATE AGENT")
132+
assertContainsStr(t, out, "UsageType")
133+
assertContainsStr(t, out, "Model")
134+
}
135+
136+
func TestShowAgentEditorKnowledgeBases_Mock(t *testing.T) {
137+
mod := mkModule("M")
138+
kb := &agenteditor.KnowledgeBase{
139+
BaseElement: model.BaseElement{ID: nextID("aekb")},
140+
ContainerID: mod.ID,
141+
Name: "MyKB",
142+
Provider: "MxCloudGenAI",
143+
Key: &agenteditor.ConstantRef{QualifiedName: "M.KBKey"},
144+
ModelDisplayName: "text-embedding-ada-002",
145+
}
146+
147+
h := mkHierarchy(mod)
148+
withContainer(h, kb.ContainerID, mod.ID)
149+
150+
mb := &mock.MockBackend{
151+
IsConnectedFunc: func() bool { return true },
152+
ListAgentEditorKnowledgeBasesFunc: func() ([]*agenteditor.KnowledgeBase, error) { return []*agenteditor.KnowledgeBase{kb}, nil },
153+
}
154+
155+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
156+
assertNoError(t, showAgentEditorKnowledgeBases(ctx, ""))
157+
158+
out := buf.String()
159+
assertContainsStr(t, out, "Qualified Name")
160+
assertContainsStr(t, out, "Provider")
161+
assertContainsStr(t, out, "Key Constant")
162+
assertContainsStr(t, out, "Embedding Model")
163+
assertContainsStr(t, out, "M.MyKB")
164+
assertContainsStr(t, out, "MxCloudGenAI")
165+
assertContainsStr(t, out, "M.KBKey")
166+
assertContainsStr(t, out, "text-embedding-ada-002")
167+
}
168+
169+
func TestDescribeAgentEditorKnowledgeBase_Mock(t *testing.T) {
170+
mod := mkModule("M")
171+
kb := &agenteditor.KnowledgeBase{
172+
BaseElement: model.BaseElement{ID: nextID("aekb")},
173+
ContainerID: mod.ID,
174+
Name: "MyKB",
175+
Provider: "MxCloudGenAI",
176+
Key: &agenteditor.ConstantRef{QualifiedName: "M.KBKey"},
177+
}
178+
179+
h := mkHierarchy(mod)
180+
withContainer(h, kb.ContainerID, mod.ID)
181+
182+
mb := &mock.MockBackend{
183+
IsConnectedFunc: func() bool { return true },
184+
ListAgentEditorKnowledgeBasesFunc: func() ([]*agenteditor.KnowledgeBase, error) { return []*agenteditor.KnowledgeBase{kb}, nil },
185+
}
186+
187+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
188+
assertNoError(t, describeAgentEditorKnowledgeBase(ctx, ast.QualifiedName{Module: "M", Name: "MyKB"}))
189+
190+
out := buf.String()
191+
assertContainsStr(t, out, "CREATE KNOWLEDGE BASE")
192+
assertContainsStr(t, out, "Provider")
193+
}
194+
195+
func TestShowAgentEditorConsumedMCPServices_Mock(t *testing.T) {
196+
mod := mkModule("M")
197+
svc := &agenteditor.ConsumedMCPService{
198+
BaseElement: model.BaseElement{ID: nextID("aemcp")},
199+
ContainerID: mod.ID,
200+
Name: "MySvc",
201+
ProtocolVersion: "2025-03-26",
202+
Version: "1.0.0",
203+
ConnectionTimeoutSeconds: 30,
204+
}
205+
206+
h := mkHierarchy(mod)
207+
withContainer(h, svc.ContainerID, mod.ID)
208+
209+
mb := &mock.MockBackend{
210+
IsConnectedFunc: func() bool { return true },
211+
ListAgentEditorConsumedMCPServicesFunc: func() ([]*agenteditor.ConsumedMCPService, error) { return []*agenteditor.ConsumedMCPService{svc}, nil },
212+
}
213+
214+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
215+
assertNoError(t, showAgentEditorConsumedMCPServices(ctx, ""))
216+
217+
out := buf.String()
218+
assertContainsStr(t, out, "Qualified Name")
219+
assertContainsStr(t, out, "Protocol")
220+
assertContainsStr(t, out, "Version")
221+
assertContainsStr(t, out, "Timeout")
222+
assertContainsStr(t, out, "M.MySvc")
223+
assertContainsStr(t, out, "2025-03-26")
224+
assertContainsStr(t, out, "1.0.0")
225+
}
226+
227+
func TestDescribeAgentEditorConsumedMCPService_Mock(t *testing.T) {
228+
mod := mkModule("M")
229+
svc := &agenteditor.ConsumedMCPService{
230+
BaseElement: model.BaseElement{ID: nextID("aemcp")},
231+
ContainerID: mod.ID,
232+
Name: "MySvc",
233+
ProtocolVersion: "2025-03-26",
234+
Version: "1.0.0",
235+
ConnectionTimeoutSeconds: 30,
236+
}
237+
238+
h := mkHierarchy(mod)
239+
withContainer(h, svc.ContainerID, mod.ID)
240+
241+
mb := &mock.MockBackend{
242+
IsConnectedFunc: func() bool { return true },
243+
ListAgentEditorConsumedMCPServicesFunc: func() ([]*agenteditor.ConsumedMCPService, error) { return []*agenteditor.ConsumedMCPService{svc}, nil },
244+
}
245+
246+
ctx, buf := newMockCtx(t, withBackend(mb), withHierarchy(h))
247+
assertNoError(t, describeAgentEditorConsumedMCPService(ctx, ast.QualifiedName{Module: "M", Name: "MySvc"}))
248+
249+
out := buf.String()
250+
assertContainsStr(t, out, "CREATE CONSUMED MCP SERVICE")
251+
assertContainsStr(t, out, "ProtocolVersion")
252+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
package executor
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mendixlabs/mxcli/mdl/backend/mock"
9+
"github.com/mendixlabs/mxcli/model"
10+
"github.com/mendixlabs/mxcli/sdk/domainmodel"
11+
)
12+
13+
func TestShowAssociations_Mock(t *testing.T) {
14+
mod := mkModule("MyModule")
15+
ent1 := mkEntity(mod.ID, "Order")
16+
ent2 := mkEntity(mod.ID, "Customer")
17+
assoc := mkAssociation(mod.ID, "Order_Customer", ent1.ID, ent2.ID)
18+
19+
dm := &domainmodel.DomainModel{
20+
BaseElement: model.BaseElement{ID: nextID("dm")},
21+
ContainerID: mod.ID,
22+
Entities: []*domainmodel.Entity{ent1, ent2},
23+
Associations: []*domainmodel.Association{assoc},
24+
}
25+
26+
mb := &mock.MockBackend{
27+
IsConnectedFunc: func() bool { return true },
28+
ListModulesFunc: func() ([]*model.Module, error) { return []*model.Module{mod}, nil },
29+
ListDomainModelsFunc: func() ([]*domainmodel.DomainModel, error) { return []*domainmodel.DomainModel{dm}, nil },
30+
}
31+
32+
ctx, buf := newMockCtx(t, withBackend(mb))
33+
assertNoError(t, showAssociations(ctx, ""))
34+
35+
out := buf.String()
36+
assertContainsStr(t, out, "MyModule.Order_Customer")
37+
assertContainsStr(t, out, "MyModule.Order")
38+
assertContainsStr(t, out, "MyModule.Customer")
39+
assertContainsStr(t, out, "Reference")
40+
assertContainsStr(t, out, "(1 associations)")
41+
}
42+
43+
func TestShowAssociations_Mock_FilterByModule(t *testing.T) {
44+
mod1 := mkModule("Sales")
45+
mod2 := mkModule("HR")
46+
ent1 := mkEntity(mod1.ID, "Order")
47+
ent2 := mkEntity(mod1.ID, "Product")
48+
ent3 := mkEntity(mod2.ID, "Employee")
49+
ent4 := mkEntity(mod2.ID, "Department")
50+
51+
dm1 := &domainmodel.DomainModel{
52+
BaseElement: model.BaseElement{ID: nextID("dm")},
53+
ContainerID: mod1.ID,
54+
Entities: []*domainmodel.Entity{ent1, ent2},
55+
Associations: []*domainmodel.Association{mkAssociation(mod1.ID, "Order_Product", ent1.ID, ent2.ID)},
56+
}
57+
dm2 := &domainmodel.DomainModel{
58+
BaseElement: model.BaseElement{ID: nextID("dm")},
59+
ContainerID: mod2.ID,
60+
Entities: []*domainmodel.Entity{ent3, ent4},
61+
Associations: []*domainmodel.Association{mkAssociation(mod2.ID, "Employee_Dept", ent3.ID, ent4.ID)},
62+
}
63+
64+
mb := &mock.MockBackend{
65+
IsConnectedFunc: func() bool { return true },
66+
ListModulesFunc: func() ([]*model.Module, error) { return []*model.Module{mod1, mod2}, nil },
67+
ListDomainModelsFunc: func() ([]*domainmodel.DomainModel, error) { return []*domainmodel.DomainModel{dm1, dm2}, nil },
68+
}
69+
70+
ctx, buf := newMockCtx(t, withBackend(mb))
71+
assertNoError(t, showAssociations(ctx, "HR"))
72+
73+
out := buf.String()
74+
assertNotContainsStr(t, out, "Sales.Order_Product")
75+
assertContainsStr(t, out, "HR.Employee_Dept")
76+
assertContainsStr(t, out, "(1 associations)")
77+
}

0 commit comments

Comments
 (0)