218 lines
5.1 KiB
Go
218 lines
5.1 KiB
Go
package restic
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/stretchr/testify/assert"
|
|
"os"
|
|
"os/exec"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestWrapper_LastSnapshot(t *testing.T) {
|
|
shot1 := ResticSnapshot{
|
|
Id: "shot1",
|
|
Time: time.Date(2020, 10, 10, 10, 10, 10, 10, time.UTC),
|
|
}
|
|
shot2 := ResticSnapshot{
|
|
Id: "shot2",
|
|
Time: time.Date(2022, 11, 11, 11, 11, 11, 11, time.UTC),
|
|
}
|
|
|
|
cases := []struct {
|
|
label string
|
|
snapshots []ResticSnapshot
|
|
expected ResticSnapshot
|
|
}{
|
|
{
|
|
"None yet",
|
|
[]ResticSnapshot{},
|
|
ResticSnapshot{
|
|
Id: "(no snapshots yet)",
|
|
Time: time.Time{},
|
|
},
|
|
},
|
|
{
|
|
"take the last from the array",
|
|
[]ResticSnapshot{shot1, shot2},
|
|
shot2,
|
|
},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.label, func(t *testing.T) {
|
|
wrapper := Wrapper{
|
|
LatestSnapshots: tc.snapshots,
|
|
}
|
|
last := wrapper.LastSnapshot()
|
|
|
|
assert.Equal(t, tc.expected.Id, last.Id)
|
|
assert.Equal(t, tc.expected.Time.Hour(), last.Time.Hour())
|
|
assert.Equal(t, tc.expected.Time.Minute(), last.Time.Minute())
|
|
})
|
|
}
|
|
}
|
|
|
|
// a very clever way to test exec.Command in go: https://jamiethompson.me/posts/Unit-Testing-Exec-Command-In-Golang/
|
|
func fakeExecCommand(whichTest string) func(args ...string) *exec.Cmd {
|
|
return func(args ...string) *exec.Cmd {
|
|
cs := []string{"-test.run=" + whichTest, "--", "restic"}
|
|
cs = append(cs, args...)
|
|
cmd := exec.Command(os.Args[0], cs...)
|
|
cmd.Env = []string{"GO_TEST_PROCESS=1"}
|
|
return cmd
|
|
}
|
|
}
|
|
|
|
func TestCmdnapshotSuccessWithinTime(t *testing.T) {
|
|
if os.Getenv("GO_TEST_PROCESS") != "1" {
|
|
return
|
|
}
|
|
snapshots := []ResticSnapshot{
|
|
{
|
|
Id: "id1",
|
|
Time: timeNow(),
|
|
Tree: "tree",
|
|
Paths: []string{
|
|
"/path/one",
|
|
"/path/two",
|
|
},
|
|
},
|
|
{
|
|
Id: "id2",
|
|
Time: timeNow(),
|
|
Tree: "tree2",
|
|
Paths: []string{
|
|
"/path2/one",
|
|
"/path2/two",
|
|
},
|
|
},
|
|
}
|
|
bytes, _ := json.Marshal(snapshots)
|
|
fmt.Fprintf(os.Stdout, string(bytes))
|
|
os.Exit(0)
|
|
}
|
|
|
|
func TestCmdSuccessWithinTime(t *testing.T) {
|
|
if os.Getenv("GO_TEST_PROCESS") != "1" {
|
|
return
|
|
}
|
|
fmt.Fprintf(os.Stdout, "some output\n")
|
|
os.Exit(0)
|
|
}
|
|
func TestCmdExitCode1WithinTime(t *testing.T) {
|
|
if os.Getenv("GO_TEST_PROCESS") != "1" {
|
|
return
|
|
}
|
|
fmt.Fprintf(os.Stdout, "some output\n")
|
|
os.Exit(1)
|
|
}
|
|
func TestCmdExitCode3WithinTime(t *testing.T) {
|
|
if os.Getenv("GO_TEST_PROCESS") != "1" {
|
|
return
|
|
}
|
|
fmt.Fprintf(os.Stdout, "some output\n")
|
|
os.Exit(3)
|
|
}
|
|
|
|
func TestCmdTakesLongerThan2sButOutputsFaster(t *testing.T) {
|
|
if os.Getenv("GO_TEST_PROCESS") != "1" {
|
|
return
|
|
}
|
|
// don't forget \n as the scanner splits on ScanLines
|
|
time.Sleep(1 * time.Second)
|
|
fmt.Fprintf(os.Stdout, "some output after 1s\n")
|
|
time.Sleep(2 * time.Second)
|
|
fmt.Fprintf(os.Stdout, "some output after 3s\n")
|
|
os.Exit(0)
|
|
}
|
|
|
|
func TestCmdTakesLongerThan2sToOutputAnything(t *testing.T) {
|
|
if os.Getenv("GO_TEST_PROCESS") != "1" {
|
|
return
|
|
}
|
|
time.Sleep(3 * time.Second)
|
|
fmt.Fprintf(os.Stdout, "some output, too late!\n")
|
|
os.Exit(0)
|
|
}
|
|
|
|
func TestBackup_ExitCode1_ReturnsError(t *testing.T) {
|
|
resticCmd = fakeExecCommand("TestCmdExitCode1WithinTime")
|
|
|
|
wrapper := Wrapper{}
|
|
err := wrapper.Backup(&Config{})
|
|
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestBackup_ExitCode3_IssuesWarning(t *testing.T) {
|
|
resticCmd = fakeExecCommand("TestCmdExitCode3WithinTime")
|
|
var logBuffer bytes.Buffer
|
|
log.Logger = log.Output(&logBuffer)
|
|
|
|
wrapper := Wrapper{}
|
|
err := wrapper.Backup(&Config{})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, logBuffer.String(), "permission problem?")
|
|
}
|
|
|
|
func TestBackup_OutputsAndFinishesAsExpected(t *testing.T) {
|
|
resticCmd = fakeExecCommand("TestCmdSuccessWithinTime")
|
|
var logBuffer bytes.Buffer
|
|
log.Logger = log.Output(&logBuffer)
|
|
|
|
wrapper := Wrapper{}
|
|
err := wrapper.Backup(&Config{})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, logBuffer.String(), "some output")
|
|
}
|
|
|
|
func TestBackup_OutputsBefore2sButStillTakesLonger(t *testing.T) {
|
|
resticCmd = fakeExecCommand("TestCmdTakesLongerThan2sButOutputsFaster")
|
|
cmdTimeout = 2 * time.Second
|
|
|
|
wrapper := Wrapper{}
|
|
err := wrapper.Backup(&Config{})
|
|
|
|
assert.NoError(t, err)
|
|
}
|
|
|
|
func TestBackup_TakesLongerThanNeededToOutput_TimesOut(t *testing.T) {
|
|
resticCmd = fakeExecCommand("TestCmdTakesLongerThan2sToOutputAnything")
|
|
cmdTimeout = 2 * time.Second
|
|
|
|
wrapper := Wrapper{}
|
|
err := wrapper.Backup(&Config{})
|
|
|
|
assert.ErrorIs(t, err, ResticCmdTimeoutError)
|
|
}
|
|
|
|
func TestUpdateLatestSnapshot_TakesLongerThanNeededToOutput_TimesOut(t *testing.T) {
|
|
resticCmd = fakeExecCommand("TestCmdTakesLongerThan2sToOutputAnything")
|
|
cmdTimeout = 2 * time.Second
|
|
|
|
wrapper := Wrapper{}
|
|
err := wrapper.UpdateLatestSnapshots(&Config{})
|
|
|
|
assert.Empty(t, wrapper.LatestSnapshots)
|
|
assert.ErrorIs(t, err, ResticCmdTimeoutError)
|
|
}
|
|
|
|
func TestUpdateLatestSnapshots_FromJSONOutputOfRestic(t *testing.T) {
|
|
resticCmd = fakeExecCommand("TestCmdnapshotSuccessWithinTime")
|
|
|
|
wrapper := Wrapper{}
|
|
err := wrapper.UpdateLatestSnapshots(&Config{})
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "id1", wrapper.LatestSnapshots[0].Id)
|
|
assert.Equal(t, "/path/two", wrapper.LatestSnapshots[0].Paths[1])
|
|
assert.Equal(t, "id2", wrapper.LatestSnapshots[1].Id)
|
|
assert.Equal(t, "/path2/two", wrapper.LatestSnapshots[1].Paths[1])
|
|
}
|