diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md new file mode 100644 index 000000000..8f3ad21f0 --- /dev/null +++ b/.planning/codebase/ARCHITECTURE.md @@ -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* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md new file mode 100644 index 000000000..3c16ae564 --- /dev/null +++ b/.planning/codebase/CONCERNS.md @@ -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* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md new file mode 100644 index 000000000..3db0690f3 --- /dev/null +++ b/.planning/codebase/CONVENTIONS.md @@ -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
...
; + }; + + 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* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md new file mode 100644 index 000000000..1b2c7f82b --- /dev/null +++ b/.planning/codebase/INTEGRATIONS.md @@ -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* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md new file mode 100644 index 000000000..4c779e424 --- /dev/null +++ b/.planning/codebase/STACK.md @@ -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* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md new file mode 100644 index 000000000..5d5352776 --- /dev/null +++ b/.planning/codebase/STRUCTURE.md @@ -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 `
` +- `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* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md new file mode 100644 index 000000000..b14db5939 --- /dev/null +++ b/.planning/codebase/TESTING.md @@ -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*