103 lines
3.3 KiB
Go
103 lines
3.3 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
"git.casaderoll.de/michael/urbm/internal/model"
|
|
"git.casaderoll.de/michael/urbm/internal/store"
|
|
)
|
|
|
|
func TestRepositoryLockHonorsContextCancellation(t *testing.T) {
|
|
s := &Service{repositoryLocks: map[string]chan struct{}{}}
|
|
repo := model.Repository{Type: model.RepositoryLocal, Location: "/repo"}
|
|
unlock, err := s.lockRepository(context.Background(), repo)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Millisecond)
|
|
defer cancel()
|
|
if _, err := s.lockRepository(ctx, repo); err == nil {
|
|
t.Fatal("concurrent repository access ignored the request deadline")
|
|
}
|
|
unlock()
|
|
}
|
|
|
|
func TestBackupMetricsSurviveClearedRunSnapshot(t *testing.T) {
|
|
st := store.New(t.TempDir())
|
|
if err := st.Init(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s := &Service{store: st, persistRuns: make(chan []model.Run, 1)}
|
|
finished := time.Date(2026, 7, 13, 1, 30, 0, 0, time.UTC)
|
|
run := model.Run{ID: "run-1", JobID: "job-1", TaskType: "backup", Status: "success", FinishedAt: &finished, BytesProcessed: 1024, FilesProcessed: 12}
|
|
s.queuePersistence([]model.Run{run})
|
|
s.queuePersistence(nil)
|
|
metrics := s.BackupMetrics()
|
|
if len(metrics) != 1 || metrics[0].RunID != run.ID || metrics[0].FilesProcessed != 12 {
|
|
t.Fatalf("backup metrics after clear = %#v", metrics)
|
|
}
|
|
}
|
|
|
|
func TestBackupMetricsOnlyCaptureCompletedSuccessfulBackupsOnce(t *testing.T) {
|
|
st := store.New(t.TempDir())
|
|
s := &Service{store: st}
|
|
finished := time.Now().UTC()
|
|
valid := model.Run{ID: "valid", JobID: "job-1", TaskType: "backup", Status: "warning", FinishedAt: &finished}
|
|
s.captureBackupMetrics([]model.Run{
|
|
valid,
|
|
valid,
|
|
{ID: "failed", TaskType: "backup", Status: "failed", FinishedAt: &finished},
|
|
{ID: "active", TaskType: "backup", Status: "running"},
|
|
{ID: "rsync", TaskType: "rsync", Status: "success", FinishedAt: &finished},
|
|
})
|
|
metrics := s.BackupMetrics()
|
|
if len(metrics) != 1 || metrics[0].RunID != valid.ID {
|
|
t.Fatalf("captured backup metrics = %#v", metrics)
|
|
}
|
|
}
|
|
|
|
func TestPersistentRunLogsAreWrittenAndPruned(t *testing.T) {
|
|
dir := t.TempDir()
|
|
config := model.DefaultConfig()
|
|
config.Settings.PersistentLogDir = dir
|
|
s := &Service{config: config}
|
|
finished := time.Now()
|
|
run := model.Run{ID: "run-1", FinishedAt: &finished, LiveLog: []string{"first", "second"}}
|
|
if err := s.persistRunLogs([]model.Run{run}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(dir, "urbm-run-1.log")
|
|
if contents, err := os.ReadFile(path); err != nil || string(contents) != "first\nsecond\n" {
|
|
t.Fatalf("persistent log = %q, %v", contents, err)
|
|
}
|
|
if err := s.persistRunLogs(nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
|
t.Fatalf("cleared run log still exists: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestQueuePersistenceDoesNotBlockWhenWriterIsBusy(t *testing.T) {
|
|
s := &Service{persistRuns: make(chan []model.Run, 1)}
|
|
s.persistRuns <- []model.Run{{ID: "old"}}
|
|
done := make(chan struct{})
|
|
go func() {
|
|
s.queuePersistence([]model.Run{{ID: "latest"}})
|
|
close(done)
|
|
}()
|
|
select {
|
|
case <-done:
|
|
case <-time.After(100 * time.Millisecond):
|
|
t.Fatal("queue persistence blocked the queue callback")
|
|
}
|
|
runs := <-s.persistRuns
|
|
if len(runs) != 1 || runs[0].ID != "latest" {
|
|
t.Fatalf("queued persistence state = %#v", runs)
|
|
}
|
|
}
|