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:
parent
e39ebb5e23
commit
f6d805b0f9
|
|
@ -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*
|
||||
|
|
@ -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*
|
||||
|
|
@ -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*
|
||||
|
|
@ -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*
|
||||
|
|
@ -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*
|
||||
|
|
@ -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*
|
||||
|
|
@ -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*
|
||||
Loading…
Reference in New Issue