forked from TrueCloudLab/restic
lock: Add integration test
The tests check that the wrapped context is properly canceled whenever the repository is unlock or when the lock refresh fails.
This commit is contained in:
parent
c3538b063a
commit
9959190e39
1 changed files with 130 additions and 0 deletions
130
cmd/restic/lock_test.go
Normal file
130
cmd/restic/lock_test.go
Normal file
|
@ -0,0 +1,130 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/restic/restic/internal/repository"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
rtest "github.com/restic/restic/internal/test"
|
||||
)
|
||||
|
||||
func openTestRepo(t *testing.T, wrapper backendWrapper) (*repository.Repository, func(), *testEnvironment) {
|
||||
env, cleanup := withTestEnvironment(t)
|
||||
if wrapper != nil {
|
||||
env.gopts.backendTestHook = wrapper
|
||||
}
|
||||
testRunInit(t, env.gopts)
|
||||
|
||||
repo, err := OpenRepository(context.TODO(), env.gopts)
|
||||
rtest.OK(t, err)
|
||||
return repo, cleanup, env
|
||||
}
|
||||
|
||||
func checkedLockRepo(ctx context.Context, t *testing.T, repo restic.Repository) (*restic.Lock, context.Context) {
|
||||
lock, wrappedCtx, err := lockRepo(ctx, repo)
|
||||
rtest.OK(t, err)
|
||||
rtest.OK(t, wrappedCtx.Err())
|
||||
if lock.Stale() {
|
||||
t.Fatal("lock returned stale lock")
|
||||
}
|
||||
return lock, wrappedCtx
|
||||
}
|
||||
|
||||
func TestLock(t *testing.T) {
|
||||
repo, cleanup, _ := openTestRepo(t, nil)
|
||||
defer cleanup()
|
||||
|
||||
lock, wrappedCtx := checkedLockRepo(context.Background(), t, repo)
|
||||
unlockRepo(lock)
|
||||
if wrappedCtx.Err() == nil {
|
||||
t.Fatal("unlock did not cancel context")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLockCancel(t *testing.T) {
|
||||
repo, cleanup, _ := openTestRepo(t, nil)
|
||||
defer cleanup()
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
lock, wrappedCtx := checkedLockRepo(ctx, t, repo)
|
||||
cancel()
|
||||
if wrappedCtx.Err() == nil {
|
||||
t.Fatal("canceled parent context did not cancel context")
|
||||
}
|
||||
|
||||
// unlockRepo should not crash
|
||||
unlockRepo(lock)
|
||||
}
|
||||
|
||||
func TestLockUnlockAll(t *testing.T) {
|
||||
repo, cleanup, _ := openTestRepo(t, nil)
|
||||
defer cleanup()
|
||||
|
||||
lock, wrappedCtx := checkedLockRepo(context.Background(), t, repo)
|
||||
_, err := unlockAll(0)
|
||||
rtest.OK(t, err)
|
||||
if wrappedCtx.Err() == nil {
|
||||
t.Fatal("canceled parent context did not cancel context")
|
||||
}
|
||||
|
||||
// unlockRepo should not crash
|
||||
unlockRepo(lock)
|
||||
}
|
||||
|
||||
func TestLockConflict(t *testing.T) {
|
||||
repo, cleanup, env := openTestRepo(t, nil)
|
||||
defer cleanup()
|
||||
repo2, err := OpenRepository(context.TODO(), env.gopts)
|
||||
rtest.OK(t, err)
|
||||
|
||||
lock, _, err := lockRepoExclusive(context.Background(), repo)
|
||||
rtest.OK(t, err)
|
||||
defer unlockRepo(lock)
|
||||
_, _, err = lockRepo(context.Background(), repo2)
|
||||
if err == nil {
|
||||
t.Fatal("second lock should have failed")
|
||||
}
|
||||
}
|
||||
|
||||
type writeOnceBackend struct {
|
||||
restic.Backend
|
||||
written bool
|
||||
}
|
||||
|
||||
func (b *writeOnceBackend) Save(ctx context.Context, h restic.Handle, rd restic.RewindReader) error {
|
||||
if b.written {
|
||||
return fmt.Errorf("fail after first write")
|
||||
}
|
||||
b.written = true
|
||||
return b.Backend.Save(ctx, h, rd)
|
||||
}
|
||||
|
||||
func TestLockFailedRefresh(t *testing.T) {
|
||||
repo, cleanup, _ := openTestRepo(t, func(r restic.Backend) (restic.Backend, error) {
|
||||
return &writeOnceBackend{Backend: r}, nil
|
||||
})
|
||||
defer cleanup()
|
||||
|
||||
// reduce locking intervals to be suitable for testing
|
||||
ri, rt := refreshInterval, refreshabilityTimeout
|
||||
refreshInterval = 20 * time.Millisecond
|
||||
refreshabilityTimeout = 100 * time.Millisecond
|
||||
defer func() {
|
||||
refreshInterval, refreshabilityTimeout = ri, rt
|
||||
}()
|
||||
|
||||
lock, wrappedCtx := checkedLockRepo(context.Background(), t, repo)
|
||||
|
||||
select {
|
||||
case <-wrappedCtx.Done():
|
||||
// expected lock refresh failure
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("failed lock refresh did not cause context cancellation")
|
||||
}
|
||||
// unlockRepo should not crash
|
||||
unlockRepo(lock)
|
||||
}
|
Loading…
Reference in a new issue