refactor: implement worker pool pattern, add persistent task queue, and decouple target loading logic

This commit is contained in:
2026-08-12 23:28:31 -07:00
parent 51b9468737
commit 890469fed8
18 changed files with 694 additions and 442 deletions
+11 -10
View File
@@ -10,8 +10,8 @@ import (
"github.com/hireme/hireme/internal/analyzer"
"github.com/hireme/hireme/internal/config"
"github.com/hireme/hireme/internal/db"
"github.com/hireme/hireme/internal/service"
"github.com/hireme/hireme/internal/ui"
"github.com/hireme/hireme/internal/worker"
"github.com/spf13/cobra"
)
@@ -42,7 +42,12 @@ func newRootCmd() *cobra.Command {
if err != nil {
return fmt.Errorf("fatal DB error: %w", err)
}
defer database.Close()
defer func() { _ = database.Close() }()
// Load targets from YAML config if database is empty
if err := database.LoadTargetsFromYAML(cfg.TargetConfig); err != nil {
log.Printf("[Main] Target YAML loading status: %v", err)
}
// Initialize LLM Analyzer
llmAnalyzer, err := analyzer.NewAnalyzer(cfg)
@@ -50,16 +55,12 @@ func newRootCmd() *cobra.Command {
return fmt.Errorf("fatal Analyzer setup error: %w", err)
}
// Initialize Pipeline Service
pipeline := service.NewPipelineService(database, llmAnalyzer)
// Load targets from YAML config if database is empty
if err := pipeline.LoadTargetsFromYAML(cfg.TargetConfig); err != nil {
log.Printf("[Main] Target YAML loading status: %v", err)
}
// Initialize Worker Pool
pool := worker.NewWorkerPool(database, llmAnalyzer, 3)
pool.Start() // Auto start the worker pool
// Setup UI Server & Routes
server := ui.NewServer(cfg, database, pipeline)
server := ui.NewServer(cfg, database, pool)
mux := http.NewServeMux()
server.RegisterRoutes(mux)
-109
View File
@@ -1,109 +0,0 @@
package main
import (
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"github.com/hireme/hireme/internal/analyzer"
"github.com/hireme/hireme/internal/config"
"github.com/hireme/hireme/internal/db"
"github.com/hireme/hireme/internal/service"
"github.com/hireme/hireme/internal/ui"
)
func TestFullApplicationIntegration(t *testing.T) {
tempDir := t.TempDir()
dbPath := filepath.Join(tempDir, "test_hireme.db")
yamlPath := filepath.Join(tempDir, "test_targets.yaml")
yamlContent := `targets:
- name: "Google Software Engineer (Seattle)"
target_url: "https://www.google.com/about/careers/applications/jobs/results/?employment_type=FULL_TIME&q=%22Software%20Engineer%22&location=Seattle%2C%20WA%2C%20USA&target_level=MID"
platform: "google_jobs"
is_active: true
`
if err := os.WriteFile(yamlPath, []byte(yamlContent), 0644); err != nil {
t.Fatalf("failed to create test targets.yaml: %v", err)
}
cfg := &config.Config{
Port: "8080",
DBPath: dbPath,
TargetConfig: yamlPath,
LLMProvider: "mock",
}
database, err := db.InitDB(cfg.DBPath)
if err != nil {
t.Fatalf("InitDB failed: %v", err)
}
defer database.Close()
llmAnalyzer, err := analyzer.NewAnalyzer(cfg)
if err != nil {
t.Fatalf("NewAnalyzer failed: %v", err)
}
pipeline := service.NewPipelineService(database, llmAnalyzer)
// Verify target loading from YAML into SQLite
if err := pipeline.LoadTargetsFromYAML(cfg.TargetConfig); err != nil {
t.Fatalf("LoadTargetsFromYAML failed: %v", err)
}
targets, err := database.GetAllTargets()
if err != nil {
t.Fatalf("GetAllTargets failed: %v", err)
}
if len(targets) != 1 {
t.Errorf("expected 1 target from YAML, got %d", len(targets))
}
// Verify jobs start empty on clean SQLite DB startup (0 jobs)
jobs, err := database.GetJobsWithAnalysis()
if err != nil {
t.Fatalf("GetJobsWithAnalysis failed: %v", err)
}
if len(jobs) != 0 {
t.Errorf("expected 0 jobs on clean startup, got %d", len(jobs))
}
// Setup UI Server & routes
server := ui.NewServer(cfg, database, pipeline)
mux := http.NewServeMux()
server.RegisterRoutes(mux)
// Test GET /
reqRoot := httptest.NewRequest("GET", "/", nil)
wRoot := httptest.NewRecorder()
mux.ServeHTTP(wRoot, reqRoot)
if wRoot.Code != http.StatusOK {
t.Errorf("expected 200 OK for GET /, got %d", wRoot.Code)
}
if !strings.Contains(wRoot.Body.String(), "Hireme") {
t.Errorf("expected homepage HTML to contain 'Hireme'")
}
// Test GET /targets
reqTargets := httptest.NewRequest("GET", "/targets", nil)
wTargets := httptest.NewRecorder()
mux.ServeHTTP(wTargets, reqTargets)
if wTargets.Code != http.StatusOK {
t.Errorf("expected 200 OK for GET /targets, got %d", wTargets.Code)
}
// Test GET /jobs
reqJobs := httptest.NewRequest("GET", "/jobs", nil)
wJobs := httptest.NewRecorder()
mux.ServeHTTP(wJobs, reqJobs)
if wJobs.Code != http.StatusOK {
t.Errorf("expected 200 OK for GET /jobs, got %d", wJobs.Code)
}
}