////////////////////////////////////////////////////////////////////////////////
// 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"
	"gitlab.com/elixxir/client/storage/versioned"
	ftCrypto "gitlab.com/elixxir/crypto/fileTransfer"
	"gitlab.com/elixxir/ekv"
	"gitlab.com/elixxir/primitives/format"
	"gitlab.com/xx_network/primitives/netTime"
	"math/rand"
	"reflect"
	"sort"
	"strings"
	"testing"
)

// Tests that NewReceivedFileTransfers creates a new object with empty maps and
// that it is saved to storage
func TestNewReceivedFileTransfers(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedRFT := &ReceivedFileTransfers{
		transfers: make(map[ftCrypto.TransferID]*ReceivedTransfer),
		info:      make(map[format.Fingerprint]*partInfo),
		kv:        kv.Prefix(receivedFileTransfersPrefix),
	}

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

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

	// Ensure that the transfer list is saved to storage
	_, err = expectedRFT.kv.Get(
		receivedFileTransfersKey, receivedFileTransfersVersion)
	if err != nil {
		t.Errorf("Failed to load transfer list from storage: %+v", err)
	}
}

// Tests that ReceivedFileTransfers.AddTransfer adds a new transfer and adds all
// the fingerprints to the info map.
func TestReceivedFileTransfers_AddTransfer(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Generate info for new transfer
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")
	fileSize := uint32(256)
	numParts, numFps := uint16(16), uint16(24)

	// Add the transfer
	tid, err := rft.AddTransfer(key, mac, fileSize, numParts, numFps, prng)
	if err != nil {
		t.Errorf("AddTransfer returned an error: %+v", err)
	}

	// Check that the transfer was added to the map
	transfer, exists := rft.transfers[tid]
	if !exists {
		t.Errorf("Transfer with ID %s not found.", tid)
	} else {
		if transfer.GetFileSize() != fileSize {
			t.Errorf("New transfer has incorrect file size."+
				"\nexpected: %d\nreceived: %d", fileSize, transfer.GetFileSize())
		}
		if transfer.GetNumParts() != numParts {
			t.Errorf("New transfer has incorrect number of parts."+
				"\nexpected: %d\nreceived: %d", numParts, transfer.GetNumParts())
		}
		if transfer.GetTransferKey() != key {
			t.Errorf("New transfer has incorrect transfer key."+
				"\nexpected: %s\nreceived: %s", key, transfer.GetTransferKey())
		}
		if transfer.GetNumFps() != numFps {
			t.Errorf("New transfer has incorrect number of fingerprints."+
				"\nexpected: %d\nreceived: %d", numFps, transfer.GetNumFps())
		}
	}

	// Check that the transfer was added to storage
	_, err = loadReceivedTransfer(tid, rft.kv)
	if err != nil {
		t.Errorf("Transfer with ID %s not found in storage: %+v", tid, err)
	}

	// Check that all the fingerprints are in the info map
	for fpNum, fp := range ftCrypto.GenerateFingerprints(key, numFps) {
		info, exists := rft.info[fp]
		if !exists {
			t.Errorf("Part fingerprint %s (#%d) not found.", fp, fpNum)
		}

		if int(info.fpNum) != fpNum {
			t.Errorf("Fingerprint %s has incorrect fingerprint number."+
				"\nexpected: %d\nreceived: %d", fp, fpNum, info.fpNum)
		}

		if info.id != tid {
			t.Errorf("Fingerprint %s has incorrect transfer ID."+
				"\nexpected: %s\nreceived: %s", fp, tid, info.id)
		}
	}
}

// Error path: tests that ReceivedFileTransfers.AddTransfer returns the expected
// error when the PRNG returns an error.
func TestReceivedFileTransfers_AddTransfer_NewTransferIdRngError(t *testing.T) {
	prng := NewPrngErr()
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Add the transfer
	expectedErr := strings.Split(addTransferNewIdErr, "%")[0]
	_, err = rft.AddTransfer(ftCrypto.TransferKey{}, nil, 0, 0, 0, prng)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("AddTransfer did not return the expected error when the PRNG "+
			"should have errored.\nexpected: %s\nrecieved: %+v", expectedErr, err)
	}

}

// Tests that ReceivedFileTransfers.addFingerprints adds all the fingerprints
// to the map
func TestReceivedFileTransfers_addFingerprints(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	key, _ := ftCrypto.NewTransferKey(prng)
	tid, _ := ftCrypto.NewTransferID(prng)
	numFps := uint16(24)

	rft.addFingerprints(key, tid, numFps)

	// Check that all the fingerprints are in the info map
	for fpNum, fp := range ftCrypto.GenerateFingerprints(key, numFps) {
		info, exists := rft.info[fp]
		if !exists {
			t.Errorf("Part fingerprint %s (#%d) not found.", fp, fpNum)
		}

		if int(info.fpNum) != fpNum {
			t.Errorf("Fingerprint %s has incorrect fingerprint number."+
				"\nexpected: %d\nreceived: %d", fp, fpNum, info.fpNum)
		}

		if info.id != tid {
			t.Errorf("Fingerprint %s has incorrect transfer ID."+
				"\nexpected: %s\nreceived: %s", fp, tid, info.id)
		}
	}
}

// Tests that ReceivedFileTransfers.GetTransfer returns the newly added
// transfer.
func TestReceivedFileTransfers_GetTransfer(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Generate random info for new transfer
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")
	fileSize := uint32(256)
	numParts, numFps := uint16(16), uint16(24)

	// Add the transfer
	tid, err := rft.AddTransfer(key, mac, fileSize, numParts, numFps, prng)
	if err != nil {
		t.Errorf("Failed to add new transfer: %+v", err)
	}

	// Get the transfer
	transfer, err := rft.GetTransfer(tid)
	if err != nil {
		t.Errorf("GetTransfer returned an error: %+v", err)
	}

	if transfer.GetFileSize() != fileSize {
		t.Errorf("New transfer has incorrect file size."+
			"\nexpected: %d\nreceived: %d", fileSize, transfer.GetFileSize())
	}

	if transfer.GetNumParts() != numParts {
		t.Errorf("New transfer has incorrect number of parts."+
			"\nexpected: %d\nreceived: %d", numParts, transfer.GetNumParts())
	}

	if transfer.GetNumFps() != numFps {
		t.Errorf("New transfer has incorrect number of fingerprints."+
			"\nexpected: %d\nreceived: %d", numFps, transfer.GetNumFps())
	}

	if transfer.GetTransferKey() != key {
		t.Errorf("New transfer has incorrect transfer key."+
			"\nexpected: %s\nreceived: %s", key, transfer.GetTransferKey())
	}
}

// Error path: tests that ReceivedFileTransfers.GetTransfer returns the expected
// error when the provided transfer ID does not correlate to any saved transfer.
func TestReceivedFileTransfers_GetTransfer_NoTransferError(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Generate random info for new transfer
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")

	// Add the transfer
	_, err = rft.AddTransfer(key, mac, 256, 16, 24, prng)
	if err != nil {
		t.Errorf("Failed to add new transfer: %+v", err)
	}

	// Get the transfer
	invalidTid, _ := ftCrypto.NewTransferID(prng)
	expectedErr := fmt.Sprintf(getReceivedTransferErr, invalidTid)
	_, err = rft.GetTransfer(invalidTid)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("GetTransfer did not return the expected error when no "+
			"transfer for the ID exists.\nexpected: %s\nreceived: %+v",
			expectedErr, err)
	}
}

// Tests that DeleteTransfer removed a transfer from memory and storage.
func TestReceivedFileTransfers_DeleteTransfer(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Add the transfer
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")
	numFps := uint16(24)
	tid, err := rft.AddTransfer(key, mac, 256, 16, numFps, prng)
	if err != nil {
		t.Errorf("Failed to add new transfer: %+v", err)
	}

	// Delete the transfer
	err = rft.DeleteTransfer(tid)
	if err != nil {
		t.Errorf("DeleteTransfer returned an error: %+v", err)
	}

	// Check that the transfer was deleted from the map
	_, exists := rft.transfers[tid]
	if exists {
		t.Errorf("Transfer with ID %s found in map when it should have been "+
			"deleted.", tid)
	}

	// Check that the transfer was deleted from storage
	_, err = loadReceivedTransfer(tid, rft.kv)
	if err == nil {
		t.Errorf("Transfer with ID %s found in storage when it should have "+
			"been deleted.", tid)
	}

	// Check that all the fingerprints in the info map were deleted
	for fpNum, fp := range ftCrypto.GenerateFingerprints(key, numFps) {
		_, exists := rft.info[fp]
		if exists {
			t.Errorf("Part fingerprint %s (#%d) found in map when it should "+
				"have been deleted.", fp, fpNum)
		}
	}
}

// Error path: tests that ReceivedFileTransfers.DeleteTransfer returns the
// expected error when the provided transfer ID does not correlate to any saved
// transfer.
func TestReceivedFileTransfers_DeleteTransfer_NoTransferError(t *testing.T) {
	prng := NewPrng(42)
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Delete the transfer
	invalidTid, _ := ftCrypto.NewTransferID(prng)
	expectedErr := fmt.Sprintf(getReceivedTransferErr, invalidTid)
	err = rft.DeleteTransfer(invalidTid)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("DeleteTransfer did not return the expected error when no "+
			"transfer for the ID exists.\nexpected: %s\nreceived: %+v",
			expectedErr, err)
	}
}

// Tests that ReceivedFileTransfers.AddPart modifies the expected transfer in
// memory.
func TestReceivedFileTransfers_AddPart(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	prng := NewPrng(42)
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")

	tid, err := rft.AddTransfer(key, mac, 256, 16, 24, prng)
	if err != nil {
		t.Errorf("Failed to add new transfer: %+v", err)
	}

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

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

	// Make sure its fingerprint was removed from the map
	_, exists := rft.info[fp]
	if exists {
		t.Errorf("Fingerprints %s for added part found when it should have "+
			"been deleted.", fp)
	}

	// Check that the transfer is correct
	expectedRT := rft.transfers[tid]
	if !reflect.DeepEqual(expectedRT, rt) {
		t.Errorf("Returned transfer does not match expected."+
			"\nexpected: %+v\nreceived: %+v", expectedRT, rt)
	}

	// Check that the correct part was stored
	receivedPart := expectedRT.receivedParts.parts[partNum]
	if !bytes.Equal(receivedPart, expectedData) {
		t.Errorf("Part in memory is not expected."+
			"\nexpected: %q\nreceived: %q", expectedData, receivedPart)
	}
}

// Error path: tests that ReceivedFileTransfers.AddPart returns the expected
// error when the provided fingerprint does not correlate to any part.
func TestReceivedFileTransfers_AddPart_NoFingerprintError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Create encrypted part
	fp := format.NewFingerprint([]byte("invalidTransferKey"))

	// Add encrypted part
	expectedErr := fmt.Sprintf(noFingerprintErr, fp)
	_, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("AddPart did not return the expected error when no part for "+
			"the fingerprint exists.\nexpected: %s\nreceived: %+v",
			expectedErr, err)
	}
}

// Error path: tests that ReceivedFileTransfers.AddPart returns the expected
// error when the provided transfer ID does not correlate to any saved transfer.
func TestReceivedFileTransfers_AddPart_NoTransferError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	prng := NewPrng(42)
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")

	_, err = rft.AddTransfer(key, mac, 256, 16, 24, prng)
	if err != nil {
		t.Errorf("Failed to add new transfer: %+v", err)
	}

	// Create encrypted part
	fp := ftCrypto.GenerateFingerprint(key, 1)
	invalidTid, _ := ftCrypto.NewTransferID(prng)
	rft.info[fp].id = invalidTid

	// Add encrypted part
	expectedErr := fmt.Sprintf(getReceivedTransferErr, invalidTid)
	_, _, err = rft.AddPart([]byte{}, []byte{}, []byte{}, 0, fp)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("AddPart did not return the expected error when no transfer "+
			"for the ID exists.\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that ReceivedFileTransfers.AddPart returns the expected
// error when the encrypted part data, MAC, and padding are invalid.
func TestReceivedFileTransfers_AddPart_AddPartError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	prng := NewPrng(42)
	key, _ := ftCrypto.NewTransferKey(prng)
	mac := []byte("transferMAC")
	numParts := uint16(16)

	tid, err := rft.AddTransfer(key, mac, 256, numParts, 24, prng)
	if err != nil {
		t.Errorf("Failed to add new transfer: %+v", err)
	}

	// Create encrypted part
	partNum, fpNum := uint16(1), uint16(1)
	encryptedPart := []byte("invalidPart")
	mac = []byte("invalidMAC")
	padding := make([]byte, 24)
	fp := ftCrypto.GenerateFingerprint(key, fpNum)

	// Add encrypted part
	expectedErr := fmt.Sprintf(addPartErr, partNum, numParts, tid, "")
	_, _, err = rft.AddPart(encryptedPart, padding, mac, partNum, fp)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("AddPart did not return the expected error when the "+
			"encrypted part, padding, and MAC are invalid."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Tests that ReceivedFileTransfers.GetFile returns the complete file.
func TestReceivedFileTransfers_GetFile(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	prng := NewPrng(42)
	numParts := uint16(16)

	// Generate file parts and expected file
	parts, expectedFile := newRandomPartStore(
		numParts, kv.Prefix("test"), rand.New(rand.NewSource(42)), t)

	key, _ := ftCrypto.NewTransferKey(prng)
	mac := ftCrypto.CreateTransferMAC(expectedFile, key)

	fileSize := uint32(len(expectedFile))

	tid, err := rft.AddTransfer(key, mac, fileSize, numParts, 24, prng)
	if err != nil {
		t.Errorf("Failed to add new transfer: %+v", err)
	}

	for partNum, part := range parts.parts {
		fp := ftCrypto.GenerateFingerprint(key, partNum)
		encPart, partMac, padding := newEncryptedPartData(key, part, partNum, t)
		_, _, err = rft.AddPart(encPart, padding, partMac, partNum, fp)
		if err != nil {
			t.Errorf("AddPart encountered an error: %+v", err)
		}
	}

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

	if !bytes.Equal(expectedFile, receivedFile) {
		t.Errorf("Received file does not match expected."+
			"\nexpected: %q\nreceived: %q", expectedFile, receivedFile)
	}
}

// Error path: tests that ReceivedFileTransfers.GetFile returns the expected
// error when no transfer with the ID exists.
func TestReceivedFileTransfers_GetFile_NoTransferError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	tid := ftCrypto.UnmarshalTransferID([]byte("invalidID"))

	expectedErr := fmt.Sprintf(getReceivedTransferErr, tid)
	_, err = rft.GetFile(tid)
	if err == nil || err.Error() != expectedErr {
		t.Errorf("GetFile did not return the expected error."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

////////////////////////////////////////////////////////////////////////////////
// Storage Functions                                                          //
////////////////////////////////////////////////////////////////////////////////

// Tests that the ReceivedFileTransfers loaded from storage by
// LoadReceivedFileTransfers matches the original in memory.
func TestLoadReceivedFileTransfers(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to make new ReceivedFileTransfers: %+v", err)
	}

	// Add 10 transfers to map in memory
	list := make([]ftCrypto.TransferID, 10)
	for i := range list {
		tid, rt, _ := newRandomReceivedTransfer(16, 24, rft.kv, t)

		// Add to transfer
		rft.transfers[tid] = rt

		// Add unused fingerprints
		for n := uint32(0); n < rt.fpVector.GetNumKeys(); n++ {
			if !rt.fpVector.Used(n) {
				fp := ftCrypto.GenerateFingerprint(rt.key, uint16(n))
				rft.info[fp] = &partInfo{tid, uint16(n)}
			}
		}

		// Add ID to list
		list[i] = tid
	}

	// Save list to storage
	if err = rft.saveTransfersList(); err != nil {
		t.Errorf("Faileds to save transfers list: %+v", err)
	}

	// Load ReceivedFileTransfers from storage
	loadedRFT, err := LoadReceivedFileTransfers(kv)
	if err != nil {
		t.Errorf("LoadReceivedFileTransfers returned an error: %+v", err)
	}

	if !reflect.DeepEqual(rft, loadedRFT) {
		t.Errorf("Loaded ReceivedFileTransfers does not match original in "+
			"memory.\nexpected: %+v\nreceived: %+v", rft, loadedRFT)
	}
}

// Error path: tests that ReceivedFileTransfers returns the expected error when
// the transfer list cannot be loaded from storage.
func TestLoadReceivedFileTransfers_NoListInStorageError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedErr := strings.Split(loadReceivedTransfersListErr, "%")[0]

	// Load ReceivedFileTransfers from storage
	_, err := LoadReceivedFileTransfers(kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("LoadReceivedFileTransfers did not return the expected error "+
			"when there is no transfer list saved in storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Error path: tests that ReceivedFileTransfers returns the expected error when
// the first transfer loaded from storage does not exist.
func TestLoadReceivedFileTransfers_NoTransferInStorageError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedErr := strings.Split(loadReceivedFileTransfersErr, "%")[0]

	// Save list of one transfer ID to storage
	obj := &versioned.Object{
		Version:   receivedFileTransfersVersion,
		Timestamp: netTime.Now(),
		Data:      ftCrypto.UnmarshalTransferID([]byte("testID_01")).Bytes(),
	}
	err := kv.Prefix(receivedFileTransfersPrefix).Set(
		receivedFileTransfersKey, receivedFileTransfersVersion, obj)

	// Load ReceivedFileTransfers from storage
	_, err = LoadReceivedFileTransfers(kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("LoadReceivedFileTransfers did not return the expected error "+
			"when there is no transfer saved in storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Tests that the ReceivedFileTransfers loaded from storage by
// NewOrLoadReceivedFileTransfers matches the original in memory.
func TestNewOrLoadReceivedFileTransfers(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to make new ReceivedFileTransfers: %+v", err)
	}

	// Add 10 transfers to map in memory
	list := make([]ftCrypto.TransferID, 10)
	for i := range list {
		tid, rt, _ := newRandomReceivedTransfer(16, 24, rft.kv, t)

		// Add to transfer
		rft.transfers[tid] = rt

		// Add unused fingerprints
		for n := uint32(0); n < rt.fpVector.GetNumKeys(); n++ {
			if !rt.fpVector.Used(n) {
				fp := ftCrypto.GenerateFingerprint(rt.key, uint16(n))
				rft.info[fp] = &partInfo{tid, uint16(n)}
			}
		}

		// Add ID to list
		list[i] = tid
	}

	// Save list to storage
	if err = rft.saveTransfersList(); err != nil {
		t.Errorf("Faileds to save transfers list: %+v", err)
	}

	// Load ReceivedFileTransfers from storage
	loadedRFT, err := NewOrLoadReceivedFileTransfers(kv)
	if err != nil {
		t.Errorf("NewOrLoadReceivedFileTransfers returned an error: %+v", err)
	}

	if !reflect.DeepEqual(rft, loadedRFT) {
		t.Errorf("Loaded ReceivedFileTransfers does not match original in "+
			"memory.\nexpected: %+v\nreceived: %+v", rft, loadedRFT)
	}
}

// Tests that NewOrLoadReceivedFileTransfers returns a new ReceivedFileTransfers
// when there is none in storage.
func TestNewOrLoadReceivedFileTransfers_NewSentFileTransfers(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))

	// Load ReceivedFileTransfers from storage
	loadedRFT, err := NewOrLoadReceivedFileTransfers(kv)
	if err != nil {
		t.Errorf("NewOrLoadReceivedFileTransfers returned an error: %+v", err)
	}

	newRFT, _ := NewReceivedFileTransfers(kv)

	if !reflect.DeepEqual(newRFT, loadedRFT) {
		t.Errorf("Returned ReceivedFileTransfers does not match new."+
			"\nexpected: %+v\nreceived: %+v", newRFT, loadedRFT)
	}
}

// Error path: tests that NewOrLoadReceivedFileTransfers returns the expected
// error when the first transfer loaded from storage does not exist.
func TestNewOrLoadReceivedFileTransfers_NoTransferInStorageError(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	expectedErr := strings.Split(loadReceivedFileTransfersErr, "%")[0]

	// Save list of one transfer ID to storage
	obj := &versioned.Object{
		Version:   receivedFileTransfersVersion,
		Timestamp: netTime.Now(),
		Data:      ftCrypto.UnmarshalTransferID([]byte("testID_01")).Bytes(),
	}
	err := kv.Prefix(receivedFileTransfersPrefix).Set(
		receivedFileTransfersKey, receivedFileTransfersVersion, obj)

	// Load ReceivedFileTransfers from storage
	_, err = NewOrLoadReceivedFileTransfers(kv)
	if err == nil || !strings.Contains(err.Error(), expectedErr) {
		t.Errorf("NewOrLoadReceivedFileTransfers did not return the expected "+
			"error when there is no transfer saved in storage."+
			"\nexpected: %s\nreceived: %+v", expectedErr, err)
	}
}

// Tests that the list saved by ReceivedFileTransfers.saveTransfersList matches
// the list loaded by ReceivedFileTransfers.load.
func TestLoadReceivedFileTransfers_saveTransfersList_loadTransfersList(t *testing.T) {
	rft, err := NewReceivedFileTransfers(versioned.NewKV(make(ekv.Memstore)))
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Fill map with transfers
	expectedList := make([]ftCrypto.TransferID, 7)
	for i := range expectedList {
		prng := NewPrng(int64(i))
		key, _ := ftCrypto.NewTransferKey(prng)
		mac := []byte("transferMAC")
		numParts := uint16(i)
		numFps := uint16(float32(i) * 2.5)

		expectedList[i], err = rft.AddTransfer(
			key, mac, 256, numParts, numFps, prng)
		if err != nil {
			t.Errorf("Failed to add new transfer #%d: %+v", i, err)
		}
	}

	// Save the list
	err = rft.saveTransfersList()
	if err != nil {
		t.Errorf("saveTransfersList returned an error: %+v", err)
	}

	// Load the list
	loadedList, err := rft.loadTransfersList()
	if err != nil {
		t.Errorf("loadTransfersList returned an error: %+v", err)
	}

	// Sort slices so they can be compared
	sort.SliceStable(expectedList, func(i, j int) bool {
		return bytes.Compare(expectedList[i].Bytes(), expectedList[j].Bytes()) == -1
	})
	sort.SliceStable(loadedList, func(i, j int) bool {
		return bytes.Compare(loadedList[i].Bytes(), loadedList[j].Bytes()) == -1
	})

	if !reflect.DeepEqual(expectedList, loadedList) {
		t.Errorf("Loaded transfer list does not match expected."+
			"\nexpected: %v\nreceived: %v", expectedList, loadedList)
	}
}

// Tests that the list loaded by ReceivedFileTransfers.load matches the original
// saved to storage.
func TestLoadReceivedFileTransfers_load(t *testing.T) {
	kv := versioned.NewKV(make(ekv.Memstore))
	rft, err := NewReceivedFileTransfers(kv)
	if err != nil {
		t.Fatalf("Failed to create new ReceivedFileTransfers: %+v", err)
	}

	// Fill map with transfers
	idList := make([]ftCrypto.TransferID, 7)
	for i := range idList {
		prng := NewPrng(int64(i))
		key, _ := ftCrypto.NewTransferKey(prng)
		mac := []byte("transferMAC")

		idList[i], err = rft.AddTransfer(key, mac, 256, 16, 24, prng)
		if err != nil {
			t.Errorf("Failed to add new transfer #%d: %+v", i, err)
		}
	}

	// Save the list
	err = rft.saveTransfersList()
	if err != nil {
		t.Errorf("saveTransfersList returned an error: %+v", err)
	}

	// Build new ReceivedFileTransfers
	newRFT := &ReceivedFileTransfers{
		transfers: make(map[ftCrypto.TransferID]*ReceivedTransfer),
		info:      make(map[format.Fingerprint]*partInfo),
		kv:        kv.Prefix(receivedFileTransfersPrefix),
	}

	// Load saved transfers from storage
	err = newRFT.load(idList)
	if err != nil {
		t.Errorf("load returned an error: %+v", err)
	}

	// Check that all transfer were loaded from storage
	for _, id := range idList {
		transfer, exists := newRFT.transfers[id]
		if !exists {
			t.Errorf("Transfer %s not loaded from storage.", id)
		}

		// Check that the loaded transfer matches the original in memory
		if !reflect.DeepEqual(transfer, rft.transfers[id]) {
			t.Errorf("Loaded transfer does not match original."+
				"\noriginal: %+v\nreceived: %+v", rft.transfers[id], transfer)
		}

		// Make sure all fingerprints are present
		for fpNum, fp := range ftCrypto.GenerateFingerprints(transfer.key, 24) {
			info, exists := newRFT.info[fp]
			if !exists {
				t.Errorf("Fingerprint %d for transfer %s does not exist in "+
					"the map.", fpNum, id)
			}

			if info.id != id {
				t.Errorf("Fingerprint has wrong transfer ID."+
					"\nexpected: %s\nreceived: %s", id, info.id)
			}
			if int(info.fpNum) != fpNum {
				t.Errorf("Fingerprint has wrong number."+
					"\nexpected: %d\nreceived: %d", fpNum, info.fpNum)
			}
		}
	}
}

// Tests that a transfer list marshalled with
// ReceivedFileTransfers.marshalTransfersList and unmarshalled with
// unmarshalTransfersList matches the original.
func TestReceivedFileTransfers_marshalTransfersList_unmarshalTransfersList(t *testing.T) {
	prng := NewPrng(42)
	rft := &ReceivedFileTransfers{
		transfers: make(map[ftCrypto.TransferID]*ReceivedTransfer),
	}

	// Add 10 transfers to map in memory
	for i := 0; i < 10; i++ {
		tid, _ := ftCrypto.NewTransferID(prng)
		rft.transfers[tid] = &ReceivedTransfer{}
	}

	// Marshal into byte slice
	marshalledBytes := rft.marshalTransfersList()

	// Unmarshal marshalled bytes into transfer ID list
	list := unmarshalTransfersList(marshalledBytes)

	// Check that the list has all the transfer IDs in memory
	for _, tid := range list {
		if _, exists := rft.transfers[tid]; !exists {
			t.Errorf("No transfer for ID %s exists.", tid)
		} else {
			delete(rft.transfers, tid)
		}
	}
}