// 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 )
}
}