Test Files Added: - integration_test.sh: Automated integration test script * Tests full user flow: register, login, library creation, scanning * Tests device registration and management * Color-coded output with pass/fail tracking * Generates detailed test results report - cmd/server/tests/device_test.go: Unit tests for device endpoints * TestDeviceRegistrationFlow: Full registration flow test * TestListDevices: Device listing functionality * TestUpdateDevice: Device settings updates * TestDeleteDevice: Device removal * TestDeviceAuthentication: Device auth middleware test - cmd/server/tests/phase1_integration_test.go: Phase 1 integration tests * Tests universal progress tracking * Tests format group detection * Tests progress conversion Test Coverage: - Device registration with web-based approval flow - Device management (list, update, delete) - Device authentication and token validation - User authentication and authorization - Library creation and management - Scanner integration - Media items listing Notes: - Tests designed to run against live server on localhost:8765 - Integration test script uses bash/curl for endpoint testing - Device tests require helper functions to be implemented
189 lines
5.2 KiB
Go
189 lines
5.2 KiB
Go
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)
|
|
}
|