package dump_wal import ( "bytes" "fmt" "io" "os" "testing" "github.com/golang/snappy" "github.com/influxdata/influxdb/v2/tsdb/engine/tsm1" "github.com/spf13/cobra" "github.com/stretchr/testify/require" ) func Test_DumpWal_No_Args(t *testing.T) { params := cmdParams{ walPaths: []string{}, expectErr: true, expectedOut: "requires at least 1 arg(s), only received 0", } runCommand(t, params) } func Test_DumpWal_Bad_Path(t *testing.T) { params := cmdParams{ findDuplicates: false, walPaths: []string{"badpath.wal"}, expectErr: true, expectedOut: "open badpath.wal", } runCommand(t, params) } func Test_DumpWal_Wrong_File_Type(t *testing.T) { // Creates a temporary .txt file (wrong extension) file := newTempWal(t, false, false) params := cmdParams{ walPaths: []string{file}, expectedOut: fmt.Sprintf("invalid wal file path, skipping %s", file), expectErr: false, } runCommand(t, params) } func Test_DumpWal_File_Valid(t *testing.T) { file := newTempWal(t, true, false) params := cmdParams{ walPaths: []string{file}, expectedOuts: []string{ "[write]", "cpu,host=A#!~#float 1.1 1", "cpu,host=A#!~#int 1i 1", "cpu,host=A#!~#bool true 1", "cpu,host=A#!~#string \"string\" 1", "cpu,host=A#!~#unsigned 10u 5", }, } runCommand(t, params) } func Test_DumpWal_Find_Duplicates_None(t *testing.T) { file := newTempWal(t, true, false) params := cmdParams{ findDuplicates: true, walPaths: []string{file}, expectedOut: "No duplicates or out of order timestamps found", } runCommand(t, params) } func Test_DumpWal_Find_Duplicates_Present(t *testing.T) { file := newTempWal(t, true, true) params := cmdParams{ findDuplicates: true, walPaths: []string{file}, expectedOut: "cpu,host=A#!~#unsigned", } runCommand(t, params) } func newTempWal(t *testing.T, validExt bool, withDuplicate bool) string { t.Helper() dir := t.TempDir() if !validExt { file, err := os.CreateTemp(dir, "dumpwaltest*.txt") require.NoError(t, err) t.Cleanup(func() { file.Close() }) return file.Name() } file, err := os.CreateTemp(dir, "dumpwaltest*"+"."+tsm1.WALFileExtension) require.NoError(t, err) t.Cleanup(func() { file.Close() }) p1 := tsm1.NewValue(10, 1.1) p2 := tsm1.NewValue(1, int64(1)) p3 := tsm1.NewValue(1, true) p4 := tsm1.NewValue(1, "string") p5 := tsm1.NewValue(5, uint64(10)) values := map[string][]tsm1.Value{ "cpu,host=A#!~#float": {p1}, "cpu,host=A#!~#int": {p2}, "cpu,host=A#!~#bool": {p3}, "cpu,host=A#!~#string": {p4}, "cpu,host=A#!~#unsigned": {p5}, } if withDuplicate { p6 := tsm1.NewValue(1, uint64(70)) values = map[string][]tsm1.Value{ "cpu,host=A#!~#unsigned": {p5, p6}, } } // Write to WAL File writeWalFile(t, file, values) return file.Name() } func writeWalFile(t *testing.T, file *os.File, vals map[string][]tsm1.Value) { t.Helper() e := &tsm1.WriteWALEntry{Values: vals} b, err := e.Encode(nil) require.NoError(t, err) w := tsm1.NewWALSegmentWriter(file) err = w.Write(e.Type(), snappy.Encode(nil, b)) require.NoError(t, err) err = w.Flush() require.NoError(t, err) err = file.Sync() require.NoError(t, err) } type cmdParams struct { findDuplicates bool walPaths []string expectedOut string expectedOuts []string expectErr bool expectExactEqual bool } func initCommand(t *testing.T, params cmdParams) *cobra.Command { t.Helper() // Creates new command and sets args cmd := NewDumpWALCommand() allArgs := params.walPaths if params.findDuplicates { allArgs = append(allArgs, "--find-duplicates") } cmd.SetArgs(allArgs) return cmd } func getOutput(t *testing.T, cmd *cobra.Command) []byte { t.Helper() b := bytes.NewBufferString("") cmd.SetOut(b) cmd.SetErr(b) require.NoError(t, cmd.Execute()) out, err := io.ReadAll(b) require.NoError(t, err) return out } func runCommand(t *testing.T, params cmdParams) { t.Helper() cmd := initCommand(t, params) if params.expectErr { require.Contains(t, cmd.Execute().Error(), params.expectedOut) return } // Get output out := getOutput(t, cmd) // Check output if params.expectExactEqual { require.Equal(t, string(out), params.expectedOut) return } if params.expectedOut != "" { require.Contains(t, string(out), params.expectedOut) } else { for _, output := range params.expectedOuts { require.Contains(t, string(out), output) } } }