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
Showing
6 changed files
with
1533 additions
and
0 deletions
| ... | @@ -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"/> | ... | ... |
service/org/moqui/mcp/McpTestServices.xml
0 → 100644
| 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 && customerResult.success && 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 |
test/README.md
0 → 100644
| 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 |
test/client/McpTestClient.groovy
0 → 100644
| 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 |
test/run-tests.sh
0 → 100755
| 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 |
test/workflows/EcommerceWorkflowTest.groovy
0 → 100644
| 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 |
-
Please register or sign in to post a comment