Files
branderson 4130adfe58 docs: document Dotcom Arcade, streaming, proxy sign-in and saves
A new README for the combined project with setup from the Flashpoint
Ultimate torrent. New feature docs for streaming, proxy sign-in and
game saves; deployment, upgrade and streaming READMEs updated for the
single compose file; the project renamed throughout.
2026-10-13 19:31:06 -07:00

5.1 KiB

Testing Guide

Testing practices for Dotcom Arcade.

Testing Philosophy

Pyramid

  • Unit Tests (70%): Individual functions in isolation
  • Integration Tests (20%): Service interactions and API endpoints
  • E2E Tests (10%): Critical user flows

Coverage Goals

  • Backend: 80% target
  • Critical services: 90%+
  • Routes: Test all endpoints

When to Test

Always:

  • Business logic in services
  • Database operations
  • Authentication and authorization
  • API endpoints
  • Utility functions
  • Data transformations

Skip:

  • Simple getter/setter methods
  • Trivial UI components
  • Configuration files
  • Type definitions

Backend Testing (Vitest)

Configuration

// vitest.config.ts
import { defineConfig } from 'vitest/config';
import path from 'path';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    setupFiles: ['./src/tests/setup.ts'],
    coverage: {
      provider: 'v8',
      reporter: ['text', 'json', 'html'],
    },
  },
});

Running Tests

cd backend

npm test                                  # Run all tests
npm test -- --watch                       # Watch mode
npm test -- --coverage                    # With coverage
npm test src/services/GameService.test.ts # Specific file
npm test -- --grep "authentication"       # Pattern matching

Test Structure

  • Co-locate tests with implementation: GameService.test.ts
  • Use fixtures for test data in src/tests/fixtures/
  • Use helpers in src/tests/helpers/

Unit Test Example

describe('GameService', () => {
  let gameService: GameService;
  let mockDb: DatabaseService;

  beforeEach(() => {
    mockDb = { prepare: vi.fn() } as unknown as DatabaseService;
    gameService = new GameService(mockDb);
  });

  describe('getGameById', () => {
    it('should return game when found', () => {
      const mockGame = { id: 'game-123', title: 'Test Game' };
      const mockPrepare = vi
        .fn()
        .mockReturnValue({ get: vi.fn().mockReturnValue(mockGame) });
      mockDb.prepare = mockPrepare;

      const result = gameService.getGameById('game-123');

      expect(result).toEqual(mockGame);
    });

    it('should return null when game not found', () => {
      const mockPrepare = vi
        .fn()
        .mockReturnValue({ get: vi.fn().mockReturnValue(undefined) });
      mockDb.prepare = mockPrepare;

      const result = gameService.getGameById('nonexistent');

      expect(result).toBeNull();
    });
  });
});

Integration Test Example

describe('Games API', () => {
  let authToken: string;

  beforeAll(async () => {
    const user = await createTestUser({ username: 'testuser' });
    authToken = await getAuthToken(user.id);
  });

  describe('GET /api/games', () => {
    it('should return list of games', async () => {
      const response = await request(app).get('/api/games').expect(200);

      expect(response.body).toHaveProperty('data');
      expect(Array.isArray(response.body.data)).toBe(true);
    });
  });
});

Test Data Fixtures

// src/tests/fixtures/games.ts
export const testGames = {
  flashGame: {
    id: 'flash-1',
    title: 'Flash Game',
    platform: 'Flash',
    library: 'arcade',
  },
};

export function createTestGame(overrides = {}) {
  return { ...testGames.flashGame, ...overrides };
}

Testing Patterns

Async Code:

it('should fetch games asynchronously', async () => {
  const games = await service.fetchGames();
  expect(games).toHaveLength(10);
});

Error Handling:

it('should throw error on invalid input', () => {
  expect(() => validateGameData({ title: '' })).toThrow('Title is required');
});

Mocking:

vi.mock('@/utils/jwt', () => ({
  verifyToken: vi.fn(),
}));

const mockVerify = vi.mocked(verifyToken);
mockVerify.mockReturnValue({ userId: '123' });

Frontend Testing

Frontend testing setup is planned but not yet implemented. When implemented, it will use:

  • Vitest: Test runner
  • React Testing Library: Component testing
  • MSW: API mocking
  • Playwright/Cypress: E2E testing

Coverage

Running Coverage Reports

cd backend
npm test -- --coverage

Coverage Thresholds

Add to vitest.config.ts:

export default defineConfig({
  test: {
    coverage: {
      thresholds: {
        statements: 80,
        branches: 75,
        functions: 80,
        lines: 80,
      },
    },
  },
});

Best Practices

Do's

  • Write tests before fixing bugs (regression testing)
  • Test one thing per test
  • Use descriptive test names ("should return null when game not found")
  • Keep tests simple and readable
  • Mock external dependencies
  • Use fixtures for test data
  • Test edge cases and error paths

Don'ts

  • Don't test implementation details
  • Don't make tests dependent on each other
  • Don't use production databases
  • Don't test external libraries
  • Don't ignore failing tests

Additional Resources