package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" "testing" "time" "github.com/stretchr/testify/assert" ) const baseTestURL = "http://localhost:8765/api" // Integration test sequence for Phase 1 Universal Progress func TestPhase1Integration(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") } // Step 1: Create first user (should be admin) t.Run("Step1_CreateFirstUser", func(t *testing.T) { userReq := map[string]interface{}{ "email": "admin@bookmann.test", "username": "admin", "password": "SecurePass123!", "first_name": "Admin", "last_name": "User", } body, _ := json.Marshal(userReq) resp, err := http.Post(baseTestURL+"/auth/register", "application/json", bytes.NewBuffer(body)) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) user, ok := result["user"].(map[string]interface{}) assert.True(t, ok, "User field should exist") assert.Equal(t, "admin", user["username"]) assert.Equal(t, "admin", user["role"], "First user should be admin") t.Logf("✅ Step 1 PASSED: First user created with admin role") }) // Login as admin var adminToken string t.Run("LoginAsAdmin", func(t *testing.T) { loginReq := map[string]interface{}{ "identifier": "admin@bookmann.test", "password": "SecurePass123!", } body, _ := json.Marshal(loginReq) resp, err := http.Post(baseTestURL+"/auth/login", "application/json", bytes.NewBuffer(body)) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) adminToken = result["token"].(string) assert.NotEmpty(t, adminToken) }) // Step 2: Create first library with ebook type var libraryID string t.Run("Step2_CreateFirstLibrary", func(t *testing.T) { libraryReq := map[string]interface{}{ "name": "Test Library", "description": "Integration test library", "type": "ebook", } body, _ := json.Marshal(libraryReq) req, _ := http.NewRequest("POST", baseTestURL+"/libraries", bytes.NewBuffer(body)) req.Header.Set("Authorization", "Bearer "+adminToken) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) libraryID = result["id"].(string) assert.NotEmpty(t, libraryID) assert.Equal(t, "Test Library", result["name"]) t.Logf("✅ Step 2 PASSED: First library created with ID: %s", libraryID) }) // Step 3: Add /app/uploads folder to the library t.Run("Step3_AddUploadsFolder", func(t *testing.T) { folderReq := map[string]interface{}{ "folder_path": "/app/uploads", } body, _ := json.Marshal(folderReq) url := fmt.Sprintf("%s/libraries/%s/folders", baseTestURL, libraryID) req, _ := http.NewRequest("POST", url, bytes.NewBuffer(body)) req.Header.Set("Authorization", "Bearer "+adminToken) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusCreated, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) assert.Equal(t, "/app/uploads", result["folder_path"]) t.Logf("✅ Step 3 PASSED: /app/uploads folder added to library") }) // Step 4: Scan the library t.Run("Step4_ScanLibrary", func(t *testing.T) { scanReq := map[string]interface{}{ "library_id": libraryID, } body, _ := json.Marshal(scanReq) req, _ := http.NewRequest("POST", baseTestURL+"/scanner/scan", bytes.NewBuffer(body)) req.Header.Set("Authorization", "Bearer "+adminToken) req.Header.Set("Content-Type", "application/json") client := &http.Client{} resp, err := client.Do(req) assert.NoError(t, err) defer resp.Body.Close() // Accept 200 or 202 assert.Contains(t, []int{http.StatusOK, http.StatusAccepted}, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) assert.Equal(t, "success", result["status"]) t.Logf("✅ Step 4 PASSED: Library scan initiated") }) // Wait for scan to complete time.Sleep(2 * time.Second) // Step 5: List media-items t.Run("Step5_ListMediaItems", func(t *testing.T) { req, _ := http.NewRequest("GET", baseTestURL+"/media-items?limit=50", nil) req.Header.Set("Authorization", "Bearer "+adminToken) client := &http.Client{} resp, err := client.Do(req) assert.NoError(t, err) defer resp.Body.Close() assert.Equal(t, http.StatusOK, resp.StatusCode) var result map[string]interface{} json.NewDecoder(resp.Body).Decode(&result) items, ok := result["items"].([]interface{}) assert.True(t, ok, "Items field should exist") assert.True(t, len(items) >= 0, "Should return items array") t.Logf("✅ Step 5 PASSED: Media items listed (count: %d)", len(items)) }) } // Helper function to read response body func readBody(resp *http.Response) string { body, _ := io.ReadAll(resp.Body) return string(body) }