package server_test import ( "bytes" "encoding/json" "io" "net/http" "os" "path/filepath" "testing" "time" "github.com/sirupsen/logrus" "markdown-editor-backend/server" ) func TestServerCRUD(t *testing.T) { // Create temp directory for test data tempDir, err := os.MkdirTemp("", "markdown-test-*") if err != nil { t.Fatalf("Failed to create temp dir: %v", err) } defer os.RemoveAll(tempDir) // Create static directory staticDir := filepath.Join(tempDir, "static") if err := os.MkdirAll(staticDir, 0755); err != nil { t.Fatalf("Failed to create static dir: %v", err) } if err := os.WriteFile(filepath.Join(staticDir, "index.html"), []byte(""), 0644); err != nil { t.Fatalf("Failed to create index.html: %v", err) } // Create logger log := logrus.New() log.SetLevel(logrus.WarnLevel) // Create server with dynamic port srv := server.New(tempDir, "127.0.0.1", 0, log) srv.SetStaticPath(staticDir) // Start server asynchronously addr, err := srv.StartAsync() if err != nil { t.Fatalf("Failed to start server: %v", err) } defer srv.Stop() // Wait for server to be ready time.Sleep(100 * time.Millisecond) baseURL := "http://" + addr client := &http.Client{Timeout: 5 * time.Second} // Test health endpoint t.Run("HealthCheck", func(t *testing.T) { resp, err := client.Get(baseURL + "/api/health") if err != nil { t.Fatalf("Health check failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) } var result map[string]string if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { t.Fatalf("Failed to decode response: %v", err) } if result["status"] != "ok" { t.Errorf("Expected status 'ok', got '%s'", result["status"]) } }) // Test Create t.Run("CreateFile", func(t *testing.T) { body := map[string]string{ "name": "test-file", "content": "# Hello World\n\nThis is a test file.", } jsonBody, _ := json.Marshal(body) resp, err := client.Post(baseURL+"/api/files", "application/json", bytes.NewBuffer(jsonBody)) if err != nil { t.Fatalf("Create file failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusCreated { t.Errorf("Expected status 201, got %d", resp.StatusCode) } var result map[string]string if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { t.Fatalf("Failed to decode response: %v", err) } if result["name"] != "test-file" { t.Errorf("Expected name 'test-file', got '%s'", result["name"]) } }) // Test List t.Run("ListFiles", func(t *testing.T) { resp, err := client.Get(baseURL + "/api/files") if err != nil { t.Fatalf("List files failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) } var result []string if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { t.Fatalf("Failed to decode response: %v", err) } found := false for _, name := range result { if name == "test-file" { found = true break } } if !found { t.Errorf("Expected 'test-file' in list, got %v", result) } }) // Test Get t.Run("GetFile", func(t *testing.T) { resp, err := client.Get(baseURL + "/api/files/test-file") if err != nil { t.Fatalf("Get file failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) } var result map[string]string if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { t.Fatalf("Failed to decode response: %v", err) } if result["name"] != "test-file" { t.Errorf("Expected name 'test-file', got '%s'", result["name"]) } if result["content"] != "# Hello World\n\nThis is a test file." { t.Errorf("Expected specific content, got '%s'", result["content"]) } }) // Test Update t.Run("UpdateFile", func(t *testing.T) { body := map[string]string{ "content": "# Updated Content\n\nThis file has been updated.", } jsonBody, _ := json.Marshal(body) req, _ := http.NewRequest(http.MethodPut, baseURL+"/api/files/test-file", bytes.NewBuffer(jsonBody)) req.Header.Set("Content-Type", "application/json") resp, err := client.Do(req) if err != nil { t.Fatalf("Update file failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) } // Verify the update resp2, _ := client.Get(baseURL + "/api/files/test-file") defer resp2.Body.Close() var result map[string]string json.NewDecoder(resp2.Body).Decode(&result) if result["content"] != "# Updated Content\n\nThis file has been updated." { t.Errorf("Expected updated content, got '%s'", result["content"]) } }) // Test Delete t.Run("DeleteFile", func(t *testing.T) { req, _ := http.NewRequest(http.MethodDelete, baseURL+"/api/files/test-file", nil) resp, err := client.Do(req) if err != nil { t.Fatalf("Delete file failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusNoContent { t.Errorf("Expected status 204, got %d", resp.StatusCode) } // Verify deletion resp2, _ := client.Get(baseURL + "/api/files/test-file") defer resp2.Body.Close() if resp2.StatusCode != http.StatusNotFound { t.Errorf("Expected status 404 for deleted file, got %d", resp2.StatusCode) } }) // Test 404 t.Run("GetNonExistentFile", func(t *testing.T) { resp, err := client.Get(baseURL + "/api/files/nonexistent") if err != nil { t.Fatalf("Request failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusNotFound { t.Errorf("Expected status 404, got %d", resp.StatusCode) } var result map[string]string if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { t.Fatalf("Failed to decode response: %v", err) } if result["error"] != "file not found" { t.Errorf("Expected error 'file not found', got '%s'", result["error"]) } }) // Test static file serving t.Run("StaticFiles", func(t *testing.T) { resp, err := client.Get(baseURL + "/") if err != nil { t.Fatalf("Get static file failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("Expected status 200, got %d", resp.StatusCode) } body, _ := io.ReadAll(resp.Body) if string(body) != "" { t.Errorf("Expected '', got '%s'", string(body)) } }) }