๐Ÿงช Testing & Sandbox

Learn how to test your integration in a safe sandbox environment.

๐Ÿงช Testing & Sandbox

Learn how to test your integration in a safe sandbox environment.

Development Mode

The rPPG API backend includes a development mode that bypasses authentication for easier local testing.

Enabling Dev Mode

Set the DEV_MODE environment variable on the backend:

# Backend .env file
DEV_MODE=true

When dev mode is enabled:

  • โœ… Any API key value is accepted (authentication bypassed)
  • โœ… Rate limiting is relaxed
  • โœ… Useful for local development and testing

โš ๏ธ Warning: Never enable DEV_MODE in production!

Using Dev Mode

import { RPPGClient } from 'rppg-api-client';

// Any API key works in dev mode
const client = new RPPGClient({
  apiKey: 'dev-key',
  baseUrl: 'http://localhost/api'
});

const vitals = await client.analyzeVideo(videoFile);

Local Testing Setup

Option 1: Docker Compose (Recommended)

The quickest way to get a complete local testing environment:

# Clone the repository
git clone https://github.com/mediverusapi/rppg-model.git
cd rppg-model

# Start all services
make start-bg

# Or without make:
docker-compose up -d

Services will be available at:

  • Frontend: http://localhost (for visual testing)
  • Backend API: http://localhost/api
  • API docs: Check this documentation

Option 2: Manual Setup

# Terminal 1: Model service
cd model
python -m venv venv
source venv/bin/activate
pip install -e .
python -m uvicorn src.server:app --port 8001

# Terminal 2: Backend API
cd backend
python -m venv venv
source venv/bin/activate
pip install -e .
export DEV_MODE=true
python -m uvicorn src.main:app --port 8000

# Terminal 3: Frontend (optional, for visual testing)
cd frontend
npm install
npm run dev

Test Video Files

Creating Test Videos

For consistent testing, create test videos with these characteristics:

Good test video:

  • Duration: 10-15 seconds
  • Face centered and well-lit
  • Minimal movement
  • Frontal view
  • Resolution: 720p or higher

Sample recording script:

// Record test video from webcam
async function recordTestVideo(duration = 10000) {
  const stream = await navigator.mediaDevices.getUserMedia({
    video: { facingMode: 'user', width: 1280, height: 720 }
  });
  
  const mediaRecorder = new MediaRecorder(stream, {
    mimeType: 'video/webm;codecs=vp8,opus'
  });
  
  const chunks = [];
  
  mediaRecorder.ondataavailable = (e) => chunks.push(e.data);
  
  return new Promise((resolve) => {
    mediaRecorder.onstop = () => {
      const blob = new Blob(chunks, { type: 'video/webm' });
      resolve(blob);
    };
    
    mediaRecorder.start();
    
    setTimeout(() => {
      mediaRecorder.stop();
      stream.getTracks().forEach(track => track.stop());
    }, duration);
  });
}

Example Test Videos

You can find sample test videos in the repository:

# Download test videos
cd rppg-model/test-data
# Contains various test scenarios:
# - good-lighting-frontal.mp4
# - low-lighting.mp4
# - side-angle.mp4
# - motion-artifacts.mp4

Testing Checklist

Basic Functionality Tests

  • [ ] Authentication

    • [ ] Valid API key works
    • [ ] Invalid API key returns 401
    • [ ] Missing API key returns 401
  • [ ] Session Creation

    • [ ] Can create a new session
    • [ ] Session ID is returned
    • [ ] Session ID format is valid (sess_*)
  • [ ] Video Upload

    • [ ] Can upload valid video
    • [ ] Large files (>100MB) are rejected
    • [ ] Invalid file types are rejected
    • [ ] Empty files are rejected
  • [ ] Results Retrieval

    • [ ] Can poll for results
    • [ ] Results eventually return completed status
    • [ ] Vitals data is present and valid
    • [ ] Confidence scores are between 0-1
  • [ ] Error Handling

    • [ ] Network errors are caught
    • [ ] Timeout scenarios are handled
    • [ ] Invalid session IDs return 404

Edge Cases

  • [ ] Video Quality

    • [ ] Test with good lighting
    • [ ] Test with poor lighting
    • [ ] Test with motion artifacts
    • [ ] Test with occluded face
    • [ ] Test with no face present
  • [ ] File Formats

    • [ ] MP4 files work
    • [ ] WebM files work
    • [ ] MOV files work
    • [ ] Other formats are rejected gracefully
  • [ ] Concurrency

    • [ ] Multiple sessions can run simultaneously
    • [ ] Session IDs don't collide
    • [ ] Results are correctly associated with sessions

Test Code Examples

Unit Test Example (Jest)

import { RPPGClient } from 'rppg-api-client';

describe('RPPGClient', () => {
  let client;
  
  beforeAll(() => {
    client = new RPPGClient({
      apiKey: 'test-key',
      baseUrl: 'http://localhost/api'
    });
  });
  
  test('creates a session', async () => {
    const session = await client.createSession();
    
    expect(session).toHaveProperty('session_id');
    expect(session.session_id).toMatch(/^sess_/);
  });
  
  test('rejects files over 100MB', async () => {
    const largeFile = new File(
      [new ArrayBuffer(101 * 1024 * 1024)],
      'large.mp4',
      { type: 'video/mp4' }
    );
    
    await expect(
      client.analyzeVideo(largeFile)
    ).rejects.toThrow('file size');
  });
  
  test('analyzes valid video', async () => {
    const videoFile = await loadTestVideo('good-lighting-frontal.mp4');
    
    const vitals = await client.analyzeVideo(videoFile);
    
    expect(vitals).toHaveProperty('heart_rate');
    expect(vitals.heart_rate).toBeGreaterThan(40);
    expect(vitals.heart_rate).toBeLessThan(200);
  }, 60000); // 60 second timeout
});

Integration Test Example

describe('Full Analysis Flow', () => {
  test('complete video analysis', async () => {
    const client = new RPPGClient({
      apiKey: 'test-key',
      baseUrl: 'http://localhost/api'
    });
    
    // Step 1: Create session
    const session = await client.createSession();
    expect(session.session_id).toBeDefined();
    
    // Step 2: Upload video
    const videoFile = await loadTestVideo('test-video.mp4');
    const uploadResult = await client.uploadVideo(
      session.session_id,
      videoFile
    );
    expect(uploadResult.status).toBe('processing');
    
    // Step 3: Wait for results
    const vitals = await client.waitForVitals(session.session_id, {
      maxPollingTime: 120000
    });
    
    // Step 4: Verify results
    expect(vitals).toMatchObject({
      heart_rate: expect.any(Number),
      respiratory_rate: expect.any(Number),
      confidence: expect.any(Number)
    });
    
    expect(vitals.confidence).toBeGreaterThan(0);
    expect(vitals.confidence).toBeLessThanOrEqual(1);
  }, 180000); // 3 minute timeout
});

Load Testing Example (Artillery)

# artillery.yml
config:
  target: "http://localhost/api"
  phases:
    - duration: 60
      arrivalRate: 5
  variables:
    apiKey: "test-key"
    
scenarios:
  - name: "Video Analysis"
    flow:
      - post:
          url: "/v1/scan-session"
          headers:
            Authorization: "Bearer {{ apiKey }}"
          capture:
            - json: "$.session_id"
              as: "sessionId"
      
      - post:
          url: "/v1/scan-session/{{ sessionId }}/video"
          headers:
            Authorization: "Bearer {{ apiKey }}"
          beforeRequest: "loadVideoFile"
      
      - loop:
          - get:
              url: "/v1/scan-session/{{ sessionId }}/vitals"
              headers:
                Authorization: "Bearer {{ apiKey }}"
          count: 40
          delay: 3

Debugging Tips

Enable Verbose Logging

// Set environment variable
process.env.DEBUG = 'rppg:*';

// Or use console.log in SDK
const client = new RPPGClient({
  apiKey: 'test-key',
  baseUrl: 'http://localhost/api',
  debug: true  // If supported
});

Check Backend Logs

# Docker logs
docker-compose logs -f backend
docker-compose logs -f model

# Or with make
make logs-backend
make logs-model

Inspect Network Requests

Use browser DevTools or tools like:

  • Chrome DevTools โ†’ Network tab
  • Postman โ†’ For API testing
  • cURL โ†’ Command-line testing

Example cURL commands:

# Create session
curl -v -X POST http://localhost/api/v1/scan-session \
  -H "Authorization: Bearer test-key"

# Check health
curl http://localhost/api/health

# Get vitals
curl http://localhost/api/v1/scan-session/sess_xxx/vitals \
  -H "Authorization: Bearer test-key"

Common Issues

Issue: "Connection refused"

  • Solution: Ensure backend is running on the correct port

Issue: "404 Not Found"

  • Solution: Check API base URL includes /api prefix

Issue: "CORS errors"

  • Solution: Use backend proxy or enable CORS in dev mode

Issue: "Video processing never completes"

  • Solution: Check model service logs for errors

Issue: "Heart rate returns 0"

  • Solution: Ensure face is clearly visible in video, check lighting

Performance Benchmarks

Expected performance metrics for testing:

| Metric | Value | |--------|-------| | Session creation | < 100ms | | Video upload (10s, 720p) | < 2s | | Processing time | 30-40s | | Result retrieval | < 50ms | | Total end-to-end | 35-45s |

Mock Data

For frontend development without a backend:

// Mock client for testing
class MockRPPGClient {
  async createSession() {
    await this.delay(100);
    return {
      session_id: 'sess_mock_123',
      created_at: new Date().toISOString(),
      owner: 'mock-user'
    };
  }
  
  async uploadVideo(sessionId, file) {
    await this.delay(500);
    return {
      session_id: sessionId,
      status: 'processing'
    };
  }
  
  async waitForVitals(sessionId, options = {}) {
    // Simulate processing time
    await this.delay(5000);
    
    return {
      session_id: sessionId,
      heart_rate: 72 + Math.random() * 10,
      respiratory_rate: 16 + Math.random() * 4,
      hrv: 45 + Math.random() * 10,
      blood_pressure: null,
      confidence: 0.85 + Math.random() * 0.1,
      timestamp: new Date().toISOString(),
      processing_time_ms: 5000
    };
  }
  
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Use in development
const client = process.env.USE_MOCK 
  ? new MockRPPGClient()
  : new RPPGClient({ apiKey: 'key', baseUrl: '/api' });

Test Environment URLs

Local Development

  • Backend: http://localhost/api
  • Frontend: http://localhost

Staging (if available)

  • Backend: https://staging-api.yourdomain.com/api

Production

  • Backend: https://api.yourdomain.com/api

Next Steps

Have questions? Contact us at support@circadify.com