458 lines
11 KiB
Go
458 lines
11 KiB
Go
package experiment
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/jfraeys/fetch_ml/internal/experiment"
|
|
)
|
|
|
|
func findArchivedExperiment(t *testing.T, basePath, commitID string) string {
|
|
t.Helper()
|
|
archiveRoot := filepath.Join(basePath, "archive")
|
|
var found string
|
|
_ = filepath.WalkDir(archiveRoot, func(path string, d os.DirEntry, err error) error {
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
if d.IsDir() && filepath.Base(path) == commitID {
|
|
found = path
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
})
|
|
return found
|
|
}
|
|
|
|
const (
|
|
experimentsPath = "/experiments"
|
|
testCommitID = "abc123"
|
|
)
|
|
|
|
func TestNewManager(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
// Test that manager was created successfully by checking it can generate paths
|
|
path := manager.GetExperimentPath("test")
|
|
if path == "" {
|
|
t.Error("Manager should be able to generate paths")
|
|
}
|
|
}
|
|
|
|
func TestGetExperimentPath(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
const experimentsPath = "/experiments"
|
|
const testCommitID = "abc123"
|
|
|
|
basePath := experimentsPath
|
|
manager := experiment.NewManager(basePath)
|
|
commitID := testCommitID
|
|
|
|
expectedPath := filepath.Join(basePath, commitID)
|
|
actualPath := manager.GetExperimentPath(commitID)
|
|
|
|
if actualPath != expectedPath {
|
|
t.Errorf("Expected path %s, got %s", expectedPath, actualPath)
|
|
}
|
|
}
|
|
|
|
func TestGetFilesPath(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := experimentsPath
|
|
manager := experiment.NewManager(basePath)
|
|
commitID := testCommitID
|
|
|
|
expectedPath := filepath.Join(basePath, commitID, "files")
|
|
actualPath := manager.GetFilesPath(commitID)
|
|
|
|
if actualPath != expectedPath {
|
|
t.Errorf("Expected path %s, got %s", expectedPath, actualPath)
|
|
}
|
|
}
|
|
|
|
func TestGetMetadataPath(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := experimentsPath
|
|
manager := experiment.NewManager(basePath)
|
|
commitID := testCommitID
|
|
|
|
expectedPath := filepath.Join(basePath, commitID, "meta.bin")
|
|
actualPath := manager.GetMetadataPath(commitID)
|
|
|
|
if actualPath != expectedPath {
|
|
t.Errorf("Expected path %s, got %s", expectedPath, actualPath)
|
|
}
|
|
}
|
|
|
|
func TestExperimentExists(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
// Test non-existent experiment
|
|
if manager.ExperimentExists("nonexistent") {
|
|
t.Error("Experiment should not exist")
|
|
}
|
|
|
|
// Create experiment directory
|
|
commitID := testCommitID
|
|
experimentPath := manager.GetExperimentPath(commitID)
|
|
err := os.MkdirAll(experimentPath, 0750)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create experiment directory: %v", err)
|
|
}
|
|
|
|
// Test existing experiment
|
|
if !manager.ExperimentExists(commitID) {
|
|
t.Error("Experiment should exist")
|
|
}
|
|
}
|
|
|
|
func TestCreateExperiment(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
commitID := testCommitID
|
|
|
|
err := manager.CreateExperiment(commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create experiment: %v", err)
|
|
}
|
|
|
|
// Verify experiment directory exists
|
|
if !manager.ExperimentExists(commitID) {
|
|
t.Error("Experiment should exist after creation")
|
|
}
|
|
|
|
// Verify files directory exists
|
|
filesPath := manager.GetFilesPath(commitID)
|
|
info, err := os.Stat(filesPath)
|
|
if err != nil {
|
|
t.Fatalf("Files directory should exist: %v", err)
|
|
}
|
|
|
|
if !info.IsDir() {
|
|
t.Error("Files path should be a directory")
|
|
}
|
|
}
|
|
|
|
func TestWriteAndReadMetadata(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
commitID := testCommitID
|
|
originalMetadata := &experiment.Metadata{
|
|
CommitID: commitID,
|
|
Timestamp: time.Now().Unix(),
|
|
JobName: "test_experiment",
|
|
User: "testuser",
|
|
}
|
|
|
|
// Create experiment first
|
|
err := manager.CreateExperiment(commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create experiment: %v", err)
|
|
}
|
|
|
|
// Write metadata
|
|
err = manager.WriteMetadata(originalMetadata)
|
|
if err != nil {
|
|
t.Fatalf("Failed to write metadata: %v", err)
|
|
}
|
|
|
|
// Read metadata
|
|
loadedMetadata, err := manager.ReadMetadata(commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read metadata: %v", err)
|
|
}
|
|
|
|
// Verify metadata
|
|
if loadedMetadata.CommitID != originalMetadata.CommitID {
|
|
t.Errorf("Expected commit ID %s, got %s", originalMetadata.CommitID, loadedMetadata.CommitID)
|
|
}
|
|
|
|
if loadedMetadata.Timestamp != originalMetadata.Timestamp {
|
|
t.Errorf("Expected timestamp %d, got %d", originalMetadata.Timestamp, loadedMetadata.Timestamp)
|
|
}
|
|
|
|
if loadedMetadata.JobName != originalMetadata.JobName {
|
|
t.Errorf("Expected job name %s, got %s", originalMetadata.JobName, loadedMetadata.JobName)
|
|
}
|
|
|
|
if loadedMetadata.User != originalMetadata.User {
|
|
t.Errorf("Expected user %s, got %s", originalMetadata.User, loadedMetadata.User)
|
|
}
|
|
}
|
|
|
|
func TestReadMetadataNonExistent(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
// Try to read metadata from non-existent experiment
|
|
_, err := manager.ReadMetadata("nonexistent")
|
|
if err == nil {
|
|
t.Error("Expected error when reading metadata from non-existent experiment")
|
|
}
|
|
}
|
|
|
|
func TestWriteMetadataNonExistentDir(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
commitID := testCommitID
|
|
metadata := &experiment.Metadata{
|
|
CommitID: commitID,
|
|
Timestamp: time.Now().Unix(),
|
|
JobName: "test_experiment",
|
|
User: "testuser",
|
|
}
|
|
|
|
// Try to write metadata without creating experiment directory first
|
|
err := manager.WriteMetadata(metadata)
|
|
if err == nil {
|
|
t.Error("Expected error when writing metadata to non-existent experiment")
|
|
}
|
|
}
|
|
|
|
func TestListExperiments(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
// Create multiple experiments
|
|
experiments := []string{"abc123", "def456", "ghi789"}
|
|
for _, commitID := range experiments {
|
|
err := manager.CreateExperiment(commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create experiment %s: %v", commitID, err)
|
|
}
|
|
}
|
|
|
|
// List experiments
|
|
experimentList, err := manager.ListExperiments()
|
|
if err != nil {
|
|
t.Fatalf("Failed to list experiments: %v", err)
|
|
}
|
|
|
|
if len(experimentList) != 3 {
|
|
t.Errorf("Expected 3 experiments, got %d", len(experimentList))
|
|
}
|
|
|
|
// Verify all experiments are listed
|
|
for _, commitID := range experiments {
|
|
found := false
|
|
for _, exp := range experimentList {
|
|
if exp == commitID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
t.Errorf("Experiment %s not found in list", commitID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestPruneExperiments(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
// Create experiments with different timestamps
|
|
now := time.Now()
|
|
experiments := []struct {
|
|
commitID string
|
|
timestamp int64
|
|
}{
|
|
{"old1", now.AddDate(0, 0, -10).Unix()},
|
|
{"old2", now.AddDate(0, 0, -5).Unix()},
|
|
{"recent", now.AddDate(0, 0, -1).Unix()},
|
|
}
|
|
|
|
for _, exp := range experiments {
|
|
// Create experiment directory
|
|
err := manager.CreateExperiment(exp.commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create experiment %s: %v", exp.commitID, err)
|
|
}
|
|
|
|
// Write metadata
|
|
metadata := &experiment.Metadata{
|
|
CommitID: exp.commitID,
|
|
Timestamp: exp.timestamp,
|
|
JobName: "experiment_" + exp.commitID,
|
|
User: "testuser",
|
|
}
|
|
|
|
err = manager.WriteMetadata(metadata)
|
|
if err != nil {
|
|
t.Fatalf("Failed to write metadata for %s: %v", exp.commitID, err)
|
|
}
|
|
}
|
|
|
|
// Prune experiments (keep 1, prune older than 3 days)
|
|
pruned, err := manager.PruneExperiments(1, 3)
|
|
if err != nil {
|
|
t.Fatalf("Failed to prune experiments: %v", err)
|
|
}
|
|
|
|
// Should prune old1 and old2 (older than 3 days)
|
|
if len(pruned) != 2 {
|
|
t.Errorf("Expected 2 pruned experiments, got %d", len(pruned))
|
|
}
|
|
|
|
// Verify recent experiment still exists
|
|
if !manager.ExperimentExists("recent") {
|
|
t.Error("Recent experiment should still exist")
|
|
}
|
|
|
|
// Verify old experiments are gone
|
|
if manager.ExperimentExists("old1") {
|
|
t.Error("Old experiment 1 should be pruned")
|
|
}
|
|
|
|
if manager.ExperimentExists("old2") {
|
|
t.Error("Old experiment 2 should be pruned")
|
|
}
|
|
|
|
if archived := findArchivedExperiment(t, basePath, "old1"); archived == "" {
|
|
t.Error("Old experiment 1 should be archived")
|
|
}
|
|
|
|
if archived := findArchivedExperiment(t, basePath, "old2"); archived == "" {
|
|
t.Error("Old experiment 2 should be archived")
|
|
}
|
|
}
|
|
|
|
func TestPruneExperimentsKeepCount(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
// Create experiments with different timestamps
|
|
now := time.Now()
|
|
experiments := []string{"exp1", "exp2", "exp3", "exp4"}
|
|
|
|
for i, commitID := range experiments {
|
|
// Create experiment directory
|
|
err := manager.CreateExperiment(commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create experiment %s: %v", commitID, err)
|
|
}
|
|
|
|
// Write metadata with different timestamps (newer first)
|
|
metadata := &experiment.Metadata{
|
|
CommitID: commitID,
|
|
Timestamp: now.Add(-time.Duration(i) * time.Hour).Unix(),
|
|
JobName: "experiment_" + commitID,
|
|
User: "testuser",
|
|
}
|
|
|
|
err = manager.WriteMetadata(metadata)
|
|
if err != nil {
|
|
t.Fatalf("Failed to write metadata for %s: %v", commitID, err)
|
|
}
|
|
}
|
|
|
|
// Prune experiments (keep 2 newest, no age limit)
|
|
pruned, err := manager.PruneExperiments(2, 0)
|
|
if err != nil {
|
|
t.Fatalf("Failed to prune experiments: %v", err)
|
|
}
|
|
|
|
// Should prune 2 oldest experiments
|
|
if len(pruned) != 2 {
|
|
t.Errorf("Expected 2 pruned experiments, got %d", len(pruned))
|
|
}
|
|
|
|
// Verify newest experiments still exist
|
|
if !manager.ExperimentExists("exp1") {
|
|
t.Error("Newest experiment should still exist")
|
|
}
|
|
|
|
if !manager.ExperimentExists("exp2") {
|
|
t.Error("Second newest experiment should still exist")
|
|
}
|
|
|
|
// Verify oldest experiments are gone
|
|
if manager.ExperimentExists("exp3") {
|
|
t.Error("Old experiment 3 should be pruned")
|
|
}
|
|
|
|
if manager.ExperimentExists("exp4") {
|
|
t.Error("Old experiment 4 should be pruned")
|
|
}
|
|
|
|
if archived := findArchivedExperiment(t, basePath, "exp3"); archived == "" {
|
|
t.Error("Old experiment 3 should be archived")
|
|
}
|
|
|
|
if archived := findArchivedExperiment(t, basePath, "exp4"); archived == "" {
|
|
t.Error("Old experiment 4 should be archived")
|
|
}
|
|
}
|
|
|
|
func TestMetadataPartialFields(t *testing.T) {
|
|
t.Parallel() // Enable parallel execution
|
|
|
|
basePath := t.TempDir()
|
|
manager := experiment.NewManager(basePath)
|
|
|
|
commitID := "abc123"
|
|
|
|
// Create experiment
|
|
err := manager.CreateExperiment(commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create experiment: %v", err)
|
|
}
|
|
|
|
// Test metadata with only required fields
|
|
metadata := &experiment.Metadata{
|
|
CommitID: commitID,
|
|
Timestamp: time.Now().Unix(),
|
|
// JobName and User are optional
|
|
}
|
|
|
|
err = manager.WriteMetadata(metadata)
|
|
if err != nil {
|
|
t.Fatalf("Failed to write metadata: %v", err)
|
|
}
|
|
|
|
// Read it back
|
|
loadedMetadata, err := manager.ReadMetadata(commitID)
|
|
if err != nil {
|
|
t.Fatalf("Failed to read metadata: %v", err)
|
|
}
|
|
|
|
if loadedMetadata.CommitID != commitID {
|
|
t.Errorf("Expected commit ID %s, got %s", commitID, loadedMetadata.CommitID)
|
|
}
|
|
|
|
if loadedMetadata.JobName != "" {
|
|
t.Errorf("Expected empty job name, got %s", loadedMetadata.JobName)
|
|
}
|
|
|
|
if loadedMetadata.User != "" {
|
|
t.Errorf("Expected empty user, got %s", loadedMetadata.User)
|
|
}
|
|
}
|