restic/cmd/restic/cmd_ls_integration_test.go
Winfried Plappert cc4712f8e9
add a test to cmd_ls_integration_test.go: test rest ls --json (#5255)
* cmd_ls: one more test: ls --json to check the JSON lines

validate that the individual JSON lines are valid JSON statements.
Check for snap ID and the path names in the backup.
2025-03-22 14:20:19 +01:00

164 lines
4.1 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/restic/restic/internal/restic"
rtest "github.com/restic/restic/internal/test"
)
func testRunLsWithOpts(t testing.TB, gopts GlobalOptions, opts LsOptions, args []string) []byte {
buf, err := withCaptureStdout(func() error {
gopts.Quiet = true
return runLs(context.TODO(), opts, gopts, args)
})
rtest.OK(t, err)
return buf.Bytes()
}
func testRunLs(t testing.TB, gopts GlobalOptions, snapshotID string) []string {
out := testRunLsWithOpts(t, gopts, LsOptions{}, []string{snapshotID})
return strings.Split(string(out), "\n")
}
func assertIsValidJSON(t *testing.T, data []byte) {
// Sanity check: output must be valid JSON.
var v []any
err := json.Unmarshal(data, &v)
rtest.OK(t, err)
rtest.Assert(t, len(v) == 4, "invalid ncdu output, expected 4 array elements, got %v", len(v))
}
func TestRunLsNcdu(t *testing.T) {
env, cleanup := withTestEnvironment(t)
defer cleanup()
testSetupBackupData(t, env)
opts := BackupOptions{}
// backup such that there are multiple toplevel elements
testRunBackup(t, env.testdata+"/0", []string{"."}, opts, env.gopts)
for _, paths := range [][]string{
{"latest"},
{"latest", "/0"},
{"latest", "/0", "/0/9"},
} {
ncdu := testRunLsWithOpts(t, env.gopts, LsOptions{Ncdu: true}, paths)
assertIsValidJSON(t, ncdu)
}
}
func TestRunLsSort(t *testing.T) {
rtest.Equals(t, SortMode(0), SortModeName, "unexpected default sort mode")
env, cleanup := withTestEnvironment(t)
defer cleanup()
testSetupBackupData(t, env)
opts := BackupOptions{}
testRunBackup(t, env.testdata+"/0", []string{"for_cmd_ls"}, opts, env.gopts)
for _, test := range []struct {
mode SortMode
expected []string
}{
{
SortModeSize,
[]string{
"/for_cmd_ls",
"/for_cmd_ls/file2.txt",
"/for_cmd_ls/file1.txt",
"/for_cmd_ls/python.py",
"",
},
},
{
SortModeExt,
[]string{
"/for_cmd_ls",
"/for_cmd_ls/python.py",
"/for_cmd_ls/file1.txt",
"/for_cmd_ls/file2.txt",
"",
},
},
{
SortModeName,
[]string{
"/for_cmd_ls",
"/for_cmd_ls/file1.txt",
"/for_cmd_ls/file2.txt",
"/for_cmd_ls/python.py",
"", // last empty line
},
},
} {
out := testRunLsWithOpts(t, env.gopts, LsOptions{Sort: test.mode}, []string{"latest"})
fileList := strings.Split(string(out), "\n")
rtest.Equals(t, test.expected, fileList, fmt.Sprintf("mismatch for mode %v", test.mode))
}
}
// JSON lines test
func TestRunLsJson(t *testing.T) {
pathList := []string{
"/0",
"/0/for_cmd_ls",
"/0/for_cmd_ls/file1.txt",
"/0/for_cmd_ls/file2.txt",
"/0/for_cmd_ls/python.py",
}
env, cleanup := withTestEnvironment(t)
defer cleanup()
testSetupBackupData(t, env)
opts := BackupOptions{}
testRunBackup(t, env.testdata, []string{"0/for_cmd_ls"}, opts, env.gopts)
snapshotIDs := testListSnapshots(t, env.gopts, 1)
env.gopts.Quiet = true
env.gopts.JSON = true
buf := testRunLsWithOpts(t, env.gopts, LsOptions{}, []string{"latest"})
byteLines := bytes.Split(buf, []byte{'\n'})
// partial copy of snapshot structure from cmd_ls
type lsSnapshot struct {
*restic.Snapshot
ID *restic.ID `json:"id"`
ShortID string `json:"short_id"` // deprecated
MessageType string `json:"message_type"` // "snapshot"
StructType string `json:"struct_type"` // "snapshot", deprecated
}
var snappy lsSnapshot
rtest.OK(t, json.Unmarshal(byteLines[0], &snappy))
rtest.Equals(t, snappy.ShortID, snapshotIDs[0].Str(), "expected snap IDs to be identical")
// partial copy of node structure from cmd_ls
type lsNode struct {
Name string `json:"name"`
Type string `json:"type"`
Path string `json:"path"`
Permissions string `json:"permissions,omitempty"`
Inode uint64 `json:"inode,omitempty"`
MessageType string `json:"message_type"` // "node"
StructType string `json:"struct_type"` // "node", deprecated
}
var testNode lsNode
for i, nodeLine := range byteLines[1:] {
if len(nodeLine) == 0 {
break
}
rtest.OK(t, json.Unmarshal(nodeLine, &testNode))
rtest.Equals(t, pathList[i], testNode.Path)
}
}