Skip to main content
Glama

Context Pods

by conorluddy
turbo-integration.test.tsโ€ข12.7 kB
/** * Unit tests for Turbo configuration and integration * Tests the functionality of TurboRepo monorepo task orchestration and optimization */ import { describe, it, expect, beforeEach, vi } from 'vitest'; import { promises as fs } from 'fs'; // Mock fs for testing turbo.json validation vi.mock('fs', () => ({ promises: { readFile: vi.fn(), access: vi.fn(), stat: vi.fn(), }, })); describe('Turbo Integration', () => { let mockFs: { readFile: ReturnType<typeof vi.fn>; access: ReturnType<typeof vi.fn>; stat: ReturnType<typeof vi.fn>; }; beforeEach(() => { vi.clearAllMocks(); mockFs = { readFile: vi.mocked(fs.readFile), access: vi.mocked(fs.access), stat: vi.mocked(fs.stat), }; }); describe('Turbo Configuration Validation', () => { it('should validate turbo.json schema structure', async () => { const validTurboConfig = { $schema: 'https://turbo.build/schema.json', tasks: { build: { dependsOn: ['^build'], outputs: ['dist/**'], }, test: { dependsOn: ['build'], outputs: ['coverage/**'], }, lint: { dependsOn: ['^build'], outputs: [], }, }, }; mockFs.readFile.mockResolvedValue(JSON.stringify(validTurboConfig)); const config = JSON.parse(await fs.readFile('turbo.json', 'utf-8')); expect(config).toHaveProperty('$schema'); expect(config).toHaveProperty('tasks'); expect(config.tasks).toHaveProperty('build'); expect(config.tasks).toHaveProperty('test'); expect(config.tasks).toHaveProperty('lint'); }); it('should validate required task dependencies', async () => { const turboConfig = { $schema: 'https://turbo.build/schema.json', tasks: { build: { dependsOn: ['^build'], outputs: ['dist/**'], }, test: { dependsOn: ['build'], outputs: ['coverage/**'], }, lint: { dependsOn: ['^build'], outputs: [], }, 'type-check': { dependsOn: ['^build'], outputs: [], }, }, }; mockFs.readFile.mockResolvedValue(JSON.stringify(turboConfig)); const config = JSON.parse(await fs.readFile('turbo.json', 'utf-8')); // Test that test depends on build expect(config.tasks.test.dependsOn).toContain('build'); // Test that lint depends on ^build (upstream builds) expect(config.tasks.lint.dependsOn).toContain('^build'); // Test that type-check depends on ^build expect(config.tasks['type-check'].dependsOn).toContain('^build'); }); it('should validate output directories are correctly configured', async () => { const turboConfig = { $schema: 'https://turbo.build/schema.json', tasks: { build: { outputs: ['dist/**', '.next/**', '!.next/cache/**'], }, test: { outputs: ['coverage/**'], }, }, }; mockFs.readFile.mockResolvedValue(JSON.stringify(turboConfig)); const config = JSON.parse(await fs.readFile('turbo.json', 'utf-8')); // Build should output to dist expect(config.tasks.build.outputs).toContain('dist/**'); // Test should output coverage expect(config.tasks.test.outputs).toContain('coverage/**'); // Should exclude cache directories expect(config.tasks.build.outputs).toContain('!.next/cache/**'); }); it('should validate cache configuration for development tasks', async () => { const turboConfig = { tasks: { dev: { cache: false, persistent: true, }, clean: { cache: false, }, 'cli:wrap': { cache: false, outputs: ['generated/**'], }, }, }; mockFs.readFile.mockResolvedValue(JSON.stringify(turboConfig)); const config = JSON.parse(await fs.readFile('turbo.json', 'utf-8')); // Dev tasks should not be cached expect(config.tasks.dev.cache).toBe(false); expect(config.tasks.clean.cache).toBe(false); expect(config.tasks['cli:wrap'].cache).toBe(false); // Dev should be persistent expect(config.tasks.dev.persistent).toBe(true); }); }); describe('Template Turbo Optimization', () => { it('should identify turbo-optimized templates correctly', () => { const turboOptimizedTemplate = { name: 'typescript-advanced', optimization: { turboRepo: true, hotReload: true, sharedDependencies: true, buildCaching: true, }, }; const basicTemplate = { name: 'python-basic', optimization: { turboRepo: false, hotReload: false, sharedDependencies: false, buildCaching: false, }, }; expect(turboOptimizedTemplate.optimization.turboRepo).toBe(true); expect(turboOptimizedTemplate.optimization.buildCaching).toBe(true); expect(basicTemplate.optimization.turboRepo).toBe(false); expect(basicTemplate.optimization.buildCaching).toBe(false); }); it('should validate turbo optimization flags', () => { const optimizationConfig = { turboRepo: true, hotReload: true, sharedDependencies: true, buildCaching: true, }; // All optimization flags should be boolean expect(typeof optimizationConfig.turboRepo).toBe('boolean'); expect(typeof optimizationConfig.hotReload).toBe('boolean'); expect(typeof optimizationConfig.sharedDependencies).toBe('boolean'); expect(typeof optimizationConfig.buildCaching).toBe('boolean'); }); }); describe('Package Dependencies', () => { it('should validate turbo is installed as dev dependency', async () => { const packageJson = { devDependencies: { turbo: '^2.5.5', typescript: '^5.5.0', vitest: '^1.6.0', }, }; mockFs.readFile.mockResolvedValue(JSON.stringify(packageJson)); const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8')); expect(pkg.devDependencies).toHaveProperty('turbo'); expect(pkg.devDependencies.turbo).toMatch(/^\^2\./); }); it('should validate npm scripts use turbo commands', async () => { const packageJson = { scripts: { build: 'turbo build', test: 'turbo test', lint: 'turbo lint', dev: 'turbo dev', clean: 'turbo clean', }, }; mockFs.readFile.mockResolvedValue(JSON.stringify(packageJson)); const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8')); expect(pkg.scripts.build).toBe('turbo build'); expect(pkg.scripts.test).toBe('turbo test'); expect(pkg.scripts.lint).toBe('turbo lint'); expect(pkg.scripts.dev).toBe('turbo dev'); expect(pkg.scripts.clean).toBe('turbo clean'); }); }); describe('Task Execution Order', () => { it('should validate task dependency chains', () => { const taskGraph = { build: { dependsOn: ['^build'] }, test: { dependsOn: ['build'] }, lint: { dependsOn: ['^build'] }, 'type-check': { dependsOn: ['^build'] }, }; // Test should wait for build to complete expect(taskGraph.test.dependsOn).toContain('build'); // Lint should wait for upstream builds expect(taskGraph.lint.dependsOn).toContain('^build'); // type-check should wait for upstream builds expect(taskGraph['type-check'].dependsOn).toContain('^build'); }); it('should validate parallel task execution for independent tasks', () => { // These tasks can run in parallel since they only depend on upstream builds const parallelTasks = ['lint', 'type-check']; const taskConfigs = { lint: { dependsOn: ['^build'] }, 'type-check': { dependsOn: ['^build'] }, }; parallelTasks.forEach((task) => { // Both only depend on upstream builds, not on each other expect(taskConfigs[task as keyof typeof taskConfigs].dependsOn).not.toContain('lint'); expect(taskConfigs[task as keyof typeof taskConfigs].dependsOn).not.toContain('type-check'); }); }); }); describe('Caching Strategy', () => { it('should validate caching for build outputs', () => { const cachingConfig = { build: { outputs: ['dist/**'], cache: true }, test: { outputs: ['coverage/**'], cache: true }, lint: { outputs: [], cache: true }, dev: { cache: false, persistent: true }, clean: { cache: false }, }; // Build tasks should be cached expect(cachingConfig.build.cache).toBe(true); expect(cachingConfig.test.cache).toBe(true); expect(cachingConfig.lint.cache).toBe(true); // Interactive/cleanup tasks should not be cached expect(cachingConfig.dev.cache).toBe(false); expect(cachingConfig.clean.cache).toBe(false); }); it('should validate output patterns for cache invalidation', () => { const outputPatterns = { build: ['dist/**', '.next/**', '!.next/cache/**'], test: ['coverage/**'], lint: [], }; // Build should track multiple output directories expect(outputPatterns.build).toContain('dist/**'); expect(outputPatterns.build).toContain('.next/**'); // Should exclude cache directories from tracking expect(outputPatterns.build).toContain('!.next/cache/**'); // Test should track coverage expect(outputPatterns.test).toContain('coverage/**'); // Lint has no outputs (just checks) expect(outputPatterns.lint).toEqual([]); }); }); describe('Error Handling', () => { it('should handle invalid turbo.json gracefully', async () => { mockFs.readFile.mockResolvedValue('invalid json{'); await expect(async () => { const content = await fs.readFile('turbo.json', 'utf-8'); JSON.parse(content); }).rejects.toThrow(); }); it('should handle missing turbo.json file', async () => { mockFs.readFile.mockRejectedValue(new Error('ENOENT: no such file or directory')); await expect(fs.readFile('turbo.json', 'utf-8')).rejects.toThrow('ENOENT'); }); it('should validate required schema fields exist', async () => { const incompleteTurboConfig = { // Missing $schema and tasks }; mockFs.readFile.mockResolvedValue(JSON.stringify(incompleteTurboConfig)); const config = JSON.parse(await fs.readFile('turbo.json', 'utf-8')); // Should not have required fields expect(config).not.toHaveProperty('$schema'); expect(config).not.toHaveProperty('tasks'); }); }); describe('Workspace Integration', () => { it('should validate workspace package discovery', async () => { const workspaceConfig = { workspaces: ['packages/*'], }; mockFs.readFile.mockResolvedValue(JSON.stringify(workspaceConfig)); const pkg = JSON.parse(await fs.readFile('package.json', 'utf-8')); expect(pkg.workspaces).toContain('packages/*'); }); it('should validate cross-package dependencies with ^build pattern', () => { const buildDependency = '^build'; // ^build means "wait for upstream packages to build first" expect(buildDependency).toMatch(/^\^build$/); }); it('should validate package-specific build tasks', () => { const packageTasks = { 'cli:wrap': { dependsOn: ['build'], cache: false, outputs: ['generated/**'], }, 'cli:generate': { dependsOn: ['build'], cache: false, outputs: ['generated/**'], }, }; // CLI tasks should depend on build expect(packageTasks['cli:wrap'].dependsOn).toContain('build'); expect(packageTasks['cli:generate'].dependsOn).toContain('build'); // CLI tasks should not be cached (dynamic generation) expect(packageTasks['cli:wrap'].cache).toBe(false); expect(packageTasks['cli:generate'].cache).toBe(false); // Should output to generated directory expect(packageTasks['cli:wrap'].outputs).toContain('generated/**'); expect(packageTasks['cli:generate'].outputs).toContain('generated/**'); }); }); });

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/conorluddy/ContextPods'

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