////////////////////////////////////////////////////////////////////////////////
// Copyright © 2020 xx network SEZC                                           //
//                                                                            //
// Use of this source code is governed by a license that can be found in the  //
// LICENSE file                                                               //
////////////////////////////////////////////////////////////////////////////////

package fileTransfer

import (
	"bytes"
	"fmt"
	"github.com/pkg/errors"
	"gitlab.com/elixxir/client/interfaces"
	"gitlab.com/elixxir/client/storage/utility"
	"gitlab.com/elixxir/client/storage/versioned"
	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
	"gitlab.com/elixxir/ekv"
	"reflect"
	"strings"
	"sync"
	"sync/atomic"
	"testing"
	"time"
)

// Tests that NewReceivedTransfer correctly creates a new ReceivedTransfer and
// that it is saved to storage.
func Test_NewReceivedTransfer(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, _ := ftCrypto.NewTransferID(prng)
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")
	kvPrefixed := kv.Prefix(makeReceivedTransferPrefix(tid))
	fileSize := uint32(256)
	numParts, numFps := uint16(16), uint16(24)
	fpVector, _ := utility.NewStateVector(
		kvPrefixed, receivedFpVectorKey, uint32(numFps))
	receivedVector, _ := utility.NewStateVector(
		kvPrefixed, receivedVectorKey, uint32(numParts))

	expected := &ReceivedTransfer{
		key:         key,
		transferMAC: mac,
		fileSize:    fileSize,
		numParts:    numParts,
		numFps:      numFps,
		fpVector:    fpVector,
		receivedParts: &partStore{
			parts:    make(map[uint16][]byte, numParts),
			numParts: numParts,
			kv:       kvPrefixed,
		},
		receivedStatus:    receivedVector,
		progressCallbacks: []*receivedCallbackTracker{},
		mux:               sync.RWMutex{},
		kv:                kvPrefixed,
	}

	// Create new ReceivedTransfer
	rt, err := NewReceivedTransfer(tid, key, mac, fileSize, numParts, numFps, kv)
	if err != nil {
		t.Fatalf("NewReceivedTransfer returned an error: %v", err)
	}

	// Check that the new object matches the expected
	if !reflect.DeepEqual(expected, rt) {
		t.Errorf("New ReceivedTransfer does not match expected."+
			"\nexpected: %#v\nreceived: %#v", expected, rt)
	}

	// Make sure it is saved to storage
	_, err = kvPrefixed.Get(receivedTransferKey, receivedTransferVersion)
	if err != nil {
		t.Fatalf("Failed to load ReceivedTransfer from storage: %+v", err)
	}

	// Check that the fingerprint vector has correct values
	if rt.fpVector.GetNumAvailable() != uint32(numFps) {
		t.Errorf("Incorrect number of available keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps, rt.fpVector.GetNumAvailable())
	}
	if rt.fpVector.GetNumKeys() != uint32(numFps) {
		t.Errorf("Incorrect number of keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps, rt.fpVector.GetNumKeys())
	}
	if rt.fpVector.GetNumUsed() != 0 {
		t.Errorf("Incorrect number of used keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", 0, rt.fpVector.GetNumUsed())
	}
}

// Tests that ReceivedTransfer.GetTransferKey returns the expected key.
func TestReceivedTransfer_GetTransferKey(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))

	tid, _ := ftCrypto.NewTransferID(prng)
	mac := []byte("transferMAC")
	expectedKey, _ := ftCrypto.NewTransferKey(prng)

	rt, err := NewReceivedTransfer(tid, expectedKey, mac, 256, 16, 1, kv)
	if err != nil {
		t.Errorf("Failed to create new ReceivedTransfer: %+v", err)
	}

	if expectedKey != rt.GetTransferKey() {
		t.Errorf("Failed to get expected transfer key."+
			"\nexpected: %s\nreceived: %s", expectedKey, rt.GetTransferKey())
	}
}

// Tests that ReceivedTransfer.GetTransferMAC returns the expected bytes.
func TestReceivedTransfer_GetTransferMAC(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))

	tid, _ := ftCrypto.NewTransferID(prng)
	expectedMAC := []byte("transferMAC")
	key, _ := ftCrypto.NewTransferKey(prng)

	rt, err := NewReceivedTransfer(tid, key, expectedMAC, 256, 16, 1, kv)
	if err != nil {
		t.Errorf("Failed to create new ReceivedTransfer: %+v", err)
	}

	if !bytes.Equal(expectedMAC, rt.GetTransferMAC()) {
		t.Errorf("Failed to get expected transfer MAC."+
			"\nexpected: %v\nreceived: %v", expectedMAC, rt.GetTransferMAC())
	}
}

// Tests that ReceivedTransfer.GetNumParts returns the expected number of parts.
func TestReceivedTransfer_GetNumParts(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedNumParts := uint16(16)
	_, rt, _ := newRandomReceivedTransfer(expectedNumParts, 20, kv, t)

	if expectedNumParts != rt.GetNumParts() {
		t.Errorf("Failed to get expected number of parts."+
			"\nexpected: %d\nreceived: %d", expectedNumParts, rt.GetNumParts())
	}
}

// Tests that ReceivedTransfer.GetNumFps returns the expected number of
// fingerprints.
func TestReceivedTransfer_GetNumFps(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedNumFps := uint16(20)
	_, rt, _ := newRandomReceivedTransfer(16, expectedNumFps, kv, t)

	if expectedNumFps != rt.GetNumFps() {
		t.Errorf("Failed to get expected number of fingerprints."+
			"\nexpected: %d\nreceived: %d", expectedNumFps, rt.GetNumFps())
	}
}

// Tests that ReceivedTransfer.GetNumAvailableFps returns the expected number of
// available fingerprints.
func TestReceivedTransfer_GetNumAvailableFps(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	numParts, numFps := uint16(16), uint16(24)
	_, rt, _ := newRandomReceivedTransfer(numParts, numFps, kv, t)

	if numFps-numParts != rt.GetNumAvailableFps() {
		t.Errorf("Failed to get expected number of available fingerprints."+
			"\nexpected: %d\nreceived: %d",
			numFps-numParts, rt.GetNumAvailableFps())
	}
}

// Tests that ReceivedTransfer.GetFileSize returns the expected file size.
func TestReceivedTransfer_GetFileSize(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, file := newRandomReceivedTransfer(16, 20, kv, t)
	expectedFileSize := len(file)

	if expectedFileSize != int(rt.GetFileSize()) {
		t.Errorf("Failed to get expected file size."+
			"\nexpected: %d\nreceived: %d", expectedFileSize, rt.GetFileSize())
	}
}

// Tests that ReceivedTransfer.IsPartReceived returns false for unreceived file
// parts and true when the file part has been received.
func TestReceivedTransfer_IsPartReceived(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)

	partNum := uint16(5)

	if rt.IsPartReceived(partNum) {
		t.Errorf("Part number %d received.", partNum)
	}

	_ = rt.receivedParts.addPart([]byte("part"), partNum)

	if !rt.IsPartReceived(partNum) {
		t.Errorf("Part number %d not received.", partNum)
	}
}

// checkReceivedProgress compares the output of ReceivedTransfer.GetProgress to
// expected values.
func checkReceivedProgress(completed bool, received, total uint16,
	eCompleted bool, eReceived, eTotal uint16) error {
	if eCompleted != completed || eReceived != received || eTotal != total {
		return errors.Errorf("Returned progress does not match expected."+
			"\n          completed  received  total"+
			"\nexpected:     %5t       %3d    %3d"+
			"\nreceived:     %5t       %3d    %3d",
			eCompleted, eReceived, eTotal,
			completed, received, total)
	}

	return nil
}

// checkReceivedTracker checks that the ReceivedPartTracker is reporting the
// correct values for each part. Also checks that ReceivedPartTracker.GetNumParts
// returns the expected value (make sure numParts comes from a correct source).
func checkReceivedTracker(track ReceivedPartTracker, numParts uint16,
	received []uint16, t *testing.T) {
	if track.GetNumParts() != numParts {
		t.Errorf("Tracker reported incorrect number of parts."+
			"\nexpected: %d\nreceived: %d", numParts, track.GetNumParts())
		return
	}

	for partNum := uint16(0); partNum < numParts; partNum++ {
		var done bool
		for _, receivedNum := range received {
			if receivedNum == partNum {
				if track.GetPartStatus(partNum) != interfaces.FpReceived {
					t.Errorf("Part number %d has unexpected status."+
						"\nexpected: %d\nreceived: %d", partNum,
						interfaces.FpReceived, track.GetPartStatus(partNum))
				}
				done = true
				break
			}
		}
		if done {
			continue
		}

		if track.GetPartStatus(partNum) != interfaces.FpUnsent {
			t.Errorf("Part number %d has incorrect status."+
				"\nexpected: %d\nreceived: %d",
				partNum, interfaces.FpUnsent, track.GetPartStatus(partNum))
		}
	}
}

// Tests that ReceivedTransfer.GetProgress returns the expected progress metrics
// for various transfer states.
func TestReceivedTransfer_GetProgress(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	numParts := uint16(16)
	_, rt, _ := newEmptyReceivedTransfer(numParts, 20, kv, t)

	completed, received, total, track := rt.GetProgress()
	err := checkReceivedProgress(completed, received, total, false, 0, numParts)
	if err != nil {
		t.Error(err)
	}
	checkReceivedTracker(track, rt.numParts, nil, t)

	_, _ = rt.fpVector.Next()
	_, _ = rt.receivedStatus.Next()

	completed, received, total, track = rt.GetProgress()
	err = checkReceivedProgress(completed, received, total, false, 1, numParts)
	if err != nil {
		t.Error(err)
	}
	checkReceivedTracker(track, rt.numParts, []uint16{0}, t)

	for i := 0; i < 4; i++ {
		_, _ = rt.fpVector.Next()
		_, _ = rt.receivedStatus.Next()
	}

	completed, received, total, track = rt.GetProgress()
	err = checkReceivedProgress(completed, received, total, false, 5, numParts)
	if err != nil {
		t.Error(err)
	}
	checkReceivedTracker(track, rt.numParts, []uint16{0, 1, 2, 3, 4}, t)

	for i := 0; i < 6; i++ {
		_, _ = rt.fpVector.Next()
		_, _ = rt.receivedStatus.Next()
	}

	completed, received, total, track = rt.GetProgress()
	err = checkReceivedProgress(completed, received, total, false, 11, numParts)
	if err != nil {
		t.Error(err)
	}
	checkReceivedTracker(track, rt.numParts, []uint16{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, t)

	for i := 0; i < 4; i++ {
		_, _ = rt.fpVector.Next()
		_, _ = rt.receivedStatus.Next()
	}

	completed, received, total, track = rt.GetProgress()
	err = checkReceivedProgress(completed, received, total, false, 15, numParts)
	if err != nil {
		t.Error(err)
	}
	checkReceivedTracker(track, rt.numParts, []uint16{0, 1, 2, 3, 4, 5, 6, 7, 8,
		9, 10, 11, 12, 13, 14}, t)

	_, _ = rt.fpVector.Next()
	_, _ = rt.receivedStatus.Next()

	completed, received, total, track = rt.GetProgress()
	err = checkReceivedProgress(completed, received, total, true, 16, numParts)
	if err != nil {
		t.Error(err)
	}
	checkReceivedTracker(track, rt.numParts, []uint16{0, 1, 2, 3, 4, 5, 6, 7, 8,
		9, 10, 11, 12, 13, 14, 15}, t)
}

// Tests that 5 different callbacks all receive the expected data when
// ReceivedTransfer.CallProgressCB is called at different stages of transfer.
func TestReceivedTransfer_CallProgressCB(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)

	type progressResults struct {
		completed       bool
		received, total uint16
		tr              interfaces.FilePartTracker
		err             error
	}

	period := time.Millisecond

	wg := sync.WaitGroup{}
	var step0, step1, step2, step3 uint64
	numCallbacks := 5

	for i := 0; i < numCallbacks; i++ {
		progressChan := make(chan progressResults)

		cbFunc := func(completed bool, received, total uint16,
			tr interfaces.FilePartTracker, err error) {
			progressChan <- progressResults{completed, received, total, tr, err}
		}
		wg.Add(1)

		go func(i int) {
			defer wg.Done()
			n := 0
			for {
				select {
				case <-time.NewTimer(time.Second).C:
					t.Errorf("Timed out after %s waiting for callback (%d).",
						period*5, i)
					return
				case r := <-progressChan:
					switch n {
					case 0:
						if err := checkReceivedProgress(r.completed, r.received,
							r.total, false, 0, rt.numParts); err != nil {
							t.Errorf("%2d: %v", i, err)
						}
						atomic.AddUint64(&step0, 1)
					case 1:
						if err := checkReceivedProgress(r.completed, r.received,
							r.total, false, 0, rt.numParts); err != nil {
							t.Errorf("%2d: %v", i, err)
						}
						atomic.AddUint64(&step1, 1)
					case 2:
						if err := checkReceivedProgress(r.completed, r.received,
							r.total, false, 4, rt.numParts); err != nil {
							t.Errorf("%2d: %v", i, err)
						}
						atomic.AddUint64(&step2, 1)
					case 3:
						if err := checkReceivedProgress(r.completed, r.received,
							r.total, true, 16, rt.numParts); err != nil {
							t.Errorf("%2d: %v", i, err)
						}
						atomic.AddUint64(&step3, 1)
						return
					default:
						t.Errorf("n (%d) is great than 3 (%d)", n, i)
						return
					}
					n++
				}
			}
		}(i)

		rt.AddProgressCB(cbFunc, period)
	}

	for !atomic.CompareAndSwapUint64(&step0, uint64(numCallbacks), 0) {
	}

	rt.CallProgressCB(nil)

	for !atomic.CompareAndSwapUint64(&step1, uint64(numCallbacks), 0) {
	}

	for i := 0; i < 4; i++ {
		_, _ = rt.receivedStatus.Next()
	}

	rt.CallProgressCB(nil)

	for !atomic.CompareAndSwapUint64(&step2, uint64(numCallbacks), 0) {
	}

	for i := 0; i < 12; i++ {
		_, _ = rt.receivedStatus.Next()
	}

	rt.CallProgressCB(nil)

	wg.Wait()
}

// Tests that ReceivedTransfer.StopScheduledProgressCB stops a scheduled
// callback from being triggered.
func TestReceivedTransfer_StopScheduledProgressCB(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)

	cbChan := make(chan struct{}, 5)
	cbFunc := interfaces.ReceivedProgressCallback(
		func(completed bool, received, total uint16,
			t interfaces.FilePartTracker, err error) {
			cbChan <- struct{}{}
		})
	rt.AddProgressCB(cbFunc, 150*time.Millisecond)
	select {
	case <-time.NewTimer(10 * time.Millisecond).C:
		t.Error("Timed out waiting for callback.")
	case <-cbChan:
	}

	rt.CallProgressCB(nil)
	rt.CallProgressCB(nil)
	select {
	case <-time.NewTimer(10 * time.Millisecond).C:
		t.Error("Timed out waiting for callback.")
	case <-cbChan:
	}

	err := rt.StopScheduledProgressCB()
	if err != nil {
		t.Errorf("StopScheduledProgressCB returned an error: %+v", err)
	}

	select {
	case <-time.NewTimer(200 * time.Millisecond).C:
	case <-cbChan:
		t.Error("Callback called when it should have been stopped.")
	}
}

// Tests that ReceivedTransfer.AddProgressCB adds an item to the progress
// callback list.
func TestReceivedTransfer_AddProgressCB(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)

	type callbackResults struct {
		completed       bool
		received, total uint16
		err             error
	}
	cbChan := make(chan callbackResults)
	cbFunc := interfaces.ReceivedProgressCallback(
		func(completed bool, received, total uint16,
			t interfaces.FilePartTracker, err error) {
			cbChan <- callbackResults{completed, received, total, err}
		})

	done := make(chan bool)
	go func() {
		select {
		case <-time.NewTimer(time.Millisecond).C:
			t.Error("Timed out waiting for progress callback to be called.")
		case r := <-cbChan:
			err := checkReceivedProgress(
				r.completed, r.received, r.total, false, 0, 16)
			if err != nil {
				t.Error(err)
			}
			if r.err != nil {
				t.Errorf("Callback returned an error: %+v", err)
			}
		}
		done <- true
	}()

	period := time.Millisecond
	rt.AddProgressCB(cbFunc, period)

	if len(rt.progressCallbacks) != 1 {
		t.Errorf("Callback list should only have one item."+
			"\nexpected: %d\nreceived: %d", 1, len(rt.progressCallbacks))
	}

	if rt.progressCallbacks[0].period != period {
		t.Errorf("Callback has wrong lastCall.\nexpected: %s\nreceived: %s",
			period, rt.progressCallbacks[0].period)
	}

	if rt.progressCallbacks[0].lastCall != (time.Time{}) {
		t.Errorf("Callback has wrong time.\nexpected: %s\nreceived: %s",
			time.Time{}, rt.progressCallbacks[0].lastCall)
	}

	if rt.progressCallbacks[0].scheduled {
		t.Errorf("Callback has wrong scheduled.\nexpected: %t\nreceived: %t",
			false, rt.progressCallbacks[0].scheduled)
	}
	<-done
}

// Tests that ReceivedTransfer.AddPart adds a part in the correct place in the
// list of parts.
func TestReceivedTransfer_AddPart(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	numFps := uint16(20)
	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)

	// Create encrypted part
	expectedData := []byte("test")
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart, mac, padding := newEncryptedPartData(
		rt.key, expectedData, fpNum, t)

	// Add encrypted part
	err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
	if err != nil {
		t.Errorf("AddPart returned an error: %+v", err)
	}

	receivedData, exists := rt.receivedParts.parts[partNum]
	if !exists {
		t.Errorf("Part #%d not found in part map.", partNum)
	} else if !bytes.Equal(expectedData, receivedData) {
		t.Fatalf("Part data in list does not match expected."+
			"\nexpected: %+v\nreceived: %+v", expectedData, receivedData)
	}

	// Check that the fingerprint vector has correct values
	if rt.fpVector.GetNumAvailable() != uint32(numFps-1) {
		t.Errorf("Incorrect number of available keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps, rt.fpVector.GetNumAvailable())
	}
	if rt.fpVector.GetNumKeys() != uint32(numFps) {
		t.Errorf("Incorrect number of keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", numFps, rt.fpVector.GetNumKeys())
	}
	if rt.fpVector.GetNumUsed() != 1 {
		t.Errorf("Incorrect number of used keys in fingerprint list."+
			"\nexpected: %d\nreceived: %d", 1, rt.fpVector.GetNumUsed())
	}

	// Check that the part was properly marked as received on the received
	// status vector
	if !rt.receivedStatus.Used(uint32(partNum)) {
		t.Errorf("Part number %d not marked as used in received status vector.",
			partNum)
	}
	if rt.receivedStatus.GetNumUsed() != 1 {
		t.Errorf("Incorrect number of received parts in vector."+
			"\nexpected: %d\nreceived: %d", 1, rt.receivedStatus.GetNumUsed())
	}
}

// Error path: tests that ReceivedTransfer.AddPart returns the expected error
// when the provided MAC is invalid.
func TestReceivedTransfer_AddPart_DecryptPartError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, _ := newEmptyReceivedTransfer(16, 20, kv, t)

	// Create encrypted part
	Data := []byte("test")
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart, _, padding := newEncryptedPartData(rt.key, Data, fpNum, t)
	mac := []byte("invalidMAC")

	// Add encrypted part
	expectedErr := "reconstructed MAC from decrypting does not match MAC from sender"
	err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("AddPart did not return the expected error when the MAC is "+
			"invalid.\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Tests that ReceivedTransfer.GetFile returns the expected file data.
func TestReceivedTransfer_GetFile(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, expectedFile := newRandomReceivedTransfer(16, 20, kv, t)

	receivedFile, err := rt.GetFile()
	if err != nil {
		t.Errorf("GetFile returned an error: %+v", err)
	}

	// Check that the received file is correct
	if !bytes.Equal(expectedFile, receivedFile) {
		t.Errorf("File does not match expected.\nexpected: %+v\nreceived: %+v",
			expectedFile, receivedFile)
	}
}

// Error path: tests that ReceivedTransfer.GetFile returns the expected error
// when not all file parts are present.
func TestReceivedTransfer_GetFile_MissingPartsError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	numParts := uint16(16)
	_, rt, _ := newEmptyReceivedTransfer(numParts, 20, kv, t)
	expectedErr := fmt.Sprintf(getFileErr, numParts, numParts)

	_, err := rt.GetFile()
	if err == nil || err.Error() != expectedErr {
		t.Errorf("GetFile failed to return the expected error when all file "+
			"parts are missing.\nexpected: %s\nreceived: %+v",
			expectedErr, err)
	}
}

// Error path: tests that ReceivedTransfer.GetFile returns the expected error
// when the stored transfer MAC does not match the one generated from the file.
func TestReceivedTransfer_GetFile_InvalidMacError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, _ := newRandomReceivedTransfer(16, 20, kv, t)

	rt.transferMAC = []byte("invalidMAC")

	_, err := rt.GetFile()
	if err == nil || err.Error() != getTransferMacErr {
		t.Errorf("GetFile failed to return the expected error when the file "+
			"MAC cannot be verified.\nexpected: %s\nreceived: %+v",
			getTransferMacErr, err)
	}
}

////////////////////////////////////////////////////////////////////////////////
// Storage Function Testing                                                   //
////////////////////////////////////////////////////////////////////////////////

// Tests that loadReceivedTransfer returns a ReceivedTransfer that matches the
// original object in memory.
func Test_loadReceivedTransfer(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, expectedRT, _ := newRandomReceivedTransfer(16, 20, kv, t)

	// Create encrypted part
	expectedData := []byte("test")
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart, mac, padding := newEncryptedPartData(
		expectedRT.key, expectedData, fpNum, t)

	// Add encrypted part
	err := expectedRT.AddPart(encryptedPart, padding, mac, partNum, fpNum)
	if err != nil {
		t.Errorf("Failed to add test part: %+v", err)
	}

	loadedRT, err := loadReceivedTransfer(tid, kv)
	if err != nil {
		t.Errorf("loadReceivedTransfer returned an error: %+v", err)
	}

	if !reflect.DeepEqual(expectedRT, loadedRT) {
		t.Errorf("Loaded ReceivedTransfer does not match expected"+
			".\nexpected: %+v\nreceived: %+v", expectedRT, loadedRT)
	}
}

// Error path: tests that loadReceivedTransfer returns the expected error when
// no info is in storage to load.
func Test_loadReceivedTransfer_LoadInfoError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid := ftCrypto.UnmarshalTransferID([]byte("invalidTransferID"))
	expectedErr := strings.Split(loadReceivedStoreErr, "%")[0]

	_, err := loadReceivedTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadReceivedTransfer did not return the expected error when "+
			"trying to load info from storage that does not exist."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadReceivedTransfer returns the expected error when
// the fingerprint state vector has been deleted from storage.
func Test_loadReceivedTransfer_LoadFpVectorError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, rt, _ := newRandomReceivedTransfer(16, 20, kv, t)

	// Create encrypted part
	data := []byte("test")
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)

	// Add encrypted part
	err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
	if err != nil {
		t.Errorf("Failed to add test part: %+v", err)
	}

	// Delete fingerprint state vector from storage
	err = rt.fpVector.Delete()
	if err != nil {
		t.Errorf("Failed to delete fingerprint vector: %+v", err)
	}

	expectedErr := strings.Split(loadReceiveFpVectorErr, "%")[0]
	_, err = loadReceivedTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadReceivedTransfer did not return the expected error when "+
			"the state vector was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadReceivedTransfer returns the expected error when
// the part store has been deleted from storage.
func Test_loadReceivedTransfer_LoadPartStoreError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, rt, _ := newRandomReceivedTransfer(16, 20, kv, t)

	// Create encrypted part
	data := []byte("test")
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)

	// Add encrypted part
	err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
	if err != nil {
		t.Errorf("Failed to add test part: %+v", err)
	}

	// Delete fingerprint state vector from storage
	err = rt.receivedParts.delete()
	if err != nil {
		t.Errorf("Failed to delete part store: %+v", err)
	}

	expectedErr := strings.Split(loadReceivePartStoreErr, "%")[0]
	_, err = loadReceivedTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadReceivedTransfer did not return the expected error when "+
			"the part store was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that loadReceivedTransfer returns the expected error when
// the received status state vector has been deleted from storage.
func Test_loadReceivedTransfer_LoadReceivedVectorError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	tid, rt, _ := newRandomReceivedTransfer(16, 20, kv, t)

	// Create encrypted part
	data := []byte("test")
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart, mac, padding := newEncryptedPartData(rt.key, data, fpNum, t)

	// Add encrypted part
	err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
	if err != nil {
		t.Errorf("Failed to add test part: %+v", err)
	}

	// Delete fingerprint state vector from storage
	err = rt.receivedStatus.Delete()
	if err != nil {
		t.Errorf("Failed to received status vector: %+v", err)
	}

	expectedErr := strings.Split(loadReceivedVectorErr, "%")[0]
	_, err = loadReceivedTransfer(tid, kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("loadReceivedTransfer did not return the expected error when "+
			"the received status vector was deleted from storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Tests that ReceivedTransfer.saveInfo saves the expected data to storage.
func TestReceivedTransfer_saveInfo(t *testing.T) {
	rt := &ReceivedTransfer{
		key:         ftCrypto.UnmarshalTransferKey([]byte("key")),
		transferMAC: []byte("transferMAC"),
		numParts:    16,
		kv:          versioned.NewKV(make(ekv.Memstore)),
	}

	err := rt.saveInfo()
	if err != nil {
		t.Fatalf("saveInfo returned an error: %v", err)
	}

	vo, err := rt.kv.Get(receivedTransferKey, receivedTransferVersion)
	if err != nil {
		t.Fatalf("Failed to load ReceivedTransfer from storage: %+v", err)
	}

	if !bytes.Equal(rt.marshal(), vo.Data) {
		t.Errorf("Marshalled data loaded from storage does not match expected."+
			"\nexpected: %+v\nreceived: %+v", rt.marshal(), vo.Data)
	}
}

// Tests that ReceivedTransfer.loadInfo loads a saved ReceivedTransfer from
// storage.
func TestReceivedTransfer_loadInfo(t *testing.T) {
	rt := &ReceivedTransfer{
		key:         ftCrypto.UnmarshalTransferKey([]byte("key")),
		transferMAC: []byte("transferMAC"),
		numParts:    16,
		kv:          versioned.NewKV(make(ekv.Memstore)),
	}

	err := rt.saveInfo()
	if err != nil {
		t.Errorf("failed to save new ReceivedTransfer to storage: %+v", err)
	}

	loadedRT := &ReceivedTransfer{kv: rt.kv}
	err = loadedRT.loadInfo()
	if err != nil {
		t.Errorf("load returned an error: %+v", err)
	}

	if !reflect.DeepEqual(rt, loadedRT) {
		t.Errorf("Loaded ReceivedTransfer does not match expected."+
			"\nexpected: %+v\nreceived: %+v", rt, loadedRT)
	}
}

// Error path: tests that ReceivedTransfer.loadInfo returns an error when there
// is no object in storage to load
func TestReceivedTransfer_loadInfo_Error(t *testing.T) {
	loadedRT := &ReceivedTransfer{kv: versioned.NewKV(make(ekv.Memstore))}
	err := loadedRT.loadInfo()
	if err == nil {
		t.Errorf("Loaded object that should not be in storage: %+v", err)
	}
}

// Tests that ReceivedTransfer.delete removes all data from storage.
func TestReceivedTransfer_delete(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	_, rt, _ := newRandomReceivedTransfer(16, 20, kv, t)

	// Create encrypted part
	expectedData := []byte("test")
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart, mac, padding := newEncryptedPartData(
		rt.key, expectedData, fpNum, t)

	// Add encrypted part
	err := rt.AddPart(encryptedPart, padding, mac, partNum, fpNum)
	if err != nil {
		t.Fatalf("Failed to add test part: %+v", err)
	}

	// Delete everything from storage
	err = rt.delete()
	if err != nil {
		t.Errorf("delete returned an error: %+v", err)
	}

	// Check that the SentTransfer info was deleted
	err = rt.loadInfo()
	if err == nil {
		t.Error("Successfully loaded SentTransfer info from storage when it " +
			"should have been deleted.")
	}

	// Check that the parts store were deleted
	_, err = loadPartStore(rt.kv)
	if err == nil {
		t.Error("Successfully loaded file parts from storage when it should " +
			"have been deleted.")
	}

	// Check that the fingerprint vector was deleted
	_, err = utility.LoadStateVector(rt.kv, receivedFpVectorKey)
	if err == nil {
		t.Error("Successfully loaded fingerprint vector from storage when it " +
			"should have been deleted.")
	}

	// Check that the received status vector was deleted
	_, err = utility.LoadStateVector(rt.kv, receivedVectorKey)
	if err == nil {
		t.Error("Successfully loaded received status vector from storage when " +
			"it should have been deleted.")
	}
}

// Tests that ReceivedTransfer.deleteInfo removes the saved ReceivedTransfer
// data from storage.
func TestReceivedTransfer_deleteInfo(t *testing.T) {
	rt := &ReceivedTransfer{
		key:         ftCrypto.UnmarshalTransferKey([]byte("key")),
		transferMAC: []byte("transferMAC"),
		numParts:    16,
		kv:          versioned.NewKV(make(ekv.Memstore)),
	}

	// Save from storage
	err := rt.saveInfo()
	if err != nil {
		t.Errorf("failed to save new ReceivedTransfer to storage: %+v", err)
	}

	// Delete from storage
	err = rt.deleteInfo()
	if err != nil {
		t.Errorf("deleteInfo returned an error: %+v", err)
	}

	// Make sure deleted object cannot be loaded from storage
	_, err = rt.kv.Get(receivedTransferKey, receivedTransferVersion)
	if err == nil {
		t.Error("Loaded object that should be deleted from storage.")
	}
}

// Tests that a ReceivedTransfer marshalled with ReceivedTransfer.marshal and
// then unmarshalled with unmarshalReceivedTransfer matches the original.
func TestReceivedTransfer_marshal_unmarshalReceivedTransfer(t *testing.T) {
	rt := &ReceivedTransfer{
		key:         ftCrypto.UnmarshalTransferKey([]byte("key")),
		transferMAC: []byte("transferMAC"),
		fileSize:    256,
		numParts:    16,
		numFps:      20,
	}

	marshaledData := rt.marshal()
	key, mac, fileSize, numParts, numFps := unmarshalReceivedTransfer(marshaledData)

	if rt.key != key {
		t.Errorf("Failed to get expected key.\nexpected: %s\nreceived: %s",
			rt.key, key)
	}

	if !bytes.Equal(rt.transferMAC, mac) {
		t.Errorf("Failed to get expected transfer MAC."+
			"\nexpected: %v\nreceived: %v", rt.transferMAC, mac)
	}

	if rt.fileSize != fileSize {
		t.Errorf("Failed to get expected file size."+
			"\nexpected: %d\nreceived: %d", rt.fileSize, fileSize)
	}

	if rt.numParts != numParts {
		t.Errorf("Failed to get expected number of parts."+
			"\nexpected: %d\nreceived: %d", rt.numParts, numParts)
	}

	if rt.numFps != numFps {
		t.Errorf("Failed to get expected number of fingerprints."+
			"\nexpected: %d\nreceived: %d", rt.numFps, numFps)
	}
}

// Consistency test: tests that makeReceivedTransferPrefix returns the expected
// prefixes for the provided transfer IDs.
func Test_makeReceivedTransferPrefix_Consistency(t *testing.T) {
	prng := NewPrng(42)
	expectedPrefixes := []string{
		"FileTransferReceivedTransferStoreU4x/lrFkvxuXu59LtHLon1sUhPJSCcnZND6SugndnVI=",
		"FileTransferReceivedTransferStore39ebTXZCm2F6DJ+fDTulWwzA1hRMiIU1hBrL4HCbB1g=",
		"FileTransferReceivedTransferStoreCD9h03W8ArQd9PkZKeGP2p5vguVOdI6B555LvW/jTNw=",
		"FileTransferReceivedTransferStoreuoQ+6NY+jE/+HOvqVG2PrBPdGqwEzi6ih3xVec+ix44=",
		"FileTransferReceivedTransferStoreGwuvrogbgqdREIpC7TyQPKpDRlp4YgYWl4rtDOPGxPM=",
		"FileTransferReceivedTransferStorernvD4ElbVxL+/b4MECiH4QDazS2IX2kstgfaAKEcHHA=",
		"FileTransferReceivedTransferStoreceeWotwtwlpbdLLhKXBeJz8FySMmgo4rBW44F2WOEGE=",
		"FileTransferReceivedTransferStoreSYlH/fNEQQ7UwRYCP6jjV2tv7Sf/iXS6wMr9mtBWkrE=",
		"FileTransferReceivedTransferStoreNhnnOJZN/ceejVNDc2Yc/WbXT+weG4lJGrcjbkt1IWI=",
		"FileTransferReceivedTransferStorekM8r60LDyicyhWDxqsBnzqbov0bUqytGgEAsX7KCDog=",
	}

	for i, expected := range expectedPrefixes {
		tid, _ := ftCrypto.NewTransferID(prng)
		prefix := makeReceivedTransferPrefix(tid)

		if expected != prefix {
			t.Errorf("New ReceivedTransfer prefix does not match expected (%d)."+
				"\nexpected: %s\nreceived: %s", i, expected, prefix)
		}
	}
}

// newRandomReceivedTransfer generates a new ReceivedTransfer with random data.
// Returns the generated transfer ID, new ReceivedTransfer, and the full file.
func newRandomReceivedTransfer(numParts, numFps uint16, kv *versioned.KV,
	t *testing.T) (ftCrypto.TransferID, *ReceivedTransfer, []byte) {

	prng := NewPrng(42)
	tid, _ := ftCrypto.NewTransferID(prng)
	key, _ := ftCrypto.NewTransferKey(prng)
	parts, fileData := newRandomPartStore(numParts, kv, prng, t)
	fileSize := uint32(len(fileData))
	mac := ftCrypto.CreateTransferMAC(fileData, key)

	rt, err := NewReceivedTransfer(tid, key, mac, fileSize, numParts, numFps, kv)
	if err != nil {
		t.Errorf("Failed to create new ReceivedTransfer: %+v", err)
	}

	for partNum, part := range parts.parts {
		encryptedPart, mac, padding := newEncryptedPartData(key, part, partNum, t)
		err := rt.AddPart(encryptedPart, padding, mac, partNum, partNum)
		if err != nil {
			t.Errorf("Failed to add part #%d: %+v", partNum, err)
		}
	}

	return tid, rt, fileData
}

// newRandomReceivedTransfer generates a new empty ReceivedTransfer. Returns the
// generated transfer ID, new ReceivedTransfer, and the full file.
func newEmptyReceivedTransfer(numParts, numFps uint16, kv *versioned.KV,
	t *testing.T) (ftCrypto.TransferID, *ReceivedTransfer, []byte) {

	prng := NewPrng(42)
	tid, _ := ftCrypto.NewTransferID(prng)
	key, _ := ftCrypto.NewTransferKey(prng)
	_, fileData := newRandomPartStore(numParts, kv, prng, t)
	fileSize := uint32(len(fileData))

	mac := ftCrypto.CreateTransferMAC(fileData, key)

	rt, err := NewReceivedTransfer(tid, key, mac, fileSize, numParts, numFps, kv)
	if err != nil {
		t.Errorf("Failed to create new ReceivedTransfer: %+v", err)
	}

	return tid, rt, fileData
}

// newEncryptedPartData encrypts the part data and returns the encrypted part
// its MAC, and its padding.
func newEncryptedPartData(key ftCrypto.TransferKey, part []byte, fpNum uint16,
	t *testing.T) ([]byte, []byte, []byte) {
	// Create encrypted part
	prng := NewPrng(42)
	encPart, mac, padding, err := ftCrypto.EncryptPart(key, part, fpNum, prng)
	if err != nil {
		t.Fatalf("Failed to encrypt data: %+v", err)
	}

	return encPart, mac, padding
}