Problem: TestWorker_ConcurrentJobs was failing because it created empty temporary directories and submitted scan jobs, but never added any test files for the scanner to process. The scanner would complete successfully but create no media items, causing the test to fail with 'Should NOT be empty, but was []'. Root Cause: The test was incomplete - it created the directory structure but didn't populate the directories with test .epub files that the scanner could process into media items. Solution: Added code to create 2 test .epub files in each of the 3 temporary directories before submitting concurrent scan jobs: - Directory 1: book0.epub, book1.epub - Directory 2: book0.epub, book1.epub - Directory 3: book0.epub, book1.epub - Total: 6 test files to be scanned concurrently This matches the pattern used in TestWorker_DirectoryScanJob which creates test files before scanning. Files changed: - cmd/server/tests/worker_test.go: Added test file creation loop
325 lines
9.2 KiB
Go
325 lines
9.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bookhoard/internal/database"
|
|
"bookhoard/internal/services"
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestWorker_DirectoryScanJob(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
token := setup.Token
|
|
|
|
// Create library
|
|
createLibReq := map[string]interface{}{
|
|
"name": "Worker Test Library",
|
|
"description": "Test library for worker",
|
|
"type": "ebooks",
|
|
}
|
|
createLibURL := setup.Server.URL + "/api/libraries"
|
|
createLibReqHTTP, _ := http.NewRequest("POST", createLibURL, bytes.NewBuffer(jsonMarshal(createLibReq)))
|
|
createLibReqHTTP.Header.Set("Content-Type", "application/json")
|
|
createLibReqHTTP.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
createLibResp, err := client.Do(createLibReqHTTP)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusCreated, createLibResp.StatusCode)
|
|
|
|
var createLibResponse map[string]interface{}
|
|
json.NewDecoder(createLibResp.Body).Decode(&createLibResponse)
|
|
createLibResp.Body.Close()
|
|
|
|
libraryID, ok := createLibResponse["id"].(string)
|
|
require.True(t, ok)
|
|
require.NotEmpty(t, libraryID)
|
|
|
|
// Create a temporary directory for testing
|
|
tmpDir := t.TempDir()
|
|
|
|
// Add folder to library
|
|
folderURL := fmt.Sprintf("%s/api/libraries/%s/folders", setup.Server.URL, libraryID)
|
|
folderReq := map[string]interface{}{
|
|
"folder_path": tmpDir,
|
|
}
|
|
req, _ := http.NewRequest("POST", folderURL, bytes.NewBuffer(jsonMarshal(folderReq)))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
resp, err := client.Do(req)
|
|
require.NoError(t, err)
|
|
resp.Body.Close()
|
|
require.Equal(t, http.StatusCreated, resp.StatusCode)
|
|
|
|
// Create test files in the directory
|
|
for i := 0; i < 3; i++ {
|
|
filePath := filepath.Join(tmpDir, fmt.Sprintf("book%d.epub", i))
|
|
err := os.WriteFile(filePath, []byte(fmt.Sprintf("test content %d", i)), 0644)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
// Submit directory scan job through WorkerInstance
|
|
job := &services.Job{
|
|
ID: uuid.New().String(),
|
|
Type: services.JobTypeDirectoryScan,
|
|
Params: map[string]interface{}{
|
|
"directory": tmpDir,
|
|
"db": setup.DB,
|
|
},
|
|
Status: services.JobStatusPending,
|
|
}
|
|
|
|
// Use EnqueueJob which returns errors
|
|
err = services.WorkerInstance.EnqueueJob(job)
|
|
require.NoError(t, err, "Failed to enqueue job")
|
|
// Poll for job completion
|
|
timeout := time.Now().Add(10 * time.Second)
|
|
for time.Now().Before(timeout) {
|
|
if status, exists := services.WorkerInstance.GetJobStatus(job.ID); exists {
|
|
if status.Status == services.JobStatusCompleted || status.Status == services.JobStatusFailed {
|
|
break
|
|
}
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
|
|
// Debug: Check job status after polling
|
|
if finalStatus, exists := services.WorkerInstance.GetJobStatus(job.ID); exists {
|
|
t.Logf("Job %s final status: %s, error: %s", job.ID, finalStatus.Status, finalStatus.Error)
|
|
} else {
|
|
t.Logf("Job %s not found in results", job.ID)
|
|
}
|
|
|
|
// Check that items were created in database
|
|
ctx := context.Background()
|
|
items, err := setup.DB.ListMediaItems(ctx, database.ListMediaItemsParams{
|
|
Limit: 1000,
|
|
Offset: 0,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Should have at least 3 items (our test files)
|
|
greaterOrEqual := func(a, b int) bool {
|
|
return a >= b
|
|
}
|
|
assert.True(t, greaterOrEqual(len(items), 3), fmt.Sprintf("Expected at least 3 items, got %d", len(items)))
|
|
}
|
|
|
|
func TestWorker_SetFoldersJob(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
token := setup.Token
|
|
|
|
// Create library
|
|
createLibReq := map[string]interface{}{
|
|
"name": "Set Folders Test Library",
|
|
"description": "Test library for set folders",
|
|
"type": "ebooks",
|
|
}
|
|
createLibURL := setup.Server.URL + "/api/libraries"
|
|
createLibReqHTTP, _ := http.NewRequest("POST", createLibURL, bytes.NewBuffer(jsonMarshal(createLibReq)))
|
|
createLibReqHTTP.Header.Set("Content-Type", "application/json")
|
|
createLibReqHTTP.Header.Set("Authorization", "Bearer "+token)
|
|
|
|
client := &http.Client{}
|
|
createLibResp, err := client.Do(createLibReqHTTP)
|
|
require.NoError(t, err)
|
|
require.Equal(t, http.StatusCreated, createLibResp.StatusCode)
|
|
|
|
var createLibResponse map[string]interface{}
|
|
json.NewDecoder(createLibResp.Body).Decode(&createLibResponse)
|
|
createLibResp.Body.Close()
|
|
|
|
libraryID, ok := createLibResponse["id"].(string)
|
|
require.True(t, ok)
|
|
require.NotEmpty(t, libraryID)
|
|
|
|
// Create temporary directory
|
|
tmpDir := t.TempDir()
|
|
|
|
// Add folder to library via HTTP API
|
|
addFolderToLibrary(t, setup, libraryID, tmpDir)
|
|
|
|
// Submit set folders job through WorkerInstance
|
|
job := &services.Job{
|
|
ID: uuid.New().String(),
|
|
Type: services.JobTypeSetFolders,
|
|
Params: map[string]interface{}{
|
|
"folders": []string{tmpDir},
|
|
"db": setup.DB,
|
|
},
|
|
Status: services.JobStatusPending,
|
|
}
|
|
|
|
err = services.WorkerInstance.EnqueueJob(job)
|
|
require.NoError(t, err, "Failed to enqueue set folders job")
|
|
|
|
// Wait for job to process
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// Verify folder was added to library
|
|
ctx := context.Background()
|
|
folders, err := setup.DB.GetLibraryFolders(ctx, pgtype.UUID{Bytes: [16]byte(uuid.MustParse(libraryID)), Valid: true})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, folders, "Library should have folders")
|
|
|
|
// Find our folder
|
|
found := false
|
|
for _, folder := range folders {
|
|
if folder.FolderPath == tmpDir {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
assert.True(t, found, "Folder should be in library")
|
|
}
|
|
|
|
func TestWorker_ConcurrentJobs(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create a library and get admin user
|
|
adminID := getTestUserID(t, setup.DB)
|
|
|
|
libraryType, err := setup.DB.GetLibraryTypeByName(ctx, "ebooks")
|
|
require.NoError(t, err)
|
|
|
|
library, err := setup.DB.CreateLibrary(ctx, database.CreateLibraryParams{
|
|
Name: "Concurrent Job Test Library",
|
|
Description: pgtype.Text{String: "Test library", Valid: true},
|
|
LibraryTypeID: libraryType.ID,
|
|
CreatedByAdminID: pgtype.UUID{Bytes: [16]byte(adminID), Valid: true},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create multiple temporary directories
|
|
var tmpDirs []string
|
|
for i := 0; i < 3; i++ {
|
|
tmpDir := t.TempDir()
|
|
tmpDirs = append(tmpDirs, tmpDir)
|
|
|
|
// Add folder to library
|
|
_, err = setup.DB.AddLibraryFolder(ctx, database.AddLibraryFolderParams{
|
|
LibraryID: library.ID,
|
|
FolderPath: tmpDir,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Create test files in each directory
|
|
for j := 0; j < 2; j++ {
|
|
filePath := filepath.Join(tmpDir, fmt.Sprintf("book%d.epub", j))
|
|
err = os.WriteFile(filePath, []byte(fmt.Sprintf("test content %d", j)), 0644)
|
|
require.NoError(t, err, "Failed to create test file")
|
|
}
|
|
}
|
|
|
|
// Submit multiple concurrent jobs
|
|
for _, tmpDir := range tmpDirs {
|
|
job := &services.Job{
|
|
ID: uuid.New().String(),
|
|
Type: services.JobTypeDirectoryScan,
|
|
Params: map[string]interface{}{
|
|
"directory": tmpDir,
|
|
"db": setup.DB,
|
|
},
|
|
Status: services.JobStatusPending,
|
|
}
|
|
err = services.WorkerInstance.EnqueueJob(job)
|
|
require.NoError(t, err, "Failed to enqueue set folders job")
|
|
}
|
|
|
|
// Wait for all jobs to process
|
|
time.Sleep(3 * time.Second)
|
|
|
|
// Verify all directories were processed
|
|
allItems, err := setup.DB.ListMediaItems(ctx, database.ListMediaItemsParams{
|
|
Limit: 1000,
|
|
Offset: 0,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, allItems, "Should have media items from concurrent jobs")
|
|
}
|
|
|
|
func TestWorker_JobStatusTracking(t *testing.T) {
|
|
setup := setupTestServer(t)
|
|
|
|
ctx := context.Background()
|
|
|
|
// Create test library
|
|
adminID := getTestUserID(t, setup.DB)
|
|
libraryType, err := setup.DB.GetLibraryTypeByName(ctx, "ebooks")
|
|
require.NoError(t, err)
|
|
|
|
library, err := setup.DB.CreateLibrary(ctx, database.CreateLibraryParams{
|
|
Name: "Job Status Test Library",
|
|
Description: pgtype.Text{String: "Test library", Valid: true},
|
|
LibraryTypeID: libraryType.ID,
|
|
CreatedByAdminID: pgtype.UUID{Bytes: [16]byte(adminID), Valid: true},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
tmpDir := t.TempDir()
|
|
_, err = setup.DB.AddLibraryFolder(ctx, database.AddLibraryFolderParams{
|
|
LibraryID: library.ID,
|
|
FolderPath: tmpDir,
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
// Submit job and track status
|
|
jobID := uuid.New().String()
|
|
job := &services.Job{
|
|
ID: jobID,
|
|
Type: services.JobTypeDirectoryScan,
|
|
Params: map[string]interface{}{
|
|
"directory": tmpDir,
|
|
"db": setup.DB,
|
|
},
|
|
Status: services.JobStatusPending,
|
|
}
|
|
|
|
err = services.WorkerInstance.EnqueueJob(job)
|
|
require.NoError(t, err, "Failed to enqueue set folders job")
|
|
|
|
// Poll job status
|
|
var finalStatus string
|
|
for i := 0; i < 10; i++ {
|
|
time.Sleep(500 * time.Millisecond)
|
|
|
|
result, exists := services.WorkerInstance.GetJobStatus(jobID)
|
|
if exists {
|
|
finalStatus = string(result.Status)
|
|
if result.Status == services.JobStatusCompleted || result.Status == services.JobStatusFailed {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// Job should complete (success or failure)
|
|
assert.True(t, finalStatus == "completed" || finalStatus == "failed",
|
|
fmt.Sprintf("Job should complete, got status: %s", finalStatus))
|
|
}
|
|
|
|
// Helper function to marshal JSON
|
|
func jsonMarshal(v interface{}) []byte {
|
|
b, err := json.Marshal(v)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
return b
|
|
}
|