Learning Objectives

  • Write comprehensive test suites
  • Implement performance benchmarks
  • Prepare for production deployment
  • Document the project

Comprehensive Test Suite

Unit Tests for Utilities

// tests/utils/retry.test.js
import { retry } from '../../src/utils/retry.js';

describe('retry', () => {
  test('succeeds on first attempt', async () => {
    const fn = jest.fn().mockResolvedValue('success');
    const result = await retry(fn, { retries: 3 });
    
    expect(result).toBe('success');
    expect(fn).toHaveBeenCalledTimes(1);
  });
  
  test('retries on failure', async () => {
    const fn = jest.fn()
      .mockRejectedValueOnce(new Error('Fail 1'))
      .mockRejectedValueOnce(new Error('Fail 2'))
      .mockResolvedValueOnce('success');
    
    const result = await retry(fn, { retries: 3 });
    
    expect(result).toBe('success');
    expect(fn).toHaveBeenCalledTimes(3);
  });
  
  test('throws after max retries', async () => {
    const fn = jest.fn().mockRejectedValue(new Error('Always fails'));
    
    await expect(retry(fn, { retries: 2 }))
      .rejects.toThrow('Always fails');
    
    expect(fn).toHaveBeenCalledTimes(2);
  });
});

Integration Tests

// tests/integration/aggregator.test.js
import { EnhancedAggregator } from '../../src/aggregator/enhanced.js';

describe('EnhancedAggregator Integration', () => {
  let aggregator;
  
  beforeEach(() => {
    aggregator = new EnhancedAggregator({ logLevel: 'error' });
  });
  
  afterEach(() => {
    aggregator.clearCache();
  });
  
  describe('fetchAll', () => {
    test('fetches data from all sources', async () => {
      const config = {
        city: 'London',
        newsCategory: 'tech',
        stockSymbol: 'AAPL'
      };
      
      const result = await aggregator.fetchAll(config);
      
      expect(result.success).toBe(true);
      expect(result.data).toHaveProperty('weather');
      expect(result.data).toHaveProperty('news');
      expect(result.data).toHaveProperty('stocks');
      expect(result.metadata).toHaveProperty('duration');
      expect(result.metadata).toHaveProperty('stats');
    });
    
    test('handles partial failures gracefully', async () => {
      const config = {
        city: 'InvalidCity',
        newsCategory: 'tech',
        stockSymbol: 'AAPL'
      };
      
      const result = await aggregator.fetchAll(config);
      
      expect(result.success).toBe(true);
      // Some data may be null due to failures
      expect(result.data).toBeDefined();
    });
  });
  
  describe('caching', () => {
    test('uses cache on subsequent requests', async () => {
      const config = {
        city: 'London',
        newsCategory: 'tech',
        stockSymbol: 'AAPL'
      };
      
      // First request
      await aggregator.fetchAll(config);
      
      // Second request (should use cache)
      await aggregator.fetchAll(config);
      
      const stats = aggregator.getStats();
      
      // At least one cache hit
      const totalCacheHits = 
        stats.weather.cacheHits +
        stats.news.cacheHits +
        stats.stocks.cacheHits;
      
      expect(totalCacheHits).toBeGreaterThan(0);
    });
    
    test('cache can be cleared', async () => {
      const config = {
        city: 'London',
        newsCategory: 'tech',
        stockSymbol: 'AAPL'
      };
      
      await aggregator.fetchAll(config);
      aggregator.clearCache();
      
      const stats = aggregator.getStats();
      expect(stats.weather.cache.size).toBe(0);
    });
  });
});

Performance Tests

// tests/performance/benchmark.test.js
import { EnhancedAggregator } from '../../src/aggregator/enhanced.js';

describe('Performance Benchmarks', () => {
  let aggregator;
  
  beforeEach(() => {
    aggregator = new EnhancedAggregator({ logLevel: 'error' });
  });
  
  test('completes within acceptable time', async () => {
    const config = {
      city: 'London',
      newsCategory: 'tech',
      stockSymbol: 'AAPL'
    };
    
    const start = performance.now();
    await aggregator.fetchAll(config);
    const duration = performance.now() - start;
    
    // Should complete in under 5 seconds
    expect(duration).toBeLessThan(5000);
  });
  
  test('cache improves performance', async () => {
    const config = {
      city: 'London',
      newsCategory: 'tech',
      stockSymbol: 'AAPL'
    };
    
    // First request (no cache)
    const start1 = performance.now();
    await aggregator.fetchAll(config);
    const duration1 = performance.now() - start1;
    
    // Second request (with cache)
    const start2 = performance.now();
    await aggregator.fetchAll(config);
    const duration2 = performance.now() - start2;
    
    // Cached request should be faster
    expect(duration2).toBeLessThan(duration1);
  });
  
  test('handles concurrent requests efficiently', async () => {
    const config = {
      city: 'London',
      newsCategory: 'tech',
      stockSymbol: 'AAPL'
    };
    
    const start = performance.now();
    
    await Promise.all([
      aggregator.fetchAll(config),
      aggregator.fetchAll(config),
      aggregator.fetchAll(config)
    ]);
    
    const duration = performance.now() - start;
    
    // Should handle 3 concurrent requests efficiently
    expect(duration).toBeLessThan(6000);
  });
});

Test Configuration

jest.config.js

export default {
  testEnvironment: 'node',
  transform: {},
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
  },
  coverageDirectory: 'coverage',
  collectCoverageFrom: [
    'src/**/*.js',
    '!src/index.js',
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
      statements: 80
    }
  }
};

Documentation

README.md

# Data Aggregator

A production-ready data aggregation system built with JavaScript Promises.

## Features

- ✅ Parallel data fetching from multiple sources
- ✅ Intelligent caching with TTL
- ✅ Rate limiting to prevent API throttling
- ✅ Automatic retry with exponential backoff
- ✅ Comprehensive error handling
- ✅ Performance monitoring and statistics
- ✅ Full test coverage

## Installation

```bash
npm install
```

## Usage

```javascript
import { EnhancedAggregator } from './src/aggregator/enhanced.js';

const aggregator = new EnhancedAggregator({
  logLevel: 'info'
});

const result = await aggregator.fetchAll({
  city: 'London',
  newsCategory: 'technology',
  stockSymbol: 'AAPL'
});

console.log(result);
```

## Running Tests

```bash
# Run all tests
npm test

# Run with coverage
npm test -- --coverage

# Run specific test file
npm test -- tests/utils/retry.test.js
```

## Performance

- Average response time: < 2 seconds
- Cache hit rate: > 80% after warm-up
- Supports 10+ concurrent requests
- Memory efficient with LRU cache

## Architecture

```
src/
├── api/          # API clients
├── cache/        # Caching layer
├── utils/        # Utility functions
└── aggregator/   # Main aggregator logic
```

## Configuration

Configure via constructor options:

```javascript
const aggregator = new EnhancedAggregator({
  logLevel: 'info',      // Log level
  cacheTTL: 300000,      # Cache TTL in ms
  requestsPerSecond: 10  // Rate limit
});
```

## License

MIT

Production Checklist

  1. ✅ All tests passing
  2. ✅ Code coverage > 80%
  3. ✅ Error handling implemented
  4. ✅ Logging configured
  5. ✅ Performance benchmarks met
  6. ✅ Documentation complete
  7. ✅ Environment variables for API keys
  8. ✅ Rate limiting configured
  9. ✅ Monitoring and alerting ready
  10. ✅ Security review completed

Deployment

.env.example

WEATHER_API_KEY=your_key_here
NEWS_API_KEY=your_key_here
STOCKS_API_KEY=your_key_here

CACHE_TTL=300000
RATE_LIMIT_RPS=10
LOG_LEVEL=info

Docker Support

# Dockerfile
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY src ./src

EXPOSE 3000

CMD ["node", "src/index.js"]

docker-compose.yml

version: '3.8'

services:
  aggregator:
    build: .
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - LOG_LEVEL=info
    restart: unless-stopped

Monitoring

Health Check Endpoint

// src/server.js
import express from 'express';
import { EnhancedAggregator } from './aggregator/enhanced.js';

const app = express();
const aggregator = new EnhancedAggregator();

app.get('/health', (req, res) => {
  const stats = aggregator.getStats();
  
  res.json({
    status: 'healthy',
    uptime: process.uptime(),
    memory: process.memoryUsage(),
    stats
  });
});

app.get('/api/aggregate', async (req, res) => {
  try {
    const result = await aggregator.fetchAll({
      city: req.query.city || 'London',
      newsCategory: req.query.category || 'tech',
      stockSymbol: req.query.symbol || 'AAPL'
    });
    
    res.json(result);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Final Project Structure

data-aggregator/
├── src/
│   ├── api/
│   │   ├── client.js
│   │   ├── cachedClient.js
│   │   ├── weatherAPI.js
│   │   ├── newsAPI.js
│   │   └── stocksAPI.js
│   ├── cache/
│   │   └── index.js
│   ├── utils/
│   │   ├── delay.js
│   │   ├── timeout.js
│   │   ├── retry.js
│   │   ├── rateLimiter.js
│   │   └── logger.js
│   ├── aggregator/
│   │   ├── index.js
│   │   └── enhanced.js
│   ├── server.js
│   └── index.js
├── tests/
│   ├── utils/
│   ├── integration/
│   └── performance/
├── .env.example
├── .gitignore
├── Dockerfile
├── docker-compose.yml
├── jest.config.js
├── package.json
└── README.md

🎉 Congratulations!

You've completed the JavaScript Promises course and built a production-ready data aggregator!

What You've Learned

Next Steps

Key Takeaways

  • Comprehensive testing ensures reliability
  • Performance benchmarks validate optimization
  • Documentation makes code maintainable
  • Monitoring enables production readiness
  • Best practices lead to robust applications

Thank You!

Thank you for completing this comprehensive JavaScript Promises tutorial. You now have the skills to build robust, performant async applications. Keep practicing and happy coding! 🎉