summaryrefslogtreecommitdiff
path: root/internal/sync/backup_test.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-29 16:46:50 +0300
committerPaul Buetow <paul@buetow.org>2026-05-29 16:46:50 +0300
commitf6e69200944a1fbc0f1fc0ecd8beb2f8659161c1 (patch)
treee4b52e6449a476b683ada33397d1c8ecfded79f7 /internal/sync/backup_test.go
parent1125cdcda9590c81308ca82e6b0eb9fcb10e7a6e (diff)
refactor(sync): move backup session state to Syncer instance (qq)
Diffstat (limited to 'internal/sync/backup_test.go')
-rw-r--r--internal/sync/backup_test.go63
1 files changed, 57 insertions, 6 deletions
diff --git a/internal/sync/backup_test.go b/internal/sync/backup_test.go
index 9bdbff3..fd15d04 100644
--- a/internal/sync/backup_test.go
+++ b/internal/sync/backup_test.go
@@ -2,15 +2,15 @@ package sync
import (
"errors"
+ "fmt"
+ stdsync "sync"
+ "sync/atomic"
"testing"
"codeberg.org/snonux/gitsyncer/internal/config"
)
func TestHandlePushError_DisablesBackupForSession(t *testing.T) {
- resetBackupSessionState()
- t.Cleanup(resetBackupSessionState)
-
syncer := &Syncer{}
syncer.SetBackupEnabled(true)
@@ -24,9 +24,6 @@ func TestHandlePushError_DisablesBackupForSession(t *testing.T) {
}
func TestHandlePushError_PropagatesPrimaryRemoteFailure(t *testing.T) {
- resetBackupSessionState()
- t.Cleanup(resetBackupSessionState)
-
syncer := &Syncer{}
syncer.SetBackupEnabled(true)
@@ -37,6 +34,60 @@ func TestHandlePushError_PropagatesPrimaryRemoteFailure(t *testing.T) {
}
}
+func TestHandlePushError_BackupDisableIsIsolatedPerSyncer(t *testing.T) {
+ backupOrg := &config.Organization{BackupLocation: true}
+
+ syncerA := &Syncer{}
+ syncerA.SetBackupEnabled(true)
+
+ syncerB := &Syncer{}
+ syncerB.SetBackupEnabled(true)
+
+ err := syncerA.handlePushError("backup-a", backupOrg, errors.New("dial tcp: connection refused"))
+ if err != nil {
+ t.Fatalf("expected backup push failure to be downgraded, got %v", err)
+ }
+
+ if syncerA.backupActive() {
+ t.Fatal("expected syncerA backup sync to be disabled for the remainder of the session")
+ }
+ if !syncerB.backupActive() {
+ t.Fatal("expected syncerB backup session to remain active")
+ }
+}
+
+func TestBackupSessionState_DisableIsThreadSafe(t *testing.T) {
+ var session backupSessionState
+ var firstDisableCount atomic.Int32
+
+ const workers = 32
+ var wg stdsync.WaitGroup
+ wg.Add(workers)
+
+ for i := 0; i < workers; i++ {
+ go func(i int) {
+ defer wg.Done()
+ if session.disable(fmt.Sprintf("reason-%d", i)) {
+ firstDisableCount.Add(1)
+ }
+ }(i)
+ }
+
+ wg.Wait()
+
+ if got := firstDisableCount.Load(); got != 1 {
+ t.Fatalf("expected exactly one successful disable transition, got %d", got)
+ }
+
+ disabled, reason := session.status()
+ if !disabled {
+ t.Fatal("expected backup session to be disabled")
+ }
+ if reason == "" {
+ t.Fatal("expected disable reason to be recorded")
+ }
+}
+
func TestParseSSHLocation_SupportsSSHURLWithPort(t *testing.T) {
t.Parallel()