Skip to main content
Glama
integration_test.go9.47 kB
package aicoder import ( "context" "fmt" "os" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) func TestAICoderIntegration_FullLifecycle(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } // Setup manager, eventBus, tmpDir := setupIntegrationTest(t) defer cleanupIntegrationTest(t, tmpDir) // Set up event bus mock expectations eventBus.On("Emit", mock.Anything, mock.Anything) // Test data req := CreateCoderRequest{ Provider: "mock", Task: "Create a simple REST API with authentication", WorkspaceFiles: []string{ "main.go", "auth.go", "handlers.go", }, } // Step 1: Create AI coder coder, err := manager.CreateCoder(context.Background(), req) require.NoError(t, err) assert.Equal(t, StatusCreating, coder.Status) // Verify workspace created assert.DirExists(t, coder.WorkspaceDir) // Step 2: Start AI coder err = manager.StartCoder(coder.ID) require.NoError(t, err) // Wait for status update time.Sleep(100 * time.Millisecond) updated, exists := manager.GetCoder(coder.ID) require.True(t, exists) assert.Equal(t, StatusRunning, updated.Status) // Step 3: Simulate some progress err = updated.UpdateProgress(0.5, "Generated authentication module") require.NoError(t, err) // Step 4: Create some workspace files err = updated.WriteFile("main.go", []byte("package main\n\nfunc main() {\n\t// TODO: implement\n}")) require.NoError(t, err) err = updated.WriteFile("auth.go", []byte("package main\n\n// Authentication module")) require.NoError(t, err) // Verify files exist files, err := updated.ListWorkspaceFiles() require.NoError(t, err) assert.Contains(t, files, "main.go") assert.Contains(t, files, "auth.go") // Step 5: Complete the task err = updated.UpdateProgress(1.0, "Task completed successfully") require.NoError(t, err) updated.SetStatus(StatusCompleted) // Step 6: Verify final state final, exists := manager.GetCoder(coder.ID) require.True(t, exists) assert.Equal(t, StatusCompleted, final.Status) assert.Equal(t, 1.0, final.Progress) // Verify events were emitted assert.Greater(t, len(eventBus.events), 0) } func TestAICoderIntegration_WorkspaceOperations(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } // Setup manager, eventBus, tmpDir := setupIntegrationTest(t) defer cleanupIntegrationTest(t, tmpDir) // Set up event bus mock expectations eventBus.On("Emit", mock.Anything, mock.Anything) // Create coder coder, err := manager.CreateCoder(context.Background(), CreateCoderRequest{ Provider: "mock", Task: "test workspace operations", }) require.NoError(t, err) // Test file operations testCases := []struct { filename string content string }{ {"hello.go", "package main\n\nfunc main() {\n\tfmt.Println(\"Hello, World!\")\n}"}, {"README.md", "# Test Project\n\nThis is a test project."}, {"config.json", "{\"name\": \"test\", \"version\": \"1.0.0\"}"}, } // Write files for _, tc := range testCases { err := coder.WriteFile(tc.filename, []byte(tc.content)) require.NoError(t, err, "Failed to write %s", tc.filename) } // List files files, err := coder.ListWorkspaceFiles() require.NoError(t, err) for _, tc := range testCases { assert.Contains(t, files, tc.filename) } // Read files back for _, tc := range testCases { content, err := coder.ReadFile(tc.filename) require.NoError(t, err, "Failed to read %s", tc.filename) assert.Equal(t, tc.content, string(content)) } // Test path validation (security) err = coder.WriteFile("../outside.txt", []byte("should fail")) assert.Error(t, err, "Should prevent writing outside workspace") err = coder.WriteFile("/etc/passwd", []byte("should fail")) assert.Error(t, err, "Should prevent writing to system files") } func TestAICoderIntegration_ErrorHandling(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } // Setup manager, eventBus, tmpDir := setupIntegrationTest(t) defer cleanupIntegrationTest(t, tmpDir) // Set up event bus mock expectations eventBus.On("Emit", mock.Anything, mock.Anything) // Test provider error handling mockProvider := &MockAIProvider{} mockProvider.On("Name").Return("failing-provider") mockProvider.On("GetCapabilities").Return(ProviderCapabilities{ SupportsStreaming: false, MaxContextTokens: 1000, MaxOutputTokens: 100, SupportedModels: []string{"failing-model"}, }) mockProvider.On("GenerateCode", mock.Anything, mock.Anything, mock.Anything). Return((*GenerateResult)(nil), assert.AnError) regErr := manager.RegisterProvider("failing-provider", mockProvider) require.NoError(t, regErr) // Create coder with failing provider coder, err := manager.CreateCoder(context.Background(), CreateCoderRequest{ Provider: "failing-provider", Task: "test error handling", }) require.NoError(t, err) // Start coder - should handle provider failure gracefully err = manager.StartCoder(coder.ID) assert.Error(t, err) // Verify coder status reflects failure updated, exists := manager.GetCoder(coder.ID) require.True(t, exists) assert.Equal(t, StatusFailed, updated.Status) } func TestAICoderIntegration_ConcurrentWorkspaceAccess(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } // Setup manager, eventBus, tmpDir := setupIntegrationTest(t) defer cleanupIntegrationTest(t, tmpDir) eventBus.On("Emit", mock.Anything, mock.Anything) // Create a coder coder, err := manager.CreateCoder(context.Background(), CreateCoderRequest{ Provider: "mock", Task: "concurrent workspace test", }) require.NoError(t, err) const numWorkers = 5 const filesPerWorker = 10 done := make(chan error, numWorkers) // Start multiple workers writing files concurrently for worker := 0; worker < numWorkers; worker++ { go func(workerID int) { var err error defer func() { done <- err }() for i := 0; i < filesPerWorker; i++ { filename := fmt.Sprintf("worker_%d_file_%d.txt", workerID, i) content := fmt.Sprintf("Content from worker %d, file %d", workerID, i) writeErr := coder.WriteFile(filename, []byte(content)) if writeErr != nil { err = writeErr return } // Verify we can read it back readContent, readErr := coder.ReadFile(filename) if readErr != nil { err = readErr return } if string(readContent) != content { err = fmt.Errorf("content mismatch for %s", filename) return } } }(worker) } // Wait for all workers to complete for i := 0; i < numWorkers; i++ { select { case err := <-done: require.NoError(t, err, "Worker %d failed", i) case <-time.After(10 * time.Second): t.Fatal("Concurrent workspace test timed out") } } // Verify all files were created files, err := coder.ListWorkspaceFiles() require.NoError(t, err) // Account for the metadata file created by WorkspaceManager expectedFiles := numWorkers*filesPerWorker + 1 // +1 for .aicoder/metadata.json assert.Len(t, files, expectedFiles) } func TestAICoderIntegration_WorkspaceCleanup(t *testing.T) { if testing.Short() { t.Skip("skipping integration test") } // Setup manager, eventBus, tmpDir := setupIntegrationTest(t) defer cleanupIntegrationTest(t, tmpDir) // Set up event bus mock expectations eventBus.On("Emit", mock.Anything, mock.Anything) // Create multiple coders var coders []*AICoderProcess var workspaceDirs []string for i := 0; i < 3; i++ { coder, err := manager.CreateCoder(context.Background(), CreateCoderRequest{ Provider: "mock", Task: fmt.Sprintf("workspace cleanup test %d", i), }) require.NoError(t, err) coders = append(coders, coder) workspaceDirs = append(workspaceDirs, coder.WorkspaceDir) // Create some files in the workspace err = coder.WriteFile("test.txt", []byte("test content")) require.NoError(t, err) } // Verify all workspaces exist for _, dir := range workspaceDirs { assert.DirExists(t, dir) } // Delete coders for _, coder := range coders { err := manager.DeleteCoder(coder.ID) require.NoError(t, err) } // Verify all workspaces are cleaned up for _, dir := range workspaceDirs { assert.NoDirExists(t, dir) } } // Helper functions func setupIntegrationTest(t *testing.T) (*AICoderManager, *MockEventBus, string) { tmpDir := t.TempDir() eventBus := &MockEventBus{} config := &TestConfig{ WorkspaceBaseDir: tmpDir, MaxConcurrent: 5, DefaultProvider: "mock", TimeoutMinutes: 10, } manager, err := NewAICoderManagerWithoutMockProvider(config, eventBus) require.NoError(t, err) // Register working mock provider mockProvider := &MockAIProvider{} mockProvider.On("Name").Return("mock") mockProvider.On("GetCapabilities").Return(ProviderCapabilities{ SupportsStreaming: true, MaxContextTokens: 100000, MaxOutputTokens: 4096, SupportedModels: []string{"mock-model"}, }) mockProvider.On("GenerateCode", mock.Anything, mock.Anything, mock.Anything). Return(&GenerateResult{ Code: "// Generated code", Summary: "Code generated successfully", }, nil) err = manager.RegisterProvider("mock", mockProvider) require.NoError(t, err) return manager, eventBus, tmpDir } func cleanupIntegrationTest(t *testing.T, tmpDir string) { err := os.RemoveAll(tmpDir) if err != nil { t.Logf("Warning: failed to cleanup test directory: %v", err) } }

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/standardbeagle/brummer'

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