Skip to main content
Glama
TEST-TROUBLESHOOTING.md17.7 kB
# Test Troubleshooting Quick Reference ## Common Test Issues and Solutions This is a focused troubleshooting guide for the most common test issues encountered during development. Solutions have been tested and validated during Phase 1 Infrastructure Setup. **Phase 1 Status**: ✅ COMPLETE - All 113 tests passing with zero failures! ## Quick Fixes ### 1. TypeScript Compilation Errors in Tests **Error**: `Property 'hasNextPage' is missing in type` ```bash src/domains/keys/keys.formatter.test.ts:64:25 - error TS2322 ``` **Fix**: Use mock builders instead of manual mock creation ```typescript // ❌ Bad: Manual mock creation const mockResult: PaginatedResult<Key> = { items: [], totalResults: 0 // Missing pagination methods }; // ✅ Fixed: Use mock builder const mockBuilder = new KeysMockBuilder(); const result = mockBuilder .withKey({ description: "test" }) .withPagination(1, 100) .build(); // Complete interface implementation ``` ### 2. Snapshot Test Failures **Error**: Snapshot differs with timestamps ``` - Received + Expected - Created: 2024-08-24 15:32:41 + Created: 2024-01-15 10:30:00 ``` **Fix**: Mock Date constructor consistently (Phase 1 validated pattern) ```typescript describe("FormatterTests", () => { const mockDate = new Date("2024-01-15T10:30:00.000Z"); let originalDate: DateConstructor; beforeAll(() => { originalDate = global.Date; global.Date = class extends originalDate { constructor(...args: ConstructorParameters<DateConstructor>) { if (args.length === 0) { super(mockDate.getTime()); } else { super(...args); } } static now() { return mockDate.getTime(); } } as DateConstructor; // Preserve static methods (CRITICAL for avoiding errors) global.Date.UTC = originalDate.UTC; global.Date.parse = originalDate.parse; }); afterAll(() => { global.Date = originalDate; }); }); ``` ### 3. Mock Builder State Pollution **Error**: Tests pass individually but fail when run together ``` Expected 1 key, received 3 keys ``` **Fix**: Create fresh builder instances per test ```typescript describe("Tests", () => { let builder: KeysMockBuilder; beforeEach(() => { builder = new KeysMockBuilder(); // Fresh instance }); // Tests now have isolated state it("should work independently", () => { const result = builder.withKey({ name: "Test" }).build(); expect(result.items).toHaveLength(1); }); }); ``` ### 4. Pagination Type Confusion **Error**: `Type 'PaginatedResult<Key>' is not assignable to 'CursorPaginatedResult<Key>'` **Fix**: Use appropriate mock builder methods ```typescript // ✅ Standard pagination const standardResult = mockBuilder .withKey({ name: "Standard" }) .withPagination(1, 100) .build(); // ✅ Cursor pagination const cursorResult = mockBuilder .withKey({ name: "Cursor" }) .withCursorPagination("cursor-123", 100) .build(); console.log(standardResult.currentPage); // 1 console.log(cursorResult.nextCursor); // "cursor-123" ``` ### 5. Test Expectations Mismatch **Error**: Test expects raw data but gets formatted output ``` Expected: "key_id: 123" Received: "**Key ID**: 123" ``` **Fix**: Test formatted Markdown output expectations ```typescript // ✅ Test actual formatter output expect(result).toContain("**Key ID**: 123"); expect(result).toContain("# Translation Key Details"); expect(result).toContain("## Translations"); ``` ### 6. Mock Builder Chaining Issues **Error**: `Cannot read properties of undefined` when chaining methods ```typescript // ❌ Problem: Chaining without initial item const result = mockBuilder .withTranslations([...]) // No key to attach translations to .build(); ``` **Fix**: Ensure proper method order ```typescript // ✅ Fixed: Create key first, then add translations const result = mockBuilder .withKey({ description: "Base key" }) .withTranslations([ { language_iso: "en", translation: "Hello" } ]) .build(); ``` ### 7. Bulk Operations Testing **Error**: Testing bulk operations without proper error simulation ```typescript // ❌ Problem: Not testing partial failures const result = mockBuilder .withKey({ name: "Success 1" }) .withKey({ name: "Success 2" }) .build(); ``` **Fix**: Use buildBulkResult for testing create/update operations ```typescript // ✅ Fixed: Test bulk operations with errors const bulkResult = mockBuilder .withKey({ name: "Success 1" }) .withKey({ name: "Success 2" }) .buildBulkResult([ { item: { name: "Failed" }, message: "Validation error" } ]); expect(bulkResult.items).toHaveLength(2); expect(bulkResult.errors).toHaveLength(1); ``` ## Error Patterns and Solutions ### Pattern: "Cannot read properties of undefined" **Typical Causes:** 1. Missing mock builder setup 2. Incomplete object initialization 3. Wrong method chaining order **Debug Steps:** ```typescript // 1. Use mock builders consistently const mockBuilder = new KeysMockBuilder(); // 2. Log the actual object structure const result = mockBuilder.withKey({}).build(); console.log(JSON.stringify(result, null, 2)); // 3. Use generators for realistic data const result = mockBuilder .withKey({ key_id: generators.key.id(), key_name: generators.key.name() }) .build(); ``` ### Pattern: Jest Module Resolution Failures **Error**: `SyntaxError: Cannot use import statement outside a module` **Fix**: Verified jest.config.js configuration (Phase 1 validated) ```javascript export default { preset: "ts-jest/presets/default-esm", testEnvironment: "node", extensionsToTreatAsEsm: [".ts"], moduleNameMapping: { "^(\\.{1,2}/.*)\\.js$": "$1", }, transform: { "^.+\\.tsx?$": ["ts-jest", { useESM: true }], }, transformIgnorePatterns: ["node_modules/(?!(@lokalise/node-api)/)"], // Critical for UTC timestamps testEnvironment: "node", setupFilesAfterEnv: ["<rootDir>/src/test-utils/setup.ts"], }; ``` ### Pattern: Memory Leaks and Performance Issues **Symptoms:** - Tests slow down over time - Jest warnings about memory usage - Tests timeout randomly **Fix**: Proper cleanup (Phase 1 established pattern) ```typescript describe("DomainTests", () => { let mockBuilder: DomainMockBuilder; let originalDate: DateConstructor; beforeAll(() => { // Mock Date once per test suite originalDate = global.Date; // ... date mocking setup }); beforeEach(() => { // Fresh builder per test mockBuilder = new DomainMockBuilder(); }); afterEach(() => { jest.clearAllMocks(); jest.restoreAllMocks(); }); afterAll(() => { // Restore global modifications global.Date = originalDate; }); }); ``` ### Pattern: Inconsistent Generator Data **Error**: Tests fail randomly due to dynamic data generation ```typescript // ❌ Problem: Random data in snapshots const key_id = Math.floor(Math.random() * 1000000); ``` **Fix**: Use deterministic generators with Phase 1 patterns ```typescript // ✅ Fixed: Deterministic generator functions import { generators } from "../test-utils/fixture-helpers/generators.js"; const result = mockBuilder .withKey({ key_id: generators.key.id(), // Deterministic within test run created_at: generators.timestamp().formatted // Consistent format }) .build(); ``` ## Debugging Strategies ### 1. Isolate the Problem ```bash # Run single test file npm test keys.formatter.test.ts # Run specific test npm test -- --testNamePattern="should format key details" # Run with verbose output for troubleshooting npm test -- --verbose # Run tests serially to avoid race conditions npm test -- --runInBand ``` ### 2. Enable Console Output ```typescript import { restoreConsole } from '../test-utils/setup.js'; it('should debug mock builder issue', () => { restoreConsole(); // Temporarily restore console const mockBuilder = new KeysMockBuilder(); const result = mockBuilder.withKey({ name: "Debug" }).build(); console.log('Mock result:', JSON.stringify(result, null, 2)); console.log('Items count:', result.items.length); console.log('Pagination methods:', { hasNextPage: typeof result.hasNextPage, totalResults: result.totalResults }); // Your test code }); ``` ### 3. Use Jest Debugging ```bash # Debug with Node.js inspector node --inspect-brk node_modules/.bin/jest --runInBand # Debug specific test node --inspect-brk node_modules/.bin/jest --runInBand --testNamePattern="your test name" ``` ### 4. Mock Builder Debugging ```typescript // Debug mock builder state describe("MockBuilderDebugging", () => { it("should debug builder state", () => { const builder = new KeysMockBuilder(); // Step 1: Check empty state console.log("Empty builder:", builder.build().items.length); // 0 // Step 2: Add items and check builder.withKey({ name: "Test 1" }); console.log("After first key:", builder.build().items.length); // 1 builder.withKey({ name: "Test 2" }); console.log("After second key:", builder.build().items.length); // 2 // Step 3: Check final result const result = builder.build(); console.log("Final result:", result.items.map(item => item.name)); }); }); ``` ## Quick Validation Commands ```bash # Phase 1 validated command sequence # 1. Run linting (fix formatting issues) npm run lint # 2. Check formatting npm run format # 3. TypeScript compilation (must be clean) npm run build # 4. Run tests (all must pass) npm test # 5. Check coverage npm run test:coverage # 6. Update snapshots if needed npm test -- --updateSnapshot ``` ## Test Quality Checklist When fixing test issues, ensure: - [ ] **Tests pass individually**: `npm test -- keys.formatter.test.ts` - [ ] **Tests pass in suite**: `npm test` - [ ] **No TypeScript errors**: `npm run build` - [ ] **Linting passes**: `npm run lint` - [ ] **Proper formatting**: `npm run format` - [ ] **Mock builders used**: No manual mock creation - [ ] **Fresh instances**: New builder per test - [ ] **Date mocking**: Consistent timestamps - [ ] **Snapshots reviewed**: Changes are intentional - [ ] **Coverage maintained**: Above 18% threshold (current baseline) ## Phase 1 Solutions Validated ### Mock Builder Solutions ✅ All mock builder patterns have been tested with 113 passing tests: ```typescript // ✅ Keys domain - 66 snapshot tests passing const keysResult = new KeysMockBuilder() .withKey({ description: "Tested pattern" }) .withTranslations([{ language_iso: "en", translation: "Test" }]) .withCursorPagination("cursor", 100) .build(); // ✅ Projects domain - All formatter tests passing const projectsResult = new ProjectsMockBuilder() .withProject({ name: "Test Project", statistics: { keys_total: 50 } }) .withPagination(1, 10) .build(); // ✅ Tasks domain - Complex progress tracking const tasksResult = new TasksMockBuilder() .withTask({ status: "in_progress", progress: { total: 100, completed: 75 } }) .build(); // ✅ Languages domain - RTL support const languagesResult = new LanguagesMockBuilder() .withLanguage({ lang_iso: "ar", is_rtl: true }) .build(); ``` ### Date Mocking Solutions ✅ Consistent date mocking pattern used across all 66 snapshot tests: ```typescript // ✅ Validated date mocking (no snapshot failures) const mockDate = new Date("2024-01-15T10:30:00.000Z"); // [Established pattern preserved - all tests passing] ``` ## Emergency Fixes ### Reset All Snapshots ```bash npm test -- --updateSnapshot ``` ### Clear Jest Cache ```bash npx jest --clearCache ``` ### Reset Node Modules ```bash rm -rf node_modules package-lock.json npm install ``` ### Force Garbage Collection ```bash NODE_OPTIONS="--max-old-space-size=4096" npm test ``` ## MCP-Specific Test Issues ### 8. MCP Tool Schema Registration **Error**: TypeScript build failure with Zod schemas ``` error TS2769: No overload matches this call. Argument of type 'ZodObject' is not assignable to parameter of type 'ZodRawShape' ``` **Fix**: Use `.shape` property for MCP tool registration ```typescript // ❌ Wrong: Passing full Zod schema server.tool( "lokalise_list_projects", "Description", ListProjectsToolArgs, // Full ZodObject handler ); // ✅ Correct: Pass the shape property server.tool( "lokalise_list_projects", "Description", ListProjectsToolArgs.shape, // ZodRawShape handler ); ``` **Test Validation**: ```typescript // Test that schemas are properly formatted it("should validate input schemas", () => { const calls = (server.tool as any).mock.calls; calls.forEach(([_name, _desc, schema]) => { expect(schema).toBeDefined(); expect(typeof schema).toBe("object"); // Shape objects have field definitions expect(Object.keys(schema).length).toBeGreaterThan(0); }); }); ``` ### 9. Custom Protocol URL Parsing **Error**: Resource tests fail with "Project ID is required" ``` Error: Project ID is required in the URI path ``` **Issue**: Custom protocol URLs parse differently than HTTP - For `lokalise://projects/test-123`: - `uri.host` = "projects" (not part of path) - `uri.pathname` = "/test-123" (not "/projects/test-123") **Fix**: Adjust parsing logic for custom protocols ```typescript // ❌ Wrong: Assuming HTTP-style paths const pathParts = uri.pathname.split("/").filter(Boolean); const projectId = pathParts[1]; // undefined! // ✅ Correct: Account for custom protocol structure const pathParts = uri.pathname.split("/").filter(Boolean); const projectId = pathParts.length > 0 ? decodeURIComponent(pathParts[0]) : ""; ``` ### 10. Error Response Format Consistency **Error**: Test expects `isError` property that doesn't exist ``` expected { content: [...], metadata: {...} } to have property "isError" ``` **Fix**: Match actual error formatter output ```typescript // ❌ Wrong expectation expect(result).toEqual({ content: [{ type: "text", text: expect.stringContaining("Error") }], isError: true, // Not included by formatter }); // ✅ Correct expectation expect(result).toHaveProperty("content"); expect(result).toHaveProperty("metadata"); expect(result.content[0]).toEqual({ type: "text", text: expect.stringContaining("Error"), }); ``` ### 11. Validation Error Types and Messages **Error**: Controller tests expect wrong error types ``` expected ErrorType.API_ERROR to be ErrorType.VALIDATION_ERROR ``` **Fix**: Use correct error types and include prefixes ```typescript // ❌ Wrong: API_ERROR for validation issues throw new McpError( "Name is required", ErrorType.API_ERROR ); // ✅ Correct: VALIDATION_ERROR with prefix throw new McpError( "VALIDATION_ERROR: Name is required", ErrorType.VALIDATION_ERROR ); ``` ### 12. Resource Response Structure **Error**: Resource tests missing required fields ``` expected { text, mimeType } to equal { uri, text, mimeType, description } ``` **Fix**: Include all required fields in resource responses ```typescript // ❌ Incomplete response return { contents: [{ text: result.content, mimeType: "text/markdown" }] }; // ✅ Complete response return { contents: [{ uri: uri.toString(), text: result.content, mimeType: "text/markdown", description: `Lokalise Project Details: ${projectId}` }] }; ``` ### 13. NaN vs undefined in Number Parsing **Error**: Invalid number parsed as NaN, not undefined ``` expected limit: undefined but got limit: NaN ``` **Fix**: Check for NaN explicitly ```typescript // ❌ Wrong: NaN passes through const limit = urlParams.get("limit") ? Number.parseInt(urlParams.get("limit") ?? "100", 10) : undefined; // ✅ Correct: Check for NaN if (urlParams.get("limit")) { const parsed = Number.parseInt(urlParams.get("limit") ?? "100", 10); limit = Number.isNaN(parsed) ? undefined : parsed; } ``` ## Common Anti-Patterns to Avoid ### ❌ Don't Do This ```typescript // Manual mock creation (Type errors guaranteed) const mockResult = { items: [{ key_id: 123, /* missing 50+ properties */ }] }; // Shared test state (State pollution) const builder = new MockBuilder(); // Outside test // Ignoring TypeScript errors const result = mockData as unknown; // Testing implementation details expect(mockService.internalMethod).toHaveBeenCalled(); // Dynamic data in snapshots expect(formatResult()).toMatchSnapshot(); // Contains timestamps ``` ### ✅ Do This Instead (Phase 1 Validated) ```typescript // Use mock builders (100% type safety) const mockBuilder = new KeysMockBuilder(); const result = mockBuilder.withKey({ key_id: 123 }).build(); // Isolated test state (No pollution) beforeEach(() => { builder = new KeysMockBuilder(); }); // Proper TypeScript usage const result: PaginatedResult<Key> = mockBuilder.build(); // Test behavior (What matters to users) expect(result.content).toContain("expected output"); // Stable snapshots (Date mocking applied) beforeAll(() => mockDateSetup()); expect(formatResult()).toMatchSnapshot(); ``` ## Success Metrics ### Phase 1 Achievements 🎉 - ✅ **0 failing tests** (113/113 passing) - ✅ **0.663 seconds** execution time - ✅ **66 snapshot tests** stable - ✅ **4 mock builders** operational - ✅ **Zero TypeScript errors** - ✅ **All linting checks passed** - ✅ **Coverage baseline**: 18.18% ### Quality Standards Met - ✅ **Mock isolation**: No state pollution between tests - ✅ **Date consistency**: All snapshots stable with date mocking - ✅ **Type safety**: Complete SDK interface implementation - ✅ **Error handling**: Comprehensive error simulation - ✅ **Performance**: Sub-second test execution --- *For detailed explanations and examples, see the full [Testing Guide](./TESTING-GUIDE.md) and [New Domain Testing](./NEW-DOMAIN-TESTING.md) guides.*

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/AbdallahAHO/lokalise-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server