**Worker Refactoring:** - Update internal/worker/factory.go, worker.go, snapshot_store.go - Update native_bridge.go and native_bridge_nocgo.go for native library integration **Test Updates:** - Update all worker unit tests for new interfaces - Update chaos tests - Update container/podman_test.go - Add internal/workertest/worker.go for shared test utilities **Documentation:** - Update native/README.md
150 lines
4.1 KiB
Go
150 lines
4.1 KiB
Go
// Package workertest provides test helpers for the worker package.
|
|
// This package is only intended for use in tests and is separate from
|
|
// production code to maintain clean separation of concerns.
|
|
package workertest
|
|
|
|
import (
|
|
"log/slog"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/jfraeys/fetch_ml/internal/logging"
|
|
"github.com/jfraeys/fetch_ml/internal/manifest"
|
|
"github.com/jfraeys/fetch_ml/internal/metrics"
|
|
"github.com/jfraeys/fetch_ml/internal/queue"
|
|
"github.com/jfraeys/fetch_ml/internal/worker"
|
|
"github.com/jfraeys/fetch_ml/internal/worker/executor"
|
|
"github.com/jfraeys/fetch_ml/internal/worker/lifecycle"
|
|
)
|
|
|
|
// SimpleManifestWriter is a basic ManifestWriter implementation for testing
|
|
type SimpleManifestWriter struct{}
|
|
|
|
func (w *SimpleManifestWriter) Upsert(dir string, task *queue.Task, mutate func(*manifest.RunManifest)) {
|
|
// Try to load existing manifest, or create new one
|
|
m, err := manifest.LoadFromDir(dir)
|
|
if err != nil {
|
|
m = w.BuildInitial(task, "")
|
|
}
|
|
mutate(m)
|
|
_ = m.WriteToDir(dir)
|
|
}
|
|
|
|
func (w *SimpleManifestWriter) BuildInitial(task *queue.Task, podmanImage string) *manifest.RunManifest {
|
|
m := manifest.NewRunManifest(
|
|
"run-"+task.ID,
|
|
task.ID,
|
|
task.JobName,
|
|
time.Now().UTC(),
|
|
)
|
|
m.CommitID = task.Metadata["commit_id"]
|
|
m.DepsManifestName = task.Metadata["deps_manifest_name"]
|
|
return m
|
|
}
|
|
|
|
// NewTestWorker creates a minimal Worker for testing purposes.
|
|
// It initializes only the fields needed for unit tests.
|
|
func NewTestWorker(cfg *worker.Config) *worker.Worker {
|
|
if cfg == nil {
|
|
cfg = &worker.Config{}
|
|
}
|
|
|
|
logger := logging.NewLogger(slog.LevelInfo, false)
|
|
metricsObj := &metrics.Metrics{}
|
|
|
|
// Create executors and runner for testing
|
|
writer := &SimpleManifestWriter{}
|
|
localExecutor := executor.NewLocalExecutor(logger, writer)
|
|
containerExecutor := executor.NewContainerExecutor(
|
|
logger,
|
|
nil,
|
|
executor.ContainerConfig{
|
|
PodmanImage: cfg.PodmanImage,
|
|
BasePath: cfg.BasePath,
|
|
},
|
|
)
|
|
jobRunner := executor.NewJobRunner(
|
|
localExecutor,
|
|
containerExecutor,
|
|
writer,
|
|
logger,
|
|
)
|
|
|
|
return &worker.Worker{
|
|
ID: cfg.WorkerID,
|
|
Config: cfg,
|
|
Logger: logger,
|
|
Metrics: metricsObj,
|
|
Health: lifecycle.NewHealthMonitor(),
|
|
Runner: jobRunner,
|
|
}
|
|
}
|
|
|
|
// NewTestWorkerWithQueue creates a test Worker with a queue client.
|
|
func NewTestWorkerWithQueue(cfg *worker.Config, queueClient queue.Backend) *worker.Worker {
|
|
w := NewTestWorker(cfg)
|
|
w.QueueClient = queueClient
|
|
return w
|
|
}
|
|
|
|
// NewTestWorkerWithJupyter creates a test Worker with Jupyter manager.
|
|
func NewTestWorkerWithJupyter(cfg *worker.Config, jupyterMgr worker.JupyterManager) *worker.Worker {
|
|
w := NewTestWorker(cfg)
|
|
w.Jupyter = jupyterMgr
|
|
return w
|
|
}
|
|
|
|
// NewTestWorkerWithRunner creates a test Worker with JobRunner initialized.
|
|
// Note: This creates a minimal runner for testing purposes.
|
|
func NewTestWorkerWithRunner(cfg *worker.Config) *worker.Worker {
|
|
return NewTestWorker(cfg)
|
|
}
|
|
|
|
// NewTestWorkerWithRunLoop creates a test Worker with RunLoop initialized.
|
|
// Note: RunLoop requires proper queue client setup.
|
|
func NewTestWorkerWithRunLoop(cfg *worker.Config, queueClient queue.Backend) *worker.Worker {
|
|
return NewTestWorkerWithQueue(cfg, queueClient)
|
|
}
|
|
|
|
// ResolveDatasets resolves dataset paths for a task.
|
|
// This version matches the test expectations for backwards compatibility.
|
|
// Priority: DatasetSpecs > Datasets > Args parsing
|
|
func ResolveDatasets(task *queue.Task) []string {
|
|
if task == nil {
|
|
return nil
|
|
}
|
|
|
|
// Priority 1: DatasetSpecs
|
|
if len(task.DatasetSpecs) > 0 {
|
|
var paths []string
|
|
for _, spec := range task.DatasetSpecs {
|
|
paths = append(paths, spec.Name)
|
|
}
|
|
return paths
|
|
}
|
|
|
|
// Priority 2: Datasets
|
|
if len(task.Datasets) > 0 {
|
|
return task.Datasets
|
|
}
|
|
|
|
// Priority 3: Parse from Args
|
|
if task.Args != "" {
|
|
// Simple parsing: --datasets a,b,c or --datasets a b c
|
|
args := task.Args
|
|
if idx := strings.Index(args, "--datasets"); idx != -1 {
|
|
after := args[idx+len("--datasets "):]
|
|
after = strings.TrimSpace(after)
|
|
// Split by comma or space
|
|
if strings.Contains(after, ",") {
|
|
return strings.Split(after, ",")
|
|
}
|
|
parts := strings.Fields(after)
|
|
if len(parts) > 0 {
|
|
return parts
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|