@fluentcommerce/fluent-mcp-extn 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,739 +1,739 @@
1
- # End-to-End Testing Guide
2
-
3
- This guide walks through full validation of `fluent-mcp-extn` from startup to
4
- real Fluent API operations.
5
-
6
- ## Script inventory
7
-
8
- | Script | Type | Purpose |
9
- |--------|------|---------|
10
- | `e2e-smoke.ts` | E2E | Full MCP smoke cycle: tool discovery, config, health, events, GraphQL |
11
- | `e2e-negative.ts` | E2E | Validation error and malformed input handling |
12
- | `e2e-profile-test.mjs` | E2E | Quick FLUENT_PROFILE integration check |
13
- | `e2e-full-order.ts` | E2E | Single order lifecycle: create, validate, fulfil, ship, deliver |
14
- | `e2e-full-multi.ts` | E2E | ORDER::MULTI lifecycle with discovery and fulfilment event sequence |
15
- | `e2e-agentic.ts` | E2E | All 11 agentic tools: entity CRUD, workflow, settings, environment |
16
- | `e2e-response-shaping.ts` | E2E | Response budget enforcement and auto-summarization proof |
17
- | `run-flow-inspect.ts` | Utility | FlowInspect forensics with local analysis, LLM packet, and drilldowns |
18
- | `order-event-inspect.ts` | Utility | Order-specific event deep dive with audit trail extraction |
19
- | `check-event-status.ts` | Utility | Event status polling by ID, name, or entity ref |
20
-
21
- All scripts require a built server (`npm run build`) and valid Fluent credentials.
22
-
23
- ## 1) Test levels
24
-
25
- - **Level 1: local quality gate** (no network)
26
- - `npm run build`
27
- - `npm test`
28
- - **Level 2: MCP runtime check** (server starts, tools load)
29
- - `config.validate`
30
- - `health.ping`
31
- - `connection.test`
32
- - **Level 3: Fluent API smoke** (real API calls)
33
- - `event.send` dry-run and real send
34
- - `event.get` on sent event ID
35
- - `graphql.query`
36
- - `graphql.queryAll` (pagination timeout + aggregation)
37
- - optional batch flow (`batch.create` -> `batch.send` -> `batch.status`)
38
- - optional webhook verification (`webhook.validate` with `rawBody`)
39
-
40
- ## 2) Prerequisites
41
-
42
- - valid Fluent sandbox/prod API base URL
43
- - auth configured using one of:
44
- - `FLUENT_PROFILE` (optionally `FLUENT_PROFILE_RETAILER`)
45
- - OAuth env vars
46
- - `TOKEN_COMMAND`
47
- - `FLUENT_ACCESS_TOKEN`
48
- - known retailer ID or retailer ref for your target environment
49
-
50
- ## 3) Configure `.mcp.json`
51
-
52
- Use this as a baseline:
53
-
54
- ```json
55
- {
56
- "mcpServers": {
57
- "fluent-mcp-extn": {
58
- "type": "stdio",
59
- "command": "node",
60
- "args": ["fluent-mcp-extn/dist/index.js"],
61
- "env": {
62
- "FLUENT_BASE_URL": "https://YOUR_ACCOUNT.sandbox.api.fluentretail.com",
63
- "FLUENT_RETAILER_ID": "YOUR_RETAILER_ID",
64
- "TOKEN_COMMAND": "vault read -field=token secret/fluent/client-dev",
65
- "FLUENT_REQUEST_TIMEOUT_MS": "30000",
66
- "FLUENT_RETRY_ATTEMPTS": "3"
67
- }
68
- }
69
- }
70
- }
71
- ```
72
-
73
- After editing `.mcp.json`, reload your IDE so MCP servers restart with new env.
74
-
75
- Profile-first setup via AI Skills:
76
-
77
- ```bash
78
- npx @fluentcommerce/ai-skills mcp-setup --profile YOUR_PROFILE --profile-retailer YOUR_RETAILER_REF
79
- ```
80
-
81
- Then confirm extension env contains:
82
-
83
- - `FLUENT_PROFILE=YOUR_PROFILE`
84
- - `FLUENT_PROFILE_RETAILER=YOUR_RETAILER_REF` (retailer **ref**, not ID)
85
-
86
- ## 4) Build and test locally
87
-
88
- From `fluent-mcp-extn/`:
89
-
90
- ```bash
91
- npm install
92
- npm run build
93
- npm test
94
- ```
95
-
96
- Expected: build succeeds and all unit tests pass.
97
-
98
- Optional automated smoke runner:
99
-
100
- ```bash
101
- npm run e2e:smoke
102
- ```
103
-
104
- This runs: tool discovery, config validation, health, event build, event dry-run,
105
- GraphQL checks, order lookup, real event send, and event retrieval by ID.
106
-
107
- Profile-driven and multi-retailer variants:
108
-
109
- ```bash
110
- npm run e2e:smoke -- --wizard
111
- npm run e2e:smoke -- --profile CLIENT_PROFILE
112
- npm run e2e:smoke -- --profile CLIENT_PROFILE --retailer RETAILER_REF_OR_ID
113
- npm run e2e:smoke -- --profile CLIENT_PROFILE --all-retailers
114
- ```
115
-
116
- Notes:
117
-
118
- - `--wizard` guides profile/account + retailer selection in interactive terminals
119
- - `--wizard` requires an interactive terminal (TTY); for CI/non-interactive runs use explicit flags (`--profile <name> --retailer <id|ref>` or `--profile <name> --all-retailers`)
120
- - `--profile` reads from `~/.fluentcommerce/<profile>/`
121
- - profile defaults are applied only when equivalent env vars are missing
122
- - `--retailer` accepts retailer ID or retailer ref
123
- - `--all-retailers` runs one full smoke cycle per retailer file in the profile
124
- - `--skip-real-send` performs dry-run checks only
125
- - with `--profile` only (no `--retailer` / `--all-retailers`), smoke may fall back to a retailer that has candidate orders for real-send validation; use `--retailer` for strict retailer scope
126
-
127
- ### All-retailers run behavior
128
-
129
- `--all-retailers` requires `--profile` and executes one smoke cycle per
130
- `retailer.<ref>.json` file under `~/.fluentcommerce/<profile>/`. Output labels
131
- use retailer IDs (for example `retailer-5`).
132
-
133
- When a retailer has no candidate order, order lookup and real send steps are
134
- reported as skipped for that retailer (not failed). The command exits non-zero
135
- only when non-skipped steps fail.
136
-
137
- #### Interpreting results
138
-
139
- | Status | Meaning | Action |
140
- |---|---|---|
141
- | `PASS` | Step completed successfully for that retailer. | No action needed. |
142
- | `SKIP` | Step was intentionally not executed (for example, no candidate order in `--all-retailers` mode). | Verify preconditions only if you expected execution. |
143
- | `FAIL` | Step executed and returned an error or unexpected result. | Investigate payload/error details and fix before relying on run output. |
144
-
145
- ## 5) E2E script reference
146
-
147
- ### `e2e-smoke.ts`
148
-
149
- Full MCP client smoke cycle that exercises the core tool chain end to end.
150
-
151
- **What it tests:** tool discovery, `config.validate`, `health.ping`, `event.build`,
152
- `event.send` (dry-run and real), `graphql.query`, `graphql.queryAll`, order lookup,
153
- `event.get` by ID.
154
-
155
- **How to run:**
156
-
157
- ```bash
158
- npm run e2e:smoke
159
- npm run e2e:smoke -- --profile HMDEV
160
- npm run e2e:smoke -- --profile HMDEV --retailer HM_TEST
161
- npm run e2e:smoke -- --profile HMDEV --all-retailers
162
- npm run e2e:smoke -- --skip-real-send
163
- ```
164
-
165
- **Environment:** requires `FLUENT_BASE_URL` + auth env vars, or `--profile`.
166
-
167
- ---
168
-
169
- ### `e2e-negative.ts`
170
-
171
- Validates that the server returns correct error envelopes for invalid inputs.
172
-
173
- **What it tests:**
174
- - `event.send` with missing required `entityRef` field
175
- - `graphql.query` with malformed GraphQL syntax
176
- - `event.get` with a nonexistent event ID
177
- - `graphql.introspect` with a nonexistent mutation name
178
- - `webhook.validate` with an invalid signature and public key
179
-
180
- Each test case asserts: `ok: false`, standard error envelope shape
181
- (`code` + `message` + `retryable`), and expected error code where applicable
182
- (e.g., `VALIDATION_ERROR`).
183
-
184
- **How to run:**
185
-
186
- ```bash
187
- npx tsx scripts/e2e-negative.ts
188
- npx tsx scripts/e2e-negative.ts --profile HMDEV
189
- npx tsx scripts/e2e-negative.ts --profile HMDEV --retailer HM_TEST
190
- ```
191
-
192
- **Environment:** requires `FLUENT_BASE_URL` + auth env vars, or `--profile`.
193
-
194
- ---
195
-
196
- ### `e2e-profile-test.mjs`
197
-
198
- Minimal FLUENT_PROFILE integration check. Loads config, creates SDK client, and
199
- runs four quick API calls to verify profile-based auth works end to end.
200
-
201
- **What it tests:**
202
- 1. Config loads correctly with profile auth strategy
203
- 2. GraphQL `me` query returns user and retailer info
204
- 3. `getEvents` returns event results
205
- 4. `getPlugins` returns the plugin registry
206
- 5. `getTransitions` returns user actions for ORDER::HD at CREATED status
207
-
208
- **How to run:**
209
-
210
- ```bash
211
- FLUENT_PROFILE=HMDEV FLUENT_PROFILE_RETAILER=HM_TEST node scripts/e2e-profile-test.mjs
212
- ```
213
-
214
- **Environment:** requires `FLUENT_PROFILE` and optionally `FLUENT_PROFILE_RETAILER`
215
- set as env vars. Uses compiled `dist/` output directly (not MCP stdio transport).
216
-
217
- ---
218
-
219
- ### `e2e-full-order.ts`
220
-
221
- Discovery-first single order lifecycle test. Creates an order with
222
- `sourcingLocationRef`, then drives it through the full fulfilment workflow.
223
-
224
- **What it tests:**
225
- 1. Config validation
226
- 2. Discovery: locations, catalogues, products, customers, settings
227
- 3. `createOrder` mutation with sourcing attributes
228
- 4. `ConfirmValidation` event on order
229
- 5. Query order + fulfilments post-validation
230
- 6. Fulfilment events: `ConfirmAllocation`, `CreateInvoice`, `ConfirmPick`,
231
- `ConfirmShipment`, `ConfirmDelivery`
232
- 7. Final order/fulfilment state verification
233
- 8. Event trace: counts SUCCESS vs FAILED events for the order
234
-
235
- **How to run:**
236
-
237
- ```bash
238
- npx tsx scripts/e2e-full-order.ts --profile SAGIRISH --retailer 2
239
- FLUENT_PROFILE=SAGIRISH FLUENT_RETAILER_ID=2 npx tsx scripts/e2e-full-order.ts
240
- ```
241
-
242
- **Environment:** requires `FLUENT_PROFILE` + `FLUENT_RETAILER_ID`, or `--profile`
243
- and `--retailer` flags. Environment must have warehouse locations, product
244
- catalogue with active products, and at least one customer entity.
245
-
246
- **Note:** this script creates real orders and fires real events. Use a sandbox.
247
-
248
- ---
249
-
250
- ### `e2e-full-multi.ts`
251
-
252
- ORDER::MULTI lifecycle test. Similar to `e2e-full-order.ts` but targets the
253
- MULTI order type workflow and includes workflow transition discovery.
254
-
255
- **What it tests:**
256
- 1. Config validation
257
- 2. Discovery: retailer, locations, catalogues, products, customers, settings
258
- 3. `workflow.transitions` for ORDER::MULTI at ON_VALIDATION status
259
- 4. `createOrder` mutation with MULTI type
260
- 5. `ConfirmValidation` event
261
- 6. Fulfilment creation verification
262
- 7. Fulfilment event sequence: `ConfirmAllocation`, `CreateInvoice`,
263
- `ConfirmPick`, `ConfirmShipment`, `ConfirmDelivery`
264
- 8. Event trace on failure
265
-
266
- **How to run:**
267
-
268
- ```bash
269
- npx tsx scripts/e2e-full-multi.ts
270
- ```
271
-
272
- **Environment:** defaults to `FLUENT_BASE_URL=https://sagirish.test.api.fluentretail.com`
273
- and `FLUENT_RETAILER_ID=2`. Override with env vars. Requires OAuth or profile
274
- credentials.
275
-
276
- **Note:** creates real orders and fires real events. Use a sandbox.
277
-
278
- ---
279
-
280
- ### `e2e-agentic.ts`
281
-
282
- Comprehensive smoke test for all 11 agentic tools against a live environment.
283
-
284
- **What it tests:**
285
- - `environment.discover` (retailer + locations, catalogues)
286
- - `environment.validate` (auth, retailer, locations checks)
287
- - `entity.create` (dryRun validation, required field rejection, live creation)
288
- - `entity.get` (by ID, by ref, nonexistent ref)
289
- - `entity.update` (attribute update)
290
- - `test.assert` (status match, status mismatch)
291
- - `setting.upsert` (create new, update existing with previous value)
292
- - `workflow.diff` (summary format, mermaid format)
293
- - `workflow.simulate` (status matching, custom rule confidence, no-match)
294
- - `workflow.upload` (dryRun structure validation)
295
-
296
- **How to run:**
297
-
298
- ```bash
299
- npx tsx scripts/e2e-agentic.ts --profile SAGIRISH
300
- ```
301
-
302
- **Environment:** requires `FLUENT_PROFILE` or `--profile` flag. Defaults to
303
- `FLUENT_RETAILER_ID=2`.
304
-
305
- **Note:** creates real orders and settings. Use a sandbox.
306
-
307
- ---
308
-
309
- ### `e2e-response-shaping.ts`
310
-
311
- Proves that response shaping (auto-summarization) works correctly on real API
312
- responses.
313
-
314
- **What it proves:**
315
- 1. `event.flowInspect` compact response stays within budget (50k chars)
316
- 2. `event.flowInspect` full mode (`compact: false`) gets auto-summarized, not truncated
317
- 3. `plugin.list` (unfiltered, 500+ rules) gets shaped when large
318
- 4. `event.list` (500 events) gets shaped when large
319
- 5. `_responseMeta` appears with accurate stats (originalChars, shapedChars, reductionPercent)
320
- 6. No `_truncated` markers anywhere (replaced by `_summarized`)
321
-
322
- **How to run:**
323
-
324
- ```bash
325
- npx tsx scripts/e2e-response-shaping.ts --profile SAGIRISH
326
- ```
327
-
328
- **Environment:** requires `FLUENT_PROFILE` or `--profile`. Uses default budget
329
- (50k chars). The environment must have enough events and plugins to exceed the
330
- budget threshold.
331
-
332
- ---
333
-
334
- ### `run-flow-inspect.ts`
335
-
336
- Advanced FlowInspect forensics runner. Calls `event.flowInspect`, then performs
337
- local analysis: risk scoring, finding extraction, evidence ranking, and optional
338
- per-event drilldowns via `event.get`.
339
-
340
- **Outputs:**
341
- - Raw JSON: full flowInspect response
342
- - Summary JSON: risk score, status/entity distributions, anomaly counts, webhook
343
- failure breakdown, exception classes, slow rulesets, mismatch reasons
344
- - LLM packet (Markdown): compact handoff document for AI consumption
345
- - Drilldown JSON: classified event details (webhook, mutation, sendEvent, mismatch)
346
-
347
- **How to run:**
348
-
349
- ```bash
350
- npx tsx scripts/run-flow-inspect.ts <ROOT_ENTITY_REF> --profile SAGIRISH
351
- npx tsx scripts/run-flow-inspect.ts HD-001 --root-type ORDER --profile HMDEV
352
- npx tsx scripts/run-flow-inspect.ts HD-001 --root-id 12345 --light --no-drilldown
353
- npx tsx scripts/run-flow-inspect.ts HD-001 --drilldown-classes webhook,mutation --drilldown-limit 20
354
- ```
355
-
356
- **Options:**
357
-
358
- | Flag | Default | Description |
359
- |------|---------|-------------|
360
- | `--profile <name>` | `SAGIRISH` | Fluent CLI profile |
361
- | `--root-type <type>` | any | Root entity type (ORDER, FULFILMENT, etc.) |
362
- | `--root-id <id>` | - | Root entity ID for disambiguation |
363
- | `--out <path>` | `accounts/<profile>/analysis/EVENT_FLOW_INSPECT_<ref>.json` | Raw output path |
364
- | `--summary-out <path>` | `<base>.summary.json` | Summary output path |
365
- | `--packet-out <path>` | `<base>.llm-packet.md` | LLM packet output path |
366
- | `--drilldown-out <path>` | `<base>.drilldown.json` | Drilldown output path |
367
- | `--light` | false | Compact mode (fewer audit details) |
368
- | `--no-drilldown` | false | Skip per-event drilldowns |
369
- | `--drilldown-limit <n>` | 15 | Max events to drill into |
370
- | `--drilldown-classes <list>` | all | Comma-separated: webhook,mutation,sendEvent,mismatch,exception,scheduled,slow,status |
371
- | `--top-n <n>` | 10 | Top-N items per section |
372
- | `--evidence-limit <n>` | 20 | Max evidence event IDs |
373
- | `--truncate <n>` | 220 | Max chars for text fields |
374
- | `--max-pages <n>` | 10 | Max 500-event pages to fetch |
375
- | `--no-llm-pack` | false | Skip LLM packet generation |
376
-
377
- **Environment:** requires `FLUENT_PROFILE` or env vars.
378
-
379
- ---
380
-
381
- ### `order-event-inspect.ts`
382
-
383
- Order-specific event deep dive. Looks up an order by ref, then fetches all
384
- ORCHESTRATION and ORCHESTRATION_AUDIT events, classifying them into webhooks,
385
- mutations, sendEvent actions, scheduled actions, and ruleset durations.
386
-
387
- **Outputs:** a single JSON file with:
388
- - Order entity details (status, attributes, fulfilment choices)
389
- - Orchestration events (status counts, entity type counts, top event names)
390
- - Audit trail (webhook actions, mutation actions, sendEvent actions, scheduled
391
- actions, ruleset durations, exceptions)
392
- - Drilldown details for NO_MATCH and PENDING events
393
-
394
- **How to run:**
395
-
396
- ```bash
397
- npx tsx scripts/order-event-inspect.ts <ORDER_REF> --profile SAGIRISH
398
- npx tsx scripts/order-event-inspect.ts HD-001 --profile HMDEV --out /tmp/inspect.json
399
- ```
400
-
401
- **Environment:** requires `FLUENT_PROFILE` or env vars.
402
-
403
- ---
404
-
405
- ### `check-event-status.ts`
406
-
407
- Lightweight event status checker. Fetches events by ID or lists events by name
408
- and entity ref.
409
-
410
- **How to run:**
411
-
412
- ```bash
413
- # Fetch specific events by ID
414
- npx tsx scripts/check-event-status.ts --event-id <uuid> --profile SAGIRISH
415
-
416
- # List events by name
417
- npx tsx scripts/check-event-status.ts --event-name ConfirmValidation --profile SAGIRISH
418
-
419
- # List events by entity ref
420
- npx tsx scripts/check-event-status.ts --entity-ref HD-001 --profile SAGIRISH
421
-
422
- # Combine filters
423
- npx tsx scripts/check-event-status.ts --event-name ConfirmValidation --entity-ref HD-001 --count 20 --profile SAGIRISH
424
- ```
425
-
426
- **Options:**
427
-
428
- | Flag | Default | Description |
429
- |------|---------|-------------|
430
- | `--profile <name>` | - | Fluent CLI profile |
431
- | `--retailer <id\|ref>` | from profile/env | Retailer filter |
432
- | `--event-id <uuid>` | - | Fetch specific event (repeatable) |
433
- | `--event-name <name>` | - | Event name filter for list |
434
- | `--entity-ref <ref>` | - | Entity ref filter for list |
435
- | `--count <n>` | 10 | Max list size (max 500) |
436
-
437
- **Environment:** requires `FLUENT_BASE_URL` + auth, or `--profile`.
438
-
439
- ## 6) MCP smoke sequence in your IDE
440
-
441
- Run these tools in order from the assistant:
442
-
443
- ### Step 1: `config.validate`
444
-
445
- Request:
446
-
447
- ```json
448
- {}
449
- ```
450
-
451
- Expected:
452
-
453
- - `ok: true`
454
- - `validation.isReadyForApiCalls: true`
455
- - no blocking errors
456
-
457
- ### Step 2: `health.ping`
458
-
459
- Request:
460
-
461
- ```json
462
- {}
463
- ```
464
-
465
- Expected:
466
-
467
- - `status: "ok"`
468
- - `sdkClient: "connected"` (or equivalent ready status)
469
- - config summary present without secret values
470
-
471
- ### Step 3: `connection.test`
472
-
473
- Request:
474
-
475
- ```json
476
- {}
477
- ```
478
-
479
- Expected:
480
-
481
- - `ok: true`
482
- - `details` includes user, retailer, and location info
483
- - confirms end-to-end GraphQL connectivity
484
-
485
- ### Step 4: `event.build`
486
-
487
- Request:
488
-
489
- ```json
490
- {
491
- "name": "ORDER_CREATED",
492
- "entityRef": "E2E-ORDER-1001",
493
- "entityType": "ORDER",
494
- "attributes": {
495
- "sourceSystem": "e2e-test",
496
- "testRunId": "run-001"
497
- }
498
- }
499
- ```
500
-
501
- Expected:
502
-
503
- - `ok: true`
504
- - complete event payload returned
505
- - defaults applied (`rootEntityType`, `source`, `type`)
506
-
507
- ### Step 5: `event.send` dry-run
508
-
509
- Request:
510
-
511
- ```json
512
- {
513
- "name": "ORDER_CREATED",
514
- "entityRef": "E2E-ORDER-1001",
515
- "entityId": "12345",
516
- "entityType": "ORDER",
517
- "dryRun": true
518
- }
519
- ```
520
-
521
- Expected:
522
-
523
- - `ok: true`
524
- - `dryRun: true`
525
- - no API call made
526
-
527
- ### Step 6: `event.send` real call
528
-
529
- Request:
530
-
531
- ```json
532
- {
533
- "name": "ORDER_CREATED",
534
- "entityRef": "E2E-ORDER-1001",
535
- "entityId": "12345",
536
- "entityType": "ORDER",
537
- "retailerId": "YOUR_ENTITY_RETAILER_ID",
538
- "mode": "async",
539
- "dryRun": false,
540
- "attributes": {
541
- "sourceSystem": "e2e-test",
542
- "testRunId": "run-001"
543
- }
544
- }
545
- ```
546
-
547
- Expected:
548
-
549
- - `ok: true`
550
- - response from Fluent event API
551
- - retailer ID in payload matches the entity's retailer
552
- - operation executed once (write tools do not auto-retry)
553
-
554
- If this fails, investigate before resubmitting to avoid duplicate side effects.
555
-
556
- ### Step 7: `graphql.query` verification
557
-
558
- Use a query valid for your schema. Example shape:
559
-
560
- ```json
561
- {
562
- "query": "query VerifyOrder($ref: String!) { orders(ref: $ref) { edges { node { id ref status } } } }",
563
- "variables": {
564
- "ref": "E2E-ORDER-1001"
565
- }
566
- }
567
- ```
568
-
569
- Expected:
570
-
571
- - `ok: true`
572
- - Fluent GraphQL response payload returned
573
-
574
- > Note: exact query fields differ by tenant schema/version.
575
-
576
- ### Step 8: `graphql.queryAll` verification
577
-
578
- Validate auto-pagination and run-level timeout behavior:
579
-
580
- ```json
581
- {
582
- "query": "query($cursor: String) { orders(first: 50, after: $cursor) { edges { cursor node { id ref } } pageInfo { hasNextPage } } }",
583
- "variables": { "cursor": null },
584
- "timeoutMs": 120000
585
- }
586
- ```
587
-
588
- Expected:
589
-
590
- - `ok: true`
591
- - `response.extensions.autoPagination` present
592
- - `pagination.totalPages` / `pagination.totalRecords` present in tool response
593
-
594
- ### Step 9: `event.get` verification
595
-
596
- Use an event ID from successful `event.send` (or resolve ID via `event.list`).
597
-
598
- ```json
599
- {
600
- "eventId": "EVENT_ID_FROM_SEND_RESPONSE"
601
- }
602
- ```
603
-
604
- Expected:
605
-
606
- - `ok: true`
607
- - event payload returned from Event API
608
- - event payload includes expected `name`, `context.entityRef`, and retailer
609
-
610
- If `event.send` response does not contain `id`, resolve it first with `event.list`:
611
-
612
- ```json
613
- {
614
- "name": "ORDER_CREATED",
615
- "entityRef": "E2E-ORDER-1001",
616
- "retailerId": "YOUR_ENTITY_RETAILER_ID",
617
- "count": 1,
618
- "start": 1
619
- }
620
- ```
621
-
622
- ### Step 10 (optional): `graphql.introspect`
623
-
624
- Verify schema introspection access:
625
-
626
- ```json
627
- {
628
- "listMutations": true
629
- }
630
- ```
631
-
632
- Expected:
633
-
634
- - `ok: true`
635
- - `mutations` array returned
636
- - `count` reflects available mutations in the schema
637
-
638
- ## 7) Optional webhook signature validation
639
-
640
- Use this when validating webhook integration cryptography:
641
-
642
- ```json
643
- {
644
- "payload": {
645
- "name": "OrderCreated",
646
- "id": "event-123",
647
- "retailerId": "5"
648
- },
649
- "rawBody": "{\"name\":\"OrderCreated\",\"id\":\"event-123\",\"retailerId\":\"5\"}",
650
- "signature": "base64-encoded-signature-from-header",
651
- "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----"
652
- }
653
- ```
654
-
655
- Expected:
656
-
657
- - `ok: true` when payload and signature are valid
658
- - `signatureValidation.payloadSource` confirms whether `rawBody` was used
659
-
660
- ## 8) Optional batch E2E
661
-
662
- ### `batch.create`
663
-
664
- ```json
665
- {
666
- "name": "e2e-batch-001",
667
- "entityType": "INVENTORY_POSITION",
668
- "action": "UPSERT"
669
- }
670
- ```
671
-
672
- ### `batch.send`
673
-
674
- ```json
675
- {
676
- "jobId": "JOB_ID_FROM_CREATE",
677
- "payload": {
678
- "action": "UPSERT",
679
- "entityType": "INVENTORY_POSITION",
680
- "entities": [
681
- {
682
- "locationRef": "LOC-1",
683
- "skuRef": "SKU-1",
684
- "qty": 10,
685
- "type": "LAST_ON_HAND",
686
- "status": "ACTIVE"
687
- }
688
- ]
689
- }
690
- }
691
- ```
692
-
693
- ### `batch.status`
694
-
695
- ```json
696
- {
697
- "jobId": "JOB_ID_FROM_CREATE"
698
- }
699
- ```
700
-
701
- ## 9) Failure triage quick map
702
-
703
- - `CONFIG_ERROR`: missing env values -> run `config.validate`
704
- - `AUTH_ERROR`: invalid creds/token -> verify OAuth or `TOKEN_COMMAND`
705
- - `VALIDATION_ERROR`: invalid input shape -> fix request payload
706
- - `TIMEOUT_ERROR` / `NETWORK_ERROR`: tune timeout/retry; verify connectivity
707
- - `RATE_LIMIT` / `UPSTREAM_UNAVAILABLE`: retry later (built-in retries are applied for read operations; write retries are manual by design)
708
-
709
- ## 10) Exit criteria for successful E2E
710
-
711
- Mark E2E successful only when:
712
-
713
- - local build/test passed
714
- - config/health/connection tools are green
715
- - at least one real `event.send` succeeded
716
- - at least one `event.get` succeeded
717
- - at least one real `graphql.query` succeeded
718
- - at least one `graphql.queryAll` run succeeded
719
- - (optional) `graphql.introspect` schema access confirmed
720
- - (optional) batch cycle completed
721
-
722
- ## 11) CI-triggered smoke (GitHub Actions)
723
-
724
- Workflow: `.github/workflows/ci.yml`
725
-
726
- - `build-and-test`: runs on push and pull request
727
- - `e2e-smoke`: runs on `workflow_dispatch` only
728
-
729
- Set repository or environment secrets before running `e2e-smoke`:
730
-
731
- - `FLUENT_BASE_URL`
732
- - `FLUENT_RETAILER_ID` (optional but recommended)
733
- - `FLUENT_CLIENT_ID`
734
- - `FLUENT_CLIENT_SECRET`
735
- - `FLUENT_USERNAME` (optional for client-credentials flow)
736
- - `FLUENT_PASSWORD` (optional for client-credentials flow)
737
-
738
- CI smoke runs are typically env-driven (no `--profile` required). For profile-based runs in CI, pass explicit flags (`--profile <name> --retailer <id|ref>` or `--profile <name> --all-retailers`).
739
- If real event side effects are not desired, run with `--skip-real-send`.
1
+ # End-to-End Testing Guide
2
+
3
+ This guide walks through full validation of `fluent-mcp-extn` from startup to
4
+ real Fluent API operations.
5
+
6
+ ## Script inventory
7
+
8
+ | Script | Type | Purpose |
9
+ |--------|------|---------|
10
+ | `e2e-smoke.ts` | E2E | Full MCP smoke cycle: tool discovery, config, health, events, GraphQL |
11
+ | `e2e-negative.ts` | E2E | Validation error and malformed input handling |
12
+ | `e2e-profile-test.mjs` | E2E | Quick FLUENT_PROFILE integration check |
13
+ | `e2e-full-order.ts` | E2E | Single order lifecycle: create, validate, fulfil, ship, deliver |
14
+ | `e2e-full-multi.ts` | E2E | ORDER::MULTI lifecycle with discovery and fulfilment event sequence |
15
+ | `e2e-agentic.ts` | E2E | All 11 agentic tools: entity CRUD, workflow, settings, environment |
16
+ | `e2e-response-shaping.ts` | E2E | Response budget enforcement and auto-summarization proof |
17
+ | `run-flow-inspect.ts` | Utility | FlowInspect forensics with local analysis, LLM packet, and drilldowns |
18
+ | `order-event-inspect.ts` | Utility | Order-specific event deep dive with audit trail extraction |
19
+ | `check-event-status.ts` | Utility | Event status polling by ID, name, or entity ref |
20
+
21
+ All scripts require a built server (`npm run build`) and valid Fluent credentials.
22
+
23
+ ## 1) Test levels
24
+
25
+ - **Level 1: local quality gate** (no network)
26
+ - `npm run build`
27
+ - `npm test`
28
+ - **Level 2: MCP runtime check** (server starts, tools load)
29
+ - `config.validate`
30
+ - `health.ping`
31
+ - `connection.test`
32
+ - **Level 3: Fluent API smoke** (real API calls)
33
+ - `event.send` dry-run and real send
34
+ - `event.get` on sent event ID
35
+ - `graphql.query`
36
+ - `graphql.queryAll` (pagination timeout + aggregation)
37
+ - optional batch flow (`batch.create` -> `batch.send` -> `batch.status`)
38
+ - optional webhook verification (`webhook.validate` with `rawBody`)
39
+
40
+ ## 2) Prerequisites
41
+
42
+ - valid Fluent sandbox/prod API base URL
43
+ - auth configured using one of:
44
+ - `FLUENT_PROFILE` (optionally `FLUENT_PROFILE_RETAILER`)
45
+ - OAuth env vars
46
+ - `TOKEN_COMMAND`
47
+ - `FLUENT_ACCESS_TOKEN`
48
+ - known retailer ID or retailer ref for your target environment
49
+
50
+ ## 3) Configure `.mcp.json`
51
+
52
+ Use this as a baseline:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "fluent-mcp-extn": {
58
+ "type": "stdio",
59
+ "command": "node",
60
+ "args": ["fluent-mcp-extn/dist/index.js"],
61
+ "env": {
62
+ "FLUENT_BASE_URL": "https://YOUR_ACCOUNT.sandbox.api.fluentretail.com",
63
+ "FLUENT_RETAILER_ID": "YOUR_RETAILER_ID",
64
+ "TOKEN_COMMAND": "vault read -field=token secret/fluent/client-dev",
65
+ "FLUENT_REQUEST_TIMEOUT_MS": "30000",
66
+ "FLUENT_RETRY_ATTEMPTS": "3"
67
+ }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ After editing `.mcp.json`, reload your IDE so MCP servers restart with new env.
74
+
75
+ Profile-first setup via AI Skills:
76
+
77
+ ```bash
78
+ npx @fluentcommerce/ai-skills mcp-setup --profile YOUR_PROFILE --profile-retailer YOUR_RETAILER_REF
79
+ ```
80
+
81
+ Then confirm extension env contains:
82
+
83
+ - `FLUENT_PROFILE=YOUR_PROFILE`
84
+ - `FLUENT_PROFILE_RETAILER=YOUR_RETAILER_REF` (retailer **ref**, not ID)
85
+
86
+ ## 4) Build and test locally
87
+
88
+ From `fluent-mcp-extn/`:
89
+
90
+ ```bash
91
+ npm install
92
+ npm run build
93
+ npm test
94
+ ```
95
+
96
+ Expected: build succeeds and all unit tests pass.
97
+
98
+ Optional automated smoke runner:
99
+
100
+ ```bash
101
+ npm run e2e:smoke
102
+ ```
103
+
104
+ This runs: tool discovery, config validation, health, event build, event dry-run,
105
+ GraphQL checks, order lookup, real event send, and event retrieval by ID.
106
+
107
+ Profile-driven and multi-retailer variants:
108
+
109
+ ```bash
110
+ npm run e2e:smoke -- --wizard
111
+ npm run e2e:smoke -- --profile CLIENT_PROFILE
112
+ npm run e2e:smoke -- --profile CLIENT_PROFILE --retailer RETAILER_REF_OR_ID
113
+ npm run e2e:smoke -- --profile CLIENT_PROFILE --all-retailers
114
+ ```
115
+
116
+ Notes:
117
+
118
+ - `--wizard` guides profile/account + retailer selection in interactive terminals
119
+ - `--wizard` requires an interactive terminal (TTY); for CI/non-interactive runs use explicit flags (`--profile <name> --retailer <id|ref>` or `--profile <name> --all-retailers`)
120
+ - `--profile` reads from `~/.fluentcommerce/<profile>/`
121
+ - profile defaults are applied only when equivalent env vars are missing
122
+ - `--retailer` accepts retailer ID or retailer ref
123
+ - `--all-retailers` runs one full smoke cycle per retailer file in the profile
124
+ - `--skip-real-send` performs dry-run checks only
125
+ - with `--profile` only (no `--retailer` / `--all-retailers`), smoke may fall back to a retailer that has candidate orders for real-send validation; use `--retailer` for strict retailer scope
126
+
127
+ ### All-retailers run behavior
128
+
129
+ `--all-retailers` requires `--profile` and executes one smoke cycle per
130
+ `retailer.<ref>.json` file under `~/.fluentcommerce/<profile>/`. Output labels
131
+ use retailer IDs (for example `retailer-5`).
132
+
133
+ When a retailer has no candidate order, order lookup and real send steps are
134
+ reported as skipped for that retailer (not failed). The command exits non-zero
135
+ only when non-skipped steps fail.
136
+
137
+ #### Interpreting results
138
+
139
+ | Status | Meaning | Action |
140
+ |---|---|---|
141
+ | `PASS` | Step completed successfully for that retailer. | No action needed. |
142
+ | `SKIP` | Step was intentionally not executed (for example, no candidate order in `--all-retailers` mode). | Verify preconditions only if you expected execution. |
143
+ | `FAIL` | Step executed and returned an error or unexpected result. | Investigate payload/error details and fix before relying on run output. |
144
+
145
+ ## 5) E2E script reference
146
+
147
+ ### `e2e-smoke.ts`
148
+
149
+ Full MCP client smoke cycle that exercises the core tool chain end to end.
150
+
151
+ **What it tests:** tool discovery, `config.validate`, `health.ping`, `event.build`,
152
+ `event.send` (dry-run and real), `graphql.query`, `graphql.queryAll`, order lookup,
153
+ `event.get` by ID.
154
+
155
+ **How to run:**
156
+
157
+ ```bash
158
+ npm run e2e:smoke
159
+ npm run e2e:smoke -- --profile HMDEV
160
+ npm run e2e:smoke -- --profile HMDEV --retailer HM_TEST
161
+ npm run e2e:smoke -- --profile HMDEV --all-retailers
162
+ npm run e2e:smoke -- --skip-real-send
163
+ ```
164
+
165
+ **Environment:** requires `FLUENT_BASE_URL` + auth env vars, or `--profile`.
166
+
167
+ ---
168
+
169
+ ### `e2e-negative.ts`
170
+
171
+ Validates that the server returns correct error envelopes for invalid inputs.
172
+
173
+ **What it tests:**
174
+ - `event.send` with missing required `entityRef` field
175
+ - `graphql.query` with malformed GraphQL syntax
176
+ - `event.get` with a nonexistent event ID
177
+ - `graphql.introspect` with a nonexistent mutation name
178
+ - `webhook.validate` with an invalid signature and public key
179
+
180
+ Each test case asserts: `ok: false`, standard error envelope shape
181
+ (`code` + `message` + `retryable`), and expected error code where applicable
182
+ (e.g., `VALIDATION_ERROR`).
183
+
184
+ **How to run:**
185
+
186
+ ```bash
187
+ npx tsx scripts/e2e-negative.ts
188
+ npx tsx scripts/e2e-negative.ts --profile HMDEV
189
+ npx tsx scripts/e2e-negative.ts --profile HMDEV --retailer HM_TEST
190
+ ```
191
+
192
+ **Environment:** requires `FLUENT_BASE_URL` + auth env vars, or `--profile`.
193
+
194
+ ---
195
+
196
+ ### `e2e-profile-test.mjs`
197
+
198
+ Minimal FLUENT_PROFILE integration check. Loads config, creates SDK client, and
199
+ runs four quick API calls to verify profile-based auth works end to end.
200
+
201
+ **What it tests:**
202
+ 1. Config loads correctly with profile auth strategy
203
+ 2. GraphQL `me` query returns user and retailer info
204
+ 3. `getEvents` returns event results
205
+ 4. `getPlugins` returns the plugin registry
206
+ 5. `getTransitions` returns user actions for ORDER::HD at CREATED status
207
+
208
+ **How to run:**
209
+
210
+ ```bash
211
+ FLUENT_PROFILE=HMDEV FLUENT_PROFILE_RETAILER=HM_TEST node scripts/e2e-profile-test.mjs
212
+ ```
213
+
214
+ **Environment:** requires `FLUENT_PROFILE` and optionally `FLUENT_PROFILE_RETAILER`
215
+ set as env vars. Uses compiled `dist/` output directly (not MCP stdio transport).
216
+
217
+ ---
218
+
219
+ ### `e2e-full-order.ts`
220
+
221
+ Discovery-first single order lifecycle test. Creates an order with
222
+ `sourcingLocationRef`, then drives it through the full fulfilment workflow.
223
+
224
+ **What it tests:**
225
+ 1. Config validation
226
+ 2. Discovery: locations, catalogues, products, customers, settings
227
+ 3. `createOrder` mutation with sourcing attributes
228
+ 4. `ConfirmValidation` event on order
229
+ 5. Query order + fulfilments post-validation
230
+ 6. Fulfilment events: `ConfirmAllocation`, `CreateInvoice`, `ConfirmPick`,
231
+ `ConfirmShipment`, `ConfirmDelivery`
232
+ 7. Final order/fulfilment state verification
233
+ 8. Event trace: counts SUCCESS vs FAILED events for the order
234
+
235
+ **How to run:**
236
+
237
+ ```bash
238
+ npx tsx scripts/e2e-full-order.ts --profile SAGIRISH --retailer 2
239
+ FLUENT_PROFILE=SAGIRISH FLUENT_RETAILER_ID=2 npx tsx scripts/e2e-full-order.ts
240
+ ```
241
+
242
+ **Environment:** requires `FLUENT_PROFILE` + `FLUENT_RETAILER_ID`, or `--profile`
243
+ and `--retailer` flags. Environment must have warehouse locations, product
244
+ catalogue with active products, and at least one customer entity.
245
+
246
+ **Note:** this script creates real orders and fires real events. Use a sandbox.
247
+
248
+ ---
249
+
250
+ ### `e2e-full-multi.ts`
251
+
252
+ ORDER::MULTI lifecycle test. Similar to `e2e-full-order.ts` but targets the
253
+ MULTI order type workflow and includes workflow transition discovery.
254
+
255
+ **What it tests:**
256
+ 1. Config validation
257
+ 2. Discovery: retailer, locations, catalogues, products, customers, settings
258
+ 3. `workflow.transitions` for ORDER::MULTI at ON_VALIDATION status
259
+ 4. `createOrder` mutation with MULTI type
260
+ 5. `ConfirmValidation` event
261
+ 6. Fulfilment creation verification
262
+ 7. Fulfilment event sequence: `ConfirmAllocation`, `CreateInvoice`,
263
+ `ConfirmPick`, `ConfirmShipment`, `ConfirmDelivery`
264
+ 8. Event trace on failure
265
+
266
+ **How to run:**
267
+
268
+ ```bash
269
+ npx tsx scripts/e2e-full-multi.ts
270
+ ```
271
+
272
+ **Environment:** defaults to `FLUENT_BASE_URL=https://sagirish.test.api.fluentretail.com`
273
+ and `FLUENT_RETAILER_ID=2`. Override with env vars. Requires OAuth or profile
274
+ credentials.
275
+
276
+ **Note:** creates real orders and fires real events. Use a sandbox.
277
+
278
+ ---
279
+
280
+ ### `e2e-agentic.ts`
281
+
282
+ Comprehensive smoke test for all 11 agentic tools against a live environment.
283
+
284
+ **What it tests:**
285
+ - `environment.discover` (retailer + locations, catalogues)
286
+ - `environment.validate` (auth, retailer, locations checks)
287
+ - `entity.create` (dryRun validation, required field rejection, live creation)
288
+ - `entity.get` (by ID, by ref, nonexistent ref)
289
+ - `entity.update` (attribute update)
290
+ - `test.assert` (status match, status mismatch)
291
+ - `setting.upsert` (create new, update existing with previous value)
292
+ - `workflow.diff` (summary format, mermaid format)
293
+ - `workflow.simulate` (status matching, custom rule confidence, no-match)
294
+ - `workflow.upload` (dryRun structure validation)
295
+
296
+ **How to run:**
297
+
298
+ ```bash
299
+ npx tsx scripts/e2e-agentic.ts --profile SAGIRISH
300
+ ```
301
+
302
+ **Environment:** requires `FLUENT_PROFILE` or `--profile` flag. Defaults to
303
+ `FLUENT_RETAILER_ID=2`.
304
+
305
+ **Note:** creates real orders and settings. Use a sandbox.
306
+
307
+ ---
308
+
309
+ ### `e2e-response-shaping.ts`
310
+
311
+ Proves that response shaping (auto-summarization) works correctly on real API
312
+ responses.
313
+
314
+ **What it proves:**
315
+ 1. `event.flowInspect` compact response stays within budget (50k chars)
316
+ 2. `event.flowInspect` full mode (`compact: false`) gets auto-summarized, not truncated
317
+ 3. `plugin.list` (unfiltered, 500+ rules) gets shaped when large
318
+ 4. `event.list` (500 events) gets shaped when large
319
+ 5. `_responseMeta` appears with accurate stats (originalChars, shapedChars, reductionPercent)
320
+ 6. No `_truncated` markers anywhere (replaced by `_summarized`)
321
+
322
+ **How to run:**
323
+
324
+ ```bash
325
+ npx tsx scripts/e2e-response-shaping.ts --profile SAGIRISH
326
+ ```
327
+
328
+ **Environment:** requires `FLUENT_PROFILE` or `--profile`. Uses default budget
329
+ (50k chars). The environment must have enough events and plugins to exceed the
330
+ budget threshold.
331
+
332
+ ---
333
+
334
+ ### `run-flow-inspect.ts`
335
+
336
+ Advanced FlowInspect forensics runner. Calls `event.flowInspect`, then performs
337
+ local analysis: risk scoring, finding extraction, evidence ranking, and optional
338
+ per-event drilldowns via `event.get`.
339
+
340
+ **Outputs:**
341
+ - Raw JSON: full flowInspect response
342
+ - Summary JSON: risk score, status/entity distributions, anomaly counts, webhook
343
+ failure breakdown, exception classes, slow rulesets, mismatch reasons
344
+ - LLM packet (Markdown): compact handoff document for AI consumption
345
+ - Drilldown JSON: classified event details (webhook, mutation, sendEvent, mismatch)
346
+
347
+ **How to run:**
348
+
349
+ ```bash
350
+ npx tsx scripts/run-flow-inspect.ts <ROOT_ENTITY_REF> --profile SAGIRISH
351
+ npx tsx scripts/run-flow-inspect.ts HD-001 --root-type ORDER --profile HMDEV
352
+ npx tsx scripts/run-flow-inspect.ts HD-001 --root-id 12345 --light --no-drilldown
353
+ npx tsx scripts/run-flow-inspect.ts HD-001 --drilldown-classes webhook,mutation --drilldown-limit 20
354
+ ```
355
+
356
+ **Options:**
357
+
358
+ | Flag | Default | Description |
359
+ |------|---------|-------------|
360
+ | `--profile <name>` | `SAGIRISH` | Fluent CLI profile |
361
+ | `--root-type <type>` | any | Root entity type (ORDER, FULFILMENT, etc.) |
362
+ | `--root-id <id>` | - | Root entity ID for disambiguation |
363
+ | `--out <path>` | `accounts/<profile>/analysis/EVENT_FLOW_INSPECT_<ref>.json` | Raw output path |
364
+ | `--summary-out <path>` | `<base>.summary.json` | Summary output path |
365
+ | `--packet-out <path>` | `<base>.llm-packet.md` | LLM packet output path |
366
+ | `--drilldown-out <path>` | `<base>.drilldown.json` | Drilldown output path |
367
+ | `--light` | false | Compact mode (fewer audit details) |
368
+ | `--no-drilldown` | false | Skip per-event drilldowns |
369
+ | `--drilldown-limit <n>` | 15 | Max events to drill into |
370
+ | `--drilldown-classes <list>` | all | Comma-separated: webhook,mutation,sendEvent,mismatch,exception,scheduled,slow,status |
371
+ | `--top-n <n>` | 10 | Top-N items per section |
372
+ | `--evidence-limit <n>` | 20 | Max evidence event IDs |
373
+ | `--truncate <n>` | 220 | Max chars for text fields |
374
+ | `--max-pages <n>` | 10 | Max 500-event pages to fetch |
375
+ | `--no-llm-pack` | false | Skip LLM packet generation |
376
+
377
+ **Environment:** requires `FLUENT_PROFILE` or env vars.
378
+
379
+ ---
380
+
381
+ ### `order-event-inspect.ts`
382
+
383
+ Order-specific event deep dive. Looks up an order by ref, then fetches all
384
+ ORCHESTRATION and ORCHESTRATION_AUDIT events, classifying them into webhooks,
385
+ mutations, sendEvent actions, scheduled actions, and ruleset durations.
386
+
387
+ **Outputs:** a single JSON file with:
388
+ - Order entity details (status, attributes, fulfilment choices)
389
+ - Orchestration events (status counts, entity type counts, top event names)
390
+ - Audit trail (webhook actions, mutation actions, sendEvent actions, scheduled
391
+ actions, ruleset durations, exceptions)
392
+ - Drilldown details for NO_MATCH and PENDING events
393
+
394
+ **How to run:**
395
+
396
+ ```bash
397
+ npx tsx scripts/order-event-inspect.ts <ORDER_REF> --profile SAGIRISH
398
+ npx tsx scripts/order-event-inspect.ts HD-001 --profile HMDEV --out /tmp/inspect.json
399
+ ```
400
+
401
+ **Environment:** requires `FLUENT_PROFILE` or env vars.
402
+
403
+ ---
404
+
405
+ ### `check-event-status.ts`
406
+
407
+ Lightweight event status checker. Fetches events by ID or lists events by name
408
+ and entity ref.
409
+
410
+ **How to run:**
411
+
412
+ ```bash
413
+ # Fetch specific events by ID
414
+ npx tsx scripts/check-event-status.ts --event-id <uuid> --profile SAGIRISH
415
+
416
+ # List events by name
417
+ npx tsx scripts/check-event-status.ts --event-name ConfirmValidation --profile SAGIRISH
418
+
419
+ # List events by entity ref
420
+ npx tsx scripts/check-event-status.ts --entity-ref HD-001 --profile SAGIRISH
421
+
422
+ # Combine filters
423
+ npx tsx scripts/check-event-status.ts --event-name ConfirmValidation --entity-ref HD-001 --count 20 --profile SAGIRISH
424
+ ```
425
+
426
+ **Options:**
427
+
428
+ | Flag | Default | Description |
429
+ |------|---------|-------------|
430
+ | `--profile <name>` | - | Fluent CLI profile |
431
+ | `--retailer <id\|ref>` | from profile/env | Retailer filter |
432
+ | `--event-id <uuid>` | - | Fetch specific event (repeatable) |
433
+ | `--event-name <name>` | - | Event name filter for list |
434
+ | `--entity-ref <ref>` | - | Entity ref filter for list |
435
+ | `--count <n>` | 10 | Max list size (max 500) |
436
+
437
+ **Environment:** requires `FLUENT_BASE_URL` + auth, or `--profile`.
438
+
439
+ ## 6) MCP smoke sequence in your IDE
440
+
441
+ Run these tools in order from the assistant:
442
+
443
+ ### Step 1: `config.validate`
444
+
445
+ Request:
446
+
447
+ ```json
448
+ {}
449
+ ```
450
+
451
+ Expected:
452
+
453
+ - `ok: true`
454
+ - `validation.isReadyForApiCalls: true`
455
+ - no blocking errors
456
+
457
+ ### Step 2: `health.ping`
458
+
459
+ Request:
460
+
461
+ ```json
462
+ {}
463
+ ```
464
+
465
+ Expected:
466
+
467
+ - `status: "ok"`
468
+ - `sdkClient: "connected"` (or equivalent ready status)
469
+ - config summary present without secret values
470
+
471
+ ### Step 3: `connection.test`
472
+
473
+ Request:
474
+
475
+ ```json
476
+ {}
477
+ ```
478
+
479
+ Expected:
480
+
481
+ - `ok: true`
482
+ - `details` includes user, retailer, and location info
483
+ - confirms end-to-end GraphQL connectivity
484
+
485
+ ### Step 4: `event.build`
486
+
487
+ Request:
488
+
489
+ ```json
490
+ {
491
+ "name": "ORDER_CREATED",
492
+ "entityRef": "E2E-ORDER-1001",
493
+ "entityType": "ORDER",
494
+ "attributes": {
495
+ "sourceSystem": "e2e-test",
496
+ "testRunId": "run-001"
497
+ }
498
+ }
499
+ ```
500
+
501
+ Expected:
502
+
503
+ - `ok: true`
504
+ - complete event payload returned
505
+ - defaults applied (`rootEntityType`, `source`, `type`)
506
+
507
+ ### Step 5: `event.send` dry-run
508
+
509
+ Request:
510
+
511
+ ```json
512
+ {
513
+ "name": "ORDER_CREATED",
514
+ "entityRef": "E2E-ORDER-1001",
515
+ "entityId": "12345",
516
+ "entityType": "ORDER",
517
+ "dryRun": true
518
+ }
519
+ ```
520
+
521
+ Expected:
522
+
523
+ - `ok: true`
524
+ - `dryRun: true`
525
+ - no API call made
526
+
527
+ ### Step 6: `event.send` real call
528
+
529
+ Request:
530
+
531
+ ```json
532
+ {
533
+ "name": "ORDER_CREATED",
534
+ "entityRef": "E2E-ORDER-1001",
535
+ "entityId": "12345",
536
+ "entityType": "ORDER",
537
+ "retailerId": "YOUR_ENTITY_RETAILER_ID",
538
+ "mode": "async",
539
+ "dryRun": false,
540
+ "attributes": {
541
+ "sourceSystem": "e2e-test",
542
+ "testRunId": "run-001"
543
+ }
544
+ }
545
+ ```
546
+
547
+ Expected:
548
+
549
+ - `ok: true`
550
+ - response from Fluent event API
551
+ - retailer ID in payload matches the entity's retailer
552
+ - operation executed once (write tools do not auto-retry)
553
+
554
+ If this fails, investigate before resubmitting to avoid duplicate side effects.
555
+
556
+ ### Step 7: `graphql.query` verification
557
+
558
+ Use a query valid for your schema. Example shape:
559
+
560
+ ```json
561
+ {
562
+ "query": "query VerifyOrder($ref: String!) { orders(ref: $ref) { edges { node { id ref status } } } }",
563
+ "variables": {
564
+ "ref": "E2E-ORDER-1001"
565
+ }
566
+ }
567
+ ```
568
+
569
+ Expected:
570
+
571
+ - `ok: true`
572
+ - Fluent GraphQL response payload returned
573
+
574
+ > Note: exact query fields differ by tenant schema/version.
575
+
576
+ ### Step 8: `graphql.queryAll` verification
577
+
578
+ Validate auto-pagination and run-level timeout behavior:
579
+
580
+ ```json
581
+ {
582
+ "query": "query($cursor: String) { orders(first: 50, after: $cursor) { edges { cursor node { id ref } } pageInfo { hasNextPage } } }",
583
+ "variables": { "cursor": null },
584
+ "timeoutMs": 120000
585
+ }
586
+ ```
587
+
588
+ Expected:
589
+
590
+ - `ok: true`
591
+ - `response.extensions.autoPagination` present
592
+ - `pagination.totalPages` / `pagination.totalRecords` present in tool response
593
+
594
+ ### Step 9: `event.get` verification
595
+
596
+ Use an event ID from successful `event.send` (or resolve ID via `event.list`).
597
+
598
+ ```json
599
+ {
600
+ "eventId": "EVENT_ID_FROM_SEND_RESPONSE"
601
+ }
602
+ ```
603
+
604
+ Expected:
605
+
606
+ - `ok: true`
607
+ - event payload returned from Event API
608
+ - event payload includes expected `name`, `context.entityRef`, and retailer
609
+
610
+ If `event.send` response does not contain `id`, resolve it first with `event.list`:
611
+
612
+ ```json
613
+ {
614
+ "name": "ORDER_CREATED",
615
+ "entityRef": "E2E-ORDER-1001",
616
+ "retailerId": "YOUR_ENTITY_RETAILER_ID",
617
+ "count": 1,
618
+ "start": 1
619
+ }
620
+ ```
621
+
622
+ ### Step 10 (optional): `graphql.introspect`
623
+
624
+ Verify schema introspection access:
625
+
626
+ ```json
627
+ {
628
+ "listMutations": true
629
+ }
630
+ ```
631
+
632
+ Expected:
633
+
634
+ - `ok: true`
635
+ - `mutations` array returned
636
+ - `count` reflects available mutations in the schema
637
+
638
+ ## 7) Optional webhook signature validation
639
+
640
+ Use this when validating webhook integration cryptography:
641
+
642
+ ```json
643
+ {
644
+ "payload": {
645
+ "name": "OrderCreated",
646
+ "id": "event-123",
647
+ "retailerId": "5"
648
+ },
649
+ "rawBody": "{\"name\":\"OrderCreated\",\"id\":\"event-123\",\"retailerId\":\"5\"}",
650
+ "signature": "base64-encoded-signature-from-header",
651
+ "publicKey": "-----BEGIN PUBLIC KEY-----\nMIIBI...\n-----END PUBLIC KEY-----"
652
+ }
653
+ ```
654
+
655
+ Expected:
656
+
657
+ - `ok: true` when payload and signature are valid
658
+ - `signatureValidation.payloadSource` confirms whether `rawBody` was used
659
+
660
+ ## 8) Optional batch E2E
661
+
662
+ ### `batch.create`
663
+
664
+ ```json
665
+ {
666
+ "name": "e2e-batch-001",
667
+ "entityType": "INVENTORY_POSITION",
668
+ "action": "UPSERT"
669
+ }
670
+ ```
671
+
672
+ ### `batch.send`
673
+
674
+ ```json
675
+ {
676
+ "jobId": "JOB_ID_FROM_CREATE",
677
+ "payload": {
678
+ "action": "UPSERT",
679
+ "entityType": "INVENTORY_POSITION",
680
+ "entities": [
681
+ {
682
+ "locationRef": "LOC-1",
683
+ "skuRef": "SKU-1",
684
+ "qty": 10,
685
+ "type": "LAST_ON_HAND",
686
+ "status": "ACTIVE"
687
+ }
688
+ ]
689
+ }
690
+ }
691
+ ```
692
+
693
+ ### `batch.status`
694
+
695
+ ```json
696
+ {
697
+ "jobId": "JOB_ID_FROM_CREATE"
698
+ }
699
+ ```
700
+
701
+ ## 9) Failure triage quick map
702
+
703
+ - `CONFIG_ERROR`: missing env values -> run `config.validate`
704
+ - `AUTH_ERROR`: invalid creds/token -> verify OAuth or `TOKEN_COMMAND`
705
+ - `VALIDATION_ERROR`: invalid input shape -> fix request payload
706
+ - `TIMEOUT_ERROR` / `NETWORK_ERROR`: tune timeout/retry; verify connectivity
707
+ - `RATE_LIMIT` / `UPSTREAM_UNAVAILABLE`: retry later (built-in retries are applied for read operations; write retries are manual by design)
708
+
709
+ ## 10) Exit criteria for successful E2E
710
+
711
+ Mark E2E successful only when:
712
+
713
+ - local build/test passed
714
+ - config/health/connection tools are green
715
+ - at least one real `event.send` succeeded
716
+ - at least one `event.get` succeeded
717
+ - at least one real `graphql.query` succeeded
718
+ - at least one `graphql.queryAll` run succeeded
719
+ - (optional) `graphql.introspect` schema access confirmed
720
+ - (optional) batch cycle completed
721
+
722
+ ## 11) CI-triggered smoke (GitHub Actions)
723
+
724
+ Workflow: `.github/workflows/ci.yml`
725
+
726
+ - `build-and-test`: runs on push and pull request
727
+ - `e2e-smoke`: runs on `workflow_dispatch` only
728
+
729
+ Set repository or environment secrets before running `e2e-smoke`:
730
+
731
+ - `FLUENT_BASE_URL`
732
+ - `FLUENT_RETAILER_ID` (optional but recommended)
733
+ - `FLUENT_CLIENT_ID`
734
+ - `FLUENT_CLIENT_SECRET`
735
+ - `FLUENT_USERNAME` (optional for client-credentials flow)
736
+ - `FLUENT_PASSWORD` (optional for client-credentials flow)
737
+
738
+ CI smoke runs are typically env-driven (no `--profile` required). For profile-based runs in CI, pass explicit flags (`--profile <name> --retailer <id|ref>` or `--profile <name> --all-retailers`).
739
+ If real event side effects are not desired, run with `--skip-real-send`.