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:
- Edit
pb/src/client_container.proto - Run
cd pb && ./build - Generated Ruby bindings go to
pb/target/ruby/jampb - 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_tokencookie - Shares session between
www.jamkazam.localandbeta.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 %