docs: map existing codebase

- STACK.md (118 lines) - Technologies and dependencies
- ARCHITECTURE.md (168 lines) - System design and patterns
- STRUCTURE.md (195 lines) - Directory layout
- CONVENTIONS.md (167 lines) - Code style and patterns
- TESTING.md (274 lines) - Test structure
- INTEGRATIONS.md (261 lines) - External services
- CONCERNS.md (251 lines) - Technical debt and issues

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Nuwan 2026-01-11 14:59:10 +05:30
parent e39ebb5e23
commit f6d805b0f9
7 changed files with 1434 additions and 0 deletions

View File

@ -0,0 +1,168 @@
# Architecture
**Analysis Date:** 2026-01-11
## Pattern Overview
**Overall:** Hybrid Microservices with Shared Monolithic Database
**Key Characteristics:**
- Service-oriented architecture with distinct service boundaries
- Monolithic Rails backend transitioning to modern React frontend
- Shared PostgreSQL database across all services
- Real-time communication via WebSocket gateway with Protocol Buffers
- Async messaging via RabbitMQ/AMQP between services
## Layers
**Presentation Layer (Frontend):**
- Purpose: User interface and client-side logic
- Contains: React 16 SPA with Redux Toolkit state management
- Location: `jam-ui/src/`
- Entry Point: `jam-ui/src/index.js``Main.js` (providers) → `App.js` (layout router)
- Depends on: Rails API (`web/`) via REST, WebSocket gateway for real-time
- Used by: End users via browser at `http://beta.jamkazam.local:4000`
**API Layer (Backend):**
- Purpose: REST API endpoints, request processing, authentication
- Contains: Rails 4.2.8 controllers, responders, error handling
- Location: `web/app/controllers/` (83 API controller files)
- Base Controller: `api_controller.rb` provides auth, error handling, response formatting
- Depends on: Shared business logic gem (`ruby/`), PostgreSQL
- Used by: React frontend, mobile apps, third-party integrations
**Real-time Communication Layer:**
- Purpose: WebSocket connections for live session updates
- Contains: EventMachine-based WebSocket server with Protocol Buffer messaging
- Location: `websocket-gateway/`
- Protocol: Protocol Buffers (`.proto` definitions in `pb/src/client_container.proto`)
- Bridge: `jam-ui/src/jamClientProxy.js` manages native C++ client communication via QWebChannel
- Depends on: Shared Ruby gem, WebSocket protocol
- Used by: React frontend, native desktop client
**Shared Business Logic Layer:**
- Purpose: Centralized models, validations, domain logic
- Contains: ActiveRecord models (217 files), managers, message factory
- Location: `ruby/lib/jam_ruby/` (shared gem embedded via local path)
- Key Files: `ruby/lib/jam_ruby/models/` (217 model files), `message_factory.rb`, `connection_manager.rb`
- Depends on: Rails frameworks, PostgreSQL
- Used by: All services (web, admin, websocket-gateway)
**Data Layer:**
- Purpose: Persistent storage
- Contains: PostgreSQL schema, migrations
- Location: `db/up/` (403+ migration files)
- ORM: ActiveRecord models in `ruby/lib/jam_ruby/models/`
- Depends on: PostgreSQL server
- Used by: All services via ActiveRecord
## Data Flow
**REST API Request (Example: Fetch User Profile):**
1. User action in React component (e.g., `JKUserProfile.js`)
2. Redux dispatch or direct API call via `apiFetch()` (`jam-ui/src/helpers/rest.js`)
3. HTTP request to Rails controller (e.g., `api_users_controller.rb`)
4. Controller uses shared business logic (`ruby/lib/jam_ruby/models/user.rb`)
5. ActiveRecord query to PostgreSQL database
6. Response serialized (RABL template or JSON)
7. Redux store updated (`jam-ui/src/store/features/*.js`)
8. Component re-renders with new data
**Real-time Session Flow (Example: Join Music Session):**
1. Native C++ desktop client sends message
2. Proxied through `jamClientProxy.js` (QWebChannel tunnel to frontend)
3. WebSocket message to gateway (`websocket-gateway/`)
4. Protocol Buffer decoding → Rails backend logic
5. Async processing via Resque jobs or AMQP messages
6. State changes persisted to PostgreSQL
7. Broadcast back to all session participants via WebSocket
8. Protocol Buffer encoding → Frontend updates Redux state
**State Management:**
- Redux Toolkit for global app state (sessions, tracks, user, notifications)
- Context API for UI state (native client connection, media player, global settings)
- WebSocket for real-time state synchronization
## Key Abstractions
**Component (React):**
- Purpose: UI building blocks with JK prefix for brand-specific components
- Examples: `JKDashboardMain.js`, `JKSessionModal.js`, `JKJamTrackPlayer.js` in `jam-ui/src/components/`
- Pattern: Functional components with hooks, prop destructuring, Redux integration
**Hook (React):**
- Purpose: Reusable stateful logic
- Examples: `useJamServer.js` (26KB, WebSocket orchestration), `useJamTrack.js`, `useGearUtils.js` in `jam-ui/src/hooks/`
- Pattern: `use*` naming convention, returns state and handlers
**Controller (Rails):**
- Purpose: Handle HTTP requests, route to business logic
- Examples: `api_sessions_controller.rb`, `api_jam_tracks_controller.rb`, `api_users_controller.rb` in `web/app/controllers/`
- Pattern: RESTful actions, inherit from `api_controller.rb`, use responders
**Model (Rails/ActiveRecord):**
- Purpose: Domain objects with validations and business rules
- Examples: `user.rb` (3,078 lines), `music_session.rb` (1,481 lines), `jam_track.rb` in `ruby/lib/jam_ruby/models/`
- Pattern: ActiveRecord associations, scopes, state machines (AASM)
**API Service Function:**
- Purpose: Frontend-to-backend communication abstractions
- Examples: `getSession(id)`, `joinSession(options)`, `updateUser(id, data)` in `jam-ui/src/helpers/rest.js`
- Pattern: Promise-based, wraps `apiFetch()`, returns resolved/rejected promises
## Entry Points
**Frontend Entry:**
- Location: `jam-ui/src/index.js`
- Triggers: Browser loads page at `http://beta.jamkazam.local:4000`
- Responsibilities: Render React app, wrap with providers (Redux, Context), route to layouts
**Backend API Entry:**
- Location: `web/config/routes.rb` (51KB route definitions)
- Triggers: HTTP request to `/api/*` endpoints
- Responsibilities: Route to controller actions, apply middleware, return JSON responses
**WebSocket Gateway Entry:**
- Location: `websocket-gateway/` (EventMachine server)
- Triggers: WebSocket connection from client
- Responsibilities: Maintain persistent connections, decode/encode Protocol Buffers, broadcast messages
**Shared Library Entry:**
- Location: `ruby/lib/jam_ruby.rb`
- Triggers: Required by Rails services
- Responsibilities: Load all models, managers, and shared utilities
## Error Handling
**Strategy:** Exceptions bubble to top-level handler, logged and returned as JSON errors
**Patterns:**
- Controllers use `api_controller.rb` base class for centralized error handling
- Frontend catches promise rejections from `apiFetch()`, displays user-friendly errors
- Background jobs (Resque) retry on transient failures, log permanent failures
- WebSocket errors logged server-side, client notified via error messages
## Cross-Cutting Concerns
**Logging:**
- Rails logger for backend (stdout in development, files in production)
- Console logging in React frontend (removed in production builds)
- Bugsnag for error tracking (API key configured in `web/config/application.rb`)
- InfluxDB for time-series metrics (configured but disabled for production)
**Validation:**
- ActiveRecord validations in model layer (`ruby/lib/jam_ruby/models/*`)
- React Hook Form for frontend form validation
- API parameter validation in controllers
**Authentication:**
- Devise for user authentication (session-based cookies)
- OAuth via OmniAuth (Facebook, Google, Twitter)
- Session sharing between `www.jamkazam.local` (Rails) and `beta.jamkazam.local` (React) via `remember_token` cookie
---
*Architecture analysis: 2026-01-11*
*Update when major patterns change*

View File

@ -0,0 +1,251 @@
# Codebase Concerns
**Analysis Date:** 2026-01-11
## Tech Debt
**God Objects - Massive Files:**
- Issue: Several models and helpers have grown too large, violating single responsibility
- Files:
- `ruby/lib/jam_ruby/jam_track_importer.rb` - 3,617 lines (import logic)
- `ruby/lib/jam_ruby/models/user.rb` - 3,078 lines (user model with too many responsibilities)
- `ruby/lib/jam_ruby/models/notification.rb` - 1,700 lines
- `ruby/lib/jam_ruby/models/music_session.rb` - 1,481 lines
- `ruby/lib/jam_ruby/models/lesson_session.rb` - 1,312 lines
- `web/app/controllers/api_users_controller.rb` - 1,407 lines
- `jam-ui/src/helpers/MixerHelper.js` - 1,270 lines (complex audio mixer logic)
- `jam-ui/src/hooks/useMixerHelper_delete.js` - 1,326 lines (appears to be dead code)
- `web/app/assets/javascripts/session.js` - 3,345 lines
- `web/app/assets/javascripts/jam_rest.js` - 3,227 lines
- Impact: Hard to maintain, test, understand; high coupling
- Fix approach: Break into smaller modules by responsibility, extract services/managers
**N+1 Query Problems:**
- Issue: Commented-out eager loading causing N+1 queries
- Files:
- `ruby/lib/jam_ruby/models/jam_track.rb:387` - "FIXME: n+1 queries for rights and genres"
- `ruby/lib/jam_ruby/models/feed.rb:35` - "TODO: SPEED UP QUERY. CURRENTLY TAKES FOR EVER."
- Impact: Performance degradation under load, slow API responses
- Fix approach: Uncomment and fix eager loading with `includes()`, add database indexes
**Synchronous Operations (Should Be Async):**
- Issue: Blocking operations that should run in background jobs
- Files:
- `ruby/lib/jam_ruby/models/notification.rb:82-83` - All notification methods should be async
- `ruby/lib/jam_ruby/models/user.rb:1379` - "TODO: make this async" (Notification.send_new_user_follower)
- `ruby/lib/jam_ruby/models/user.rb:1392` - "TODO: make this async" (Notification.send_new_band_follower)
- `ruby/lib/jam_ruby/models/notification.rb:543` - "TODO: do this in BULK or in async block"
- Impact: Slow request/response times, poor user experience
- Fix approach: Move to Resque background jobs, use AMQP for async messaging
**Inefficient In-Memory Operations:**
- Issue: Loading large datasets into memory instead of counting in database
- Files:
- `ruby/lib/jam_ruby/models/band.rb:74` - "FIXME: this could be a lot of followers; calling size loads all the data into memory"
- Multiple methods in `ruby/lib/jam_ruby/models/user.rb` using `.size` instead of `.count()`
- Impact: High memory usage, potential OOM errors with large datasets
- Fix approach: Use `.count()` for database counting, avoid loading associations into memory
**Dead/Duplicate Code:**
- Issue: Legacy files not cleaned up
- Files:
- `jam-ui/src/hooks/useMixerHelper_delete.js` - 1,326 lines (appears to be backup)
- `jam-ui/src/sessions/JKLobbyChat copy.js` - Duplicate/backup file
- Impact: Code confusion, increased maintenance burden
- Fix approach: Delete dead code, ensure backups via Git history
## Known Bugs
**Race Conditions:**
- Symptoms: Multiple users completing uploads simultaneously triggers duplicate jobs
- Trigger: Two users complete recording uploads at same time
- File: `ruby/lib/jam_ruby/models/recording.rb:685` - "FIXME: There's a possible race condition here. If two users complete uploads at the same time, we'll schedule 2 mixes."
- Root cause: No atomic check-and-set for upload completion
- Fix: Add database-level locking or use Redis distributed lock
**Timing Vulnerabilities:**
- Symptoms: Lesson session scheduling can create past sessions
- Trigger: Scheduling logic with week offset
- File: `ruby/lib/jam_ruby/models/lesson_session.rb:1040` - "XXX: week offset as 0? This *could* still be in the past."
- Root cause: Time calculation doesn't account for current moment
- Fix: Validate scheduled time is always in future before saving
## Security Considerations
**SQL Injection Vulnerabilities:**
- Risk: String interpolation in SQL queries allows injection attacks
- Files:
- `ruby/lib/jam_ruby/models/notification.rb:75` - `Notification.delete_all "(session_id = '#{self.session_id}')"`
- `ruby/lib/jam_ruby/models/join_request.rb:25` - `JoinRequest.where("join_requests.user_id = '#{current_user.id}'")`
- `ruby/lib/jam_ruby/models/user.rb:772` - Multiple string interpolations in where clauses
- `ruby/lib/jam_ruby/models/user.rb:1397,1419,1469` - String interpolation in delete_all queries
- `ruby/lib/jam_ruby/models/ip_blacklist.rb:11,15` - SQL injection in IP validation
- Current mitigation: None (direct string interpolation used)
- Recommendations: Use parameterized queries, ActiveRecord placeholders (?, named parameters)
**Hardcoded Credentials in Source:**
- Risk: Credentials exposed in version control
- Files:
- `web/Gemfile:17` - `source 'https://jamjam:blueberryjam@int.jamkazam.com/gems/'`
- `ruby/Gemfile:4` - Same hardcoded credentials
- Current mitigation: Private repository (still not secure)
- Recommendations: Use environment variables, secure credential storage
**HTTP Over HTTPS:**
- Risk: Unencrypted communication exposes data
- File: `ruby/lib/jam_ruby/lib/s3_manager.rb:32` - "the client can not support HTTPS atm!!! AGH"
- Current mitigation: None (explicitly using HTTP)
- Recommendations: Update client to support HTTPS, enforce SSL/TLS
**Missing Error Handling:**
- Risk: Unhandled failures expose system internals
- Files:
- `web/lib/google_client.rb:56` - "XXX: what to do when this fails?"
- `web/lib/google_client.rb:372` - "TODO: how to test for this:"
- Current mitigation: None
- Recommendations: Add proper error handling with user-friendly messages
## Performance Bottlenecks
**Slow Queries:**
- Problem: Feed query takes excessive time
- File: `ruby/lib/jam_ruby/models/feed.rb:35` - Marked "CURRENTLY TAKES FOR EVER."
- Measurement: Not quantified (needs profiling)
- Cause: Likely missing indexes, complex joins, or N+1 queries
- Improvement path: Profile query, add indexes, optimize joins, eager load associations
**Memory Leaks:**
- Problem: AMQP connection manager documented as "leaky"
- File: `ruby/lib/jam_ruby/amqp/amqp_connection_manager.rb:3` - "It's 'leaky' in that it will give you a AMQP::Channel"
- Measurement: Not quantified
- Cause: Channels not properly closed/released
- Improvement path: Implement proper resource cleanup, use connection pooling
## Fragile Areas
**Complex State Management:**
- File: `ruby/lib/jam_ruby/models/recorded_track.rb:4`
- Why fragile: "TODO: use aasm instead of all these bool and bool_was (dirty module) checks"
- Common failures: State transitions not properly tracked, inconsistent state
- Safe modification: Migrate to AASM state machine for better transition management
- Test coverage: Unknown (needs investigation)
**WebSocket Message Handling:**
- File: `ruby/lib/jam_ruby/message_factory.rb` (42KB)
- Why fragile: Single massive file handling 100+ message types
- Common failures: New message types not properly handled, encoding/decoding errors
- Safe modification: Extract message handlers into separate classes per message type
- Test coverage: Partial (needs more coverage for edge cases)
## Scaling Limits
**Connection Pooling:**
- Current capacity: Default PostgreSQL connection pool (5 connections per process)
- Limit: ~50 concurrent users per web/admin instance before connection exhaustion
- Symptoms at limit: ActiveRecord::ConnectionTimeoutError, slow responses
- Scaling path: Increase connection pool size, add connection pooling middleware (PgBouncer)
**File Storage:**
- Current capacity: AWS S3 (effectively unlimited)
- Limit: Cost-based rather than technical
- Symptoms at limit: Increased AWS bills
- Scaling path: Implement CDN caching, compress media files, archive old content
## Dependencies at Risk
**Ruby Version (Critical):**
- Risk: Ruby 2.3.1-2.4.1 is severely outdated (current is 3.3+), EOL, security vulnerabilities
- Impact: Missing security patches, performance improvements, language features
- Migration plan: Upgrade to Ruby 3.x (requires extensive testing, gem compatibility checks)
**Rails Version (Critical):**
- Risk: Rails 4.2.8 is EOL (current is 7.x), no security patches
- Impact: Security vulnerabilities, missing features, incompatible with modern gems
- Migration plan: Upgrade to Rails 7.x (major breaking changes, extensive refactoring required)
**React Version (Medium):**
- Risk: React 16.13.1 is two major versions behind (current is 18.x)
- Impact: Missing concurrent rendering, automatic batching, newer React features
- Migration plan: Upgrade to React 18 (relatively straightforward, test for breaking changes)
**Nokogiri (Critical):**
- Risk: nokogiri 1.10.10 has known security vulnerabilities
- Impact: XML parsing vulnerabilities, potential XSS attacks
- Migration plan: Update to latest nokogiri version (check for breaking API changes)
**Devise (Medium):**
- Risk: devise 3.3.0 is very outdated (current is 4.x)
- Impact: Missing security improvements, authentication features
- Migration plan: Upgrade to Devise 4.x (check for breaking changes in authentication flow)
## Missing Critical Features
**Email Validation:**
- Problem: No validation of email quality in user registration
- File: `ruby/lib/jam_ruby/models/user.rb:1097` - "FIXME: Should verify that the new password meets certain quality criteria"
- Current workaround: None (accepts any password format)
- Blocks: Password security compliance, account security
- Implementation complexity: Low (add password strength validation)
**Model Validations:**
- Problem: Several validations commented out or marked as TODO
- Files:
- `ruby/lib/jam_ruby/models/rsvp_slot.rb:22` - "TODO: validates :proficiency_level"
- `ruby/lib/jam_ruby/models/user_asset.rb:11` - "TODO: validate asset_type"
- Current workaround: No validation (relies on application-level checks)
- Blocks: Data integrity, consistent database state
- Implementation complexity: Low (uncomment and add validations)
**Async Notification System:**
- Problem: Notifications sent synchronously, blocking request/response
- Current workaround: Users wait for notifications to send
- Blocks: Fast API response times, scalability
- Implementation complexity: Medium (migrate to Resque/Sidekiq jobs)
## Test Coverage Gaps
**Complex Business Logic:**
- What's not tested: Large portions of User model (3,078 lines), MusicSession lifecycle
- Risk: Changes break critical user flows without detection
- Priority: High
- Difficulty to test: Medium (requires extensive fixtures, complex setup)
**WebSocket Gateway:**
- What's not tested: Full Protocol Buffer message flow end-to-end
- Risk: Real-time features break silently
- Priority: High
- Difficulty to test: High (requires WebSocket test infrastructure)
**Legacy JavaScript:**
- What's not tested: Many legacy JavaScript files lack tests
- Files: `web/app/assets/javascripts/session.js` (3,345 lines), `jam_rest.js` (3,227 lines)
- Risk: Breaks in production, hard to refactor
- Priority: Medium
- Difficulty to test: High (legacy code, no clear boundaries)
## Documentation Gaps
**Complex Helpers:**
- What's undocumented: `MixerHelper.js` (1,270 lines) has minimal comments
- File: `jam-ui/src/helpers/MixerHelper.js`
- Risk: Hard to understand audio mixer logic, difficult to maintain
- Priority: Medium
- Fix: Add inline documentation explaining audio concepts, mixer state management
**API Endpoints:**
- What's undocumented: No centralized API documentation for frontend consumers
- Risk: Frontend developers don't know available endpoints, parameters
- Priority: Medium
- Fix: Generate API docs from Rails routes, use Swagger/OpenAPI
**Debugging Code Left In:**
- Files:
- `jam-ui/src/context/JamClientContext.js:42` - "TODO: Expose jamClient globally for debugging. This is temporarily added. Remove it later."
- `jam-ui/src/helpers/MixerHelper.js:9` - "TODO - remove mixMode from MixerHelper?"
- Impact: Temporary code becomes permanent, clutters codebase
- Fix: Clean up debugging code before merging to main
---
*Concerns audit: 2026-01-11*
*Update as issues are fixed or new ones discovered*

View File

@ -0,0 +1,167 @@
# Coding Conventions
**Analysis Date:** 2026-01-11
## Naming Patterns
**Files:**
- React components: `JK{ComponentName}.js` (JamKazam brand prefix)
- Examples: `JKDashboardMain.js`, `JKTimeAgo.js`, `JKSessionModal.js` in `jam-ui/src/components/`
- Non-JK components: `PascalCase.js` (e.g., `Avatar.js`, `FalconCardHeader.js`)
- Test files: `*.test.js` (Jest unit tests), `*.spec.ts` (Playwright E2E), `*.spec.rb` (RSpec)
**Functions:**
- camelCase for all functions: `getPersonById()`, `getUserDetail()`, `createAffiliatePartner()`
- Examples in `jam-ui/src/helpers/rest.js`
- Async functions: No special prefix (async/await pattern used)
- Event handlers: `handle{EventName}` pattern (e.g., `handleClick`, `handleSubmit`)
**Variables:**
- camelCase for variables: `firstName`, `loadingExperiences`, `classNames`
- State variables: `const [firstName, setFirstName] = useState('Anthony');`
- Constants: UPPERCASE convention not strictly enforced, but used for module-level constants
**Types:**
- Interfaces/Types: PascalCase, no `I` prefix
- React PropTypes: Imported and used for component prop validation
- Example from `jam-ui/src/components/common/Avatar.js`: `import PropTypes from 'prop-types';`
## Code Style
**Formatting:**
- Prettier enforced via `.prettierrc` in `jam-ui/`
- Single quotes: `true` (use `'string'` not `"string"`)
- Print width: `120` characters
- Indentation: 2 spaces (standard Prettier default)
- Semicolons: Not explicitly configured, follows Prettier defaults
**Linting:**
- ESLint with `.eslintrc.json` in `jam-ui/`
- Extends: `react-app`, `prettier`, `plugin:react/recommended`
- Plugins: `prettier`
- Rule enforcement: `prettier/prettier` set to `error`
- Exception: `react/no-unescaped-entities` disabled
## Import Organization
**Order:**
1. React/core libraries (e.g., `react`, `react-dom`)
2. Third-party packages (e.g., `prop-types`, `@fortawesome/react-fontawesome`)
3. Internal/relative imports (e.g., `../../helpers/utils`)
4. Styles (if applicable)
**Example from `jam-ui/src/components/common/Avatar.js`:**
```javascript
import React from 'react';
import PropTypes from 'prop-types';
import { isIterableArray } from '../../helpers/utils';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
```
**Grouping:**
- Blank lines between import groups
- Alphabetical within groups (not strictly enforced)
**Path Aliases:**
- No custom path aliases configured
- Relative imports used throughout (`../../`, `../`)
## Error Handling
**Patterns:**
- Promise-based error handling with `.catch()` or `try/catch` for async/await
- Rails backend: Exceptions bubble to `api_controller.rb` base class
- Frontend: Errors caught in `apiFetch()` wrapper, returned as rejected promises
- Background jobs: Resque retry logic for transient failures
**Error Types:**
- API errors: Returned as JSON with status codes
- Client errors: Logged to console (development), reported to Bugsnag (production)
- Validation errors: Handled by ActiveRecord validations, returned in API responses
## Logging
**Framework:**
- Rails logger for backend (configured in `web/config/environments/`)
- Console logging for frontend (`console.log`, `console.error`)
- Bugsnag for production error tracking (API key in `web/config/application.rb`)
**Patterns:**
- Log at controller/action boundaries in Rails
- Minimal logging in frontend (removed in production builds)
- InfluxDB for time-series metrics (configured but disabled for production)
## Comments
**When to Comment:**
- Explain "why" not "what": Complex business logic, non-obvious algorithms
- TODO comments: Mark incomplete features or planned improvements
- FIXME comments: Mark known issues requiring fixes
- XXX comments: Mark questionable code requiring review
**JSDoc/PropTypes:**
- PropTypes used for React component prop validation
- JSDoc not extensively used, code is generally self-documenting
- Test files have header comments describing purpose
**TODO Comments:**
- Format: `// TODO: description` or `# TODO: description`
- Examples: `// TODO: Expose jamClient globally for debugging. This is temporarily added. Remove it later.`
- No username or issue tracking in TODO comments
## Function Design
**Size:**
- Keep functions under 100 lines where practical
- Extract complex logic into helper functions
- Example: Large helpers like `MixerHelper.js` (1,270 lines) contain multiple related functions
**Parameters:**
- Destructure object parameters in function signature
- Example: `const Avatar = ({ size, rounded, src, name, emoji, className }) => { ... }`
- Options objects for 4+ parameters: `function update(id, options) { ... }`
**Return Values:**
- Explicit return statements
- Promise-based returns for async operations
- Example: `return new Promise((resolve, reject) => { ... })`
## Module Design
**Exports:**
- Named exports preferred: `export const functionName = () => { ... }`
- Default exports for React components (not universal)
- Barrel exports via `index.js` files (not extensively used)
**React Components:**
- Functional components with hooks (React 16.13.1+)
- Arrow function syntax: `const Component = (props) => { ... }`
- PropTypes at bottom of file
- Example component structure:
```javascript
import React from 'react';
import PropTypes from 'prop-types';
const Avatar = ({ size, src, name }) => {
return <div>...</div>;
};
Avatar.propTypes = {
size: PropTypes.string,
src: PropTypes.string,
name: PropTypes.string
};
export default Avatar;
```
**Ruby/Rails Patterns:**
- Class-based models inheriting from `ActiveRecord::Base`
- Controller actions follow RESTful conventions
- Snake_case for file names, method names, variables
- CamelCase for class names
---
*Convention analysis: 2026-01-11*
*Update when patterns change*

View File

@ -0,0 +1,261 @@
# External Integrations
**Analysis Date:** 2026-01-11
## APIs & External Services
**Payment Processing - Stripe:**
- Service: Stripe - Subscription billing and payments
- Files: `web/app/controllers/api_stripe_controller.rb`, `web/config/application.rb` (lines 456-463)
- SDK/Client: stripe gem
- Auth: API keys in `config.stripe[:publishable_key]` and `config.stripe[:secret_key]`
- Endpoints used: Checkout sessions, customer portal, webhooks, Stripe Connect
- OmniAuth: stripe_connect configured for OAuth seller onboarding
**Payment Processing - PayPal:**
- Service: PayPal - Payment processing
- Files: `web/app/controllers/api_pay_pal_controller.rb`, `web/config/initializers/paypal.rb`
- SDK/Client: paypal-sdk-merchant-jk 1.118.1
- Auth: Username/password/signature configured in `web/config/application.rb`
- Mode: `sandbox` (upgradeable to production)
- Features: Express Checkout API
**Subscription Management - Recurly:**
- Service: Recurly - Subscription billing
- Files: `web/config/application.rb` (lines 176-179), `web/config/initializers/recurly.rb`
- SDK/Client: recurly gem 2.19.14 (noted: should upgrade to 3.x)
- Auth: Private and public API keys configured
- Frontend: Recurly.js via `REACT_APP_RECURLY_PUBLIC_API_KEY` environment variable
- Features: Subscription management, tax estimation, webhooks
- SSL: TLSv1_2 enforced
**Alternative Payment - Braintree:**
- Service: Braintree - Payment processing
- Auth: Token configured via `config.braintree_token` (sandbox and production tokens available)
- Frontend: `REACT_APP_BRAINTREE_TOKEN` environment variable
- Features: Payment method management
**Email Delivery - AWS SES:**
- Service: AWS SES - Primary email delivery
- Files: `web/config/application.rb` (lines 271-277)
- Connection: SMTP server `email-smtp.us-east-1.amazonaws.com:587`
- Auth: IAM user `AKIA2SXEHOQFKRW3OWMG` with credentials
- Integration: ActionMailer SMTP configured
**Email Delivery - SendGrid (Backup):**
- Service: SendGrid - Email delivery
- SDK/Client: sendgrid gem 1.2.0, sendgrid_toolkit
- Auth: Username `jamkazam`, password configured in `web/config/application.rb`
- Status: Currently using AWS SES as primary
## Data Storage
**Databases:**
- PostgreSQL - Primary data store
- Connection: via `DATABASE_URL` or `web/config/database.yml`
- Client: ActiveRecord ORM 4.2.8
- Migrations: 403+ migration files in `db/up/`
- Shared: Single database across all services (web, admin, websocket-gateway)
**File Storage - AWS S3:**
- Service: AWS S3 - File and media storage
- SDK/Client: aws-sdk ~1
- Auth: `AWS_KEY` and `AWS_SECRET` environment variables
- Region: `us-east-1`
- Buckets:
- `jamkazam-dev` (private)
- `jamkazam-dev-public` (public)
- `jamkazam-analytics-dev` (analytics)
- `jamkazam-jamtracks` (music tracks)
- CDN: CloudFront distribution `d34f55ppvvtgi3.cloudfront.net`
- Integration: CarrierWave gem for file uploads
**File Storage - Filestack:**
- Service: Filestack (FilePicker) - File upload service
- SDK/Client: filepicker-rails gem 0.1.0
- Auth: API key `AoskPXootR5OS6iIGegL2z`, secret configured
- S3 bucket: `jamkazam-dev`
- Upload directory: `avatars`
**Caching:**
- Redis - Session storage and job queue
- Host: `localhost:6379`
- Client: redis gem 3.3.0/3.3.3
- Used for: Resque job queue, session caching
**Message Queue:**
- RabbitMQ/AMQP - Async messaging between services
- Client: amqp gem 1.0.2/0.9.8
- Host: `127.0.0.1:5672`
- Used for: Async message publishing between web, admin, websocket-gateway
## Authentication & Identity
**Auth Provider - Devise:**
- Service: Devise - User authentication
- SDK/Client: devise gem 3.3.0
- Implementation: Session-based cookies with `remember_token`
- Token storage: httpOnly cookies shared between `www.jamkazam.local` and `beta.jamkazam.local`
- Session management: Rails session store
**OAuth Integrations - Facebook:**
- Provider: Facebook OAuth
- Files: `web/config/application.rb` (lines 282-283), `web/config/initializers/omniauth.rb`
- Credentials: App ID `468555793186398`, secret configured
- SDK/Client: omniauth-facebook gem
- Scopes: email, location
**OAuth Integrations - Google:**
- Provider: Google OAuth 2.0
- Files: `web/config/application.rb` (lines 170-174), `web/config/initializers/omniauth.rb`
- Credentials: Client ID `785931784279-gd0g8on6sc0tuesj7cu763pitaiv2la8.apps.googleusercontent.com`
- SDK/Client: google-api-client, omniauth-google-oauth2 gems
- Scopes: youtube, youtube.force-ssl, youtube.upload, email, profile
- API Key: `AIzaSyCPTPq5PEcl4XWcm7NZ2IGClZlbsiE8JNo` (public server key)
**OAuth Integrations - Twitter:**
- Provider: Twitter OAuth
- Files: `web/config/application.rb` (lines 285-287), `web/config/initializers/omniauth.rb`
- Credentials: App ID `nQj2oEeoJZxECC33tiTuIg`, secret configured
- SDK/Client: omniauth-twitter, twitter gems
- Scopes: Write access
- Public account: `jamkazam`
## Monitoring & Observability
**Error Tracking - Bugsnag:**
- Service: Bugsnag - Error tracking and reporting
- SDK/Client: bugsnag gem 5.3.2
- Auth: API key `fa0e229f687bcb2c8711fcb80223744e`
- Files: `web/config/initializers/bugsnag.rb`
- Features: EventMachine error hook integration
**Analytics - Google Analytics:**
- Service: Google Analytics 4
- Frontend IDs:
- Development: `G-MC9BTWXWY4` (`.env.development.example`)
- Production: `G-SPTNJRW7WB` (`.env.production`)
- Backend: Universal Analytics `UA-44184562-2`
- SDK/Client: react-ga4 2.1.0
**Time Series Metrics - InfluxDB:**
- Service: InfluxDB - Metrics storage
- SDK/Client: influxdb gem 0.3.14, influxdb-rails 0.1.12
- Database: `development` (configurable)
- Auth: Username/password `root`/`root`
- Host: `localhost:8086`
- Status: Currently disabled for production (`ignored_environments` configured)
- File: `web/config/initializers/influxdb-rails.rb`
**Application Performance - New Relic:**
- Service: New Relic - APM
- SDK/Client: newrelic_rpm gem
- Deployment: Production environment only
## Search & Indexing
**Search - Elasticsearch:**
- Service: Elasticsearch - Full-text search
- SDK/Client: elasticsearch gem
- Host: `http://support.jamkazam.com:9200`
- Used for: Music session search, content search across platform
## CI/CD & Deployment
**Hosting:**
- Not explicitly defined (appears to be self-hosted or cloud infrastructure)
- Environment vars: Configured via `.env` files and `config/application.rb`
**CI Pipeline:**
- Jenkins build scripts: `*/jenkins` files in each service directory
- Build script: Root `./build` script
## Environment Configuration
**Development:**
- Required env vars: `DATABASE_URL`, `AWS_KEY`, `AWS_SECRET`, API keys for services
- Secrets location: `.env.local` files (gitignored), `config/application.rb` (hardcoded for dev)
- Mock/stub services: Sandbox modes for Stripe, PayPal, Recurly, Braintree
**Production:**
- Secrets management: Environment variables, configuration files
- Database: Production PostgreSQL with connection pooling
- Services: Production API keys for all external services
## Webhooks & Callbacks
**Incoming - Stripe:**
- Endpoint: `/api/webhooks/stripe` (inferred from controller naming)
- Verification: Signature validation via Stripe SDK
- Events: payment_intent.succeeded, customer.subscription.updated, etc.
- File: `web/app/controllers/api_stripe_controller.rb`
**Incoming - PayPal:**
- Endpoint: Configured in PayPal Express Checkout
- Verification: PayPal SDK handles IPN verification
- File: `web/app/controllers/api_pay_pal_controller.rb`
**Incoming - Recurly:**
- Endpoint: Webhook handlers for subscription events
- Verification: Recurly signature validation
- Events: Subscription updates, billing events
## Security & Fraud Prevention
**Browser Fingerprinting - Fingerprint.js:**
- Service: Fingerprint.js - Device identification
- SDK/Client: @fingerprintjs/fingerprintjs 4.6.0
- Used for: Fraud detection, device tracking
- Expiration: 14 days configured
**Email Validation - Kickbox:**
- Service: Kickbox - Email verification
- SDK/Client: kickbox gem
- Auth: API key `e262991e292dd5ef382c4a69f2b359f718cf267712b8684c9c28d6402ec18965`
- Features: Email verification, bounce checking
- Status: `check_bounced_emails = false` (can be enabled)
**reCAPTCHA - Google:**
- Service: Google reCAPTCHA - Bot protection
- Frontend: `REACT_APP_SITE_KEY=6Let8dgSAAAAAFheKGWrs6iaq_hIlPOZ2f3Bb56B`
- Backend: Public key `6Let8dgSAAAAAFheKGWrs6iaq_hIlPOZ2f3Bb56B`, private key configured
- Used for: User registration, checkout protection
## Support & Community
**Support - Desk.com:**
- Service: Desk.com - Customer support
- Files: `web/config/application.rb` (lines 184-192)
- URL: `https://jamkazam.desk.com`
- Integration: Multipass SSO for support portal
- Multipass key: Configured for single sign-on
**Forums - Vanilla Forums:**
- Service: Vanilla Forums - Community forums
- Files: `web/config/application.rb` (lines 311-315)
- URL: `http://forums.jamkazam.com`
- Integration: JSConnect for forum authentication
- Client ID: `www`, secret configured
- Staff badge: `(JamKazam Staff)` postfix
## Video & Conferencing
**WebRTC:**
- Host: `https://webrtc-demo.jamkazam.com`
- Features: WebRTC-based peer-to-peer video calls
- Config: `config.video_conferencing_host`, open room functionality
## Utilities
**Survey Integration:**
- Service: SurveyMonkey
- Signup survey: `https://www.surveymonkey.com/r/WVBKLYL`
**Live Chat - Olark:**
- Service: Olark - Live chat widget
- Status: Disabled (`config.olark_enabled = false`)
---
*Integration audit: 2026-01-11*
*Update when adding/removing external services*

118
.planning/codebase/STACK.md Normal file
View File

@ -0,0 +1,118 @@
# Technology Stack
**Analysis Date:** 2026-01-11
## Languages
**Primary:**
- Ruby 2.4.1 (pinned for production, with noted upgrade path to 2.5+) - All service Gemfiles
- JavaScript/React - Frontend application code in `jam-ui/src/`
- Protocol Buffers (proto3) - Real-time messaging definitions in `pb/src/client_container.proto`
**Secondary:**
- TypeScript 3.9.10 (dev dependency) - `jam-ui/package.json`, used for type checking
- Python - Utility scripts in `web/script/py/`
## Runtime
**Environment:**
- Node.js 14.21.3 - `jam-ui/.nvmrc`
- Ruby 2.4.1 (production), Ruby 2.3.1 (legacy compatibility) - Multiple service Gemfiles
- PostgreSQL database server
**Package Manager:**
- npm - `jam-ui/package.json` (React frontend)
- Bundler 1.17.3 - Ruby gem management (Gemfile.alt for Silicon/M1 Macs with `MODERN_OS=1` flag)
- Lockfiles: `package-lock.json` (npm), `Gemfile.lock` (bundler), `Gemfile.alt.lock` (M1 Macs)
## Frameworks
**Core:**
- Rails 4.2.8 - Backend API framework (`web/Gemfile`, `admin/Gemfile`, `websocket-gateway/Gemfile`)
- React 16.13.1 - Frontend SPA framework (`jam-ui/package.json`)
- React Router DOM 5.2.0 - Client-side routing
- Redux Toolkit 1.6.1 - State management (`jam-ui/src/store/`)
- EventMachine 1.0.4/1.2.3 - Async I/O for WebSocket gateway
**Testing:**
- Jest - Unit testing for React (inferred from test patterns)
- Playwright 1.40.0 - E2E testing (`jam-ui/package.json`, `jam-ui/playwright.config.ts`)
- RSpec 2.11 - Ruby testing framework (`ruby/Gemfile`, test specs in `*/spec/`)
- Jasmine - Legacy JavaScript tests (`web/spec/javascripts/karma.conf.js`)
- Capybara 2.13.0 - Browser automation for Rails tests
**Build/Dev:**
- React Scripts 3.4.3 (Create React App) - `jam-ui/package.json`
- Webpack CLI 4.10.0 - Module bundling
- Gulp 4.0.2 - SCSS compilation (`jam-ui/package.json`)
- Sprockets 3.6.3 - Rails asset pipeline (all Rails services)
- Babel - JavaScript transpilation (via React Scripts)
## Key Dependencies
**Critical:**
- ruby-protocol-buffers 1.2.2 - WebSocket message serialization (all service Gemfiles)
- Devise 3.3.0 - User authentication (all Rails services)
- ActiveRecord 4.2.8 - ORM for PostgreSQL (`ruby/Gemfile`)
- Resque - Background job processing (`ruby/Gemfile`, `web/Gemfile`, `admin/Gemfile`)
- amqp gem - RabbitMQ messaging (`ruby/Gemfile`, `web/Gemfile`, `websocket-gateway/Gemfile`)
**Infrastructure:**
- PostgreSQL adapter (pg 0.17.1/0.21.0) - Database connectivity
- Redis (redis gem 3.3.0/3.3.3) - Job queue and session storage
- CarrierWave 0.9.0/0.11.2 - File upload handling with S3 support
- aws-sdk ~1 - AWS S3 integration for file storage
**Frontend UI:**
- Bootstrap 4.5.3 - CSS framework
- Reactstrap 8.6.0 - Bootstrap React components
- Font Awesome 5.15.1 - Icon library
- Howler.js 2.2.4 - Audio playback
- Chart.js 2.9.3, ECharts 4.9.0 - Data visualization
- FullCalendar 5.3.1 - Calendar UI
- Leaflet 1.7.1 - Maps integration
- React Hook Form 7.11.1 - Form handling
- i18next 21.3.3 - Internationalization
**Payment/Billing:**
- Stripe (via stripe gem) - Payment processing
- PayPal SDK (paypal-sdk-merchant-jk 1.118.1) - PayPal integration
- Recurly 2.19.14 - Subscription management
## Configuration
**Environment:**
- `.env` files for environment-specific config - `jam-ui/.env.development`, `.env.staging`, `.env.production`
- Rails environments - `web/config/environments/*.rb`
- Application config - `web/config/application.rb` (534 lines of configuration)
- Initializers - `web/config/initializers/` (26+ initialization files)
**Build:**
- `tsconfig.json` - TypeScript compiler options (if TypeScript used)
- `jam-ui/package.json` - Node dependencies and scripts
- Gemfiles - Ruby dependencies for each service
- `.prettierrc` - Code formatting (`jam-ui/`)
- `.eslintrc.json` - JavaScript linting (`jam-ui/`)
## Platform Requirements
**Development:**
- macOS/Linux/Windows with Node.js 14.21.3
- Ruby 2.4.1 (or 2.3.1 for legacy)
- PostgreSQL server
- Redis server (for Resque)
- RabbitMQ server (for AMQP messaging)
- Local hosts configuration: `www.jamkazam.local` (Rails), `beta.jamkazam.local` (React)
**Production:**
- Linux server environment
- PostgreSQL production database
- Redis for job queuing
- RabbitMQ for async messaging
- AWS S3 for file storage with CloudFront CDN
- WebSocket gateway server with EventMachine
---
*Stack analysis: 2026-01-11*
*Update after major dependency changes*

View File

@ -0,0 +1,195 @@
# Codebase Structure
**Analysis Date:** 2026-01-11
## Directory Layout
```
jam-cloud/
├── jam-ui/ # React SPA frontend (port 4000)
├── web/ # Rails 4.2.8 API backend (port 3000)
├── admin/ # Rails admin portal (ActiveAdmin)
├── websocket-gateway/ # EventMachine WebSocket server
├── ruby/ # Shared business logic gem (jam_ruby)
├── pb/ # Protocol Buffer definitions
├── db/ # PostgreSQL migrations (403+ files)
├── lambda/ # AWS Lambda functions
├── monitor/ # Monitoring/observability
├── wordpress/ # WordPress integration
├── CLAUDE.md # Development documentation
└── .planning/ # GSD project planning
```
## Directory Purposes
**jam-ui/**
- Purpose: Modern React 16 frontend SPA
- Contains: React components, hooks, Redux store, helpers, layouts
- Key files: `src/index.js` (entry), `src/App.js` (root), `package.json` (deps)
- Subdirectories:
- `src/components/` - 47 component directories (auth, client, dashboard, jamtracks, feed, common, etc.)
- `src/hooks/` - 41 custom React hooks
- `src/helpers/` - 22 utility files (rest.js, apiFetch.js, MixerHelper.js, JamServer.js)
- `src/store/` - Redux Toolkit setup with 12 feature slices
- `src/layouts/` - 18 layout components
- `src/context/` - 18 Context API providers
**web/**
- Purpose: Rails 4.2.8 REST API backend
- Contains: Controllers (83 API), models (via ruby gem), views (legacy), mailers
- Key files: `config/routes.rb` (51KB), `config/application.rb` (534 lines of config)
- Subdirectories:
- `app/controllers/` - 83 API controller files
- `app/views/` - 71 view directories (legacy Rails views)
- `config/initializers/` - 26 initialization files
- `spec/` - RSpec tests
**ruby/**
- Purpose: Shared business logic gem (jam_ruby)
- Contains: ActiveRecord models, managers, validators, message factory
- Key files: `lib/jam_ruby.rb` (gem manifest), `lib/jam_ruby/message_factory.rb` (42KB)
- Subdirectories:
- `lib/jam_ruby/models/` - 217 model files (User, MusicSession, JamTrack, Recording, etc.)
- `lib/jam_ruby/lib/` - 29 manager/utility files
- `lib/jam_ruby/amqp/` - Message queue handlers
- `lib/jam_ruby/resque/` - Background job definitions
**websocket-gateway/**
- Purpose: Real-time WebSocket communication server
- Contains: EventMachine-based server, WebSocket protocol handling
- Key files: Gemfile (dependencies), lib/ (implementation)
- Subdirectories: `spec/` for RSpec tests
**pb/**
- Purpose: Protocol Buffer message definitions
- Contains: `.proto` source files, generated Ruby/JavaScript bindings
- Key files: `src/client_container.proto` (WebSocket message types)
- Subdirectories:
- `target/ruby/jampb/` - Generated Ruby Protocol Buffer classes
- `target/javascript/` - Generated JavaScript bindings
**admin/**
- Purpose: Rails admin portal with ActiveAdmin
- Contains: Admin controllers, configurations, views
- Key files: Gemfile (includes activeadmin), config/
- Subdirectories: Same structure as Rails web service
**db/**
- Purpose: Database schema and migrations
- Contains: 403+ migration files in `up/` directory
- Key files: `build` (migration runner), `manifest` (migration manifest)
- Subdirectories: `up/` - All migration SQL/Ruby files
## Key File Locations
**Entry Points:**
- `jam-ui/src/index.js` - React DOM render entry
- `jam-ui/public/index.html` - HTML template with `<div id="main">`
- `web/config/routes.rb` - Rails route definitions (51KB)
- `web/config/application.rb` - Rails configuration (534 lines)
- `ruby/lib/jam_ruby.rb` - Shared gem manifest
**Configuration:**
- `jam-ui/.env.development`, `.env.staging`, `.env.production` - Frontend environment config
- `jam-ui/.eslintrc.json` - ESLint configuration
- `jam-ui/.prettierrc` - Prettier formatting rules
- `jam-ui/package.json` - Node dependencies and scripts
- `web/config/database.yml` - PostgreSQL connection config
- `web/config/environments/*.rb` - Rails environment-specific settings
- `*/Gemfile`, `*/Gemfile.alt` - Ruby gem dependencies (Gemfile.alt for M1 Macs)
**Core Logic:**
- `jam-ui/src/components/` - All React components
- `jam-ui/src/helpers/rest.js` - 70+ API operation functions (945 lines)
- `jam-ui/src/helpers/MixerHelper.js` - Audio mixer logic (1,270 lines)
- `jam-ui/src/hooks/useJamServer.js` - WebSocket orchestration (26KB)
- `web/app/controllers/` - All Rails API controllers
- `ruby/lib/jam_ruby/models/` - All business logic models
**Testing:**
- `jam-ui/tests/` - React tests (Playwright, Jest)
- `jam-ui/playwright.config.ts` - Playwright E2E config
- `web/spec/` - Rails RSpec tests
- `web/spec/javascripts/` - Legacy Jasmine tests with Karma
- `ruby/spec/` - Ruby gem RSpec tests
**Documentation:**
- `CLAUDE.md` - Project documentation for Claude Code
- `README.md` - Project setup and usage (root and service-level)
## Naming Conventions
**Files:**
- React components: `JK{ComponentName}.js` (e.g., `JKDashboardMain.js`, `JKSessionModal.js`)
- Non-JK components: `PascalCase.js` (e.g., `Avatar.js`, `FalconCardHeader.js`)
- React hooks: `use{Feature}.js` (e.g., `useJamServer.js`, `useGearUtils.js`)
- Helpers: `camelCase.js` (e.g., `apiFetch.js`, `rest.js`, `utils.js`)
- Rails controllers: `api_{resource}_controller.rb` (e.g., `api_sessions_controller.rb`)
- Ruby models: `snake_case.rb` (e.g., `user.rb`, `music_session.rb`, `jam_track.rb`)
- Test files: `*.test.js` (Jest), `*.spec.js` (Jasmine), `*.spec.rb` (RSpec), `*.spec.ts` (Playwright)
**Directories:**
- React: kebab-case or camelCase (e.g., `components/`, `dashboard/`, `jamtracks/`)
- Rails: snake_case (e.g., `app/controllers/`, `config/initializers/`)
- Ruby: snake_case (e.g., `lib/jam_ruby/models/`)
**Special Patterns:**
- `index.js` - Barrel exports or entry points
- `*_controller.rb` - Rails controllers
- `*_spec.rb` - RSpec test files
- `*.proto` - Protocol Buffer definitions
## Where to Add New Code
**New React Feature:**
- Primary code: `jam-ui/src/components/{category}/JK{FeatureName}.js`
- State management: `jam-ui/src/store/features/{feature}Slice.js`
- API calls: Add functions to `jam-ui/src/helpers/rest.js`
- Custom hooks: `jam-ui/src/hooks/use{Feature}.js`
- Tests: `jam-ui/tests/{category}/{feature}.test.js`
**New API Endpoint:**
- Controller: `web/app/controllers/api_{resource}_controller.rb`
- Routes: Add to `web/config/routes.rb`
- Business logic: `ruby/lib/jam_ruby/models/{model}.rb`
- Tests: `web/spec/controllers/api_{resource}_controller_spec.rb`
**New Model/Business Logic:**
- Model definition: `ruby/lib/jam_ruby/models/{model_name}.rb`
- Tests: `ruby/spec/jam_ruby/models/{model_name}_spec.rb`
**New WebSocket Message Type:**
- Protocol definition: `pb/src/client_container.proto`
- Rebuild: `cd pb && ./build`
- Handler: Add to websocket-gateway or message_factory
**New Background Job:**
- Job class: `ruby/lib/jam_ruby/resque/{job_name}.rb`
- Enqueue: Call `Resque.enqueue(JobClass, args)` from models/controllers
## Special Directories
**pb/target/**
- Purpose: Generated Protocol Buffer bindings (Ruby/JavaScript)
- Source: Auto-generated by `pb/build` script from `.proto` files
- Committed: Yes (generated code checked in)
**node_modules/**
- Purpose: npm dependencies for jam-ui
- Source: Installed via `npm install`
- Committed: No (.gitignored)
**vendor/bundle/**
- Purpose: Bundler-installed Ruby gems
- Source: Installed via `bundle install`
- Committed: No (.gitignored)
**.planning/**
- Purpose: GSD project planning files
- Source: Created by /gsd:* commands
- Committed: Yes (project planning state)
---
*Structure analysis: 2026-01-11*
*Update when directory structure changes*

View File

@ -0,0 +1,274 @@
# Testing Patterns
**Analysis Date:** 2026-01-11
## Test Framework
**Runner:**
- Playwright 1.40.0 - E2E testing for React app (`jam-ui/package.json`, `jam-ui/playwright.config.ts`)
- Jest - Unit testing for React (inferred from test file patterns)
- RSpec 2.11 - Ruby testing framework (`ruby/Gemfile`)
- Jasmine - Legacy JavaScript tests with Karma runner (`web/spec/javascripts/karma.conf.js`)
**Assertion Library:**
- Jest `expect` - Unit tests
- Playwright assertions - E2E tests (`expect(page).toHaveText(...)`)
- RSpec expectations - Ruby tests (`.should`, `expect { }.to`)
**Run Commands:**
```bash
# React tests
cd jam-ui
npm test # Playwright E2E tests
npx cypress run # Cypress tests (alternative)
# Ruby tests
cd ruby
bundle exec rspec # Ruby gem tests
cd web
bundle exec rspec # Rails backend tests
cd websocket-gateway
bundle exec rspec # WebSocket gateway tests
```
## Test File Organization
**Location:**
- React unit tests: `jam-ui/tests/helpers/rest.test.js` (co-located pattern)
- Playwright E2E: `jam-ui/test/friends.spec.ts` (TypeScript)
- Legacy JavaScript: `web/spec/javascripts/*.spec.js`
- Ruby tests: `ruby/spec/jam_ruby/models/*.spec.rb`, `web/spec/controllers/*.spec.rb`
**Naming:**
- Unit tests: `{module}.test.js` (e.g., `rest.test.js`)
- E2E tests: `{feature}.spec.ts` (e.g., `friends.spec.ts`)
- Ruby tests: `{class_name}_spec.rb` (e.g., `text_message_spec.rb`)
**Structure:**
```
jam-ui/
tests/helpers/rest.test.js # Jest unit tests
test/friends.spec.ts # Playwright E2E
web/
spec/
controllers/ # Controller specs
javascripts/ # Legacy Jasmine tests
karma.conf.js # Karma configuration
ruby/
spec/jam_ruby/models/ # Model specs
```
## Test Structure
**Jest Unit Tests:**
```javascript
// jam-ui/tests/helpers/rest.test.js
describe('rest helpers', () => {
test('getPersonById calls correct URL', async () => {
const payload = { id: 42, name: 'John' };
mockResolved(payload);
const result = await rest.getPersonById(42);
expectCalledWith('/users/42/profile');
expect(result).toEqual(payload);
});
});
```
**Playwright E2E Tests:**
```typescript
// jam-ui/test/friends.spec.ts
import { test, expect } from '@playwright/test';
test.describe.serial('Friends page', () => {
test.use({ storageState: 'test/storageState/user1.json' });
test('Homepage', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1').first()).toHaveText('Dashboard - Home');
});
});
```
**RSpec Ruby Tests:**
```ruby
# ruby/spec/jam_ruby/models/text_message_spec.rb
describe TextMessage do
before do
TextMessage.delete_all
@target_user = FactoryGirl.create(:user)
@msg = TextMessage.new(:target_user_id => @target_user.id)
end
it "should retrieve conversation for both users" do
@msg.message = "Test message"
@msg.save!
messages = TextMessage.index(@target_user.id, @source_user.id)
messages.count.should == 1
end
end
```
**Jasmine Legacy Tests:**
```javascript
// web/spec/javascripts/jamserver.spec.js
describe("JamServer", function() {
beforeEach(function() {
jamserver = context.JK.JamServer;
});
it("Subscribing to ping should call function", function() {
var called = false;
jamserver.registerMessageCallback(context.JK.MessageType.PING_ACK, function() {
called = true;
});
expect(called).toBeTruthy();
});
});
```
**Patterns:**
- Jest: `describe()` blocks for grouping, `test()` or `it()` for individual tests
- Playwright: `test.describe()` for grouping, `test()` for individual E2E scenarios
- RSpec: `describe` blocks, `before` hooks, `it` blocks with expectations
- Jasmine: `describe()` and `it()` blocks, `beforeEach()` hooks
## Mocking
**Framework:**
- Jest built-in mocking: `jest.mock()` for module mocking
- RSpec: FactoryGirl for test data fixtures
- Manual mocks in test helpers
**Jest Patterns:**
```javascript
// jam-ui/tests/helpers/rest.test.js
// Mock apiFetch module
const mockResolved = (payload) => {
apiFetch.mockResolvedValue(payload);
};
const expectCalledWith = (url) => {
expect(apiFetch).toHaveBeenCalledWith(url);
};
```
**RSpec Patterns:**
```ruby
# ruby/spec/jam_ruby/models/text_message_spec.rb
before do
@target_user = FactoryGirl.create(:user)
@source_user = FactoryGirl.create(:user)
end
```
**What to Mock:**
- External API calls (via `apiFetch()` in frontend)
- Database queries (via FactoryGirl fixtures in Ruby)
- Time/dates (when testing time-sensitive logic)
- WebSocket connections (in unit tests)
**What NOT to Mock:**
- Pure functions and utilities
- Internal business logic
- Simple data transformations
## Fixtures and Factories
**Test Data:**
- FactoryGirl for Ruby models: `FactoryGirl.create(:user)` in `ruby/spec/`
- Manual test helpers in JavaScript: `mockResolved()`, `expectCalledWith()` in `jam-ui/tests/helpers/rest.test.js`
- Playwright storage state: `test.use({ storageState: 'test/storageState/user1.json' })` for authenticated tests
**Location:**
- JavaScript helpers: `jam-ui/tests/helpers/` (if applicable)
- Ruby factories: Defined in `ruby/spec/` or `web/spec/` (FactoryGirl configuration)
- Playwright fixtures: `jam-ui/test/storageState/` (session cookies for auth)
## Coverage
**Requirements:**
- No enforced coverage targets
- Coverage tracked for awareness, not enforcement
- Focus on critical paths (API endpoints, core models, business logic)
**Configuration:**
- Jest: Built-in coverage via `--coverage` flag
- RSpec: SimpleCov gem for Ruby code coverage
- Playwright: No coverage (E2E tests focus on integration, not coverage)
**View Coverage:**
```bash
# Jest coverage (if configured)
cd jam-ui
npm test -- --coverage
# RSpec coverage
cd ruby
bundle exec rspec
# Check coverage/index.html
```
## Test Types
**Unit Tests:**
- Scope: Single function/module in isolation
- Mocking: Mock all external dependencies (API, database)
- Speed: Fast (<100ms per test)
- Examples: `jam-ui/tests/helpers/rest.test.js`, `ruby/spec/jam_ruby/models/*.spec.rb`
**Integration Tests:**
- Scope: Multiple modules working together
- Mocking: Mock only external boundaries (network, file system)
- Examples: Rails controller specs testing controller + model interaction
**E2E Tests:**
- Framework: Playwright 1.40.0 (primary), Cypress (alternative mentioned in CLAUDE.md)
- Scope: Full user flows through UI
- Setup: Uses storage state for authenticated sessions
- Location: `jam-ui/test/*.spec.ts`
## Common Patterns
**Async Testing:**
```javascript
// Jest async/await
test('handles async operation', async () => {
const result = await asyncFunction();
expect(result).toBe('expected');
});
// Playwright async
test('async page interaction', async ({ page }) => {
await page.goto('/');
await expect(page.locator('h1')).toHaveText('Title');
});
```
**Error Testing:**
```javascript
// Jest error testing
test('throws on invalid input', () => {
expect(() => functionCall()).toThrow('error message');
});
// Async error
test('rejects on failure', async () => {
await expect(asyncCall()).rejects.toThrow('error message');
});
```
**Before/After Hooks:**
- Jest: `beforeEach()`, `afterEach()` for setup/teardown
- RSpec: `before do ... end`, `after do ... end`
- Playwright: `test.beforeAll()`, `test.afterAll()` for global setup
**Snapshot Testing:**
- Not used in this codebase
- Prefer explicit assertions for clarity
---
*Testing analysis: 2026-01-11*
*Update when test patterns change*