@aura-labs-ai/scout 0.2.0

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.
@@ -0,0 +1,514 @@
1
+ /**
2
+ * End-to-End Integration Scenario Tests
3
+ *
4
+ * Tests that verify MCP, AP2, and Visa TAP work together in realistic
5
+ * agentic commerce flows.
6
+ *
7
+ * Run:
8
+ * node --test src/tests/scenarios/integration-scenarios.test.js
9
+ */
10
+
11
+ import { test, describe } from 'node:test';
12
+ import assert from 'node:assert';
13
+ import { MCPClient } from '../../mcp/client.js';
14
+ import { AP2Mandates, generateKeyPair as generateAP2KeyPair } from '../../ap2/mandates.js';
15
+ import { VisaTAP } from '../../tap/visa.js';
16
+
17
+ // =============================================================================
18
+ // Test Setup
19
+ // =============================================================================
20
+
21
+ const userKeys = generateAP2KeyPair();
22
+ const agentKeyPair = VisaTAP.generateKeyPair();
23
+
24
+ // =============================================================================
25
+ // Scenario 1: Complete Shopping Flow with All Protocols
26
+ // =============================================================================
27
+
28
+ describe('Scenario: Complete Agentic Shopping Flow', () => {
29
+ // Shared state across test steps
30
+ let mcpClient;
31
+ let tapRegistration;
32
+ let tapCredentials;
33
+ let intentMandate;
34
+ let cartMandate;
35
+ let paymentMandate;
36
+ let signedPaymentRequest;
37
+
38
+ test('Step 1: Initialize MCP client for external context', () => {
39
+ mcpClient = new MCPClient({
40
+ clientInfo: {
41
+ name: 'shopping-scout',
42
+ version: '1.0.0',
43
+ },
44
+ });
45
+
46
+ assert.strictEqual(mcpClient.isConnected, false);
47
+ assert.ok(mcpClient.capabilities.tools);
48
+ });
49
+
50
+ test('Step 2: Register agent with Visa TAP', async () => {
51
+ tapRegistration = await VisaTAP.register({
52
+ agentId: 'integrated-shopping-scout',
53
+ publicKey: agentKeyPair.publicKey,
54
+ metadata: {
55
+ name: 'Integrated Shopping Scout',
56
+ operator: 'AURA Labs',
57
+ capabilities: ['shopping', 'comparison', 'checkout', 'payments'],
58
+ },
59
+ });
60
+
61
+ tapCredentials = VisaTAP.createCredentials({
62
+ tapId: tapRegistration.tapId,
63
+ keyPair: agentKeyPair,
64
+ });
65
+
66
+ assert.ok(tapRegistration.tapId);
67
+ assert.strictEqual(tapRegistration.status, 'active');
68
+ assert.ok(tapCredentials.keyId);
69
+ });
70
+
71
+ test('Step 3: User creates intent mandate authorizing agent', async () => {
72
+ intentMandate = await AP2Mandates.createIntent({
73
+ agentId: tapRegistration.tapId, // Use TAP ID as agent identifier
74
+ userId: 'user-integrated-001',
75
+ userKey: userKeys.privateKey,
76
+ constraints: {
77
+ maxAmount: 1000,
78
+ currency: 'USD',
79
+ categories: ['electronics', 'office-supplies'],
80
+ validUntil: new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString(),
81
+ },
82
+ metadata: {
83
+ purpose: 'Work laptop accessories',
84
+ tapRegistration: tapRegistration.tapId,
85
+ },
86
+ });
87
+
88
+ assert.strictEqual(intentMandate.type, 'intent');
89
+ assert.strictEqual(intentMandate.subject.id, tapRegistration.tapId);
90
+ assert.ok(intentMandate.proof.proofValue);
91
+ });
92
+
93
+ test('Step 4: Agent discovers offer and validates against mandate', () => {
94
+ // Simulate offer from a Beacon
95
+ const offer = {
96
+ id: 'offer-int-001',
97
+ beaconId: 'beacon-office-depot',
98
+ beaconName: 'Office Depot',
99
+ product: { name: 'USB-C Hub', sku: 'USB-HUB-7P' },
100
+ unitPrice: 49.99,
101
+ quantity: 2,
102
+ totalPrice: 99.98,
103
+ currency: 'USD',
104
+ deliveryDate: '2026-02-20',
105
+ };
106
+
107
+ const validation = AP2Mandates.validateIntentCoverage(intentMandate, {
108
+ totalAmount: offer.totalPrice,
109
+ currency: offer.currency,
110
+ category: 'electronics',
111
+ merchantId: offer.beaconId,
112
+ });
113
+
114
+ assert.strictEqual(validation.valid, true);
115
+ assert.strictEqual(validation.errors.length, 0);
116
+ });
117
+
118
+ test('Step 5: User approves purchase with cart mandate', async () => {
119
+ const offer = {
120
+ id: 'offer-int-001',
121
+ beaconId: 'beacon-office-depot',
122
+ beaconName: 'Office Depot',
123
+ product: { name: 'USB-C Hub', sku: 'USB-HUB-7P' },
124
+ unitPrice: 49.99,
125
+ quantity: 2,
126
+ totalPrice: 99.98,
127
+ currency: 'USD',
128
+ deliveryDate: '2026-02-20',
129
+ };
130
+
131
+ cartMandate = await AP2Mandates.createCart({
132
+ sessionId: 'session-integrated-001',
133
+ offer,
134
+ userKey: userKeys.privateKey,
135
+ userId: 'user-integrated-001',
136
+ intentMandateId: intentMandate.id,
137
+ });
138
+
139
+ assert.strictEqual(cartMandate.type, 'cart');
140
+ assert.strictEqual(cartMandate.intentMandateRef, intentMandate.id);
141
+ assert.strictEqual(cartMandate.cart.totalAmount, 99.98);
142
+ });
143
+
144
+ test('Step 6: Agent creates payment mandate with TAP credentials', async () => {
145
+ paymentMandate = await AP2Mandates.createPayment({
146
+ cartMandate,
147
+ paymentMethod: { type: 'card', network: 'visa' },
148
+ agentId: tapRegistration.tapId,
149
+ agentKey: agentKeyPair.privateKey,
150
+ tapCredentials: {
151
+ tapId: tapRegistration.tapId,
152
+ },
153
+ });
154
+
155
+ assert.strictEqual(paymentMandate.type, 'payment');
156
+ assert.strictEqual(paymentMandate.cartMandateRef, cartMandate.id);
157
+ assert.strictEqual(paymentMandate.agent.tapId, tapRegistration.tapId);
158
+ assert.strictEqual(paymentMandate.paymentMethod.network, 'visa');
159
+ assert.ok(paymentMandate.riskSignals.intentMandatePresent);
160
+ });
161
+
162
+ test('Step 7: Sign payment request with Visa TAP', async () => {
163
+ const paymentRequest = {
164
+ method: 'POST',
165
+ url: 'https://payments.visa.com/api/v1/process',
166
+ headers: {
167
+ 'Content-Type': 'application/json',
168
+ },
169
+ body: {
170
+ amount: paymentMandate.transaction.amount,
171
+ currency: paymentMandate.transaction.currency,
172
+ merchantId: paymentMandate.transaction.merchantId,
173
+ mandateChain: {
174
+ paymentMandateId: paymentMandate.id,
175
+ cartMandateId: paymentMandate.cartMandateRef,
176
+ intentMandateRef: cartMandate.intentMandateRef,
177
+ },
178
+ },
179
+ };
180
+
181
+ signedPaymentRequest = await VisaTAP.signRequest(paymentRequest, tapCredentials);
182
+
183
+ assert.strictEqual(signedPaymentRequest.headers['X-TAP-Agent-Id'], tapCredentials.tapId);
184
+ assert.ok(signedPaymentRequest.headers['X-TAP-Timestamp']);
185
+ assert.ok(signedPaymentRequest.headers['X-TAP-Nonce']);
186
+ assert.ok(signedPaymentRequest.headers['Signature']);
187
+ });
188
+
189
+ test('Step 8: Verify complete audit trail', () => {
190
+ // Intent -> Cart -> Payment chain
191
+ assert.strictEqual(cartMandate.intentMandateRef, intentMandate.id);
192
+ assert.strictEqual(paymentMandate.cartMandateRef, cartMandate.id);
193
+
194
+ // TAP identity flows through
195
+ assert.strictEqual(intentMandate.subject.id, tapRegistration.tapId);
196
+ assert.strictEqual(paymentMandate.agent.tapId, tapRegistration.tapId);
197
+ assert.strictEqual(signedPaymentRequest.headers['X-TAP-Agent-Id'], tapRegistration.tapId);
198
+
199
+ // All cryptographic proofs present
200
+ assert.ok(intentMandate.proof.proofValue);
201
+ assert.ok(cartMandate.proof.proofValue);
202
+ assert.ok(paymentMandate.proof.proofValue);
203
+ assert.ok(signedPaymentRequest.headers['Signature']);
204
+ });
205
+ });
206
+
207
+ // =============================================================================
208
+ // Scenario 2: Multi-Offer Comparison with Constraints
209
+ // =============================================================================
210
+
211
+ describe('Scenario: Multi-Offer Comparison with Mandate Constraints', () => {
212
+ let intentMandate;
213
+
214
+ test('Setup: Create restrictive intent mandate', async () => {
215
+ intentMandate = await AP2Mandates.createIntent({
216
+ agentId: 'comparison-scout',
217
+ userId: 'user-comparison-001',
218
+ userKey: userKeys.privateKey,
219
+ constraints: {
220
+ maxAmount: 200,
221
+ currency: 'USD',
222
+ categories: ['office-supplies'],
223
+ merchantBlocklist: ['sketchy-vendor'],
224
+ },
225
+ });
226
+ });
227
+
228
+ test('Agent filters multiple offers against mandate', () => {
229
+ const offers = [
230
+ {
231
+ id: 'offer-1',
232
+ beaconId: 'good-vendor',
233
+ totalPrice: 150,
234
+ currency: 'USD',
235
+ category: 'office-supplies',
236
+ },
237
+ {
238
+ id: 'offer-2',
239
+ beaconId: 'expensive-vendor',
240
+ totalPrice: 300, // Over budget
241
+ currency: 'USD',
242
+ category: 'office-supplies',
243
+ },
244
+ {
245
+ id: 'offer-3',
246
+ beaconId: 'sketchy-vendor', // Blocklisted
247
+ totalPrice: 100,
248
+ currency: 'USD',
249
+ category: 'office-supplies',
250
+ },
251
+ {
252
+ id: 'offer-4',
253
+ beaconId: 'electronics-store',
254
+ totalPrice: 180,
255
+ currency: 'USD',
256
+ category: 'electronics', // Wrong category
257
+ },
258
+ {
259
+ id: 'offer-5',
260
+ beaconId: 'euro-store',
261
+ totalPrice: 100,
262
+ currency: 'EUR', // Wrong currency
263
+ category: 'office-supplies',
264
+ },
265
+ ];
266
+
267
+ const validOffers = offers.filter(offer => {
268
+ const validation = AP2Mandates.validateIntentCoverage(intentMandate, {
269
+ totalAmount: offer.totalPrice,
270
+ currency: offer.currency,
271
+ category: offer.category,
272
+ merchantId: offer.beaconId,
273
+ });
274
+ return validation.valid;
275
+ });
276
+
277
+ assert.strictEqual(validOffers.length, 1);
278
+ assert.strictEqual(validOffers[0].id, 'offer-1');
279
+ });
280
+ });
281
+
282
+ // =============================================================================
283
+ // Scenario 3: Payment Network Verification
284
+ // =============================================================================
285
+
286
+ describe('Scenario: Payment Network Verifies Agent Identity', () => {
287
+ test('Payment network can verify TAP-signed request', async () => {
288
+ // Agent registration
289
+ const agentKeyPair = VisaTAP.generateKeyPair();
290
+ const registration = await VisaTAP.register({
291
+ agentId: 'payment-verification-agent',
292
+ publicKey: agentKeyPair.publicKey,
293
+ });
294
+
295
+ const credentials = VisaTAP.createCredentials({
296
+ tapId: registration.tapId,
297
+ keyPair: agentKeyPair,
298
+ });
299
+
300
+ // Agent signs a request
301
+ const request = {
302
+ method: 'POST',
303
+ url: 'https://payments.example.com/process',
304
+ body: { amount: 100, currency: 'USD' },
305
+ };
306
+
307
+ const signedRequest = await VisaTAP.signRequest(request, credentials);
308
+
309
+ // Payment network verifies
310
+ const verifyRequest = {
311
+ method: signedRequest.method,
312
+ url: signedRequest.url,
313
+ body: signedRequest.body,
314
+ headers: {
315
+ 'x-tap-agent-id': signedRequest.headers['X-TAP-Agent-Id'],
316
+ 'x-tap-timestamp': signedRequest.headers['X-TAP-Timestamp'],
317
+ 'x-tap-nonce': signedRequest.headers['X-TAP-Nonce'],
318
+ 'signature': signedRequest.headers['Signature'],
319
+ },
320
+ };
321
+
322
+ const publicKeyLookup = async (tapId) => {
323
+ if (tapId === registration.tapId) return agentKeyPair.publicKey;
324
+ throw new Error('Agent not found');
325
+ };
326
+
327
+ const result = await VisaTAP.verifyRequest(verifyRequest, publicKeyLookup);
328
+
329
+ // Verification result should have valid property
330
+ assert.ok(result.valid !== undefined);
331
+ });
332
+ });
333
+
334
+ // =============================================================================
335
+ // Scenario 4: MCP Context Enriches Intent
336
+ // =============================================================================
337
+
338
+ describe('Scenario: MCP Context Enriches Shopping Intent', () => {
339
+ test('Agent aggregates context before shopping', async () => {
340
+ const mcp = new MCPClient();
341
+
342
+ // In real scenario, agent would connect to context servers
343
+ // For testing, we verify the pattern works
344
+
345
+ const context = await mcp.aggregateContext({
346
+ includeResources: true,
347
+ resourcePatterns: ['calendar://', 'preferences://'],
348
+ });
349
+
350
+ // Context would inform intent creation
351
+ const enhancedIntent = {
352
+ rawIntent: 'I need a laptop for my upcoming trip',
353
+ context: {
354
+ availableTools: context.tools,
355
+ userResources: context.resources,
356
+ // In real scenario: calendar shows trip dates, preferences show past purchases
357
+ },
358
+ };
359
+
360
+ assert.ok(enhancedIntent.context);
361
+ assert.ok(Array.isArray(enhancedIntent.context.availableTools));
362
+ });
363
+ });
364
+
365
+ // =============================================================================
366
+ // Scenario 5: Error Recovery Flow
367
+ // =============================================================================
368
+
369
+ describe('Scenario: Error Recovery in Protocol Chain', () => {
370
+ test('Agent handles mandate validation failure gracefully', async () => {
371
+ const intentMandate = await AP2Mandates.createIntent({
372
+ agentId: 'error-recovery-agent',
373
+ userId: 'user-error-001',
374
+ userKey: userKeys.privateKey,
375
+ constraints: {
376
+ maxAmount: 100,
377
+ currency: 'USD',
378
+ },
379
+ });
380
+
381
+ // Offer exceeds budget
382
+ const expensiveOffer = {
383
+ totalAmount: 500,
384
+ currency: 'USD',
385
+ };
386
+
387
+ const validation = AP2Mandates.validateIntentCoverage(intentMandate, expensiveOffer);
388
+
389
+ // Agent should handle this gracefully
390
+ if (!validation.valid) {
391
+ // Agent could:
392
+ // 1. Request new intent mandate with higher budget
393
+ // 2. Search for cheaper alternatives
394
+ // 3. Inform user of constraint violation
395
+
396
+ const errorReport = {
397
+ type: 'constraint_violation',
398
+ violations: validation.errors,
399
+ suggestedAction: 'find_cheaper_alternative',
400
+ };
401
+
402
+ assert.strictEqual(errorReport.type, 'constraint_violation');
403
+ assert.ok(errorReport.violations.length > 0);
404
+ }
405
+ });
406
+
407
+ test('Agent handles TAP verification failure', async () => {
408
+ // Missing TAP headers
409
+ const badRequest = {
410
+ method: 'POST',
411
+ url: 'https://payments.example.com/process',
412
+ headers: {},
413
+ body: { amount: 100 },
414
+ };
415
+
416
+ const result = await VisaTAP.verifyRequest(badRequest, async () => null);
417
+
418
+ // Should fail gracefully
419
+ assert.strictEqual(result.valid, false);
420
+ assert.ok(result.error);
421
+ });
422
+ });
423
+
424
+ // =============================================================================
425
+ // Scenario 6: Time-Sensitive Transaction
426
+ // =============================================================================
427
+
428
+ describe('Scenario: Time-Sensitive Transaction Flow', () => {
429
+ test('Complete flow within mandate validity window', async () => {
430
+ // Short-lived intent mandate (1 hour)
431
+ const intentMandate = await AP2Mandates.createIntent({
432
+ agentId: 'time-sensitive-agent',
433
+ userId: 'user-time-001',
434
+ userKey: userKeys.privateKey,
435
+ constraints: {
436
+ maxAmount: 500,
437
+ validUntil: new Date(Date.now() + 60 * 60 * 1000).toISOString(), // 1 hour
438
+ },
439
+ });
440
+
441
+ // Immediate validation passes
442
+ const validation = AP2Mandates.validateIntentCoverage(intentMandate, {
443
+ totalAmount: 100,
444
+ });
445
+ assert.strictEqual(validation.valid, true);
446
+
447
+ // Cart mandate has 30 minute expiry
448
+ const cartMandate = await AP2Mandates.createCart({
449
+ sessionId: 'session-time-001',
450
+ offer: {
451
+ id: 'offer-time-001',
452
+ beaconId: 'fast-vendor',
453
+ beaconName: 'Fast Vendor',
454
+ product: { name: 'Quick Item' },
455
+ unitPrice: 100,
456
+ quantity: 1,
457
+ totalPrice: 100,
458
+ currency: 'USD',
459
+ },
460
+ userKey: userKeys.privateKey,
461
+ userId: 'user-time-001',
462
+ intentMandateId: intentMandate.id,
463
+ });
464
+
465
+ const expiresAt = new Date(cartMandate.expiresAt);
466
+ const now = new Date();
467
+ const minutesUntilExpiry = (expiresAt - now) / (1000 * 60);
468
+
469
+ assert.ok(minutesUntilExpiry > 25 && minutesUntilExpiry <= 31);
470
+ });
471
+ });
472
+
473
+ // =============================================================================
474
+ // Scenario 7: Agent Capabilities Advertisement
475
+ // =============================================================================
476
+
477
+ describe('Scenario: Agent Capabilities for Protocol Negotiation', () => {
478
+ test('Agent advertises supported protocols', async () => {
479
+ const agentCapabilities = {
480
+ mcp: {
481
+ version: '2024-11-05',
482
+ clientInfo: { name: 'aura-scout', version: '1.0.0' },
483
+ supportedFeatures: ['tools', 'resources', 'prompts'],
484
+ },
485
+ ap2: {
486
+ version: '1.0',
487
+ supportedMandateTypes: ['intent', 'cart', 'payment'],
488
+ supportedConstraints: [
489
+ 'maxAmount',
490
+ 'currency',
491
+ 'categories',
492
+ 'merchantAllowlist',
493
+ 'merchantBlocklist',
494
+ 'validUntil',
495
+ ],
496
+ },
497
+ tap: {
498
+ version: '1.0',
499
+ algorithm: 'ed25519',
500
+ signatureFormat: 'http-message-signatures',
501
+ },
502
+ };
503
+
504
+ // Verify all protocols represented
505
+ assert.ok(agentCapabilities.mcp);
506
+ assert.ok(agentCapabilities.ap2);
507
+ assert.ok(agentCapabilities.tap);
508
+
509
+ // Verify versions
510
+ assert.strictEqual(agentCapabilities.mcp.version, '2024-11-05');
511
+ assert.strictEqual(agentCapabilities.ap2.version, '1.0');
512
+ assert.strictEqual(agentCapabilities.tap.algorithm, 'ed25519');
513
+ });
514
+ });