ATOM Documentation

← Back to App

E2E Testing Guide: Personal Edition Features

**Purpose**: Comprehensive guide for end-to-end testing of personal edition features (media control, creative tools, smart home automation)

**Audience**: QA engineers, developers, technical contributors

**Coverage Target**: 85%+ across all personal edition modules

**Last Updated**: 2026-02-22

---

Table of Contents

  1. Overview
  2. Testing Philosophy
  3. Media Control Testing
  4. Creative Tools Testing
  5. Smart Home Testing
  6. Test Infrastructure
  7. Best Practices
  8. Coverage Report Template
  9. Troubleshooting

---

1. Overview

E2E Testing Philosophy for Personal Edition

Personal edition E2E testing validates **complete user workflows** across multiple integrations, simulating real-world usage patterns. Unlike unit tests (which test individual functions) or integration tests (which test module interactions), E2E tests verify **entire feature flows** from user action to system response.

Key Principles

  1. **User-Centric**: Test from user perspective, not implementation
  2. **Workflow-Based**: Validate complete scenarios, not isolated features
  3. **Cross-Platform**: Test integration between different services (e.g., "play focus music + dim lights")
  4. **Virtual Devices**: Use mocks instead of real hardware for speed and reliability
  5. **Coverage-Driven**: Aim for 85%+ coverage with meaningful tests

Coverage Goals

Module TypeCoverage TargetPriority
Media Integration (OAuth, playback)90%P0 (Critical)
Creative Tools (Canva, Adobe, Figma)85%P1 (High)
Smart Home Hubs (SmartThings, Hue, Nest)90%P0 (Critical)
Automation Engine (TAP pattern)85%P1 (High)
Voice Dispatcher (NLP parsing)80%P2 (Standard)
Energy Monitor (usage tracking)80%P2 (Standard)

Test Execution Patterns

# Run all personal edition tests
npm test -- src/lib/integrations/spotify/ src/lib/integrations/applemusic/ src/lib/creative-tools/ src/lib/smarthome/

# Run coverage report
npm run test:coverage -- src/lib/integrations/ src/lib/creative-tools/ src/lib/smarthome/

# Run specific module
npm test -- src/lib/smarthome/__tests__/automation-engine.test.ts

# Run with verbose output
npm test -- --reporter=verbose src/lib/media/

---

2. Testing Philosophy

Virtual Device Mocking Strategy

**Why Mock Instead of Real Hardware?**

  • **Speed**: Tests run in <500ms vs. minutes with real devices
  • **Reliability**: No network failures, device offline issues
  • **Determinism**: Same test data every run
  • **Cost**: No need to purchase 20+ smart home devices
  • **CI/CD**: Can run in automated pipelines

**Virtual Device Mocks (from Phase 63C-01)**:

  • MockHueBridge (853 lines) - Simulates Hue Bridge API
  • MockSmartThingsHub (404 lines) - Simulates SmartThings Hub
  • MockNestThermostat (573 lines) - Simulates Nest SDM API
  • MockHomeKitBridge (519 lines) - Simulates Home Assistant WebSocket
  • VirtualDeviceFactory (420 lines) - Creates test devices on demand

**Location**: src/lib/smarthome/__tests__/mocks/virtual-devices.ts

Test Pyramid for Personal Edition

        E2E Tests (15%)
       /             \
      /               \
   Integration (35%)    Unit Tests (50%)
  /                     \
OAuth flows          Individual
Hub communication    functions

**E2E Test Focus**:

  • Cross-platform workflows (media + smart home)
  • Complete automation scenarios (TAP pattern with multiple devices)
  • Voice command NLP variations
  • Multi-step creative workflows (design → export → social media)

Test Isolation Strategies

  1. **Database Isolation**:

afterAll(async () => {

await db.query('ROLLBACK');

});

```

  1. **API Mocking**:
  1. **Time Mocking**:

---

3. Media Control Testing

OAuth Flow Testing (Spotify, Apple Music)

Test Pattern: OAuth Authorization Code Flow

describe('Spotify OAuth Flow', () => {
  it('should complete full OAuth authorization', async () => {
    // Step 1: Redirect to Spotify
    const authUrl = await SpotifyClient.getAuthUrl(tenantId, {
      scopes: ['user-read-playback', 'user-library-read'],
      state: 'random-state-123'
    });

    expect(authUrl).toMatch(/^https:\/\/accounts\.spotify\.com\/authorize/);
    expect(authUrl).toContain('client_id=');

    // Step 2: Handle callback with code
    const callbackCode = 'auth-code-456';
    const mockTokenResponse = {
      access_token: 'token-789',
      refresh_token: 'refresh-101',
      expires_in: 3600
    };

    // Mock Spotify token endpoint
    vi.spyOn(axios, 'post').mockResolvedValue({ data: mockTokenResponse });

    const tokens = await SpotifyClient.handleCallback(tenantId, callbackCode, 'random-state-123');

    expect(tokens.access_token).toBe('token-789');
    expect(tokens.refresh_token).toBe('refresh-101');

    // Step 3: Verify tokens stored in database
    const storedTokens = await db.query(
      'SELECT * FROM tenant_settings WHERE tenant_id = $1 AND key = $2',
      [tenantId, 'spotify_tokens']
    );

    expect(storedTokens.rows.length).toBe(1);
    expect(JSON.parse(storedTokens.rows[0].value).access_token).toBe('token-789');
  });
});

Test: Token Refresh

it('should refresh access token before expiry', async () => {
  // Set up expired token
  await db.query(`
    INSERT INTO tenant_settings (tenant_id, key, value)
    VALUES ($1, 'spotify_tokens', $2)
  `, [tenantId, JSON.stringify({
    access_token: 'old-token',
    refresh_token: 'refresh-token',
    expires_at: new Date(Date.now() - 1000).toISOString() // Expired
  })]);

  // Mock refresh endpoint
  vi.spyOn(axios, 'post').mockResolvedValue({
    data: {
      access_token: 'new-token',
      expires_in: 3600
    }
  });

  const client = new SpotifyClient(tenantId);
  await client.ensureValidToken();

  // Verify new token in database
  const result = await db.query(
    "SELECT value->>'access_token' FROM tenant_settings WHERE tenant_id = $1 AND key = 'spotify_tokens'",
    [tenantId]
  );

  expect(result.rows[0].access_token).toBe('new-token');
});

Playback Control Workflows

Test: Basic Playback

it('should play, pause, and skip track', async () => {
  const client = new SpotifyClient(tenantId);

  // Mock Spotify API
  vi.spyOn(axios, 'put').mockResolvedValue({ status: 204 });

  // Play
  await client.play('device-123');
  expect(axios.put).toHaveBeenCalledWith(
    'https://api.spotify.com/v1/me/player/play',
    { device_ids: ['device-123'] }
  );

  // Pause
  await client.pause();
  expect(axios.put).toHaveBeenCalledWith('https://api.spotify.com/v1/me/player/pause');

  // Skip
  await client.skipToNext();
  expect(axios.post).toHaveBeenCalledWith('https://api.spotify.com/v1/me/player/next');
});

Test: Cross-Platform Workflow (E2E)

it('should play focus music and dim lights', async () => {
  // This test integrates media control + smart home

  // Step 1: Play focus playlist on Spotify
  const spotify = new SpotifyClient(tenantId);
  await spotify.setShuffle(true);
  await spotify.play('device-123', {
    context_uri: 'spotify:playlist:37i9dQZF1DXcBJigayD7B' // Focus playlist
  });

  // Step 2: Dim lights via Hue Bridge
  const hue = new HueBridgeClient(tenantId, {
    bridgeId: 'hue-bridge-123',
    apiKey: 'test-api-key'
  });

  await hue.setLightState('light-1', {
    on: true,
    brightness: 20, // Dim to 20%
    color: { x: 0.5, y: 0.3 } // Warm orange
  });

  await hue.setLightState('light-2', {
    on: true,
    brightness: 20,
    color: { x: 0.5, y: 0.3 }
  });

  // Step 3: Verify state
  const playbackState = await spotify.getPlaybackState();
  expect(playbackState.is_playing).toBe(true);

  const light1State = await hue.getLightState('light-1');
  expect(light1State.brightness).toBe(20);
});

Playlist Synchronization Testing

Test: Sync Playlist with Cache

it('should sync playlist from Spotify and cache locally', async () => {
  // Mock Spotify API response
  vi.spyOn(axios, 'get').mockResolvedValue({
    data: {
      items: [
        { track: { id: 'track-1', name: 'Song 1', uri: 'spotify:track:1' } },
        { track: { id: 'track-2', name: 'Song 2', uri: 'spotify:track:2' } },
        { track: { id: 'track-3', name: 'Song 3', uri: 'spotify:track:3' } }
      ]
    }
  });

  const playlistService = new PlaylistService(db, tenantId);
  await playlistService.syncPlaylist('playlist-123');

  // Verify tracks in database
  const tracks = await db.query(
    `SELECT * FROM media_playlists
     WHERE tenant_id = $1 AND playlist_id = $2
     ORDER BY position`,
    [tenantId, 'playlist-123']
  );

  expect(tracks.rows.length).toBe(3);
  expect(tracks.rows[0].track_name).toBe('Song 1');

  // Verify cache (should not call API again)
  await playlistService.syncPlaylist('playlist-123');
  expect(axios.get).toHaveBeenCalledTimes(1); // Called once, cached
});

Mock Patterns for Provider APIs

Spotify API Mock

// src/lib/integrations/spotify/__tests__/mocks.ts
export const mockSpotifyApi = () => {
  vi.mock('spotify-web-api-node', () => ({
    Client: vi.fn().mockImplementation(() => ({
      getMe: vi.fn().mockResolvedValue({ id: 'user-123' }),
      getPlayer: vi.fn().mockResolvedValue({
        device: { id: 'device-123', name: 'Living Room' },
        is_playing: true,
        item: { name: 'Test Track' }
      }),
      play: vi.fn().mockResolvedValue({ success: true }),
      pause: vi.fn().mockResolvedValue({ success: true })
    }))
  }));
};

Apple Music API Mock

export const mockAppleMusicApi = () => {
  vi.mock('music-api-kit', () => ({
    MusicKit: vi.fn().mockImplementation(() => ({
      authorize: vi.fn().mockResolvedValue(true),
      setAuthToken: vi.fn(),
      api: {
        library: {
          playlists: vi.fn().mockResolvedValue({
            data: [{ id: 'pl-1', attributes: { name: 'Test Playlist' } }]
          })
        }
      }
    }))
  }));
};

---

4. Creative Tools Testing

Design Tool OAuth Testing (Canva, Figma, Adobe)

Test: Canva OAuth 2.0 Flow

describe('Canva OAuth Integration', () => {
  it('should authorize user and access designs', async () => {
    const client = new CanvaClient(tenantId);

    // Mock Canva OAuth endpoint
    vi.spyOn(axios, 'get').mockResolvedValueOnce({
      data: { authorization_url: 'https://www.canva.com/oauth' }
    });

    const authUrl = await client.getAuthUrl();

    expect(authUrl).toContain('response_type=code');
    expect(authUrl).toContain('client_id=');

    // Mock callback handling
    vi.spyOn(axios, 'post').mockResolvedValue({
      data: {
        access_token: 'canva-token-123',
        refresh_token: 'canva-refresh-456',
        expires_in: 7200
      }
    });

    await client.handleCallback('auth-code-789');

    // Verify tokens stored
    const tokens = await client.getTokens();
    expect(tokens.access_token).toBe('canva-token-123');
  });
});

Test: Figma File Operations

it('should read and update Figma design', async () => {
  const client = new FigmaClient(tenantId, { token: 'figma-token' });

  // Mock Figma API
  vi.spyOn(axios, 'get').mockResolvedValue({
    data: {
      document: {
        id: 'file-123',
        name: 'Test Design',
        children: [{
          type: 'CANVAS',
          name: 'Page 1',
          children: [
            { type: 'RECTANGLE', id: 'rect-1', width: 100, height: 100 }
          ]
        }]
      }
    }
  });

  const file = await client.getFile('file-123');

  expect(file.name).toBe('Test Design');
  expect(file.document.children[0].children[0].type).toBe('RECTANGLE');

  // Update design
  vi.spyOn(axios, 'post').mockResolvedValue({ status: 200 });

  await client.updateFile('file-123', {
    updates: [{
      node_id: 'rect-1',
    relativeTransform: { x: 50, y: 50 }
    }]
  });

  expect(axios.post).toHaveBeenCalledWith(
    'https://api.figma.com/v1/files/file-123',
    expect.objectContaining({ updates: expect.any(Array) })
  );
});

Photo Editing Validation (Sharp Operations)

Test: Image Processing Pipeline

describe('PhotoEditorService', () => {
  it('should crop, resize, and apply filters', async () => {
    const editor = new PhotoEditorService();

    // Create test image buffer
    const testImage = Buffer.from('fake-image-data');

    // Mock Sharp operations
    const mockResize = vi.fn().mockReturnThis();
    const mockCrop = vi.fn().mockReturnThis();
    const mockToFormat = vi.fn().mockResolvedValue(Buffer.from('processed-image'));

    vi.doMock('sharp', () => ({
      default: vi.fn(() => ({
        resize: mockResize,
        crop: mockCrop,
        toFormat: mockToFormat
      }))
    });

    // Execute pipeline
    const result = await editor.processImage(testImage, {
      crop: { left: 10, top: 10, width: 100, height: 100 },
      resize: { width: 800, height: 600 },
      filter: 'grayscale',
      format: 'jpeg'
    });

    // Verify operations called in order
    expect(mockCrop).toHaveBeenCalledWith({
      left: 10, top: 10, width: 100, height: 100
    });
    expect(mockResize).toHaveBeenCalledWith(800, 600);
    expect(mockToFormat).toHaveBeenCalledWith('jpeg');
  });
});

AI Suggestions Testing (LLM Mocking)

Test: Color Scheme Generation

it('should generate color scheme using LLM', async () => {
  const service = new CreativeSuggestionsService(db, llmRouter);

  // Mock LLM response
  vi.spyOn(llmRouter, 'call').mockResolvedValue({
    choices: [{
      message: {
        content: JSON.stringify({
          colors: [
            { hex: '#FF5733', name: 'Vibrant Red' },
            { hex: '#33FF57', name: 'Fresh Green' },
            { hex: '#3357FF', name: 'Calm Blue' }
          ]
        })
      }
    }]
  });

  const suggestions = await service.generateColorScheme('modern', 'tech');

  expect(suggestions.colors).toHaveLength(3);
  expect(suggestions.colors[0].hex).toBe('#FF5733');
  expect(llmRouter.call).toHaveBeenCalledWith(
    expect.stringContaining('color scheme'),
    expect.any(Object)
  );
});

Evernote OAuth 1.0a Signature Validation

Test: HMAC-SHA1 Signature

it('should generate valid OAuth 1.0a signature', async () => {
  const client = new EvernoteClient(tenantId, {
    consumerKey: 'test-key',
    consumerSecret: 'test-secret'
  });

  const url = 'https://sandbox.evernote.com/edam/user';
  const params = {
    oauth_consumer_key: 'test-key',
    oauth_token: 'token-123',
    oauth_timestamp: '1234567890',
    oauth_nonce: 'random-nonce'
  };

  const signature = await client.generateSignature('GET', url, params);

  // Verify signature format (HMAC-SHA1)
  expect(signature).toMatch(/^[a-f0-9]{40}$/);

  // Verify signature matches expected value
  const expectedSignature = crypto
    .createHmac('sha1', 'test-secret&token-secret')
    .update('GET&' + encodeURIComponent(url) + '&' + encodeURIComponent(sortParams(params)))
    .digest('hex');

  expect(signature).toBe(expectedSignature);
});

Cross-Tool Export Workflows

Test: Canva → Figma → Export

it('should export design from Canva to Figma format', async () => {
  // Step 1: Export from Canva
  const canva = new CanvaClient(tenantId, { token: 'canva-token' });
  vi.spyOn(axios, 'get').mockResolvedValue({
    data: { export_url: 'https://canva.com/export/design-123.pdf' }
  });

  const exportUrl = await canva.exportDesign('design-123');

  // Step 2: Convert to Figma format
  const figma = new FigmaClient(tenantId, { token: 'figma-token' });
  vi.spyOn(axios, 'post').mockResolvedValue({
    data: { file: { id: 'figma-456', key: 'converted-design' } }
  });

  const converted = await figma.importFromCanva(exportUrl);

  expect(converted.file.id).toBe('figma-456');
});

---

5. Smart Home Testing

Hub Client Testing Patterns

Test: SmartThings Hub Discovery

describe('SmartThings Hub', () => {
  it('should discover hub and authenticate via OAuth', async () => {
    const client = new SmartThingsClient(tenantId);

    // Mock OAuth flow
    vi.spyOn(axios, 'post').mockResolvedValueOnce({
      data: {
        access_token: 'st-token-123',
        refresh_token: 'st-refresh-456'
      }
    });

    await client.authenticate('auth-code-789');

    // Mock device discovery
    vi.spyOn(axios, 'get').mockResolvedValue({
      data: {
        items: [
          { deviceId: 'switch-1', name: 'Living Room Light', type: 'switch' },
          { deviceId: 'switch-2', name: 'Kitchen Light', type: 'switch' }
        ]
      }
    });

    const devices = await client.getDevices();

    expect(devices).toHaveLength(2);
    expect(devices[0].name).toBe('Living Room Light');
  });
});

Test: Hue Bridge mDNS Discovery

it('should discover Hue Bridge via mDNS', async () => {
  const discovery = new HueDiscoveryService();

  // Mock mDNS browser
  vi.mock('mdns', () => ({
    createBrowser: vi.fn().mockImplementation(() => ({
      on: vi.fn((event, listener) => {
        if (event === 'ready') {
          listener({
            addresses: ['192.168.1.100'],
            name: 'Philips hue bridge',
            port: 80
          });
        }
      })
    }))
  }));

  const bridges = await discovery.discover();

  expect(bridges).toHaveLength(1);
  expect(bridges[0].ip).toBe('192.168.1.100');
});

Automation Workflow Testing (TAP Pattern)

Test: Multi-Device Scene

describe('AutomationEngine', () => {
  it('should execute "Movie Night" scene', async () => {
    const engine = new AutomationEngine(tenantId, db);

    // Create rule with multiple actions
    const rule = await engine.createRule({
      name: 'Movie Night',
      trigger: {
        type: 'voice',
        pattern: 'movie mode'
      },
      conditions: [],
      actions: [
        { deviceId: 'light-1', action: 'set_brightness', params: { level: 20 } },
        { deviceId: 'light-2', action: 'set_brightness', params: { level: 20 } },
        { deviceId: 'light-3', action: 'set_color', params: { x: 0.5, y: 0.3 } },
        { deviceId: 'thermostat-1', action: 'set_temperature', params: { temp: 68 } }
      ]
    });

    // Trigger rule
    vi.spyOn(engine, 'executeActions').mockResolvedValue(true);

    await engine.trigger('voice', { text: 'movie mode' });

    // Verify all actions executed
    expect(engine.executeActions).toHaveBeenCalledWith([
      { deviceId: 'light-1', action: 'set_brightness', params: { level: 20 } },
      { deviceId: 'light-2', action: 'set_brightness', params: { level: 20 } },
      { deviceId: 'light-3', action: 'set_color', params: { x: 0.5, y: 0.3 } },
      { deviceId: 'thermostat-1', action: 'set_temperature', params: { temp: 68 } }
    ]);
  });
});

Voice Command NLP Testing

Test: Natural Language Variations

describe('VoiceDispatcher', () => {
  it('should handle 30+ command variations', async () => {
    const dispatcher = new VoiceDispatcher(tenantId);

    const variations = [
      'turn on the lights',
      'lights on',
      'switch on lights',
      'enable lights',
      'activate lights',
      'power on lights'
    ];

    for (const command of variations) {
      const parsed = dispatcher.parse(command);

      expect(parsed.action).toBe('on');
      expect(parsed.target).toContain('light');
    }
  });

  it('should handle multi-step commands', async () => {
    const result = dispatcher.parse('turn on lights and set to blue');

    expect(result.actions).toHaveLength(2);
    expect(result.actions[0].action).toBe('on');
    expect(result.actions[1].action).toBe('set_color');
    expect(result.actions[1].params.color).toMatch(/blue/i);
  });
});

---

6. Test Infrastructure

Virtual Device Mock Reference

**Location**: src/lib/smarthome/__tests__/mocks/virtual-devices.ts

Usage Example

import { MockHueBridge, VirtualDeviceFactory } from './mocks/virtual-devices';

// Create mock Hue Bridge
const mockBridge = new MockHueBridge({
  bridgeId: 'hue-test-bridge',
  ipAddress: '192.168.1.100',
  apiKey: 'test-api-key'
});

// Add virtual lights
mockBridge.addLight({
  id: 'light-1',
  name: 'Living Room Light',
  state: { on: false, brightness: 0 }
});

mockBridge.addLight({
  id: 'light-2',
  name: 'Kitchen Light',
  state: { on: false, brightness: 0 }
});

// Use in test
const client = new HueBridgeClient(tenantId, { bridgeId: 'hue-test-bridge' });
await client.setLightState('light-1', { on: true, brightness: 50 });

// Verify state
const state = await client.getLightState('light-1');
expect(state.on).toBe(true);
expect(state.brightness).toBe(50);

Virtual Device Factory

const factory = new VirtualDeviceFactory();

// Create test scene
const livingRoom = factory.createScene('living-room', {
  lights: ['light-1', 'light-2'],
  thermostat: 'thermostat-1',
  temperature: 70
});

// Verify scene state
expect(livingRoom.getDevice('light-1').state.on).toBe(false);
livingRoom.activate('movie-mode');
expect(livingRoom.getDevice('light-1').state.brightness).toBe(20);

Test Setup Utilities

**Location**: src/lib/smarthome/__tests__/test-setup.ts

Database Helpers

import { seedTestDevices, seedTestScenes } from './test-setup';

describe('Smart Home Integration', () => {
  let tenantId: string;

  beforeEach(async () => {
    // Create test tenant
    tenantId = await createTestTenant();

    // Seed test devices
    await seedTestDevices(tenantId, [
      { id: 'light-1', name: 'Living Room Light', type: 'light', hub: 'hue' },
      { id: 'thermostat-1', name: 'Nest', type: 'thermostat', hub: 'nest' }
    ]);
  });

  afterEach(async () => {
    // Cleanup test data
    await db.query('DELETE FROM smarthome_devices WHERE tenant_id = $1', [tenantId]);
  });
});

API Response Mocks

**Location**: src/lib/smarthome/__tests__/mocks/api-responses.ts

Usage Example

import { mockOAuthTokens, mockDeviceDiscoveryResponse } from './mocks/api-responses';

describe('SmartThings Client', () => {
  beforeEach(() => {
    // Mock OAuth token response
    mockOAuthTokens({
      access_token: 'test-token',
      refresh_token: 'test-refresh',
      expires_in: 3600
    });

    // Mock device discovery
    mockDeviceDiscoveryResponse([
      { deviceId: 'switch-1', name: 'Test Switch' }
    ]);
  });
});

---

7. Best Practices

Mock vs. Real Service Usage

**When to Use Mocks**:

  • ✅ Unit tests (fast, deterministic)
  • ✅ CI/CD pipelines (no external dependencies)
  • ✅ Feature development (before integration)
  • ✅ Edge case testing (error scenarios)

**When to Use Real Services**:

  • ✅ Integration verification (before release)
  • ✅ API contract validation (after provider updates)
  • ✅ Performance testing (realistic load)
  • ❌ Daily development (too slow, unreliable)

Test Isolation Strategies

  1. **Database Transactions**:

afterEach(async () => {

await db.query('ROLLBACK');

});

```

  1. **Time Isolation**:
  1. **Environment Isolation**:

Rate Limiting Validation

it('should enforce 150 req/min rate limit for SmartThings', async () => {
  const client = new SmartThingsClient(tenantId);

  // Mock API to track requests
  const requestCount = { count: 0 };
  vi.spyOn(axios, 'get').mockImplementation(() => {
    requestCount.count++;
    if (requestCount.count > 150) {
      throw new Error('Rate limit exceeded');
    }
    return { data: {} };
  });

  // Make 151 requests
  for (let i = 0; i < 151; i++) {
    try {
      await client.getDevices();
    } catch (e) {
      expect(e.message).toBe('Rate limit exceeded');
      expect(i).toBe(150); // Should fail on 151st request
    }
  }
});

Error Scenario Coverage

it('should handle OAuth token expiry gracefully', async () => {
  const client = new SpotifyClient(tenantId);

  // Mock expired token response
  vi.spyOn(axios, 'get').mockRejectedValueOnce({
    response: { status: 401 },
    config: { url: 'https://api.spotify.com/v1/me' }
  });

  // Mock token refresh
  vi.spyOn(axios, 'post').mockResolvedValue({
    data: { access_token: 'new-token', expires_in: 3600 }
  });

  // Should automatically refresh and retry
  const user = await client.getCurrentUser();

  expect(user).toBeDefined();
  expect(axios.post).toHaveBeenCalledWith(
    'https://accounts.spotify.com/api/token',
    expect.objectContaining({ grant_type: 'refresh_token' })
  );
});

Performance Benchmarking (<500ms Target)

it('should execute automation rule in <500ms', async () => {
  const engine = new AutomationEngine(tenantId);

  const startTime = Date.now();

  await engine.executeRule('rule-123', {
    trigger: { type: 'state', deviceId: 'sensor-1', value: 'motion' },
    actions: [
      { deviceId: 'light-1', action: 'on' },
      { deviceId: 'light-2', action: 'on' }
    ]
  });

  const duration = Date.now() - startTime;

  expect(duration).toBeLessThan(500);
});

---

8. Coverage Report Template

Module-by-Module Breakdown

## Coverage Summary

**Overall Coverage**: XX%
**Target**: 85%
**Gap**: XX%

### Media Integration

| Module | Coverage | Tests | Gap |
|--------|----------|-------|-----|
| SpotifyClient | XX% | 50 | XX% |
| AppleMusicClient | XX% | 50 | XX% |
| PlaybackService | XX% | 27 | XX% |
| PlaylistService | XX% | 21 | XX% |
| RecommendationService | XX% | 23 | XX% |

**Gap Analysis**: [describe what's missing]

### Creative Tools

[Same table format]

### Smart Home

[Same table format]

Gap Identification

## Immediate Gaps (Below 85%)

1. **SmartThingsClient** (Current: 40%, Target: 85%, Gap: 45%)
   - Missing: Pagination, offline device handling, batch commands
   - Priority: HIGH
   - Estimated Effort: 4-6 hours

2. **HueBridgeClient** (Current: 35%, Target: 85%, Gap: 50%)
   - Missing: Group operations, scene management, discovery edge cases
   - Priority: HIGH
   - Estimated Effort: 4-6 hours

Improvement Tracking

## Coverage Trends

| Date | Overall | Media | Creative | Smart Home |
|------|---------|-------|----------|------------|
| 2026-02-20 | 35% | 45% | 40% | 30% |
| 2026-02-22 | 45% | 85% | 50% | 35% |
| 2026-02-25 (Target) | 85% | 90% | 85% | 85% |

---

9. Troubleshooting

Common Issues

1. Mock Constructor Issues

**Problem**: DeviceRegistry is not a constructor

**Solution**: Use class constructor mocks, not getInstance pattern:

// ❌ Wrong (singleton pattern)
vi.mock('../device-registry', () => ({
  DeviceRegistry: {
    getInstance: () => ({ ... })
  }
}));

// ✅ Correct (class constructor)
vi.mock('../device-registry', () => {
  class MockDeviceRegistry {
    getDevices = vi.fn(() => [...]);
  }
  return { DeviceRegistry: MockDeviceRegistry };
});

2. Missing Mock Methods

**Problem**: this.deviceRegistry.searchDevices is not a function

**Solution**: Implement all interface methods in mocks:

class MockDeviceRegistry {
  getDevices = vi.fn(() => [...]);
  searchDevices = vi.fn((query) => [...]); // Add missing method
  getDevice = vi.fn((id) => devices.find(d => d.id === id));
  updateDeviceState = vi.fn();
  // ... etc
}

3. Database Coupling

**Problem**: Tests expect real database connection

**Solution**: Mock database at module level:

vi.mock('../../lib/database', () => ({
  getDatabase: () => ({
    query: vi.fn(() => ({ rows: [], rowCount: 0 })),
    connect: vi.fn(),
    end: vi.fn()
  })
}));

4. Async Timeout Issues

**Problem**: Tests timeout waiting for promises

**Solution**: Use fake timers or increase timeout:

it('should handle long-running operation', async () => {
  vi.useFakeTimers();

  const promise = someAsyncOperation();
  vi.advanceTimersByTime(5000); // Advance 5 seconds
  await promise; // Now resolves immediately
}, 10000); // 10 second timeout

Test Execution Time

**Current Performance**:

  • Media tests: ~10 seconds (50 tests)
  • Creative tools: ~12 seconds (120 tests)
  • Smart home: ~15 seconds (127 tests, 121 failing)
  • **Total**: ~37 seconds (target: <60 seconds)

**Optimization Tips**:

  1. Use vi.mock() for heavy dependencies
  2. Avoid real network calls
  3. Use fake timers for schedule testing
  4. Parallel test execution (vitest --threads)
  5. Disable coverage reports during development

---

Conclusion

This guide provides comprehensive patterns and examples for E2E testing of personal edition features. Follow the testing philosophy, use virtual device mocks, and maintain 85%+ coverage target to ensure production readiness.

**Key Resources**:

  • Test Infrastructure: src/lib/smarthome/__tests__/mocks/
  • Coverage Reports: coverage/index.html
  • API Documentation: docs/MEDIA_INTEGRATION.md, docs/smarthome/*.md

**Next Steps**:

  1. Fix mock infrastructure issues (documented in 63D-01-03-SUMMARY.md)
  2. Achieve 85% coverage target
  3. Add E2E workflow tests for cross-platform scenarios
  4. Document coverage gaps and improvement roadmap