# Plan: Add Comprehensive Magnitude Tests for All Features **Priority:** CRITICAL - Must be done second (after Playwright scaffolding) **Dependencies:** 01-playwright-scaffolding.md **Affects:** Code quality, regression prevention, production confidence ## Overview Create comprehensive Magnitude test coverage for ALL existing and new features. Every user flow must be tested fully, including both happy paths and unhappy paths. ## Current State - No Magnitude tests exist - No test coverage for critical flows (auth, chat, node creation, galaxy) - No regression testing - Production deployments lack confidence ## Test Coverage Required ### 1. Authentication Flow Tests #### `tests/magnitude/auth.mag.ts` **Happy Paths:** - User can log in with valid Bluesky credentials - User can log out successfully - User session persists across page refreshes - User can access protected routes after authentication **Unhappy Paths:** - Login fails with invalid credentials - Login fails with non-existent handle - Unauthenticated user redirected from protected routes - Session expiry handled gracefully ```typescript import { test } from 'magnitude-test'; test('User can log in with valid credentials', async (agent) => { await agent.open('http://localhost:3000'); await agent.check('Login page is visible'); await agent.act('Click "Log in with Bluesky" button') .data({ handle: process.env.TEST_USER_HANDLE, password: process.env.TEST_USER_PASSWORD }); await agent.check('User is redirected to Bluesky OAuth'); await agent.check('User is redirected back to Ponderants'); await agent.check('User sees authenticated interface'); await agent.check('Profile menu is visible'); }); test('Login fails with invalid credentials', async (agent) => { await agent.open('http://localhost:3000'); await agent.act('Click "Log in with Bluesky" button') .data({ handle: 'invalid-user.bsky.social', password: 'wrongpassword' }); await agent.check('Error message is displayed'); await agent.check('User remains on login page'); }); test('User session persists across refresh', async (agent) => { // First login await agent.open('http://localhost:3000'); await agent.act('Log in with valid credentials'); await agent.check('User is authenticated'); // Refresh page await agent.act('Refresh the page'); await agent.check('User is still authenticated'); await agent.check('Profile menu is still visible'); }); test('User can log out', async (agent) => { await agent.open('http://localhost:3000'); await agent.act('Log in with valid credentials'); await agent.check('User is authenticated'); await agent.act('Click profile menu'); await agent.act('Click "Log out" button'); await agent.check('User is redirected to login page'); await agent.check('Session is cleared'); }); ``` ### 2. Chat Interface Tests #### `tests/magnitude/chat.mag.ts` **Happy Paths:** - User can send text message and receive AI response - User can use voice input and receive AI response - User can see typing indicator while AI is thinking - User can create a node from conversation - User can select AI persona - Chat history persists **Unhappy Paths:** - Empty message submission is prevented - Voice input fails gracefully without microphone permission - AI error displays user-friendly message - Network error handled with retry option ```typescript import { test } from 'magnitude-test'; test('User can send message and receive AI response', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Type "What is the meaning of life?" into chat input'); await agent.act('Press Enter to send'); await agent.check('Message appears in chat history'); await agent.check('Typing indicator is visible'); await agent.check('AI response appears after typing indicator disappears'); await agent.check('Response contains thoughtful content'); }); test('User can use voice input', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Click microphone button'); await agent.check('Recording indicator is visible'); await agent.act('Speak "Hello, how are you?"'); await agent.act('Click microphone button to stop recording'); await agent.check('Transcribed text appears in chat'); await agent.check('AI response is generated'); }); test('User can create node from conversation', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Have a conversation about "quantum computing"'); await agent.check('At least 3 messages exchanged'); await agent.act('Click "Create Node" button'); await agent.check('Node editor opens with AI-generated draft'); await agent.check('Title is populated'); await agent.check('Body contains conversation insights'); }); test('Empty message submission is prevented', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Press Enter without typing anything'); await agent.check('No message is sent'); await agent.check('Send button remains disabled'); }); test('AI error displays user-friendly message', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); // Simulate network error by blocking API calls await agent.act('Send a message while API is blocked'); await agent.check('Error notification appears'); await agent.check('Error message is user-friendly'); await agent.check('Retry option is available'); }); ``` ### 3. Node Creation and Publishing Tests #### `tests/magnitude/nodes.mag.ts` **Happy Paths:** - User can create a simple node (title + body) - Node is published to Bluesky as a post - Node is published to Bluesky as a thread (long content) - Node appears in local cache (SurrealDB) - Embedding is generated for node - Node can link to other existing nodes - AI suggests relevant node links - Node appears in galaxy after creation **Unhappy Paths:** - Node creation fails without title - Node creation fails without body - Bluesky API error handled gracefully - Embedding generation failure doesn't block node creation - Link suggestion failure doesn't block node creation ```typescript import { test } from 'magnitude-test'; test('User can create a simple node', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Click "New Node" button'); await agent.check('Node editor opens'); await agent.act('Type "My First Thought" into title field'); await agent.act('Type "This is my first ponderant about philosophy" into body field'); await agent.act('Click "Publish" button'); await agent.check('Success notification appears'); await agent.check('Node is published to Bluesky'); await agent.check('Node appears in cache'); }); test('Long node is published as thread', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); const longBody = 'This is a very long thought that exceeds 300 graphemes. '.repeat(20); await agent.act('Create new node'); await agent.act(`Type "Long Thought" into title`); await agent.act(`Type long content into body`).data({ body: longBody }); await agent.act('Click "Publish" button'); await agent.check('Node is published as Bluesky thread'); await agent.check('Thread contains multiple posts'); await agent.check('First post contains link to detail page'); await agent.check('Thread indicators show (2/3), (3/3), etc.'); }); test('Node with emojis handles grapheme counting correctly', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); const emojiContent = '🎉 '.repeat(100) + 'Hello world! 👋 '; await agent.act('Create new node'); await agent.act('Type "Emoji Test" into title'); await agent.act('Type emoji content into body').data({ body: emojiContent }); await agent.act('Click "Publish" button'); await agent.check('Node is published without exceeding 300 grapheme limit'); await agent.check('Emojis are counted as 1 grapheme each'); }); test('Node creation fails without title', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Create new node'); await agent.act('Type "Some body content" into body field'); await agent.act('Click "Publish" button without entering title'); await agent.check('Validation error appears'); await agent.check('Error message says "Title is required"'); await agent.check('Node is not published'); }); test('Node links to existing nodes', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); // Create first node await agent.act('Create node titled "Philosophy Basics"'); await agent.check('First node is published'); // Create second node that links to first await agent.act('Create new node titled "Advanced Philosophy"'); await agent.check('AI suggests linking to "Philosophy Basics"'); await agent.act('Accept suggested link'); await agent.act('Publish node'); await agent.check('Node is published with link'); await agent.check('Graph relationship exists in database'); }); ``` ### 4. Galaxy Visualization Tests #### `tests/magnitude/galaxy.mag.ts` **Happy Paths:** - Galaxy loads and displays nodes with 3D coordinates - User can rotate/zoom/pan the galaxy - User can click on a node to view details - Node detail modal displays correct information - User can navigate from modal to chat - User can navigate from modal to node detail page - Empty state shows when no nodes exist - UMAP calculation triggers automatically after 3rd node **Unhappy Paths:** - Galaxy handles zero nodes gracefully - Galaxy handles nodes without coordinates - WebGL not available fallback - Node click on non-existent node handled - Modal close doesn't break navigation ```typescript import { test } from 'magnitude-test'; test('Galaxy loads and displays nodes', async (agent) => { await agent.open('http://localhost:3000/galaxy'); await agent.act('Log in if needed'); // Ensure user has at least 3 nodes with embeddings await agent.check('Galaxy canvas is visible'); await agent.check('3D nodes are rendered'); await agent.check('Can rotate the galaxy by dragging'); await agent.check('Can zoom with scroll wheel'); }); test('User can click on node to view details', async (agent) => { await agent.open('http://localhost:3000/galaxy'); await agent.act('Log in if needed'); await agent.check('At least one node is visible'); await agent.act('Click on a node sphere'); await agent.check('Node detail modal opens'); await agent.check('Modal shows node title'); await agent.check('Modal shows node body'); await agent.check('Modal shows Bluesky post link'); }); test('Node detail modal navigation works', async (agent) => { await agent.open('http://localhost:3000/galaxy'); await agent.act('Log in if needed'); await agent.act('Click on a node'); await agent.check('Modal is open'); await agent.act('Click "View Full Detail" button'); await agent.check('Navigated to /galaxy/[node-id] page'); await agent.check('URL contains node ID'); await agent.check('Page shows full node content'); }); test('Empty galaxy shows helpful message', async (agent) => { // Use a new test user with no nodes await agent.open('http://localhost:3000/galaxy'); await agent.act('Log in with empty account'); await agent.check('Empty state message is visible'); await agent.check('Message says "No thoughts yet"'); await agent.check('Button to create first node is visible'); }); test('UMAP calculation triggers after 3rd node', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in with empty account'); // Create 3 nodes await agent.act('Create node 1: "First Thought"'); await agent.act('Create node 2: "Second Thought"'); await agent.act('Create node 3: "Third Thought"'); await agent.check('UMAP calculation started automatically'); await agent.act('Navigate to /galaxy'); await agent.check('All 3 nodes have 3D coordinates'); await agent.check('Nodes are positioned in 3D space'); }); test('Galaxy handles WebGL unavailable', async (agent) => { await agent.open('http://localhost:3000/galaxy'); await agent.act('Log in if needed'); await agent.act('Disable WebGL in browser'); await agent.check('Fallback message is displayed'); await agent.check('Message explains WebGL requirement'); await agent.check('Link to enable WebGL provided'); }); ``` ### 5. Voice Feature Tests #### `tests/magnitude/voice.mag.ts` **Happy Paths:** - User can start voice recording - User can stop voice recording - Voice is transcribed to text - Transcribed text is sent to AI - TTS plays AI response - User can toggle TTS on/off **Unhappy Paths:** - Microphone permission denied - Deepgram API error - Network error during transcription - TTS fails gracefully ```typescript import { test } from 'magnitude-test'; test('User can record and transcribe voice', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Click microphone button'); await agent.check('Recording indicator shows'); await agent.check('Microphone permission granted'); await agent.act('Speak "What is artificial intelligence?"'); await agent.wait(2000); await agent.act('Click microphone button to stop'); await agent.check('Recording stops'); await agent.check('Text is transcribed'); await agent.check('Transcribed text appears in chat input'); }); test('Microphone permission denied handled gracefully', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Deny microphone permission'); await agent.act('Click microphone button'); await agent.check('Error notification appears'); await agent.check('Error explains permission is needed'); await agent.check('Link to browser settings provided'); }); test('TTS plays AI response', async (agent) => { await agent.open('http://localhost:3000/chat'); await agent.act('Log in if needed'); await agent.act('Enable TTS in settings'); await agent.act('Send message "Hello"'); await agent.check('AI responds with text'); await agent.check('TTS audio plays automatically'); await agent.check('Audio player controls are visible'); }); ``` ### 6. Integration Tests #### `tests/magnitude/integration.mag.ts` **Full user journeys:** - Complete flow: Login → Chat → Create Node → View in Galaxy - Complete flow: Login → Galaxy → View Node → Edit Node - Complete flow: Login → Create 3 Nodes → UMAP → Explore Galaxy ```typescript import { test } from 'magnitude-test'; test('Complete user journey: Login to Galaxy visualization', async (agent) => { // 1. Login await agent.open('http://localhost:3000'); await agent.act('Log in with valid credentials'); await agent.check('User is authenticated'); // 2. Chat with AI await agent.act('Navigate to /chat'); await agent.act('Send message "Tell me about philosophy"'); await agent.check('AI responds'); // 3. Create node from conversation await agent.act('Click "Create Node" button'); await agent.check('Node editor opens with AI draft'); await agent.act('Review and edit node content'); await agent.act('Click "Publish"'); await agent.check('Node is published to Bluesky'); await agent.check('Success notification appears'); // 4. View in galaxy await agent.act('Navigate to /galaxy'); await agent.check('Galaxy canvas loads'); await agent.check('New node appears in galaxy'); await agent.act('Click on the new node'); await agent.check('Node details modal opens'); await agent.check('Content matches published node'); }); test('User creates 3 nodes and explores galaxy', async (agent) => { await agent.open('http://localhost:3000'); await agent.act('Log in with empty account'); // Create 3 nodes for (let i = 1; i <= 3; i++) { await agent.act(`Create node titled "Thought ${i}"`); await agent.check(`Node ${i} published successfully`); } // Check UMAP triggered await agent.check('UMAP calculation started'); await agent.wait(5000); // Wait for UMAP to complete // Explore galaxy await agent.act('Navigate to /galaxy'); await agent.check('All 3 nodes have coordinates'); await agent.check('Nodes are spatially distributed'); await agent.act('Rotate galaxy to different angle'); await agent.act('Zoom in on cluster of nodes'); await agent.act('Click on each node to view details'); await agent.check('All node details accessible'); }); ``` ## Implementation Steps 1. **Create test directory structure** ```bash mkdir -p tests/magnitude ``` 2. **Create test files in order** - `tests/magnitude/auth.mag.ts` - Authentication tests - `tests/magnitude/chat.mag.ts` - Chat interface tests - `tests/magnitude/nodes.mag.ts` - Node creation tests - `tests/magnitude/galaxy.mag.ts` - Galaxy visualization tests - `tests/magnitude/voice.mag.ts` - Voice feature tests - `tests/magnitude/integration.mag.ts` - Full user journeys 3. **Update package.json** ```json { "scripts": { "test": "npx magnitude", "test:watch": "npx magnitude --watch" } } ``` 4. **Create test data helpers** ```typescript // tests/magnitude/helpers/data.ts export const createTestNode = (index: number) => ({ title: `Test Node ${index}`, body: `This is test content for node ${index}. It discusses various topics.` }); ``` 5. **Run tests and fix issues** - Run each test file individually first - Fix failing tests - Run full suite - Ensure all tests pass ## Success Criteria - ✅ All authentication flows tested (happy + unhappy paths) - ✅ All chat interactions tested (text, voice, AI response) - ✅ All node creation scenarios tested (simple, thread, emojis, links) - ✅ All galaxy visualization features tested (load, click, navigate) - ✅ All voice features tested (record, transcribe, TTS) - ✅ Integration tests cover complete user journeys - ✅ All tests pass reliably - ✅ Test suite runs in CI/CD - ✅ New features cannot be merged without tests ## Files to Create 1. `tests/magnitude/auth.mag.ts` 2. `tests/magnitude/chat.mag.ts` 3. `tests/magnitude/nodes.mag.ts` 4. `tests/magnitude/galaxy.mag.ts` 5. `tests/magnitude/voice.mag.ts` 6. `tests/magnitude/integration.mag.ts` 7. `tests/magnitude/helpers/data.ts` ## Files to Update 1. `package.json` - Add test scripts 2. `.github/workflows/test.yml` - Add CI test workflow (if exists)