fetch_ml/tests/unit/worker_trust_test.go

277 lines
8.7 KiB
Go

package unit
import (
"os"
"path/filepath"
"testing"
"time"
"github.com/jfraeys/fetch_ml/internal/experiment"
"github.com/jfraeys/fetch_ml/internal/queue"
)
// TestWorkerValidateTaskForExecution tests worker validation logic
func TestWorkerValidateTaskForExecution_SucceedsWithValidExperiment(t *testing.T) {
base := t.TempDir()
commitID := "0123456789abcdef0123456789abcdef01234567"
expMgr := experiment.NewManager(base)
if err := expMgr.CreateExperiment(commitID); err != nil {
t.Fatalf("CreateExperiment: %v", err)
}
if err := expMgr.WriteMetadata(&experiment.Metadata{
CommitID: commitID,
Timestamp: time.Now().Unix(),
JobName: "job-1",
User: "user-1",
}); err != nil {
t.Fatalf("WriteMetadata: %v", err)
}
filesPath := expMgr.GetFilesPath(commitID)
if err := os.WriteFile(filepath.Join(filesPath, "train.py"), []byte("print('ok')\n"), 0600); err != nil {
t.Fatalf("write train.py: %v", err)
}
if err := os.WriteFile(filepath.Join(filesPath, "requirements.txt"), []byte(""), 0600); err != nil {
t.Fatalf("write requirements.txt: %v", err)
}
// Test that experiment validation works
task := &queue.Task{JobName: "job-1", Metadata: map[string]string{"commit_id": commitID}}
// Verify the experiment setup is valid
if task.JobName != "job-1" {
t.Fatalf("expected job name job-1, got %s", task.JobName)
}
if task.Metadata["commit_id"] != commitID {
t.Fatalf("expected commit_id %s, got %s", commitID, task.Metadata["commit_id"])
}
}
func TestWorkerValidateTaskForExecution_FailsWithoutCommitID(t *testing.T) {
task := &queue.Task{}
// Test validation logic - should fail without commit_id
if task.Metadata == nil || task.Metadata["commit_id"] == "" {
// This is expected behavior
} else {
t.Fatalf("expected missing commit_id validation to fail")
}
}
func TestWorkerValidateTaskForExecution_FailsWhenMetadataMissing(t *testing.T) {
task := &queue.Task{Metadata: map[string]string{}}
// Test validation logic - should fail with empty metadata
if task.Metadata["commit_id"] == "" {
// This is expected behavior
} else {
t.Fatalf("expected empty commit_id validation to fail")
}
}
func TestWorkerValidateTaskForExecution_FailsWhenExperimentMetadataMissing(t *testing.T) {
base := t.TempDir()
commitID := "0123456789abcdef0123456789abcdef01234567"
expMgr := experiment.NewManager(base)
if err := expMgr.CreateExperiment(commitID); err != nil {
t.Fatalf("CreateExperiment: %v", err)
}
// Intentionally do NOT write meta.bin.
// Test that reading metadata fails when it doesn't exist
_, err := expMgr.ReadMetadata(commitID)
if err == nil {
t.Fatalf("expected ReadMetadata to fail when metadata is missing")
}
}
func TestWorkerStageExperimentFiles_CopiesFilesIntoJobDir(t *testing.T) {
base := t.TempDir()
commitID := "0123456789abcdef0123456789abcdef01234567"
expMgr := experiment.NewManager(base)
if err := expMgr.CreateExperiment(commitID); err != nil {
t.Fatalf("CreateExperiment: %v", err)
}
if err := expMgr.WriteMetadata(&experiment.Metadata{
CommitID: commitID,
Timestamp: time.Now().Unix(),
JobName: "job-1",
User: "user-1",
}); err != nil {
t.Fatalf("WriteMetadata: %v", err)
}
filesPath := expMgr.GetFilesPath(commitID)
if err := os.WriteFile(filepath.Join(filesPath, "train.py"), []byte("print('ok')\n"), 0600); err != nil {
t.Fatalf("write train.py: %v", err)
}
if err := os.WriteFile(filepath.Join(filesPath, "requirements.txt"), []byte(""), 0600); err != nil {
t.Fatalf("write requirements.txt: %v", err)
}
if err := os.WriteFile(filepath.Join(filesPath, "extra.txt"), []byte("x"), 0600); err != nil {
t.Fatalf("write extra.txt: %v", err)
}
// Test file copying logic
src := expMgr.GetFilesPath(commitID)
dst := filepath.Join(base, "pending", "job-1", "code")
// Verify source files exist
if _, err := os.Stat(filepath.Join(src, "train.py")); err != nil {
t.Fatalf("expected train.py to exist in source: %v", err)
}
if _, err := os.Stat(filepath.Join(src, "requirements.txt")); err != nil {
t.Fatalf("expected requirements.txt to exist in source: %v", err)
}
if _, err := os.Stat(filepath.Join(src, "extra.txt")); err != nil {
t.Fatalf("expected extra.txt to exist in source: %v", err)
}
// Create destination and copy files
if err := os.MkdirAll(dst, 0750); err != nil {
t.Fatalf("MkdirAll dst: %v", err)
}
// Copy individual files for testing
trainSrc := filepath.Join(src, "train.py")
trainDst := filepath.Join(dst, "train.py")
if err := copyFile(trainSrc, trainDst); err != nil {
t.Fatalf("copy train.py: %v", err)
}
reqSrc := filepath.Join(src, "requirements.txt")
reqDst := filepath.Join(dst, "requirements.txt")
if err := copyFile(reqSrc, reqDst); err != nil {
t.Fatalf("copy requirements.txt: %v", err)
}
extraSrc := filepath.Join(src, "extra.txt")
extraDst := filepath.Join(dst, "extra.txt")
if err := copyFile(extraSrc, extraDst); err != nil {
t.Fatalf("copy extra.txt: %v", err)
}
// Verify files were copied
if _, err := os.Stat(filepath.Join(dst, "train.py")); err != nil {
t.Fatalf("expected train.py copied: %v", err)
}
if _, err := os.Stat(filepath.Join(dst, "requirements.txt")); err != nil {
t.Fatalf("expected requirements.txt copied: %v", err)
}
if _, err := os.Stat(filepath.Join(dst, "extra.txt")); err != nil {
t.Fatalf("expected extra.txt copied: %v", err)
}
}
// Helper function to copy files for testing
func copyFile(src, dst string) error {
data, err := os.ReadFile(src)
if err != nil {
return err
}
return os.WriteFile(dst, data, 0644)
}
// TestManifestGenerationAndValidation tests the full content integrity workflow
func TestManifestGenerationAndValidation(t *testing.T) {
base := t.TempDir()
commitID := "0123456789abcdef0123456789abcdef01234567"
expMgr := experiment.NewManager(base)
if err := expMgr.CreateExperiment(commitID); err != nil {
t.Fatalf("CreateExperiment: %v", err)
}
filesPath := expMgr.GetFilesPath(commitID)
// Create test files with known content
trainContent := "print('hello world')\n"
reqContent := "numpy==1.21.0\npandas==1.3.0\n"
extraContent := "extra data\n"
if err := os.WriteFile(filepath.Join(filesPath, "train.py"), []byte(trainContent), 0600); err != nil {
t.Fatalf("write train.py: %v", err)
}
if err := os.WriteFile(filepath.Join(filesPath, "requirements.txt"), []byte(reqContent), 0600); err != nil {
t.Fatalf("write requirements.txt: %v", err)
}
if err := os.WriteFile(filepath.Join(filesPath, "extra.txt"), []byte(extraContent), 0600); err != nil {
t.Fatalf("write extra.txt: %v", err)
}
// Generate manifest
manifest, err := expMgr.GenerateManifest(commitID)
if err != nil {
t.Fatalf("GenerateManifest: %v", err)
}
// Verify manifest structure
if manifest.CommitID != commitID {
t.Fatalf("expected commit_id %s, got %s", commitID, manifest.CommitID)
}
if len(manifest.Files) != 3 {
t.Fatalf("expected 3 files in manifest, got %d", len(manifest.Files))
}
if manifest.OverallSHA == "" {
t.Fatalf("expected overall SHA to be set")
}
// Write manifest to disk
if err := expMgr.WriteManifest(manifest); err != nil {
t.Fatalf("WriteManifest: %v", err)
}
// Read manifest back
readManifest, err := expMgr.ReadManifest(commitID)
if err != nil {
t.Fatalf("ReadManifest: %v", err)
}
// Verify read manifest matches original
if readManifest.CommitID != manifest.CommitID {
t.Fatalf("commit_id mismatch after read")
}
if readManifest.OverallSHA != manifest.OverallSHA {
t.Fatalf("overall SHA mismatch after read")
}
if len(readManifest.Files) != len(manifest.Files) {
t.Fatalf("file count mismatch after read")
}
// Validate manifest (should pass)
if err := expMgr.ValidateManifest(commitID); err != nil {
t.Fatalf("ValidateManifest should pass: %v", err)
}
// Modify a file and verify validation fails
if err := os.WriteFile(filepath.Join(filesPath, "train.py"), []byte("modified content"), 0600); err != nil {
t.Fatalf("modify train.py: %v", err)
}
if err := expMgr.ValidateManifest(commitID); err == nil {
t.Fatalf("ValidateManifest should fail after file modification")
}
}
// TestManifestValidationFailsWithMissingManifest tests validation when manifest.json is missing
func TestManifestValidationFailsWithMissingManifest(t *testing.T) {
base := t.TempDir()
commitID := "0123456789abcdef0123456789abcdef01234567"
expMgr := experiment.NewManager(base)
if err := expMgr.CreateExperiment(commitID); err != nil {
t.Fatalf("CreateExperiment: %v", err)
}
filesPath := expMgr.GetFilesPath(commitID)
if err := os.WriteFile(filepath.Join(filesPath, "train.py"), []byte("print('test')\n"), 0600); err != nil {
t.Fatalf("write train.py: %v", err)
}
// Don't write manifest - validation should fail
if err := expMgr.ValidateManifest(commitID); err == nil {
t.Fatalf("ValidateManifest should fail when manifest is missing")
}
}