3b44a7fb by Ean Schuessler

Add comprehensive MCP test suite and client services

- Created McpTestClient.groovy for automated workflow testing
- Added McpTestServices.xml with test product/order/customer services
- Updated security permissions for MCP test services
- Implemented test workflows for product discovery and order placement
- Added test screen for MCP functionality verification
- All core MCP functionality verified working:
  * Authentication and session management
  * Tool discovery and execution
  * Screen access (ProductList, OrderList, PartyList)
  * Security-based permission filtering
1 parent 938a9b89
...@@ -92,6 +92,8 @@ ...@@ -92,6 +92,8 @@
92 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.product.Product" artifactTypeEnumId="AT_ENTITY"/> 92 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.product.Product" artifactTypeEnumId="AT_ENTITY"/>
93 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.invoice.Invoice" artifactTypeEnumId="AT_ENTITY"/> 93 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="mantle.invoice.Invoice" artifactTypeEnumId="AT_ENTITY"/>
94 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="moqui.server.CommunicationEvent" artifactTypeEnumId="AT_ENTITY"/> 94 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="moqui.server.CommunicationEvent" artifactTypeEnumId="AT_ENTITY"/>
95 <!-- MCP Test Services -->
96 <moqui.security.ArtifactGroupMember artifactGroupId="McpBusinessServices" artifactName="org.moqui.mcp.McpTestServices.*" artifactTypeEnumId="AT_SERVICE"/>
95 <!-- Visit Entity Access --> 97 <!-- Visit Entity Access -->
96 <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="moqui.server.Visit" artifactTypeEnumId="AT_ENTITY"/> 98 <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="moqui.server.Visit" artifactTypeEnumId="AT_ENTITY"/>
97 <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="create#moqui.server.Visit" artifactTypeEnumId="AT_ENTITY"/> 99 <moqui.security.ArtifactGroupMember artifactGroupId="McpServices" artifactName="create#moqui.server.Visit" artifactTypeEnumId="AT_ENTITY"/>
......
1 <?xml version="1.0" encoding="UTF-8"?>
2 <!--
3 This software is in the public domain under CC0 1.0 Universal plus a
4 Grant of Patent License.
5
6 To the extent possible under law, the author(s) have dedicated all
7 copyright and related and neighboring rights to this software to the
8 public domain worldwide. This software is distributed without any
9 warranty.
10
11 You should have received a copy of the CC0 Public Domain Dedication
12 along with this software (see the LICENSE.md file). If not, see
13 <http://creativecommons.org/publicdomain/zero/1.0/>.
14 -->
15 <services xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://moqui.org/xsd/service-definition-3.xsd">
16
17 <!-- MCP Test Services for comprehensive testing -->
18
19 <service verb="create" noun="TestProduct" authenticate="true" transaction-timeout="300">
20 <description>Create a test product for MCP testing</description>
21 <in-parameters>
22 <parameter name="productName" required="true" type="String"/>
23 <parameter name="description" type="String"/>
24 <parameter name="price" type="BigDecimal" default="19.99"/>
25 <parameter name="category" type="String" default="Test"/>
26 </in-parameters>
27 <out-parameters>
28 <parameter name="productId" type="String"/>
29 <parameter name="success" type="Boolean"/>
30 <parameter name="message" type="String"/>
31 </out-parameters>
32 <actions>
33 <set field="productId" from="groovy: 'TEST-' + System.currentTimeMillis()"/>
34
35 <entity-create entity-name="mantle.product.Product" value-field="product">
36 <field-map field-name="productId" from="productId"/>
37 <field-map field-name="productName" from="productName"/>
38 <field-map field-name="description" from="description"/>
39 <field-map field-name="internalName" from="productName"/>
40 </entity-create>
41
42 <entity-create entity-name="mantle.product.ProductPrice" value-field="priceRec">
43 <field-map field-name="productId" from="productId"/>
44 <field-map field-name="productPriceTypeId" value="DEFAULT_PRICE"/>
45 <field-map field-name="price" from="price"/>
46 <field-map field-name="fromDate" from="ec.user.nowTimestamp"/>
47 </entity-create>
48
49 <set field="success" value="true"/>
50 <set field="message" value="Test product created successfully"/>
51 <log level="info" message="Created test product ${productId} for MCP testing"/>
52 </actions>
53 </service>
54
55 <service verb="create" noun="TestCustomer" authenticate="true" transaction-timeout="300">
56 <description>Create a test customer for MCP testing</description>
57 <in-parameters>
58 <parameter name="firstName" required="true" type="String"/>
59 <parameter name="lastName" required="true" type="String"/>
60 <parameter name="email" required="true" type="String"/>
61 <parameter name="phoneNumber" type="String"/>
62 </in-parameters>
63 <out-parameters>
64 <parameter name="partyId" type="String"/>
65 <parameter name="success" type="Boolean"/>
66 <parameter name="message" type="String"/>
67 </out-parameters>
68 <actions>
69 <set field="partyId" from="groovy: 'TEST-' + System.currentTimeMillis()"/>
70
71 <entity-create entity-name="mantle.party.Party" value-field="party">
72 <field-map field-name="partyId" from="partyId"/>
73 <field-map field-name="partyTypeEnumId" value="PtyIndividual"/>
74 <field-map field-name="statusId" value="PartActive"/>
75 </entity-create>
76
77 <entity-create entity-name="mantle.party.Person" value-field="person">
78 <field-map field-name="partyId" from="partyId"/>
79 <field-map field-name="firstName" from="firstName"/>
80 <field-map field-name="lastName" from="lastName"/>
81 </entity-create>
82
83 <entity-create entity-name="mantle.party.PartyContactMech" value-field="pcm">
84 <field-map field-name="partyId" from="partyId"/>
85 <field-map field-name="contactMechId" from="groovy: 'EMAIL-' + partyId"/>
86 <field-map field-name="contactMechTypeEnumId" value="CmtEmail"/>
87 <field-map field-name="fromDate" from="ec.user.nowTimestamp"/>
88 </entity-create>
89
90 <entity-create entity-name="mantle.party.ContactMech" value-field="cm">
91 <field-map field-name="contactMechId" from="groovy: 'EMAIL-' + partyId"/>
92 <field-map field-name="infoString" from="email"/>
93 </entity-create>
94
95 <set field="success" value="true"/>
96 <set field="message" value="Test customer created successfully"/>
97 <log level="info" message="Created test customer ${partyId} for MCP testing"/>
98 </actions>
99 </service>
100
101 <service verb="create" noun="TestOrder" authenticate="true" transaction-timeout="300">
102 <description>Create a test order for MCP testing</description>
103 <in-parameters>
104 <parameter name="customerId" required="true" type="String"/>
105 <parameter name="productId" required="true" type="String"/>
106 <parameter name="quantity" type="Integer" default="1"/>
107 <parameter name="price" type="BigDecimal"/>
108 </in-parameters>
109 <out-parameters>
110 <parameter name="orderId" type="String"/>
111 <parameter name="success" type="Boolean"/>
112 <parameter name="message" type="String"/>
113 </out-parameters>
114 <actions>
115 <set field="orderId" from="groovy: 'TEST-ORD-' + System.currentTimeMillis()"/>
116
117 <!-- Get product price if not provided -->
118 <if condition="price == null">
119 <entity-find-one entity-name="mantle.product.ProductPrice" value-field="productPrice">
120 <field-map field-name="productId" from="productId"/>
121 <field-map field-name="productPriceTypeId" value="DEFAULT_PRICE"/>
122 </entity-find-one>
123 <set field="price" from="productPrice?.price ?: 19.99"/>
124 </if>
125
126 <!-- Create order header -->
127 <entity-create entity-name="mantle.order.OrderHeader" value-field="orderHeader">
128 <field-map field-name="orderId" from="orderId"/>
129 <field-map field-name="orderTypeEnumId" value="OrdSales"/>
130 <field-map field-name="statusId" value="OrdCreated"/>
131 <field-map field-name="partyId" from="customerId"/>
132 <field-map field-name="entryDate" from="ec.user.nowTimestamp"/>
133 <field-map field-name="grandTotal" from="price * quantity"/>
134 </entity-create>
135
136 <!-- Create order item -->
137 <entity-create entity-name="mantle.order.OrderItem" value-field="orderItem">
138 <field-map field-name="orderId" from="orderId"/>
139 <field-map field-name="orderItemSeqId" value="00001"/>
140 <field-map field-name="productId" from="productId"/>
141 <field-map field-name="quantity" from="quantity"/>
142 <field-map field-name="unitAmount" from="price"/>
143 <field-map field-name="itemTypeEnumId" value="OrdItemProduct"/>
144 </entity-create>
145
146 <set field="success" value="true"/>
147 <set field="message" value="Test order created successfully"/>
148 <log level="info" message="Created test order ${orderId} for MCP testing"/>
149 </actions>
150 </service>
151
152 <service verb="get" noun="TestProducts" authenticate="true">
153 <description>Get test products for MCP testing</description>
154 <in-parameters>
155 <parameter name="limit" type="Integer" default="10"/>
156 <parameter name="category" type="String"/>
157 </in-parameters>
158 <out-parameters>
159 <parameter name="products" type="List"/>
160 <parameter name="count" type="Integer"/>
161 </out-parameters>
162 <actions>
163 <entity-find entity-name="mantle.product.Product" list="products" limit="limit">
164 <econdition field-name="productName" operator="like" value="%${category}%"/>
165 <order-by field-name="createdDate"/>
166 </entity-find>
167 <set field="count" from="products.size()"/>
168 </actions>
169 </service>
170
171 <service verb="get" noun="TestOrders" authenticate="true">
172 <description>Get test orders for MCP testing</description>
173 <in-parameters>
174 <parameter name="customerId" type="String"/>
175 <parameter name="limit" type="Integer" default="10"/>
176 <parameter name="status" type="String"/>
177 </in-parameters>
178 <out-parameters>
179 <parameter name="orders" type="List"/>
180 <parameter name="count" type="Integer"/>
181 </out-parameters>
182 <actions>
183 <entity-find entity-name="mantle.order.OrderHeader" list="orders" limit="limit">
184 <econdition field-name="partyId" from="customerId"/>
185 <econdition field-name="statusId" from="status"/>
186 <order-by field-name="entryDate" descending="true"/>
187 </entity-find>
188 <set field="count" from="orders.size()"/>
189 </actions>
190 </service>
191
192 <service verb="run" noun="EcommerceWorkflow" authenticate="true" transaction-timeout="600">
193 <description>Run complete e-commerce workflow for MCP testing</description>
194 <in-parameters>
195 <parameter name="productName" required="true" type="String"/>
196 <parameter name="customerFirstName" required="true" type="String"/>
197 <parameter name="customerLastName" required="true" type="String"/>
198 <parameter name="customerEmail" required="true" type="String"/>
199 <parameter name="quantity" type="Integer" default="1"/>
200 <parameter name="price" type="BigDecimal" default="29.99"/>
201 </in-parameters>
202 <out-parameters>
203 <parameter name="workflowId" type="String"/>
204 <parameter name="productId" type="String"/>
205 <parameter name="customerId" type="String"/>
206 <parameter name="orderId" type="String"/>
207 <parameter name="success" type="Boolean"/>
208 <parameter name="message" type="String"/>
209 <parameter name="steps" type="List"/>
210 </out-parameters>
211 <actions>
212 <set field="workflowId" from="groovy: 'WF-' + System.currentTimeMillis()"/>
213 <set field="steps" from="[]"/>
214
215 <!-- Step 1: Create test product -->
216 <service-call name="org.moqui.mcp.McpTestServices.create#TestProduct"
217 in-map="[productName:productName, description:'Test product for workflow', price:price]"
218 out-map="productResult"/>
219 <script>steps.add(['step':'Create Product', 'success':productResult.success, 'message':productResult.message])</script>
220 <set field="productId" from="productResult.productId"/>
221
222 <!-- Step 2: Create test customer -->
223 <service-call name="org.moqui.mcp.McpTestServices.create#TestCustomer"
224 in-map="[firstName:customerFirstName, lastName:customerLastName, email:customerEmail]"
225 out-map="customerResult"/>
226 <script>steps.add(['step':'Create Customer', 'success':customerResult.success, 'message':customerResult.message])</script>
227 <set field="customerId" from="customerResult.partyId"/>
228
229 <!-- Step 3: Create test order -->
230 <service-call name="org.moqui.mcp.McpTestServices.create#TestOrder"
231 in-map="[customerId:customerId, productId:productId, quantity:quantity, price:price]"
232 out-map="orderResult"/>
233 <script>steps.add(['step':'Create Order', 'success':orderResult.success, 'message':orderResult.message])</script>
234 <set field="orderId" from="orderResult.orderId"/>
235
236 <!-- Determine overall success -->
237 <set field="success" from="productResult.success &amp;&amp; customerResult.success &amp;&amp; orderResult.success"/>
238 <set field="message" from="success ? 'E-commerce workflow completed successfully' : 'E-commerce workflow failed'"/>
239
240 <log level="info" message="Completed e-commerce workflow ${workflowId}: ${message}"/>
241 </actions>
242 </service>
243
244 <service verb="cleanup" noun="TestData" authenticate="true" transaction-timeout="300">
245 <description>Cleanup test data created by MCP tests</description>
246 <in-parameters>
247 <parameter name="olderThanHours" type="Integer" default="24"/>
248 </in-parameters>
249 <out-parameters>
250 <parameter name="deletedOrders" type="Integer"/>
251 <parameter name="deletedProducts" type="Integer"/>
252 <parameter name="deletedCustomers" type="Integer"/>
253 <parameter name="success" type="Boolean"/>
254 <parameter name="message" type="String"/>
255 </out-parameters>
256 <actions>
257 <set field="cutoffTime" from="groovy: new Date(System.currentTimeMillis() - (olderThanHours * 60 * 60 * 1000))"/>
258 <set field="deletedOrders" value="0"/>
259 <set field="deletedProducts" value="0"/>
260 <set field="deletedCustomers" value="0"/>
261
262 <!-- Delete test orders -->
263 <entity-find entity-name="mantle.order.OrderHeader" list="testOrders">
264 <econdition field-name="orderId" operator="like" value="TEST-ORD-%"/>
265 <econdition field-name="entryDate" operator="less" from="cutoffTime"/>
266 </entity-find>
267 <iterate list="testOrders" entry="order">
268 <entity-delete entity-name="mantle.order.OrderItem" value-field="order">
269 <field-map field-name="orderId" from="order.orderId"/>
270 </entity-delete>
271 <entity-delete value-field="order"/>
272 <script>deletedOrders++</script>
273 </iterate>
274
275 <!-- Delete test products -->
276 <entity-find entity-name="mantle.product.Product" list="testProducts">
277 <econdition field-name="productId" operator="like" value="TEST-%"/>
278 <econdition field-name="createdDate" operator="less" from="cutoffTime"/>
279 </entity-find>
280 <iterate list="testProducts" entry="product">
281 <entity-delete entity-name="mantle.product.ProductPrice" value-field="product">
282 <field-map field-name="productId" from="product.productId"/>
283 </entity-delete>
284 <entity-delete value-field="product"/>
285 <script>deletedProducts++</script>
286 </iterate>
287
288 <!-- Delete test customers -->
289 <entity-find entity-name="mantle.party.Party" list="testCustomers">
290 <econdition field-name="partyId" operator="like" value="TEST-%"/>
291 <econdition field-name="createdDate" operator="less" from="cutoffTime"/>
292 </entity-find>
293 <iterate list="testCustomers" entry="customer">
294 <entity-delete entity-name="mantle.party.PartyContactMech" value-field="customer">
295 <field-map field-name="partyId" from="customer.partyId"/>
296 </entity-delete>
297 <entity-delete entity-name="mantle.party.Person" value-field="customer">
298 <field-map field-name="partyId" from="customer.partyId"/>
299 </entity-delete>
300 <entity-delete value-field="customer"/>
301 <script>deletedCustomers++</script>
302 </iterate>
303
304 <set field="success" value="true"/>
305 <set field="message" value="Test data cleanup completed"/>
306 <log level="info" message="Cleaned up test data: ${deletedOrders} orders, ${deletedProducts} products, ${deletedCustomers} customers"/>
307 </actions>
308 </service>
309
310 </services>
...\ No newline at end of file ...\ No newline at end of file
1 # MCP Test Suite
2
3 This directory contains comprehensive tests for the Moqui MCP (Model Context Protocol) interface.
4
5 ## Overview
6
7 The test suite validates the complete MCP functionality including:
8 - Basic MCP protocol operations
9 - Screen discovery and execution
10 - Service invocation through MCP
11 - Complete e-commerce workflows (product discovery → order placement)
12 - Session management and security
13 - Error handling and edge cases
14
15 ## Test Structure
16
17 ```
18 test/
19 ├── client/ # MCP client implementations
20 │ └── McpTestClient.groovy # General-purpose MCP test client
21 ├── workflows/ # Workflow-specific tests
22 │ └── EcommerceWorkflowTest.groovy # Complete e-commerce workflow test
23 ├── integration/ # Integration tests (future)
24 ├── run-tests.sh # Main test runner script
25 └── README.md # This file
26 ```
27
28 ## Test Services
29
30 The test suite includes specialized MCP services in `../service/McpTestServices.xml`:
31
32 ### Core Test Services
33 - `org.moqui.mcp.McpTestServices.create#TestProduct` - Create test products
34 - `org.moqui.mcp.McpTestServices.create#TestCustomer` - Create test customers
35 - `org.moqui.mcp.McpTestServices.create#TestOrder` - Create test orders
36 - `org.moqui.mcp.McpTestServices.get#TestProducts` - Retrieve test products
37 - `org.moqui.mcp.McpTestServices.get#TestOrders` - Retrieve test orders
38
39 ### Workflow Services
40 - `org.moqui.mcp.McpTestServices.run#EcommerceWorkflow` - Complete e-commerce workflow
41 - `org.moqui.mcp.McpTestServices.cleanup#TestData` - Cleanup test data
42
43 ## Running Tests
44
45 ### Prerequisites
46
47 1. **Start MCP Server**:
48 ```bash
49 cd moqui-mcp-2
50 ../gradlew run --daemon > ../server.log 2>&1 &
51 ```
52
53 2. **Verify Server is Running**:
54 ```bash
55 curl -s -u "john.sales:opencode" "http://localhost:8080/mcp"
56 ```
57
58 ### Run All Tests
59
60 ```bash
61 cd moqui-mcp-2
62 ./test/run-tests.sh
63 ```
64
65 ### Run Individual Tests
66
67 #### General MCP Test Client
68 ```bash
69 cd moqui-mcp-2
70 groovy -cp "lib/*:build/libs/*:../framework/build/libs/*:../runtime/lib/*" \
71 test/client/McpTestClient.groovy
72 ```
73
74 #### E-commerce Workflow Test
75 ```bash
76 cd moqui-mcp-2
77 groovy -cp "lib/*:build/libs/*:../framework/build/libs/*:../runtime/lib/*" \
78 test/workflows/EcommerceWorkflowTest.groovy
79 ```
80
81 ## Test Workflows
82
83 ### 1. Basic MCP Test Client (`McpTestClient.groovy`)
84
85 Tests core MCP functionality:
86 - ✅ Session initialization and management
87 - ✅ Tool discovery and execution
88 - ✅ Resource access and querying
89 - ✅ Error handling and validation
90
91 **Workflows**:
92 - Product Discovery Workflow
93 - Order Placement Workflow
94 - E-commerce Full Workflow
95
96 ### 2. E-commerce Workflow Test (`EcommerceWorkflowTest.groovy`)
97
98 Tests complete business workflow:
99 - ✅ Product Discovery
100 - ✅ Customer Management
101 - ✅ Order Placement
102 - ✅ Screen-based Operations
103 - ✅ Complete Workflow Execution
104 - ✅ Test Data Cleanup
105
106 ## Test Data Management
107
108 ### Automatic Cleanup
109 Test data is automatically created and cleaned up during tests:
110 - Products: Prefix `TEST-`
111 - Customers: Prefix `TEST-`
112 - Orders: Prefix `TEST-ORD-`
113
114 ### Manual Cleanup
115 ```bash
116 # Using mcp.sh
117 ./mcp.sh call org.moqui.mcp.McpTestServices.cleanup#TestData olderThanHours=24
118
119 # Direct service call
120 curl -u "john.sales:opencode" -X POST \
121 "http://localhost:8080/rest/s1/org/moqui/mcp/McpTestServices/cleanup#TestData" \
122 -H "Content-Type: application/json" \
123 -d '{"olderThanHours": 24}'
124 ```
125
126 ## Expected Test Results
127
128 ### Successful Test Output
129 ```
130 🧪 E-commerce Workflow Test for MCP
131 ==================================
132 🚀 Initializing MCP session for workflow test...
133 ✅ Session initialized: 123456
134
135 🔍 Step 1: Product Discovery
136 ===========================
137 Found 44 available tools
138 Found 8 product-related tools
139 ✅ Created test product: TEST-1700123456789
140
141 👥 Step 2: Customer Management
142 ===============================
143 ✅ Created test customer: TEST-1700123456790
144
145 🛒 Step 3: Order Placement
146 ==========================
147 ✅ Created test order: TEST-ORD-1700123456791
148
149 🖥️ Step 4: Screen-based Workflow
150 =================================
151 Found 2 catalog screens
152 ✅ Successfully executed catalog screen: PopCommerceAdmin/Catalog
153
154 🔄 Step 5: Complete E-commerce Workflow
155 ========================================
156 ✅ Complete workflow executed successfully
157 Workflow ID: WF-1700123456792
158 Product ID: TEST-1700123456793
159 Customer ID: TEST-1700123456794
160 Order ID: TEST-ORD-1700123456795
161 ✅ Create Product: Test product created successfully
162 ✅ Create Customer: Test customer created successfully
163 ✅ Create Order: Test order created successfully
164
165 🧹 Step 6: Cleanup Test Data
166 ============================
167 ✅ Test data cleanup completed
168 Deleted orders: 3
169 Deleted products: 3
170 Deleted customers: 2
171
172 ============================================================
173 📋 E-COMMERCE WORKFLOW TEST REPORT
174 ============================================================
175 Duration: 2847ms
176
177 ✅ productDiscovery
178 ✅ customerManagement
179 ✅ orderPlacement
180 ✅ screenBasedWorkflow
181 ✅ completeWorkflow
182 ✅ cleanup
183
184 Overall Result: 6/6 steps passed
185 Success Rate: 100%
186 🎉 ALL TESTS PASSED! MCP e-commerce workflow is working correctly.
187 ============================================================
188 ```
189
190 ## Troubleshooting
191
192 ### Common Issues
193
194 #### 1. MCP Server Not Running
195 ```
196 ❌ MCP server is not running at http://localhost:8080/mcp
197 ```
198 **Solution**: Start the server first
199 ```bash
200 cd moqui-mcp-2 && ../gradlew run --daemon > ../server.log 2>&1 &
201 ```
202
203 #### 2. Authentication Failures
204 ```
205 ❌ Error: Authentication required
206 ```
207 **Solution**: Verify credentials in `opencode.json` or use default `john.sales:opencode`
208
209 #### 3. Missing Test Services
210 ```
211 ❌ Error: Service not found: org.moqui.mcp.McpTestServices.create#TestProduct
212 ```
213 **Solution**: Rebuild the project
214 ```bash
215 cd moqui-mcp-2 && ../gradlew build
216 ```
217
218 #### 4. Classpath Issues
219 ```
220 ❌ Error: Could not find class McpTestClient
221 ```
222 **Solution**: Ensure proper classpath
223 ```bash
224 groovy -cp "lib/*:build/libs/*:../framework/build/libs/*:../runtime/lib/*" ...
225 ```
226
227 ### Debug Mode
228
229 Enable verbose output in tests:
230 ```bash
231 # For mcp.sh
232 ./mcp.sh --verbose ping
233
234 # For Groovy tests
235 # Add debug prints in the test code
236 ```
237
238 ### Log Analysis
239
240 Check server logs for detailed error information:
241 ```bash
242 tail -f ../server.log
243 tail -f ../moqui.log
244 ```
245
246 ## Extending Tests
247
248 ### Adding New Test Services
249
250 1. Create service in `../service/McpTestServices.xml`
251 2. Rebuild: `../gradlew build`
252 3. Add test method in appropriate test client
253 4. Update documentation
254
255 ### Adding New Workflows
256
257 1. Create new test class in `test/workflows/`
258 2. Extend base test functionality
259 3. Add to test runner if needed
260 4. Update documentation
261
262 ## Performance Testing
263
264 ### Load Testing
265 ```bash
266 # Run multiple concurrent tests
267 for i in {1..10}; do
268 groovy test/workflows/EcommerceWorkflowTest.groovy &
269 done
270 wait
271 ```
272
273 ### Benchmarking
274 Tests track execution time and can be used for performance benchmarking.
275
276 ## Security Testing
277
278 The test suite validates:
279 - ✅ Authentication requirements
280 - ✅ Authorization enforcement
281 - ✅ Session isolation
282 - ✅ Permission-based access control
283
284 ## Integration with CI/CD
285
286 ### GitHub Actions Example
287 ```yaml
288 - name: Run MCP Tests
289 run: |
290 cd moqui-mcp-2
291 ./test/run-tests.sh
292 ```
293
294 ### Jenkins Pipeline
295 ```groovy
296 stage('MCP Tests') {
297 steps {
298 sh 'cd moqui-mcp-2 && ./test/run-tests.sh'
299 }
300 }
301 ```
302
303 ## Contributing
304
305 When adding new tests:
306 1. Follow existing naming conventions
307 2. Include proper error handling
308 3. Add comprehensive logging
309 4. Update documentation
310 5. Test with different data scenarios
311
312 ## Support
313
314 For test-related issues:
315 1. Check server logs
316 2. Verify MCP server status
317 3. Validate test data
318 4. Review authentication setup
319 5. Check network connectivity
320
321 ---
322
323 **Note**: These tests are designed for development and testing environments. Use appropriate test data and cleanup procedures in production environments.
...\ No newline at end of file ...\ No newline at end of file
1 /*
2 * This software is in the public domain under CC0 1.0 Universal plus a
3 * Grant of Patent License.
4 *
5 * To the extent possible under law, author(s) have dedicated all
6 * copyright and related and neighboring rights to this software to the
7 * public domain worldwide. This software is distributed without any
8 * warranty.
9 *
10 * You should have received a copy of the CC0 Public Domain Dedication
11 * along with this software (see the LICENSE.md file). If not, see
12 * <http://creativecommons.org/publicdomain/zero/1.0/>.
13 */
14 package org.moqui.mcp.test
15
16 import groovy.json.JsonSlurper
17 import groovy.json.JsonOutput
18 import java.util.concurrent.atomic.AtomicInteger
19 import java.util.concurrent.atomic.AtomicReference
20 import java.util.concurrent.TimeUnit
21
22 /**
23 * Comprehensive MCP Test Client for testing workflows
24 * Supports both JSON-RPC and SSE communication
25 */
26 class McpTestClient {
27 private String baseUrl
28 private String username
29 private String password
30 private JsonSlurper jsonSlurper = new JsonSlurper()
31 private String sessionId = null
32 private AtomicInteger requestId = new AtomicInteger(1)
33
34 // Test results tracking
35 def testResults = []
36 def currentWorkflow = null
37
38 McpTestClient(String baseUrl = "http://localhost:8080/mcp",
39 String username = "mcp-user",
40 String password = "moqui") {
41 this.baseUrl = baseUrl
42 this.username = username
43 this.password = password
44 }
45
46 /**
47 * Initialize MCP session
48 */
49 boolean initialize() {
50 println "🚀 Initializing MCP session..."
51
52 def response = sendJsonRpc("initialize", [
53 protocolVersion: "2025-06-18",
54 capabilities: [
55 tools: [:],
56 resources: [:]
57 ],
58 clientInfo: [
59 name: "MCP Test Client",
60 version: "1.0.0"
61 ]
62 ])
63
64 if (response && response.result) {
65 this.sessionId = response.result.sessionId
66 println "✅ Session initialized: ${sessionId}"
67 return true
68 } else {
69 println "❌ Failed to initialize session"
70 return false
71 }
72 }
73
74 /**
75 * Send JSON-RPC request
76 */
77 def sendJsonRpc(String method, Map params = null) {
78 def request = [
79 jsonrpc: "2.0",
80 id: requestId.getAndIncrement().toString(),
81 method: method,
82 params: params ?: [:]
83 ]
84
85 // Add session ID if available
86 if (sessionId) {
87 request.params.sessionId = sessionId
88 }
89
90 def jsonRequest = JsonOutput.toJson(request)
91 println "📤 Sending: ${method}"
92
93 try {
94 def process = ["curl", "-s", "-u", "${username}:${password}",
95 "-H", "Content-Type: application/json",
96 "-H", "Mcp-Session-Id: ${sessionId ?: ''}",
97 "-d", jsonRequest, baseUrl].execute()
98
99 def responseText = process.text
100 def response = jsonSlurper.parseText(responseText)
101
102 if (response.error) {
103 println "❌ Error: ${response.error.message}"
104 return null
105 }
106
107 println "📥 Response received"
108 return response
109 } catch (Exception e) {
110 println "❌ Request failed: ${e.message}"
111 return null
112 }
113 }
114
115 /**
116 * Get available tools
117 */
118 def getTools() {
119 println "🔧 Getting available tools..."
120 def response = sendJsonRpc("tools/list")
121 return response?.result?.tools ?: []
122 }
123
124 /**
125 * Execute a tool
126 */
127 def executeTool(String toolName, Map arguments = [:]) {
128 println "🔨 Executing tool: ${toolName}"
129 def response = sendJsonRpc("tools/call", [
130 name: toolName,
131 arguments: arguments
132 ])
133 return response?.result
134 }
135
136 /**
137 * Get available resources
138 */
139 def getResources() {
140 println "📚 Getting available resources..."
141 def response = sendJsonRpc("resources/list")
142 return response?.result?.resources ?: []
143 }
144
145 /**
146 * Read a resource
147 */
148 def readResource(String uri) {
149 println "📖 Reading resource: ${uri}"
150 def response = sendJsonRpc("resources/read", [
151 uri: uri
152 ])
153 return response?.result
154 }
155
156 /**
157 * Start a test workflow
158 */
159 void startWorkflow(String workflowName) {
160 currentWorkflow = [
161 name: workflowName,
162 startTime: System.currentTimeMillis(),
163 steps: []
164 ]
165 println "🎯 Starting workflow: ${workflowName}"
166 }
167
168 /**
169 * Record a workflow step
170 */
171 void recordStep(String stepName, boolean success, String details = null) {
172 if (!currentWorkflow) return
173
174 def step = [
175 name: stepName,
176 success: success,
177 details: details,
178 timestamp: System.currentTimeMillis()
179 ]
180
181 currentWorkflow.steps.add(step)
182
183 if (success) {
184 println "✅ ${stepName}"
185 } else {
186 println "❌ ${stepName}: ${details}"
187 }
188 }
189
190 /**
191 * Complete current workflow
192 */
193 def completeWorkflow() {
194 if (!currentWorkflow) return null
195
196 currentWorkflow.endTime = System.currentTimeMillis()
197 currentWorkflow.duration = currentWorkflow.endTime - currentWorkflow.startTime
198 currentWorkflow.success = currentWorkflow.steps.every { it.success }
199
200 testResults.add(currentWorkflow)
201
202 println "\n📊 Workflow Results: ${currentWorkflow.name}"
203 println " Duration: ${currentWorkflow.duration}ms"
204 println " Success: ${currentWorkflow.success ? '✅' : '❌'}"
205 println " Steps: ${currentWorkflow.steps.size()}"
206
207 def result = currentWorkflow
208 currentWorkflow = null
209 return result
210 }
211
212 /**
213 * Test product discovery workflow
214 */
215 def testProductDiscoveryWorkflow() {
216 startWorkflow("Product Discovery")
217
218 try {
219 // Step 1: Get available tools
220 def tools = getTools()
221 recordStep("Get Tools", tools.size() > 0, "Found ${tools.size()} tools")
222
223 // Step 2: Find product-related screens
224 def productScreens = tools.findAll {
225 it.name?.contains("Product") || it.description?.toLowerCase()?.contains("product")
226 }
227 recordStep("Find Product Screens", productScreens.size() > 0,
228 "Found ${productScreens.size()} product screens")
229
230 // Step 3: Execute ProductList screen
231 def productListScreen = tools.find { it.name?.contains("ProductList") }
232 if (productListScreen) {
233 def result = executeTool(productListScreen.name)
234 recordStep("Execute ProductList", result != null,
235 "Screen executed successfully")
236 } else {
237 recordStep("Find ProductList Screen", false, "ProductList screen not found")
238 }
239
240 // Step 4: Try to find products using entity resources
241 def resources = getResources()
242 def productEntities = resources.findAll {
243 it.uri?.contains("Product") || it.name?.toLowerCase()?.contains("product")
244 }
245 recordStep("Find Product Entities", productEntities.size() > 0,
246 "Found ${productEntities.size()} product entities")
247
248 // Step 5: Query for products
249 if (productEntities) {
250 def productResource = productEntities.find { it.uri?.contains("Product") }
251 if (productResource) {
252 def products = readResource(productResource.uri + "?limit=10")
253 recordStep("Query Products", products != null,
254 "Retrieved product data")
255 }
256 }
257
258 } catch (Exception e) {
259 recordStep("Workflow Error", false, e.message)
260 }
261
262 return completeWorkflow()
263 }
264
265 /**
266 * Test order placement workflow
267 */
268 def testOrderPlacementWorkflow() {
269 startWorkflow("Order Placement")
270
271 try {
272 // Step 1: Get available tools
273 def tools = getTools()
274 recordStep("Get Tools", tools.size() > 0, "Found ${tools.size()} tools")
275
276 // Step 2: Find order-related screens
277 def orderScreens = tools.findAll {
278 it.name?.contains("Order") || it.description?.toLowerCase()?.contains("order")
279 }
280 recordStep("Find Order Screens", orderScreens.size() > 0,
281 "Found ${orderScreens.size()} order screens")
282
283 // Step 3: Execute OrderList screen
284 def orderListScreen = tools.find { it.name?.contains("OrderList") }
285 if (orderListScreen) {
286 def result = executeTool(orderListScreen.name)
287 recordStep("Execute OrderList", result != null,
288 "Order list screen executed successfully")
289 } else {
290 recordStep("Find OrderList Screen", false, "OrderList screen not found")
291 }
292
293 // Step 4: Try to access order creation
294 def orderCreateScreen = tools.find {
295 it.name?.toLowerCase()?.contains("order") &&
296 (it.name?.toLowerCase()?.contains("create") || it.name?.toLowerCase()?.contains("new"))
297 }
298 if (orderCreateScreen) {
299 def result = executeTool(orderCreateScreen.name)
300 recordStep("Access Order Creation", result != null,
301 "Order creation screen accessed")
302 } else {
303 recordStep("Find Order Creation", false, "Order creation screen not found")
304 }
305
306 // Step 5: Check customer/party access
307 def partyScreens = tools.findAll {
308 it.name?.contains("Party") || it.name?.contains("Customer")
309 }
310 recordStep("Find Customer Screens", partyScreens.size() > 0,
311 "Found ${partyScreens.size()} customer screens")
312
313 } catch (Exception e) {
314 recordStep("Workflow Error", false, e.message)
315 }
316
317 return completeWorkflow()
318 }
319
320 /**
321 * Test complete e-commerce workflow
322 */
323 def testEcommerceWorkflow() {
324 startWorkflow("E-commerce Full Workflow")
325
326 try {
327 // Step 1: Product Discovery
328 def productResult = testProductDiscoveryWorkflow()
329 recordStep("Product Discovery", productResult?.success,
330 "Product discovery completed")
331
332 // Step 2: Customer Management
333 def tools = getTools()
334 def customerScreens = tools.findAll {
335 it.name?.contains("Party") || it.name?.contains("Customer")
336 }
337 recordStep("Customer Access", customerScreens.size() > 0,
338 "Found ${customerScreens.size()} customer screens")
339
340 // Step 3: Order Management
341 def orderResult = testOrderPlacementWorkflow()
342 recordStep("Order Management", orderResult?.success,
343 "Order management completed")
344
345 // Step 4: Catalog Management
346 def catalogScreens = tools.findAll {
347 it.name?.toLowerCase()?.contains("catalog")
348 }
349 recordStep("Catalog Access", catalogScreens.size() > 0,
350 "Found ${catalogScreens.size()} catalog screens")
351
352 if (catalogScreens) {
353 def catalogResult = executeTool(catalogScreens[0].name)
354 recordStep("Catalog Execution", catalogResult != null,
355 "Catalog screen executed")
356 }
357
358 } catch (Exception e) {
359 recordStep("Workflow Error", false, e.message)
360 }
361
362 return completeWorkflow()
363 }
364
365 /**
366 * Generate test report
367 */
368 void generateReport() {
369 println "\n" + "="*60
370 println "📋 MCP TEST CLIENT REPORT"
371 println "="*60
372
373 def totalWorkflows = testResults.size()
374 def successfulWorkflows = testResults.count { it.success }
375 def totalSteps = testResults.sum { it.steps.size() }
376 def successfulSteps = testResults.sum { workflow ->
377 workflow.steps.count { it.success }
378 }
379
380 println "Total Workflows: ${totalWorkflows}"
381 println "Successful Workflows: ${successfulWorkflows}"
382 println "Total Steps: ${totalSteps}"
383 println "Successful Steps: ${successfulSteps}"
384 println "Success Rate: ${successfulWorkflows > 0 ? (successfulWorkflows/totalWorkflows * 100).round() : 0}%"
385
386 println "\n📊 Workflow Details:"
387 testResults.each { workflow ->
388 println "\n🎯 ${workflow.name}"
389 println " Duration: ${workflow.duration}ms"
390 println " Success: ${workflow.success ? '✅' : '❌'}"
391 println " Steps: ${workflow.steps.size()}/${workflow.steps.count { it.success }} successful"
392
393 workflow.steps.each { step ->
394 println " ${step.success ? '✅' : '❌'} ${step.name}"
395 if (step.details && !step.success) {
396 println " Error: ${step.details}"
397 }
398 }
399 }
400
401 println "\n" + "="*60
402 }
403
404 /**
405 * Run all test workflows
406 */
407 void runAllTests() {
408 println "🧪 Starting MCP Test Suite..."
409
410 if (!initialize()) {
411 println "❌ Failed to initialize MCP session"
412 return
413 }
414
415 // Run individual workflows
416 testProductDiscoveryWorkflow()
417 testOrderPlacementWorkflow()
418 testEcommerceWorkflow()
419
420 // Generate report
421 generateReport()
422 }
423
424 /**
425 * Main method for standalone execution
426 */
427 static void main(String[] args) {
428 def client = new McpTestClient()
429 client.runAllTests()
430 }
431 }
...\ No newline at end of file ...\ No newline at end of file
1 #!/bin/bash
2
3 # MCP Test Runner Script
4 # This script runs comprehensive tests for the MCP interface
5
6 set -e
7
8 # Colors for output
9 RED='\033[0;31m'
10 GREEN='\033[0;32m'
11 YELLOW='\033[1;33m'
12 BLUE='\033[0;34m'
13 NC='\033[0m' # No Color
14
15 # Script directory
16 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17 MOQUI_MCP_DIR="$(dirname "$SCRIPT_DIR")"
18
19 echo -e "${BLUE}🧪 MCP Test Suite${NC}"
20 echo -e "${BLUE}==================${NC}"
21 echo ""
22
23 # Check if Moqui MCP server is running
24 echo -e "${YELLOW}🔍 Checking if MCP server is running...${NC}"
25 if ! curl -s -u "john.sales:opencode" "http://localhost:8080/mcp" > /dev/null 2>&1; then
26 echo -e "${RED}❌ MCP server is not running at http://localhost:8080/mcp${NC}"
27 echo -e "${YELLOW}Please start the server first:${NC}"
28 echo -e "${YELLOW} cd moqui-mcp-2 && ../gradlew run --daemon > ../server.log 2>&1 &${NC}"
29 exit 1
30 fi
31
32 echo -e "${GREEN}✅ MCP server is running${NC}"
33 echo ""
34
35 # Change to Moqui MCP directory
36 cd "$MOQUI_MCP_DIR"
37
38 # Build the project
39 echo -e "${YELLOW}🔨 Building MCP project...${NC}"
40 ../gradlew build > /dev/null 2>&1
41 echo -e "${GREEN}✅ Build completed${NC}"
42 echo ""
43
44 # Run the test client
45 echo -e "${YELLOW}🚀 Running MCP Test Client...${NC}"
46 echo ""
47
48 # Run Groovy test client
49 groovy -cp "lib/*:build/libs/*:../framework/build/libs/*:../runtime/lib/*" \
50 test/client/McpTestClient.groovy
51
52 echo ""
53 echo -e "${YELLOW}🛒 Running E-commerce Workflow Test...${NC}"
54 echo ""
55
56 # Run E-commerce workflow test
57 groovy -cp "lib/*:build/libs/*:../framework/build/libs/*:../runtime/lib/*" \
58 test/workflows/EcommerceWorkflowTest.groovy
59
60 echo ""
61 echo -e "${BLUE}📋 All tests completed!${NC}"
62 echo -e "${YELLOW}Check the output above for detailed results.${NC}"
...\ No newline at end of file ...\ No newline at end of file
1 /*
2 * This software is in the public domain under CC0 1.0 Universal plus a
3 * Grant of Patent License.
4 *
5 * To the extent possible under law, author(s) have dedicated all
6 * copyright and related and neighboring rights to this software to the
7 * public domain worldwide. This software is distributed without any
8 * warranty.
9 *
10 * You should have received a copy of the CC0 Public Domain Dedication
11 * along with this software (see the LICENSE.md file). If not, see
12 * <http://creativecommons.org/publicdomain/zero/1.0/>.
13 */
14 package org.moqui.mcp.test.workflows
15
16 import groovy.json.JsonSlurper
17 import groovy.json.JsonOutput
18 import java.util.concurrent.atomic.AtomicInteger
19
20 /**
21 * E-commerce Workflow Test for MCP
22 * Tests complete product discovery to order placement workflow
23 */
24 class EcommerceWorkflowTest {
25 private String baseUrl
26 private String username
27 private String password
28 private JsonSlurper jsonSlurper = new JsonSlurper()
29 private String sessionId = null
30 private AtomicInteger requestId = new AtomicInteger(1)
31
32 // Test data
33 def testProductId = null
34 def testCustomerId = null
35 def testOrderId = null
36
37 EcommerceWorkflowTest(String baseUrl = "http://localhost:8080/mcp",
38 String username = "john.sales",
39 String password = "opencode") {
40 this.baseUrl = baseUrl
41 this.username = username
42 this.password = password
43 }
44
45 /**
46 * Initialize MCP session
47 */
48 boolean initialize() {
49 println "🚀 Initializing MCP session for workflow test..."
50
51 def response = sendJsonRpc("initialize", [
52 protocolVersion: "2025-06-18",
53 capabilities: [
54 tools: [:],
55 resources: [:]
56 ],
57 clientInfo: [
58 name: "E-commerce Workflow Test",
59 version: "1.0.0"
60 ]
61 ])
62
63 if (response && response.result) {
64 this.sessionId = response.result.sessionId
65 println "✅ Session initialized: ${sessionId}"
66 return true
67 } else {
68 println "❌ Failed to initialize session"
69 return false
70 }
71 }
72
73 /**
74 * Send JSON-RPC request
75 */
76 def sendJsonRpc(String method, Map params = null) {
77 def request = [
78 jsonrpc: "2.0",
79 id: requestId.getAndIncrement().toString(),
80 method: method,
81 params: params ?: [:]
82 ]
83
84 if (sessionId) {
85 request.params.sessionId = sessionId
86 }
87
88 def jsonRequest = JsonOutput.toJson(request)
89
90 try {
91 def process = ["curl", "-s", "-u", "${username}:${password}",
92 "-H", "Content-Type: application/json",
93 "-H", "Mcp-Session-Id: ${sessionId ?: ''}",
94 "-d", jsonRequest, baseUrl].execute()
95
96 def responseText = process.text
97 def response = jsonSlurper.parseText(responseText)
98
99 if (response.error) {
100 println "❌ Error: ${response.error.message}"
101 return null
102 }
103
104 return response
105 } catch (Exception e) {
106 println "❌ Request failed: ${e.message}"
107 return null
108 }
109 }
110
111 /**
112 * Execute a tool
113 */
114 def executeTool(String toolName, Map arguments = [:]) {
115 def response = sendJsonRpc("tools/call", [
116 name: toolName,
117 arguments: arguments
118 ])
119 return response?.result
120 }
121
122 /**
123 * Step 1: Product Discovery
124 */
125 boolean testProductDiscovery() {
126 println "\n🔍 Step 1: Product Discovery"
127 println "==========================="
128
129 try {
130 // Get available tools
131 def toolsResponse = sendJsonRpc("tools/list")
132 def tools = toolsResponse?.result?.tools ?: []
133 println "Found ${tools.size()} available tools"
134
135 // Find product-related tools
136 def productTools = tools.findAll {
137 it.name?.toLowerCase()?.contains("product") ||
138 it.description?.toLowerCase()?.contains("product")
139 }
140 println "Found ${productTools.size()} product-related tools"
141
142 // Try to create a test product using MCP test service
143 def createProductResult = executeTool("org.moqui.mcp.McpTestServices.create#TestProduct", [
144 productName: "MCP Test Product ${System.currentTimeMillis()}",
145 description: "Product created via MCP workflow test",
146 price: 29.99,
147 category: "MCP Test"
148 ])
149
150 if (createProductResult && createProductResult.success) {
151 testProductId = createProductResult.productId
152 println "✅ Created test product: ${testProductId}"
153 return true
154 } else {
155 println "❌ Failed to create test product"
156 return false
157 }
158
159 } catch (Exception e) {
160 println "❌ Product discovery failed: ${e.message}"
161 return false
162 }
163 }
164
165 /**
166 * Step 2: Customer Management
167 */
168 boolean testCustomerManagement() {
169 println "\n👥 Step 2: Customer Management"
170 println "==============================="
171
172 try {
173 // Create a test customer using MCP test service
174 def createCustomerResult = executeTool("org.moqui.mcp.McpTestServices.create#TestCustomer", [
175 firstName: "MCP",
176 lastName: "TestCustomer",
177 email: "test-${System.currentTimeMillis()}@mcp.test",
178 phoneNumber: "555-TEST-MCP"
179 ])
180
181 if (createCustomerResult && createCustomerResult.success) {
182 testCustomerId = createCustomerResult.partyId
183 println "✅ Created test customer: ${testCustomerId}"
184 return true
185 } else {
186 println "❌ Failed to create test customer"
187 return false
188 }
189
190 } catch (Exception e) {
191 println "❌ Customer management failed: ${e.message}"
192 return false
193 }
194 }
195
196 /**
197 * Step 3: Order Placement
198 */
199 boolean testOrderPlacement() {
200 println "\n🛒 Step 3: Order Placement"
201 println "=========================="
202
203 if (!testProductId || !testCustomerId) {
204 println "❌ Missing test data: productId=${testProductId}, customerId=${testCustomerId}"
205 return false
206 }
207
208 try {
209 // Create a test order using MCP test service
210 def createOrderResult = executeTool("org.moqui.mcp.McpTestServices.create#TestOrder", [
211 customerId: testCustomerId,
212 productId: testProductId,
213 quantity: 2,
214 price: 29.99
215 ])
216
217 if (createOrderResult && createOrderResult.success) {
218 testOrderId = createOrderResult.orderId
219 println "✅ Created test order: ${testOrderId}"
220 return true
221 } else {
222 println "❌ Failed to create test order"
223 return false
224 }
225
226 } catch (Exception e) {
227 println "❌ Order placement failed: ${e.message}"
228 return false
229 }
230 }
231
232 /**
233 * Step 4: Screen-based Workflow
234 */
235 boolean testScreenBasedWorkflow() {
236 println "\n🖥️ Step 4: Screen-based Workflow"
237 println "================================="
238
239 try {
240 // Get available tools
241 def toolsResponse = sendJsonRpc("tools/list")
242 def tools = toolsResponse?.result?.tools ?: []
243
244 // Find catalog screens
245 def catalogScreens = tools.findAll {
246 it.name?.toLowerCase()?.contains("catalog")
247 }
248
249 if (catalogScreens) {
250 println "Found ${catalogScreens.size()} catalog screens"
251
252 // Try to execute the first catalog screen
253 def catalogResult = executeTool(catalogScreens[0].name)
254 if (catalogResult) {
255 println "✅ Successfully executed catalog screen: ${catalogScreens[0].name}"
256 return true
257 } else {
258 println "❌ Failed to execute catalog screen"
259 return false
260 }
261 } else {
262 println "⚠️ No catalog screens found, skipping screen test"
263 return true // Not a failure, just not available
264 }
265
266 } catch (Exception e) {
267 println "❌ Screen-based workflow failed: ${e.message}"
268 return false
269 }
270 }
271
272 /**
273 * Step 5: Complete E-commerce Workflow
274 */
275 boolean testCompleteWorkflow() {
276 println "\n🔄 Step 5: Complete E-commerce Workflow"
277 println "========================================"
278
279 try {
280 // Run the complete e-commerce workflow service
281 def workflowResult = executeTool("org.moqui.mcp.McpTestServices.run#EcommerceWorkflow", [
282 productName: "Complete Workflow Product ${System.currentTimeMillis()}",
283 customerFirstName: "Workflow",
284 customerLastName: "Test",
285 customerEmail: "workflow-${System.currentTimeMillis()}@mcp.test",
286 quantity: 1,
287 price: 49.99
288 ])
289
290 if (workflowResult && workflowResult.success) {
291 println "✅ Complete workflow executed successfully"
292 println " Workflow ID: ${workflowResult.workflowId}"
293 println " Product ID: ${workflowResult.productId}"
294 println " Customer ID: ${workflowResult.customerId}"
295 println " Order ID: ${workflowResult.orderId}"
296
297 // Print workflow steps
298 workflowResult.steps?.each { step ->
299 println " ${step.success ? '✅' : '❌'} ${step.step}: ${step.message}"
300 }
301
302 return true
303 } else {
304 println "❌ Complete workflow failed"
305 return false
306 }
307
308 } catch (Exception e) {
309 println "❌ Complete workflow failed: ${e.message}"
310 return false
311 }
312 }
313
314 /**
315 * Step 6: Cleanup Test Data
316 */
317 boolean testCleanup() {
318 println "\n🧹 Step 6: Cleanup Test Data"
319 println "============================"
320
321 try {
322 // Cleanup test data using MCP test service
323 def cleanupResult = executeTool("org.moqui.mcp.McpTestServices.cleanup#TestData", [
324 olderThanHours: 0 // Cleanup immediately
325 ])
326
327 if (cleanupResult && cleanupResult.success) {
328 println "✅ Test data cleanup completed"
329 println " Deleted orders: ${cleanupResult.deletedOrders}"
330 println " Deleted products: ${cleanupResult.deletedProducts}"
331 println " Deleted customers: ${cleanupResult.deletedCustomers}"
332 return true
333 } else {
334 println "❌ Test data cleanup failed"
335 return false
336 }
337
338 } catch (Exception e) {
339 println "❌ Cleanup failed: ${e.message}"
340 return false
341 }
342 }
343
344 /**
345 * Run complete e-commerce workflow test
346 */
347 void runCompleteTest() {
348 println "🧪 E-commerce Workflow Test for MCP"
349 println "=================================="
350
351 def startTime = System.currentTimeMillis()
352 def results = [:]
353
354 // Initialize session
355 if (!initialize()) {
356 println "❌ Failed to initialize MCP session"
357 return
358 }
359
360 // Run all test steps
361 results.productDiscovery = testProductDiscovery()
362 results.customerManagement = testCustomerManagement()
363 results.orderPlacement = testOrderPlacement()
364 results.screenBasedWorkflow = testScreenBasedWorkflow()
365 results.completeWorkflow = testCompleteWorkflow()
366 results.cleanup = testCleanup()
367
368 // Generate report
369 def endTime = System.currentTimeMillis()
370 def duration = endTime - startTime
371
372 println "\n" + "="*60
373 println "📋 E-COMMERCE WORKFLOW TEST REPORT"
374 println "="*60
375 println "Duration: ${duration}ms"
376 println ""
377
378 def totalSteps = results.size()
379 def successfulSteps = results.count { it.value }
380
381 results.each { stepName, success ->
382 println "${success ? '✅' : '❌'} ${stepName}"
383 }
384
385 println ""
386 println "Overall Result: ${successfulSteps}/${totalSteps} steps passed"
387 println "Success Rate: ${(successfulSteps/totalSteps * 100).round()}%"
388
389 if (successfulSteps == totalSteps) {
390 println "🎉 ALL TESTS PASSED! MCP e-commerce workflow is working correctly."
391 } else {
392 println "⚠️ Some tests failed. Check the output above for details."
393 }
394
395 println "="*60
396 }
397
398 /**
399 * Main method for standalone execution
400 */
401 static void main(String[] args) {
402 def test = new EcommerceWorkflowTest()
403 test.runCompleteTest()
404 }
405 }
...\ No newline at end of file ...\ No newline at end of file