mirror of
https://github.com/StarFleetCPTN/GoMFT.git
synced 2026-09-08 15:41:20 +02:00
- Integrated new components for job scheduling, including a cron-based scheduler and improved job execution logic. - Refactored job executor to utilize interfaces for better dependency management and testing. - Enhanced notification system with a new Notifier interface, allowing for flexible notification service integration. - Added comprehensive unit tests for job executor, scheduler, and notification components to ensure reliability and correctness. - Updated metadata handling to support new database interfaces, improving testability and maintainability.
227 lines
7.1 KiB
Go
227 lines
7.1 KiB
Go
package scheduler
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/starfleetcptn/gomft/internal/db"
|
|
"gorm.io/gorm" // Keep for gorm.ErrRecordNotFound
|
|
)
|
|
|
|
// --- Mock DB Implementation ---
|
|
|
|
// Ensure mockMetadataDB implements the MetadataDB interface
|
|
var _ MetadataDB = (*mockMetadataDB)(nil)
|
|
|
|
type mockMetadataDB struct {
|
|
GetFileMetadataByHashFunc func(hash string) (*db.FileMetadata, error)
|
|
GetFileMetadataByJobAndNameFunc func(jobID uint, fileName string) (*db.FileMetadata, error)
|
|
}
|
|
|
|
// Implement the MetadataDB interface methods
|
|
func (m *mockMetadataDB) GetFileMetadataByHash(hash string) (*db.FileMetadata, error) {
|
|
if m.GetFileMetadataByHashFunc != nil {
|
|
return m.GetFileMetadataByHashFunc(hash)
|
|
}
|
|
return nil, errors.New("mock GetFileMetadataByHashFunc not implemented")
|
|
}
|
|
|
|
func (m *mockMetadataDB) GetFileMetadataByJobAndName(jobID uint, fileName string) (*db.FileMetadata, error) {
|
|
if m.GetFileMetadataByJobAndNameFunc != nil {
|
|
return m.GetFileMetadataByJobAndNameFunc(jobID, fileName)
|
|
}
|
|
return nil, errors.New("mock GetFileMetadataByJobAndNameFunc not implemented")
|
|
}
|
|
|
|
// --- Tests ---
|
|
|
|
func TestHasFileBeenProcessed(t *testing.T) {
|
|
testJobID := uint(1)
|
|
testHash := "testhash123"
|
|
testMetadata := &db.FileMetadata{ID: 1, JobID: testJobID, FileHash: testHash, Status: "processed"}
|
|
dbErr := errors.New("database error")
|
|
|
|
logger, _ := newTestLogger(LogLevelDebug) // Use helper from logger_test
|
|
defer logger.Close()
|
|
|
|
tests := []struct {
|
|
name string
|
|
fileHash string
|
|
mockDBFunc func(hash string) (*db.FileMetadata, error)
|
|
wantProcessed bool
|
|
wantMetadata *db.FileMetadata
|
|
wantErr error
|
|
wantLogMessage string // Optional: check log output
|
|
}{
|
|
{
|
|
name: "Empty hash",
|
|
fileHash: "",
|
|
mockDBFunc: nil, // Not called
|
|
wantProcessed: false,
|
|
wantMetadata: nil,
|
|
wantErr: nil,
|
|
},
|
|
{
|
|
name: "Hash found",
|
|
fileHash: testHash,
|
|
mockDBFunc: func(hash string) (*db.FileMetadata, error) {
|
|
if hash == testHash {
|
|
return testMetadata, nil
|
|
}
|
|
return nil, gorm.ErrRecordNotFound
|
|
},
|
|
wantProcessed: true,
|
|
wantMetadata: testMetadata,
|
|
wantErr: nil,
|
|
wantLogMessage: "Found existing metadata by hash",
|
|
},
|
|
{
|
|
name: "Hash not found",
|
|
fileHash: testHash,
|
|
mockDBFunc: func(hash string) (*db.FileMetadata, error) {
|
|
return nil, gorm.ErrRecordNotFound
|
|
},
|
|
wantProcessed: false,
|
|
wantMetadata: nil,
|
|
wantErr: nil, // Not found is not an error for this function's return
|
|
},
|
|
{
|
|
name: "DB error",
|
|
fileHash: testHash,
|
|
mockDBFunc: func(hash string) (*db.FileMetadata, error) {
|
|
return nil, dbErr
|
|
},
|
|
wantProcessed: false,
|
|
wantMetadata: nil,
|
|
wantErr: dbErr, // The DB error should be returned
|
|
wantLogMessage: "Error checking metadata by hash",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mockDB := &mockMetadataDB{ // Instantiate the mock implementing the interface
|
|
GetFileMetadataByHashFunc: tt.mockDBFunc,
|
|
}
|
|
// Recreate logger and buffer for each test run to isolate logs
|
|
logger, logBuf := newTestLogger(LogLevelDebug)
|
|
defer logger.Close()
|
|
|
|
// Pass the mockDB which now satisfies the MetadataDB interface
|
|
handler := NewMetadataHandler(mockDB, logger)
|
|
|
|
processed, metadata, err := handler.hasFileBeenProcessed(testJobID, tt.fileHash)
|
|
|
|
if processed != tt.wantProcessed {
|
|
t.Errorf("hasFileBeenProcessed() processed = %v, want %v", processed, tt.wantProcessed)
|
|
}
|
|
if metadata != tt.wantMetadata {
|
|
t.Errorf("hasFileBeenProcessed() metadata = %v, want %v", metadata, tt.wantMetadata)
|
|
}
|
|
if err != tt.wantErr {
|
|
t.Errorf("hasFileBeenProcessed() error = %v, want %v", err, tt.wantErr)
|
|
}
|
|
|
|
logOutput := logBuf.String()
|
|
if tt.wantLogMessage != "" && !strings.Contains(logOutput, tt.wantLogMessage) {
|
|
t.Errorf("Expected log message containing %q, but got:\n%s", tt.wantLogMessage, logOutput)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCheckFileProcessingHistory(t *testing.T) {
|
|
testJobID := uint(1)
|
|
testFileName := "testfile.txt"
|
|
testMetadata := &db.FileMetadata{ID: 2, JobID: testJobID, FileName: testFileName, Status: "processed"}
|
|
dbErr := errors.New("database error")
|
|
notFoundErr := fmt.Errorf("no history found for file %s in job %d", testFileName, testJobID)
|
|
|
|
logger, _ := newTestLogger(LogLevelDebug) // Use helper from logger_test
|
|
defer logger.Close()
|
|
|
|
tests := []struct {
|
|
name string
|
|
jobID uint
|
|
fileName string
|
|
mockDBFunc func(jobID uint, fileName string) (*db.FileMetadata, error)
|
|
wantMetadata *db.FileMetadata
|
|
wantErr error // Check for specific error type/message
|
|
wantLogMessage string // Optional: check log output
|
|
}{
|
|
{
|
|
name: "History found",
|
|
jobID: testJobID,
|
|
fileName: testFileName,
|
|
mockDBFunc: func(jobID uint, fileName string) (*db.FileMetadata, error) {
|
|
if jobID == testJobID && fileName == testFileName {
|
|
return testMetadata, nil
|
|
}
|
|
return nil, gorm.ErrRecordNotFound
|
|
},
|
|
wantMetadata: testMetadata,
|
|
wantErr: nil,
|
|
wantLogMessage: "Found existing metadata by name",
|
|
},
|
|
{
|
|
name: "History not found",
|
|
jobID: testJobID,
|
|
fileName: testFileName,
|
|
mockDBFunc: func(jobID uint, fileName string) (*db.FileMetadata, error) {
|
|
return nil, gorm.ErrRecordNotFound
|
|
},
|
|
wantMetadata: nil,
|
|
wantErr: notFoundErr, // Expect the specific "no history found" error
|
|
},
|
|
{
|
|
name: "DB error",
|
|
jobID: testJobID,
|
|
fileName: testFileName,
|
|
mockDBFunc: func(jobID uint, fileName string) (*db.FileMetadata, error) {
|
|
return nil, dbErr
|
|
},
|
|
wantMetadata: nil,
|
|
wantErr: notFoundErr, // Even with DB error, it returns "no history found"
|
|
wantLogMessage: "Error checking metadata by name",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
mockDB := &mockMetadataDB{ // Instantiate the mock implementing the interface
|
|
GetFileMetadataByJobAndNameFunc: tt.mockDBFunc,
|
|
}
|
|
// Recreate logger and buffer for each test run
|
|
logger, logBuf := newTestLogger(LogLevelDebug)
|
|
defer logger.Close()
|
|
|
|
// Pass the mockDB which now satisfies the MetadataDB interface
|
|
handler := NewMetadataHandler(mockDB, logger)
|
|
|
|
metadata, err := handler.checkFileProcessingHistory(tt.jobID, tt.fileName)
|
|
|
|
if metadata != tt.wantMetadata {
|
|
t.Errorf("checkFileProcessingHistory() metadata = %v, want %v", metadata, tt.wantMetadata)
|
|
}
|
|
|
|
// Check error message specifically for "not found" cases
|
|
if tt.wantErr != nil {
|
|
if err == nil {
|
|
t.Errorf("checkFileProcessingHistory() error = nil, want error containing %q", tt.wantErr.Error())
|
|
} else if !strings.Contains(err.Error(), tt.wantErr.Error()) {
|
|
t.Errorf("checkFileProcessingHistory() error = %q, want error containing %q", err.Error(), tt.wantErr.Error())
|
|
}
|
|
} else if err != nil {
|
|
t.Errorf("checkFileProcessingHistory() error = %v, want nil", err)
|
|
}
|
|
|
|
logOutput := logBuf.String()
|
|
if tt.wantLogMessage != "" && !strings.Contains(logOutput, tt.wantLogMessage) {
|
|
t.Errorf("Expected log message containing %q, but got:\n%s", tt.wantLogMessage, logOutput)
|
|
}
|
|
})
|
|
}
|
|
}
|