mirror of
https://github.com/garethgeorge/backrest.git
synced 2025-12-12 16:55:39 +00:00
389 lines
9.1 KiB
Go
389 lines
9.1 KiB
Go
package oplog
|
|
|
|
import (
|
|
"slices"
|
|
"testing"
|
|
|
|
v1 "github.com/garethgeorge/backrest/gen/go/v1"
|
|
"github.com/garethgeorge/backrest/internal/oplog/indexutil"
|
|
)
|
|
|
|
const (
|
|
snapshotId = "1234567890123456789012345678901234567890123456789012345678901234"
|
|
snapshotId2 = "abcdefgh01234567890123456789012345678901234567890123456789012345"
|
|
)
|
|
|
|
func TestCreate(t *testing.T) {
|
|
// t.Parallel()
|
|
log, err := NewOpLog(t.TempDir() + "/test.boltdb")
|
|
t.Cleanup(func() { log.Close() })
|
|
if err != nil {
|
|
t.Fatalf("error creating oplog: %s", err)
|
|
}
|
|
if err := log.Close(); err != nil {
|
|
t.Fatalf("error closing oplog: %s", err)
|
|
}
|
|
}
|
|
|
|
func TestAddOperation(t *testing.T) {
|
|
log, err := NewOpLog(t.TempDir() + "/test.boltdb")
|
|
if err != nil {
|
|
t.Fatalf("error creating oplog: %s", err)
|
|
}
|
|
t.Cleanup(func() { log.Close() })
|
|
|
|
var tests = []struct {
|
|
name string
|
|
op *v1.Operation
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "basic operation",
|
|
op: &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "basic backup operation",
|
|
op: &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
RepoId: "testrepo",
|
|
PlanId: "testplan",
|
|
InstanceId: "testinstance",
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "basic snapshot operation",
|
|
op: &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
RepoId: "testrepo",
|
|
PlanId: "testplan",
|
|
InstanceId: "testinstance",
|
|
Op: &v1.Operation_OperationIndexSnapshot{
|
|
OperationIndexSnapshot: &v1.OperationIndexSnapshot{
|
|
Snapshot: &v1.ResticSnapshot{
|
|
Id: "test",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "operation with ID",
|
|
op: &v1.Operation{
|
|
Id: 1,
|
|
RepoId: "testrepo",
|
|
PlanId: "testplan",
|
|
InstanceId: "testinstance",
|
|
UnixTimeStartMs: 1234,
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "operation with repo only",
|
|
op: &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
RepoId: "testrepo",
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "operation with plan only",
|
|
op: &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
PlanId: "testplan",
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "operation with instance only",
|
|
op: &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
InstanceId: "testinstance",
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if err := log.Add(tc.op); (err != nil) != tc.wantErr {
|
|
t.Errorf("Add() error = %v, wantErr %v", err, tc.wantErr)
|
|
}
|
|
if !tc.wantErr {
|
|
if tc.op.Id == 0 {
|
|
t.Errorf("Add() did not set op ID")
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestListOperation(t *testing.T) {
|
|
// t.Parallel()
|
|
log, err := NewOpLog(t.TempDir() + "/test.boltdb")
|
|
if err != nil {
|
|
t.Fatalf("error creating oplog: %s", err)
|
|
}
|
|
t.Cleanup(func() { log.Close() })
|
|
|
|
// these should get assigned IDs 1-3 respectively by the oplog
|
|
ops := []*v1.Operation{
|
|
{
|
|
UnixTimeStartMs: 1234,
|
|
PlanId: "plan1",
|
|
RepoId: "repo1",
|
|
InstanceId: "instance1",
|
|
DisplayMessage: "op1",
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
{
|
|
UnixTimeStartMs: 1234,
|
|
PlanId: "plan1",
|
|
RepoId: "repo2",
|
|
InstanceId: "instance2",
|
|
DisplayMessage: "op2",
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
{
|
|
UnixTimeStartMs: 1234,
|
|
PlanId: "plan2",
|
|
RepoId: "repo2",
|
|
InstanceId: "instance3",
|
|
DisplayMessage: "op3",
|
|
FlowId: 943,
|
|
Op: &v1.Operation_OperationBackup{},
|
|
},
|
|
}
|
|
|
|
for _, op := range ops {
|
|
if err := log.Add(op); err != nil {
|
|
t.Fatalf("error adding operation: %s", err)
|
|
}
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
query Query
|
|
expected []string
|
|
}{
|
|
{
|
|
name: "list plan1",
|
|
query: Query{PlanId: "plan1"},
|
|
expected: []string{"op1", "op2"},
|
|
},
|
|
{
|
|
name: "list plan2",
|
|
query: Query{PlanId: "plan2"},
|
|
expected: []string{"op3"},
|
|
},
|
|
{
|
|
name: "list repo1",
|
|
query: Query{RepoId: "repo1"},
|
|
expected: []string{"op1"},
|
|
},
|
|
{
|
|
name: "list repo2",
|
|
query: Query{RepoId: "repo2"},
|
|
expected: []string{"op2", "op3"},
|
|
},
|
|
{
|
|
name: "list flow 943",
|
|
query: Query{FlowId: 943},
|
|
expected: []string{
|
|
"op3",
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
tc := tc
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
// t.Parallel()
|
|
var ops []*v1.Operation
|
|
var err error
|
|
collect := func(op *v1.Operation) error {
|
|
ops = append(ops, op)
|
|
return nil
|
|
}
|
|
err = log.ForEach(tc.query, indexutil.CollectAll(), collect)
|
|
if err != nil {
|
|
t.Fatalf("error listing operations: %s", err)
|
|
}
|
|
got := collectMessages(ops)
|
|
if slices.Compare(got, tc.expected) != 0 {
|
|
t.Errorf("want operations: %v, got unexpected operations: %v", tc.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestBigIO(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
count := 10
|
|
|
|
log, err := NewOpLog(t.TempDir() + "/test.boltdb")
|
|
if err != nil {
|
|
t.Fatalf("error creating oplog: %s", err)
|
|
}
|
|
t.Cleanup(func() { log.Close() })
|
|
|
|
for i := 0; i < count; i++ {
|
|
if err := log.Add(&v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
PlanId: "plan1",
|
|
RepoId: "repo1",
|
|
InstanceId: "instance1",
|
|
Op: &v1.Operation_OperationBackup{},
|
|
}); err != nil {
|
|
t.Fatalf("error adding operation: %s", err)
|
|
}
|
|
}
|
|
|
|
countByPlanHelper(t, log, "plan1", count)
|
|
countByRepoHelper(t, log, "repo1", count)
|
|
}
|
|
|
|
func TestIndexSnapshot(t *testing.T) {
|
|
t.Parallel()
|
|
log, err := NewOpLog(t.TempDir() + "/test.boltdb")
|
|
if err != nil {
|
|
t.Fatalf("error creating oplog: %s", err)
|
|
}
|
|
t.Cleanup(func() { log.Close() })
|
|
|
|
op := &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
PlanId: "plan1",
|
|
RepoId: "repo1",
|
|
InstanceId: "instance1",
|
|
SnapshotId: snapshotId,
|
|
Op: &v1.Operation_OperationIndexSnapshot{},
|
|
}
|
|
if err := log.Add(op); err != nil {
|
|
t.Fatalf("error adding operation: %s", err)
|
|
}
|
|
|
|
var ops []*v1.Operation
|
|
if err := log.ForEach(Query{SnapshotId: snapshotId}, indexutil.CollectAll(), func(op *v1.Operation) error {
|
|
ops = append(ops, op)
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatalf("error listing operations: %s", err)
|
|
}
|
|
if len(ops) != 1 {
|
|
t.Fatalf("want 1 operation, got %d", len(ops))
|
|
}
|
|
if ops[0].Id != op.Id {
|
|
t.Errorf("want operation ID %d, got %d", op.Id, ops[0].Id)
|
|
}
|
|
}
|
|
|
|
func TestUpdateOperation(t *testing.T) {
|
|
t.Parallel()
|
|
log, err := NewOpLog(t.TempDir() + "/test.boltdb")
|
|
if err != nil {
|
|
t.Fatalf("error creating oplog: %s", err)
|
|
}
|
|
t.Cleanup(func() { log.Close() })
|
|
|
|
// Insert initial operation
|
|
op := &v1.Operation{
|
|
UnixTimeStartMs: 1234,
|
|
PlanId: "oldplan",
|
|
RepoId: "oldrepo",
|
|
InstanceId: "instance1",
|
|
SnapshotId: snapshotId,
|
|
}
|
|
if err := log.Add(op); err != nil {
|
|
t.Fatalf("error adding operation: %s", err)
|
|
}
|
|
opId := op.Id
|
|
|
|
// Validate initial values are indexed
|
|
countByPlanHelper(t, log, "oldplan", 1)
|
|
countByRepoHelper(t, log, "oldrepo", 1)
|
|
countBySnapshotIdHelper(t, log, snapshotId, 1)
|
|
|
|
// Update indexed values
|
|
op.SnapshotId = snapshotId2
|
|
op.PlanId = "myplan"
|
|
op.RepoId = "myrepo"
|
|
if err := log.Update(op); err != nil {
|
|
t.Fatalf("error updating operation: %s", err)
|
|
}
|
|
|
|
// Validate updated values are indexed
|
|
if opId != op.Id {
|
|
t.Errorf("want operation ID %d, got %d", opId, op.Id)
|
|
}
|
|
|
|
countByPlanHelper(t, log, "myplan", 1)
|
|
countByRepoHelper(t, log, "myrepo", 1)
|
|
countBySnapshotIdHelper(t, log, snapshotId2, 1)
|
|
|
|
// Validate prior values are gone
|
|
countByPlanHelper(t, log, "oldplan", 0)
|
|
countByRepoHelper(t, log, "oldrepo", 0)
|
|
countBySnapshotIdHelper(t, log, snapshotId, 0)
|
|
}
|
|
|
|
func collectMessages(ops []*v1.Operation) []string {
|
|
var messages []string
|
|
for _, op := range ops {
|
|
messages = append(messages, op.DisplayMessage)
|
|
}
|
|
return messages
|
|
}
|
|
|
|
func countByRepoHelper(t *testing.T, log *OpLog, repo string, expected int) {
|
|
t.Helper()
|
|
count := 0
|
|
if err := log.ForEach(Query{RepoId: repo}, indexutil.CollectAll(), func(op *v1.Operation) error {
|
|
count += 1
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatalf("error listing operations: %s", err)
|
|
}
|
|
if count != expected {
|
|
t.Errorf("want %d operations, got %d", expected, count)
|
|
}
|
|
}
|
|
|
|
func countByPlanHelper(t *testing.T, log *OpLog, plan string, expected int) {
|
|
t.Helper()
|
|
count := 0
|
|
if err := log.ForEach(Query{PlanId: plan}, indexutil.CollectAll(), func(op *v1.Operation) error {
|
|
count += 1
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatalf("error listing operations: %s", err)
|
|
}
|
|
if count != expected {
|
|
t.Errorf("want %d operations, got %d", expected, count)
|
|
}
|
|
}
|
|
|
|
func countBySnapshotIdHelper(t *testing.T, log *OpLog, snapshotId string, expected int) {
|
|
t.Helper()
|
|
count := 0
|
|
if err := log.ForEach(Query{SnapshotId: snapshotId}, indexutil.CollectAll(), func(op *v1.Operation) error {
|
|
count += 1
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatalf("error listing operations: %s", err)
|
|
}
|
|
if count != expected {
|
|
t.Errorf("want %d operations, got %d", expected, count)
|
|
}
|
|
}
|