Skip to content

Commit

Permalink
More tests (#8)
Browse files Browse the repository at this point in the history
Code cleanup and Tests
  • Loading branch information
danlapid authored Sep 6, 2022
1 parent 5bd435c commit 84fb58c
Show file tree
Hide file tree
Showing 14 changed files with 802 additions and 116 deletions.
103 changes: 103 additions & 0 deletions pkg/database/database_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package database

import (
"os"
"testing"

"gorm.io/driver/sqlite"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
)

func TestOpenDatabase(t *testing.T) {
type args struct {
tableprefix string
}
tests := []struct {
name string
args args
wantErr bool
}{
{"test-works", args{"t_"}, false},
{"test-nullbyte", args{"t_\x00"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, err := OpenDatabase(tt.args.tableprefix)
if (err != nil) != tt.wantErr {
t.Errorf("OpenDatabase() error = %v, wantErr %v", err, tt.wantErr)
return
}
_ = os.Remove(DBFILE)
})
}
}

func TestClearDatabase(t *testing.T) {
db1, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: gormlogger.Discard})
if err != nil {
t.Fatal(err)
}
if err = configureDatabase(db1); err != nil {
t.Fatal(err)
}
db2, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: gormlogger.Discard})
if err != nil {
t.Fatal(err)
}
type args struct {
db *gorm.DB
}
tests := []struct {
name string
args args
wantErr bool
}{
{"test-working", args{db1}, false},
{"test-test-no-such-table", args{db2}, true},
// {"test-test-null-db", args{&gorm.DB{}}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if err := ClearDatabase(tt.args.db); (err != nil) != tt.wantErr {
t.Errorf("ClearDatabase() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func TestQueueFileForSending(t *testing.T) {
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: gormlogger.Discard})
if err != nil {
t.Fatal(err)
}
if err = configureDatabase(db); err != nil {
t.Fatal(err)
}
type args struct {
db *gorm.DB
path string
encrypted bool
}
tests := []struct {
name string
args args
wantErr bool
}{
{"test-works", args{db, "a", false}, false},
{"test-no-such-file", args{db, "a", false}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.name != "test-no-such-file" && tt.name != "test-patherror" {
if err := os.WriteFile(tt.args.path, make([]byte, 4), os.ModePerm); err != nil {
t.Fatal(err)
}
}
defer os.Remove(tt.args.path)
if err := QueueFileForSending(tt.args.db, tt.args.path, tt.args.encrypted); (err != nil) != tt.wantErr {
t.Errorf("QueueFileForSending() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
3 changes: 2 additions & 1 deletion pkg/fecdecoder/fecdecoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ type fecDecoderConfig struct {
func worker(ctx context.Context, conf *fecDecoderConfig) {
fec, err := reedsolomon.New(conf.required, conf.total-conf.required)
if err != nil {
logrus.Fatalf("Error creating fec object: %v", err)
logrus.Errorf("Error creating fec object: %v", err)
return
}
for {
select {
Expand Down
18 changes: 9 additions & 9 deletions pkg/fecencoder/fecencoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,33 @@ type fecEncoderConfig struct {
func worker(ctx context.Context, conf *fecEncoderConfig) {
fec, err := reedsolomon.New(conf.required, conf.total-conf.required)
if err != nil {
logrus.Fatalf("Error creating fec object: %v", err)
logrus.Errorf("Error creating fec object: %v", err)
return
}
for {
select {
case <-ctx.Done():
return
case chunk := <-conf.input:
l := logrus.WithFields(logrus.Fields{
"Path": chunk.Path,
"Hash": fmt.Sprintf("%x", chunk.Hash),
})

padding := (conf.required - (len(chunk.Data) % conf.required)) % conf.required
chunk.Data = append(chunk.Data, make([]byte, padding)...)

// Split the data into shares
shares, err := fec.Split(chunk.Data)
if err != nil {
logrus.WithFields(logrus.Fields{
"Path": chunk.Path,
"Hash": fmt.Sprintf("%x", chunk.Hash),
}).Errorf("Error splitting chunk: %v", err)
l.Errorf("Error splitting chunk: %v", err)
continue
}

// Encode the parity set
err = fec.Encode(shares)
if err != nil {
logrus.WithFields(logrus.Fields{
"Path": chunk.Path,
"Hash": fmt.Sprintf("%x", chunk.Hash),
}).Errorf("Error FEC encoding chunk: %v", err)
l.Errorf("Error FEC encoding chunk: %v", err)
continue
}

Expand Down
43 changes: 15 additions & 28 deletions pkg/filecloser/filecloser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package filecloser

import (
"context"
"errors"
"fmt"
"oneway-filesync/pkg/database"
"oneway-filesync/pkg/structs"
Expand All @@ -24,31 +23,19 @@ func normalizePath(path string) string {
}
}
func closeFile(file *structs.OpenTempFile, outdir string) error {
l := logrus.WithFields(logrus.Fields{
"TempFile": file.TempFile,
"Path": file.Path,
"Hash": fmt.Sprintf("%x", file.Hash),
})

f, err := os.Open(file.TempFile)
if err != nil {
l.Errorf("Error opening tempfile: %v", err)
return err
return fmt.Errorf("error opening tempfile: %v", err)
}

hash, err := structs.HashFile(f, false)
err2 := f.Close()
_ = f.Close() // Ignoring error on purpose
if err != nil {
l.Errorf("Error hashing tempfile: %v", err)
return err
}
if err2 != nil {
l.Errorf("Error closing tempfile: %v", err2)
// Not returning error on purpose
return fmt.Errorf("error hashing tempfile: %v", err)
}

if hash != file.Hash {
l.WithField("TempFileHash", fmt.Sprintf("%x", hash)).Errorf("Hash mismatch")
return errors.New("hash mismatch")
return fmt.Errorf("hash mismatch '%v'!='%v'", fmt.Sprintf("%x", hash), fmt.Sprintf("%x", file.Hash))
}

newpath := filepath.Join(outdir, normalizePath(file.Path))
Expand All @@ -57,17 +44,14 @@ func closeFile(file *structs.OpenTempFile, outdir string) error {
}
err = os.MkdirAll(filepath.Dir(newpath), os.ModePerm)
if err != nil {
l.Errorf("Failed creating directory path: %v", err)
return err
return fmt.Errorf("failed creating directory path: %v", err)
}

err = os.Rename(file.TempFile, newpath)
if err != nil {
l.Errorf("Failed moving tempfile to new location: %v", err)
return err
return fmt.Errorf("failed moving tempfile to new location: %v", err)
}

l.WithField("NewPath", newpath).Infof("Successfully finished writing file")
return nil
}

Expand All @@ -83,6 +67,11 @@ func worker(ctx context.Context, conf *fileCloserConfig) {
case <-ctx.Done():
return
case file := <-conf.input:
l := logrus.WithFields(logrus.Fields{
"TempFile": file.TempFile,
"Path": file.Path,
"Hash": fmt.Sprintf("%x", file.Hash),
})
dbentry := database.File{
Path: file.Path,
Hash: file.Hash[:],
Expand All @@ -94,15 +83,13 @@ func worker(ctx context.Context, conf *fileCloserConfig) {
err := closeFile(file, conf.outdir)
if err != nil {
dbentry.Success = false
l.Error(err)
} else {
dbentry.Success = true
l.Infof("Successfully finished writing file")
}
if err := conf.db.Save(&dbentry).Error; err != nil {
logrus.WithFields(logrus.Fields{
"TempFile": file.TempFile,
"Path": file.Path,
"Hash": fmt.Sprintf("%x", file.Hash),
}).Errorf("Failed committing to db: %v", err)
l.Errorf("Failed committing to db: %v", err)
}
}
}
Expand Down
129 changes: 129 additions & 0 deletions pkg/filecloser/filecloser_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package filecloser

import (
"bytes"
"context"
"oneway-filesync/pkg/structs"
"os"
"runtime"
"strings"
"testing"
"time"

"github.com/sirupsen/logrus"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
gormlogger "gorm.io/gorm/logger"
)

func Test_normalizePath(t *testing.T) {
tests := []struct {
name string
path string
want string
}{
{"test1", "/tmp/out/check", "tmp/out/check"},
{"test2", "c:\\tmp\\out\\check", "c/tmp/out/check"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if runtime.GOOS == "windows" {
tt.want = strings.ReplaceAll(tt.want, "/", "\\")
if got := normalizePath(tt.path); got != tt.want {
t.Errorf("normalizePath() = %v, want %v", got, tt.want)
}
} else {
if got := normalizePath(tt.path); got != tt.want {
t.Errorf("normalizePath() = %v, want %v", got, tt.want)
}
}
})
}
}

func Test_closeFile(t *testing.T) {
data := []byte{1, 2, 3, 4}
hash := [32]byte{0x9f, 0x64, 0xa7, 0x47, 0xe1, 0xb9, 0x7f, 0x13, 0x1f, 0xab, 0xb6, 0xb4, 0x47, 0x29, 0x6c, 0x9b, 0x6f, 0x02, 0x01, 0xe7, 0x9f, 0xb3, 0xc5, 0x35, 0x6e, 0x6c, 0x77, 0xe8, 0x9b, 0x6a, 0x80, 0x6a}
wronghash := hash
wronghash[0] = 0

type args struct {
file *structs.OpenTempFile
outdir string
}
tests := []struct {
name string
args args
wantErr bool
}{
{"test-works", args{&structs.OpenTempFile{TempFile: "a", Path: "b", Hash: hash, Encrypted: false, LastUpdated: time.Now()}, "out"}, false},
{"test-hash-mismsatch", args{&structs.OpenTempFile{TempFile: "a", Path: "b", Hash: wronghash, Encrypted: false, LastUpdated: time.Now()}, "out"}, true},
{"test-no-such-file", args{&structs.OpenTempFile{TempFile: "/tmp/adsasdasdsadas/adadsada/a", Path: "b", Hash: hash, Encrypted: false, LastUpdated: time.Now()}, "out"}, true},
{"test-rename-fail", args{&structs.OpenTempFile{TempFile: "a", Path: "b\x00", Hash: hash, Encrypted: false, LastUpdated: time.Now()}, "out"}, true},
{"test-mkdirall-fail", args{&structs.OpenTempFile{TempFile: "a", Path: "b", Hash: hash, Encrypted: false, LastUpdated: time.Now()}, "out\x00"}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer os.RemoveAll(tt.args.outdir)
if tt.name != "test-no-such-file" {
if err := os.WriteFile(tt.args.file.TempFile, data, os.ModePerm); err != nil {
t.Fatal(err)
}
}
defer os.Remove(tt.args.file.TempFile)

if err := closeFile(tt.args.file, tt.args.outdir); (err != nil) != tt.wantErr {
t.Errorf("closeFile() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

func Test_worker(t *testing.T) {
data := []byte{1, 2, 3, 4}
hash := [32]byte{0x9f, 0x64, 0xa7, 0x47, 0xe1, 0xb9, 0x7f, 0x13, 0x1f, 0xab, 0xb6, 0xb4, 0x47, 0x29, 0x6c, 0x9b, 0x6f, 0x02, 0x01, 0xe7, 0x9f, 0xb3, 0xc5, 0x35, 0x6e, 0x6c, 0x77, 0xe8, 0x9b, 0x6a, 0x80, 0x6a}
wronghash := hash
wronghash[0] = 0

type args struct {
file *structs.OpenTempFile
outdir string
}
tests := []struct {
name string
args args
expected string
}{
{"test-dberror", args{&structs.OpenTempFile{TempFile: "a", Path: "b", Hash: hash, Encrypted: false, LastUpdated: time.Now()}, "out"}, "Failed committing to db"},
{"test-hash-mismsatch", args{&structs.OpenTempFile{TempFile: "a", Path: "b", Hash: wronghash, Encrypted: false, LastUpdated: time.Now()}, "out"}, "hash mismatch"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var memLog bytes.Buffer
logrus.SetOutput(&memLog)

db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: gormlogger.Discard})
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(tt.args.outdir)
if err := os.WriteFile(tt.args.file.TempFile, data, os.ModePerm); err != nil {
t.Fatal(err)
}
defer os.Remove(tt.args.file.TempFile)
ch := make(chan *structs.OpenTempFile, 5)
conf := fileCloserConfig{db: db, outdir: tt.args.outdir, input: ch}
ctx, cancel := context.WithCancel(context.Background())
go func() {
time.Sleep(2 * time.Second)
cancel()
}()
ch <- tt.args.file
worker(ctx, &conf)

if !strings.Contains(memLog.String(), tt.expected) {
t.Fatalf("Expected not in log, '%v' not in '%vs'", tt.expected, memLog.String())
}
})
}
}
Loading

0 comments on commit 84fb58c

Please sign in to comment.