Skip to Content
🚀Agentic Commerce Protocol is now live! Instant Checkout is available in ChatGPT. Learn more →
DocumentationStep-by-Step TutorialsTesting Your Integration

Testing Your Integration

Thorough testing ensures your ACP implementation works correctly and handles edge cases. This guide covers testing strategies, tools, and common scenarios.

Testing Strategy

┌─────────────────────────────────────────────────────────┐ │ Testing Pyramid │ ├─────────────────────────────────────────────────────────┤ │ │ │ ┌───────────┐ │ │ │ E2E │ ← Fewer, slower │ │ ┌─┴───────────┴─┐ │ │ │ Integration │ │ │ ┌─┴───────────────┴─┐ │ │ │ Unit Tests │ ← More, faster │ │ └───────────────────┘ │ │ │ └─────────────────────────────────────────────────────────┘

Unit Tests

Testing Cart Calculations

// test/unit/cartService.test.js const CartService = require('../../services/cartService'); describe('CartService', () => { let cartService; beforeEach(() => { cartService = new CartService(); }); describe('calculateShipping', () => { test('free shipping over $50', () => { const shipping = cartService.calculateShipping(5500, { country: 'US' }); expect(shipping).toBe(0); }); test('standard domestic shipping under $50', () => { const shipping = cartService.calculateShipping(4000, { country: 'US' }); expect(shipping).toBe(500); }); test('international shipping', () => { const shipping = cartService.calculateShipping(4000, { country: 'CA' }); expect(shipping).toBe(2500); }); }); describe('calculateTax', () => { test('California tax rate', () => { const tax = cartService.calculateTax(10000, { country: 'US', state: 'CA' }); expect(tax).toBe(725); // 7.25% }); test('no tax for missing address', () => { const tax = cartService.calculateTax(10000, null); expect(tax).toBe(0); }); }); });

Testing Session Service

// test/unit/sessionService.test.js const SessionService = require('../../services/sessionService'); describe('SessionService', () => { describe('generateSessionId', () => { test('generates valid format', () => { const service = new SessionService(); const id = service.generateSessionId(); expect(id).toMatch(/^cs_[a-f0-9]{32}$/); }); test('generates unique IDs', () => { const service = new SessionService(); const ids = new Set(); for (let i = 0; i < 1000; i++) { ids.add(service.generateSessionId()); } expect(ids.size).toBe(1000); }); }); });

Testing Validation

// test/unit/validation.test.js const { validateCreateSession } = require('../../middleware/validation'); describe('validateCreateSession', () => { let req, res, next; beforeEach(() => { req = { body: {} }; res = { status: jest.fn().mockReturnThis(), json: jest.fn() }; next = jest.fn(); }); test('rejects missing cart', () => { validateCreateSession(req, res, next); expect(res.status).toHaveBeenCalledWith(400); expect(next).not.toHaveBeenCalled(); }); test('rejects empty cart', () => { req.body = { cart: { items: [] } }; validateCreateSession(req, res, next); expect(res.status).toHaveBeenCalledWith(400); }); test('accepts valid cart', () => { req.body = { cart: { items: [{ product_id: 'prod_001', quantity: 1 }] } }; validateCreateSession(req, res, next); expect(next).toHaveBeenCalled(); }); });

Integration Tests

Testing API Endpoints

// test/integration/checkout.test.js const request = require('supertest'); const app = require('../../app'); const mongoose = require('mongoose'); describe('Checkout API', () => { beforeAll(async () => { await mongoose.connect(process.env.TEST_DATABASE_URL); }); afterAll(async () => { await mongoose.connection.close(); }); beforeEach(async () => { // Clear test data await mongoose.connection.db.dropDatabase(); }); describe('POST /acp/v1/checkout_sessions', () => { test('creates session successfully', async () => { const res = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: 'prod_001', quantity: 2 }] } }); expect(res.status).toBe(201); expect(res.body.checkout_session_id).toMatch(/^cs_/); expect(res.body.state).toBe('not_ready_for_payment'); expect(res.body.cart.items).toHaveLength(1); expect(res.body.totals.subtotal).toBe(5998); }); test('creates ready session with address', async () => { const res = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: 'prod_001', quantity: 1 }] }, buyer_context: { email: 'test@example.com', shipping_address: { line1: '123 Main St', city: 'San Francisco', state: 'CA', postal_code: '94102', country: 'US' } } }); expect(res.status).toBe(201); expect(res.body.state).toBe('ready_for_payment'); }); test('rejects invalid product', async () => { const res = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: 'invalid_product', quantity: 1 }] } }); expect(res.status).toBe(400); expect(res.body.error).toBe('Cart validation failed'); }); }); describe('GET /acp/v1/checkout_sessions/:id', () => { test('retrieves existing session', async () => { // Create session const createRes = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: 'prod_001', quantity: 1 }] } }); const sessionId = createRes.body.checkout_session_id; // Retrieve session const getRes = await request(app) .get(`/acp/v1/checkout_sessions/${sessionId}`); expect(getRes.status).toBe(200); expect(getRes.body.checkout_session_id).toBe(sessionId); }); test('returns 404 for unknown session', async () => { const res = await request(app) .get('/acp/v1/checkout_sessions/cs_nonexistent'); expect(res.status).toBe(404); }); }); describe('PATCH /acp/v1/checkout_sessions/:id', () => { test('updates buyer context', async () => { // Create session const createRes = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: 'prod_001', quantity: 1 }] } }); const sessionId = createRes.body.checkout_session_id; // Update session const updateRes = await request(app) .patch(`/acp/v1/checkout_sessions/${sessionId}`) .send({ buyer_context: { email: 'customer@example.com', shipping_address: { line1: '456 Oak Ave', city: 'Los Angeles', state: 'CA', postal_code: '90001', country: 'US' } } }); expect(updateRes.status).toBe(200); expect(updateRes.body.state).toBe('ready_for_payment'); expect(updateRes.body.buyer_context.email).toBe('customer@example.com'); }); test('recalculates totals on address change', async () => { // Create session without address const createRes = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: 'prod_001', quantity: 1 }] } }); const initialTotal = createRes.body.totals.total; const sessionId = createRes.body.checkout_session_id; // Add address (triggers tax calculation) const updateRes = await request(app) .patch(`/acp/v1/checkout_sessions/${sessionId}`) .send({ buyer_context: { shipping_address: { line1: '123 Main St', city: 'San Francisco', state: 'CA', postal_code: '94102', country: 'US' } } }); expect(updateRes.body.totals.tax).toBeGreaterThan(0); expect(updateRes.body.totals.total).toBeGreaterThan(initialTotal); }); }); describe('POST /acp/v1/checkout_sessions/:id/complete', () => { let sessionId; beforeEach(async () => { // Create ready session const res = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: 'prod_001', quantity: 1 }] }, buyer_context: { email: 'test@example.com', shipping_address: { line1: '123 Main St', city: 'San Francisco', state: 'CA', postal_code: '94102', country: 'US' } } }); sessionId = res.body.checkout_session_id; }); test('completes with valid payment token', async () => { const res = await request(app) .post(`/acp/v1/checkout_sessions/${sessionId}/complete`) .send({ payment_token: 'pm_card_visa' }); expect(res.status).toBe(200); expect(res.body.success).toBe(true); expect(res.body.order_id).toMatch(/^ord_/); expect(res.body.session.state).toBe('completed'); }); test('rejects missing payment token', async () => { const res = await request(app) .post(`/acp/v1/checkout_sessions/${sessionId}/complete`) .send({}); expect(res.status).toBe(400); }); test('prevents double completion', async () => { // Complete first time await request(app) .post(`/acp/v1/checkout_sessions/${sessionId}/complete`) .send({ payment_token: 'pm_card_visa' }); // Try to complete again const res = await request(app) .post(`/acp/v1/checkout_sessions/${sessionId}/complete`) .send({ payment_token: 'pm_card_visa' }); expect(res.status).toBe(400); expect(res.body.current_state).toBe('completed'); }); }); });

End-to-End Tests

Full Purchase Flow

// test/e2e/purchase.test.js const request = require('supertest'); const app = require('../../app'); describe('Complete Purchase Flow', () => { test('user can browse, add to cart, and checkout', async () => { // 1. Browse products const productsRes = await request(app) .get('/acp/v1/products?search=t-shirt'); expect(productsRes.status).toBe(200); expect(productsRes.body.products.length).toBeGreaterThan(0); const product = productsRes.body.products[0]; // 2. Create checkout session const createRes = await request(app) .post('/acp/v1/checkout_sessions') .send({ cart: { items: [{ product_id: product.id, quantity: 2 }] } }); expect(createRes.status).toBe(201); const sessionId = createRes.body.checkout_session_id; // 3. Add shipping info const updateRes = await request(app) .patch(`/acp/v1/checkout_sessions/${sessionId}`) .send({ buyer_context: { email: 'customer@example.com', shipping_address: { line1: '123 Main St', city: 'San Francisco', state: 'CA', postal_code: '94102', country: 'US' } } }); expect(updateRes.body.state).toBe('ready_for_payment'); // 4. Complete purchase const completeRes = await request(app) .post(`/acp/v1/checkout_sessions/${sessionId}/complete`) .send({ payment_token: 'pm_card_visa' }); expect(completeRes.status).toBe(200); expect(completeRes.body.success).toBe(true); // 5. Verify order created const orderRes = await request(app) .get(`/acp/v1/checkout_sessions/${sessionId}`); expect(orderRes.body.state).toBe('completed'); expect(orderRes.body.order.order_id).toBeDefined(); }); });

Testing Tools

Test Runner Configuration

// jest.config.js module.exports = { testEnvironment: 'node', setupFilesAfterEnv: ['./test/setup.js'], coverageThreshold: { global: { branches: 80, functions: 80, lines: 80, statements: 80 } }, testPathIgnorePatterns: ['/node_modules/'], collectCoverageFrom: [ 'services/**/*.js', 'routes/**/*.js', 'middleware/**/*.js' ] };

Test Setup

// test/setup.js const mongoose = require('mongoose'); // Use in-memory database for tests beforeAll(async () => { const { MongoMemoryServer } = require('mongodb-memory-server'); const mongoServer = await MongoMemoryServer.create(); await mongoose.connect(mongoServer.getUri()); }); afterAll(async () => { await mongoose.disconnect(); }); // Reset database between tests afterEach(async () => { const collections = mongoose.connection.collections; for (const key in collections) { await collections[key].deleteMany({}); } });

Manual Testing Checklist

Product Feed

  • All products return correctly
  • Search filters work
  • Pagination works
  • Single product endpoint works
  • Out of stock items handled

Checkout Sessions

  • Session creates with valid cart
  • Invalid products rejected
  • Session state updates correctly
  • Totals calculate accurately
  • Tax applies per state
  • Shipping calculates correctly
  • Session expires after 30 min

Payments

  • Valid card succeeds
  • Declined card handled
  • Expired card handled
  • Insufficient funds handled
  • Idempotency prevents duplicates

Error Handling

  • 404 for missing resources
  • 400 for invalid requests
  • Clear error messages
  • No sensitive data leaked

Load Testing

// loadtest/checkout.js const autocannon = require('autocannon'); const instance = autocannon({ url: 'http://localhost:3000/acp/v1/products', connections: 100, duration: 30, headers: { 'Content-Type': 'application/json' } }, (err, result) => { if (err) { console.error(err); return; } console.log(result); }); autocannon.track(instance);
📊

Target metrics:

  • Product feed: < 100ms p95
  • Create session: < 200ms p95
  • Complete checkout: < 500ms p95

Continuous Integration

# .github/workflows/test.yml name: Test on: [push, pull_request] jobs: test: runs-on: ubuntu-latest services: mongodb: image: mongo:6 ports: - 27017:27017 steps: - uses: actions/checkout@v3 - name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '18' - name: Install dependencies run: npm ci - name: Run tests run: npm test -- --coverage env: TEST_DATABASE_URL: mongodb://localhost:27017/test STRIPE_SECRET_KEY: sk_test_xxx - name: Upload coverage uses: codecov/codecov-action@v3

Summary

You’ve now learned to:

  • ✅ Write unit tests for services
  • ✅ Create integration tests for API endpoints
  • ✅ Build end-to-end tests for complete flows
  • ✅ Set up test infrastructure
  • ✅ Run manual testing checklists
  • ✅ Implement CI/CD testing

Your ACP integration is ready for production!

Next Steps

Specification maintained by OpenAI and Stripe

AboutPrivacyTermsRSS

Apache 2.0 · Open Source