// Copyright (c) HashiCorp, Inc. // SPDX-License-Identifier: BUSL-1.1 package snapshot import ( "bytes" "crypto/rand" "fmt" "io" "os" "reflect" "strings" "testing" "github.com/hashicorp/raft" ) func TestArchive(t *testing.T) { // Create some fake snapshot data. metadata := raft.SnapshotMeta{ Index: 2005, Term: 2011, Configuration: raft.Configuration{ Servers: []raft.Server{ { Suffrage: raft.Voter, ID: raft.ServerID("hello"), Address: raft.ServerAddress("127.0.0.1:8300"), }, }, }, Size: 1024, } var snap bytes.Buffer var expected bytes.Buffer both := io.MultiWriter(&snap, &expected) if _, err := io.Copy(both, io.LimitReader(rand.Reader, 1024)); err != nil { t.Fatalf("err: %v", err) } // Write out the snapshot. var archive bytes.Buffer if err := write(&archive, &metadata, &snap); err != nil { t.Fatalf("err: %v", err) } // Read the snapshot back. var newMeta raft.SnapshotMeta var newSnap bytes.Buffer if err := read(&archive, &newMeta, &newSnap); err != nil { t.Fatalf("err: %v", err) } // Check the contents. if !reflect.DeepEqual(newMeta, metadata) { t.Fatalf("bad: %#v", newMeta) } var buf bytes.Buffer if _, err := io.Copy(&buf, &newSnap); err != nil { t.Fatalf("err: %v", err) } if !bytes.Equal(buf.Bytes(), expected.Bytes()) { t.Fatalf("snapshot contents didn't match") } } func TestArchive_GoodData(t *testing.T) { paths := []string{ "../test/snapshot/spaces-meta.tar", } for i, p := range paths { f, err := os.Open(p) if err != nil { t.Fatalf("err: %v", err) } defer f.Close() var metadata raft.SnapshotMeta err = read(f, &metadata, io.Discard) if err != nil { t.Fatalf("case %d: should've read the snapshot, but didn't: %v", i, err) } } } func TestArchive_BadData(t *testing.T) { cases := []struct { Name string Error string }{ {"../test/snapshot/empty.tar", "failed checking integrity of snapshot"}, {"../test/snapshot/extra.tar", "unexpected file \"nope\""}, {"../test/snapshot/missing-meta.tar", "hash check failed for \"meta.json\""}, {"../test/snapshot/missing-state.tar", "hash check failed for \"state.bin\""}, {"../test/snapshot/missing-sha.tar", "file missing"}, {"../test/snapshot/corrupt-meta.tar", "hash check failed for \"meta.json\""}, {"../test/snapshot/corrupt-state.tar", "hash check failed for \"state.bin\""}, {"../test/snapshot/corrupt-sha.tar", "list missing hash for \"nope\""}, } for i, c := range cases { f, err := os.Open(c.Name) if err != nil { t.Fatalf("err: %v", err) } defer f.Close() var metadata raft.SnapshotMeta err = read(f, &metadata, io.Discard) if err == nil || !strings.Contains(err.Error(), c.Error) { t.Fatalf("case %d (%s): %v", i, c.Name, err) } } } func TestArchive_hashList(t *testing.T) { hl := newHashList() for i := 0; i < 16; i++ { h := hl.Add(fmt.Sprintf("file-%d", i)) if _, err := io.CopyN(h, rand.Reader, 32); err != nil { t.Fatalf("err: %v", err) } } // Do a normal round trip. var buf bytes.Buffer if err := hl.Encode(&buf); err != nil { t.Fatalf("err: %v", err) } if err := hl.DecodeAndVerify(&buf); err != nil { t.Fatalf("err: %v", err) } // Have a local hash that isn't in the file. buf.Reset() if err := hl.Encode(&buf); err != nil { t.Fatalf("err: %v", err) } hl.Add("nope") err := hl.DecodeAndVerify(&buf) if err == nil || !strings.Contains(err.Error(), "file missing for \"nope\"") { t.Fatalf("err: %v", err) } // Have a hash in the file that we haven't seen locally. buf.Reset() if err := hl.Encode(&buf); err != nil { t.Fatalf("err: %v", err) } delete(hl.hashes, "nope") err = hl.DecodeAndVerify(&buf) if err == nil || !strings.Contains(err.Error(), "list missing hash for \"nope\"") { t.Fatalf("err: %v", err) } }