jam-cloud/CLAUDE.md

485 lines
12 KiB
Markdown

# 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):**
```bash
cd pb
./build
```
**Ruby gem (shared library):**
```bash
cd ruby
bundle install
./migrate.sh
```
**Web (Rails API):**
```bash
cd web
bundle install
```
**Admin (Rails admin portal):**
```bash
cd admin
bundle install
```
**WebSocket Gateway:**
```bash
cd websocket-gateway
bundle install
```
**React frontend:**
```bash
cd jam-ui
npm install
```
## Common Commands
### Running Services
**React frontend:**
```bash
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:**
```bash
./runweb
# or manually:
cd web
bundle exec rails s
# Runs on http://www.jamkazam.local:3000
```
**Admin portal:**
```bash
./runadmin
# or manually:
cd admin
bundle exec rails s
```
**Background job workers:**
```bash
./runjobs
# or manually:
cd web
bundle exec rake all_jobs
```
**WebSocket gateway:**
```bash
cd websocket-gateway
# Start via appropriate launch script
```
### Building
**Build all services:**
```bash
./build
```
**Build individual services:**
```bash
# 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:**
```bash
./runtests
```
**Individual service tests:**
```bash
# 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:**
```bash
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**
```bash
# 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**
```bash
# 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**
```bash
# 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)**
```javascript
// 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)**
```javascript
// 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)**
```javascript
// 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**
```javascript
// 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**
```bash
npm test trackSyncService.test.js
# ❌ FAIL: syncTracksToServer is not defined
```
**Step 3: Implement minimum code**
```javascript
// 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**
```bash
npm test trackSyncService.test.js
# ✅ PASS: All tests passed
```
**Step 5: Write integration test**
```javascript
// 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**
```bash
npx playwright test track-configuration.spec.ts
# ❌ FAIL: Expected at least 1 call, received 0
```
**Step 7: Wire up component to call service**
```javascript
// src/components/client/JKSessionScreen.js
useEffect(() => {
if (sessionJoined) {
dispatch(syncTracksToServer(sessionId));
}
}, [sessionJoined]);
```
**Step 8: Run test - should PASS**
```bash
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):**
```bash
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):**
```bash
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):**
```bash
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