jam-cloud/CLAUDE.md

12 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

System Architecture

JamKazam is a real-time music collaboration platform with a service-oriented architecture:

Historically the front end of this app was made using Ruby On Rails and React/Javascript. The React/Javascript front is rendered on the Rails views. The controllers basically serves the API end points which extracts data from the Model layer. The websocket gateway acts as a realtime data bridge between the front end and the backend. jam-ui is a newer version of the front end with more recent version of reactjs in which the Rails app operates as the backend API layer. Audio interface capabilities and audio data been sent to the front end by a native C++ client (a separate project) and the front end commiunicates with this native layer using a socket connection proxied by jamClient. calls*

Core Services:

  • jam-ui: React 14.21.3 frontend SPA (port 4000)
  • web: Rails 4.2.8 backend API (port 3000)
  • admin: Rails 4.2.8 admin portal with ActiveAdmin
  • websocket-gateway: EventMachine-based WebSocket server for real-time communication
  • ruby: Shared business logic gem (jam_ruby)
  • pb: Protocol Buffer definitions for WebSocket messaging
  • db: PostgreSQL schema migrations (pg_migrate)

Communication:

  • jam-ui ↔ web: REST API (/api/* endpoints)
  • jam-ui ↔ websocket-gateway: WebSocket with Protocol Buffers
  • Services ↔ Database: Single shared PostgreSQL database
  • Async messaging: RabbitMQ/AMQP between services
  • Background jobs: Resque with Redis

Technology Stack:

  • Ruby 2.4.1 (pinned, with noted upgrade path to 2.5+)
  • Rails 4.2.8
  • React 14.21.3
  • PostgreSQL
  • Redis (Resque job queue)
  • RabbitMQ (AMQP messaging)
  • EventMachine (async I/O)
  • Protocol Buffers (real-time messaging)

Development Setup

Prerequisites

Local hosts configuration:

127.0.0.1 www.jamkazam.local     # Rails web app (web)
127.0.0.1 beta.jamkazam.local    # React app (jam-ui)

When executing Rails commands on MacOS Sillicon chip (m1, m2, ...) computers, use Gemfile.alt with bundler version 1.17.3. For exmaple to run the web server, use following command with the flags. BUNDLE_GEMFILE=Gemfile.alt MODERN_OS=1 JAM_RUBY_VERSION=2.4.1 bundle 1.17.3 exec rails s -p 3000

Installing Dependencies

Protocol Buffers (required first):

cd pb
./build

Ruby gem (shared library):

cd ruby
bundle install
./migrate.sh

Web (Rails API):

cd web
bundle install

Admin (Rails admin portal):

cd admin
bundle install

WebSocket Gateway:

cd websocket-gateway
bundle install

React frontend:

cd jam-ui
npm install

Common Commands

Running Services

React frontend:

cd jam-ui
nvm use #this set the node version specified in the .nvmrc
npm run start
# Runs on http://beta.jamkazam.local:4000

Rails web API:

./runweb
# or manually:
cd web
bundle exec rails s
# Runs on http://www.jamkazam.local:3000

Admin portal:

./runadmin
# or manually:
cd admin
bundle exec rails s

Background job workers:

./runjobs
# or manually:
cd web
bundle exec rake all_jobs

WebSocket gateway:

cd websocket-gateway
# Start via appropriate launch script

Building

Build all services:

./build

Build individual services:

# Protocol buffers
cd pb
./build

# Ruby gem
cd ruby
./build

# Web
cd web
./jenkins  # CI build script

# Admin
cd admin
./jenkins

# WebSocket gateway
cd websocket-gateway
./jenkins

# React frontend
cd jam-ui
nvm use
npm run build

Testing

Run all tests:

./runtests

Individual service tests:

# Ruby gem tests
cd ruby
bundle exec rspec

# Web tests
cd web
bundle exec rspec

# Admin tests
cd admin
bundle exec rspec

# WebSocket gateway tests
cd websocket-gateway
bundle exec rspec

# React tests
cd jam-ui
npx cypress run #Runs cypress tests
npm test  # Runs Playwright tests

Code Style

SCSS compilation:

cd jam-ui
npm run scss

Development Patterns

Naming Conventions

React components: Prefix with JK (e.g., JKMusicSessions, JKDashboardMain)

Component files: Follow the pattern: src/components/{category}/JK{ComponentName}.js

Local vs Production Gems

The Gemfiles use conditional logic:

  • Development: Uses local paths (:path => "../ruby")
  • CI/Production: Uses gem server with build numbers

When working locally, gem dependencies automatically resolve to sibling directories.

Protocol Buffers

When modifying Protocol Buffer definitions:

  1. Edit pb/src/client_container.proto
  2. Run cd pb && ./build
  3. Generated Ruby bindings go to pb/target/ruby/jampb
  4. All services automatically pick up changes via local gem path

Routes and API

React routes: Defined in jam-ui/src/components/dashboard/JKDashboardMain.js

Rails API routes:

  • Web: web/config/routes.rb
  • Admin: admin/config/routes.rb

Session Authentication

React app uses session-based authentication from Rails:

  • Looks for remember_token cookie
  • Shares session between www.jamkazam.local and beta.jamkazam.local
  • Redirects to Rails sign-in if unauthenticated

Test-Driven Development (TDD)

CRITICAL: All jam-ui code changes MUST follow TDD methodology.

TDD Workflow (RED-GREEN-REFACTOR)

1. RED - Write Failing Test First

# Write test that describes desired behavior
# Test should FAIL because feature doesn't exist yet
npm test path/to/test.spec.js

2. GREEN - Implement Minimum Code to Pass

# Write simplest code that makes test pass
# Don't worry about perfection yet
npm test path/to/test.spec.js  # Should PASS

3. REFACTOR - Improve Code Quality

# Refactor while keeping tests green
# Improve structure, remove duplication
# Tests must still pass
npm test path/to/test.spec.js  # Should still PASS

When to Use TDD

ALWAYS use TDD for:

  • New features in jam-ui
  • Bug fixes in jam-ui
  • API integrations
  • Redux state management changes
  • Business logic in services/hooks
  • Component behavior changes

TDD Optional for:

  • Styling-only changes (CSS/SCSS)
  • Documentation updates
  • Configuration changes

Test Types for jam-ui

1. Unit Tests (Jest)

// Service/hook logic, pure functions
// File: src/services/__tests__/trackSyncService.test.js
describe('buildTrackSyncPayload', () => {
  test('builds correct payload from Redux state', () => {
    const payload = buildTrackSyncPayload(mockState, sessionId);
    expect(payload.tracks).toHaveLength(1);
  });
});

2. Integration Tests (Playwright)

// API calls, user flows, component interactions
// File: test/track-sync/track-configuration.spec.ts
test('syncs tracks when user joins session', async ({ page }) => {
  await loginToJamUI(page);
  await createAndJoinSession(page);
  const trackCalls = apiInterceptor.getCallsByPath('/api/sessions/*/tracks');
  expect(trackCalls.length).toBeGreaterThan(0);
});

3. E2E Tests (Playwright)

// Complete user workflows across multiple pages
// File: test/e2e/complete-session-flow.spec.ts
test('complete flow: login → create → join', async ({ page }) => {
  // Full journey test
});

TDD Example: Adding Track Sync

Step 1: Write failing unit test

// src/services/__tests__/trackSyncService.test.js
describe('syncTracksToServer', () => {
  test('calls API with correct payload', async () => {
    const result = await syncTracksToServer(sessionId);
    expect(mockAPI.putTrackSyncChange).toHaveBeenCalledWith({
      id: sessionId,
      client_id: 'client-uuid',
      tracks: expect.any(Array),
      backing_tracks: expect.any(Array),
      metronome_open: false
    });
  });
});

Step 2: Run test - should FAIL

npm test trackSyncService.test.js
# ❌ FAIL: syncTracksToServer is not defined

Step 3: Implement minimum code

// src/services/trackSyncService.js
export const syncTracksToServer = (sessionId) => async (dispatch, getState) => {
  const payload = { /* ... */ };
  return await putTrackSyncChange({ id: sessionId, ...payload });
};

Step 4: Run test - should PASS

npm test trackSyncService.test.js
# ✅ PASS: All tests passed

Step 5: Write integration test

// test/track-sync/track-configuration.spec.ts
test('API called when joining session', async ({ page }) => {
  const interceptor = new APIInterceptor();
  interceptor.intercept(page);

  await loginToJamUI(page);
  await createAndJoinSession(page);

  const trackCalls = interceptor.getCallsByPath('/tracks');
  expect(trackCalls.length).toBeGreaterThan(0);
});

Step 6: Run test - should FAIL

npx playwright test track-configuration.spec.ts
# ❌ FAIL: Expected at least 1 call, received 0

Step 7: Wire up component to call service

// src/components/client/JKSessionScreen.js
useEffect(() => {
  if (sessionJoined) {
    dispatch(syncTracksToServer(sessionId));
  }
}, [sessionJoined]);

Step 8: Run test - should PASS

npx playwright test track-configuration.spec.ts
# ✅ PASS: API call detected

TDD Best Practices

DO:

  • Write test BEFORE implementation
  • Write smallest test possible
  • Test behavior, not implementation
  • Use descriptive test names
  • Mock external dependencies (API, native client)
  • Run tests frequently during development
  • Keep tests fast (unit tests < 100ms)

DON'T:

  • Write implementation before test
  • Skip tests to "save time"
  • Test implementation details
  • Write giant tests covering multiple features
  • Commit failing tests to main branch
  • Mock too much (makes tests brittle)

Test File Locations

jam-ui/
├── src/
│   ├── services/
│   │   ├── trackSyncService.js
│   │   └── __tests__/
│   │       └── trackSyncService.test.js          # Unit tests
│   ├── hooks/
│   │   ├── useTrackSync.js
│   │   └── __tests__/
│   │       └── useTrackSync.test.js              # Hook tests
│   └── components/
│       └── client/
│           ├── JKSessionScreen.js
│           └── __tests__/
│               └── JKSessionScreen.test.js       # Component tests
├── test/
│   ├── track-sync/
│   │   └── track-configuration.spec.ts           # Integration tests
│   └── e2e/
│       └── complete-session-flow.spec.ts         # E2E tests

Running Tests

Unit tests (Jest):

cd jam-ui
npm run test:unit                    # All unit tests
npm run test:unit -- trackSync       # Specific test
npm run test:unit -- --watch         # Watch mode

Integration tests (Playwright):

cd jam-ui
npm test                                          # All Playwright tests
npx playwright test track-configuration.spec.ts  # Specific test
npx playwright test --debug                       # Debug mode
npx playwright test --ui                          # UI mode

Playwright with Chrome (recommended for jam-ui):

npx playwright test --config=playwright.chrome.config.ts

Test Coverage Goals

  • Unit tests: 80%+ coverage for services/hooks
  • Integration tests: All critical user flows
  • E2E tests: Happy path + major error scenarios

Continuous Integration

All tests run automatically on:

  • Pull request creation
  • Commits to main/develop branches
  • Pre-deployment checks

Pull requests must:

  • Pass all existing tests
  • Include tests for new features
  • Maintain or improve coverage %

Important Notes