485 lines
12 KiB
Markdown
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
|