557 lines
14 KiB
Go
557 lines
14 KiB
Go
|
// Copyright (C) 2018 Storj Labs, Inc.
|
||
|
// See LICENSE for copying information.
|
||
|
|
||
|
package pstore
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"os"
|
||
|
"path"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/stretchr/testify/assert"
|
||
|
)
|
||
|
|
||
|
var tmpfile string
|
||
|
|
||
|
func TestStore(t *testing.T) {
|
||
|
t.Run("it stores data successfully", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
folder1 := string(hash[0:2])
|
||
|
folder2 := string(hash[2:4])
|
||
|
fileName := string(hash[4:])
|
||
|
|
||
|
createdFilePath := path.Join(os.TempDir(), folder1, folder2, fileName)
|
||
|
defer os.RemoveAll(path.Join(os.TempDir(), folder1))
|
||
|
_, lStatErr := os.Lstat(createdFilePath)
|
||
|
if lStatErr != nil {
|
||
|
t.Errorf("No file was created from Store(): %s", lStatErr.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
createdFile, openCreatedError := os.Open(createdFilePath)
|
||
|
if openCreatedError != nil {
|
||
|
t.Errorf("Error: %s opening created file %s", openCreatedError.Error(), createdFilePath)
|
||
|
}
|
||
|
defer createdFile.Close()
|
||
|
|
||
|
buffer := make([]byte, 5)
|
||
|
createdFile.Seek(0, 0)
|
||
|
_, _ = createdFile.Read(buffer)
|
||
|
|
||
|
if string(buffer) != "butts" {
|
||
|
t.Errorf("Expected data butts does not equal Actual data %s", string(buffer))
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("it stores data by offset successfully", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 2, os.TempDir())
|
||
|
|
||
|
folder1 := string(hash[0:2])
|
||
|
folder2 := string(hash[2:4])
|
||
|
fileName := string(hash[4:])
|
||
|
|
||
|
createdFilePath := path.Join(os.TempDir(), folder1, folder2, fileName)
|
||
|
defer os.RemoveAll(path.Join(os.TempDir(), folder1))
|
||
|
_, lStatErr := os.Lstat(createdFilePath)
|
||
|
if lStatErr != nil {
|
||
|
t.Errorf("No file was created from Store(): %s", lStatErr.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
createdFile, openCreatedError := os.Open(createdFilePath)
|
||
|
if openCreatedError != nil {
|
||
|
t.Errorf("Error: %s opening created file %s", openCreatedError.Error(), createdFilePath)
|
||
|
}
|
||
|
defer createdFile.Close()
|
||
|
|
||
|
buffer := make([]byte, 7)
|
||
|
createdFile.Seek(0, 0)
|
||
|
_, _ = createdFile.Read(buffer)
|
||
|
|
||
|
// \0\0butts
|
||
|
expected := []byte{0, 0, 98, 117, 116, 116, 115}
|
||
|
|
||
|
if string(buffer) != string(expected) {
|
||
|
t.Errorf("Expected data (%v) does not equal Actual data (%v)", expected, buffer)
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("it stores data by chunk successfully", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, 4, 0, os.TempDir())
|
||
|
|
||
|
folder1 := string(hash[0:2])
|
||
|
folder2 := string(hash[2:4])
|
||
|
fileName := string(hash[4:])
|
||
|
|
||
|
createdFilePath := path.Join(os.TempDir(), folder1, folder2, fileName)
|
||
|
defer os.RemoveAll(path.Join(os.TempDir(), folder1))
|
||
|
_, lStatErr := os.Lstat(createdFilePath)
|
||
|
if lStatErr != nil {
|
||
|
t.Errorf("No file was created from Store(): %s", lStatErr.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
createdFile, openCreatedError := os.Open(createdFilePath)
|
||
|
if openCreatedError != nil {
|
||
|
t.Errorf("Error: %s opening created file %s", openCreatedError.Error(), createdFilePath)
|
||
|
}
|
||
|
defer createdFile.Close()
|
||
|
|
||
|
buffer := make([]byte, 4)
|
||
|
createdFile.Seek(0, 0)
|
||
|
_, _ = createdFile.Read(buffer)
|
||
|
|
||
|
// butt
|
||
|
expected := []byte{98, 117, 116, 116}
|
||
|
|
||
|
if string(buffer) != string(expected) {
|
||
|
t.Errorf("Expected data %s does not equal Actual data %s", string(expected), string(buffer))
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("it should return hash err if the hash is too short", func(t *testing.T) {
|
||
|
assert := assert.New(t)
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "11111111"
|
||
|
|
||
|
_, err = Store(hash, file, 5, 0, os.TempDir())
|
||
|
assert.NotNil(err)
|
||
|
if err != nil {
|
||
|
assert.Equal(err.Error(), "argError: Invalid hash length", "They should have the same error message")
|
||
|
}
|
||
|
})
|
||
|
|
||
|
// Test passing in negative offset
|
||
|
t.Run("it should return an error when given negative offset", func(t *testing.T) {
|
||
|
assert := assert.New(t)
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
|
||
|
_, err = Store(hash, file, int64(fi.Size()), -12, os.TempDir())
|
||
|
|
||
|
assert.NotNil(err)
|
||
|
if err != nil {
|
||
|
assert.Equal(err.Error(), "argError: Offset is less than 0. Must be greater than or equal to 0", err.Error())
|
||
|
}
|
||
|
})
|
||
|
|
||
|
// Test passing in a negative length
|
||
|
t.Run("it should return an error when given length less than 0", func(t *testing.T) {
|
||
|
assert := assert.New(t)
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
_, err = file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
|
||
|
_, err = Store(hash, file, -1, 0, os.TempDir())
|
||
|
assert.NotNil(err)
|
||
|
if err != nil {
|
||
|
assert.Equal(err.Error(), "argError: Length is less than 0. Must be greater than or equal to 0", err.Error())
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestRetrieve(t *testing.T) {
|
||
|
t.Run("it retrieves data successfully", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
// Create file for retrieving data into
|
||
|
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
|
||
|
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
defer os.RemoveAll(retrievalFilePath)
|
||
|
defer retrievalFile.Close()
|
||
|
|
||
|
_, err = Retrieve(hash, retrievalFile, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
if err != nil {
|
||
|
if err != io.EOF {
|
||
|
t.Errorf("Retrieve Error: %s", err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
buffer := make([]byte, 5)
|
||
|
|
||
|
retrievalFile.Seek(0, 0)
|
||
|
_, _ = retrievalFile.Read(buffer)
|
||
|
|
||
|
fmt.Printf("Retrieved data: %s", string(buffer))
|
||
|
|
||
|
if string(buffer) != "butts" {
|
||
|
t.Errorf("Expected data butts does not equal Actual data %s", string(buffer))
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("it retrieves data by offset successfully", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
// Create file for retrieving data into
|
||
|
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
|
||
|
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
defer os.RemoveAll(retrievalFilePath)
|
||
|
defer retrievalFile.Close()
|
||
|
|
||
|
_, err = Retrieve(hash, retrievalFile, int64(fi.Size()), 2, os.TempDir())
|
||
|
|
||
|
if err != nil {
|
||
|
if err != io.EOF {
|
||
|
t.Errorf("Retrieve Error: %s", err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
buffer := make([]byte, 3)
|
||
|
|
||
|
retrievalFile.Seek(0, 0)
|
||
|
_, _ = retrievalFile.Read(buffer)
|
||
|
|
||
|
fmt.Printf("Retrieved data: %s", string(buffer))
|
||
|
|
||
|
if string(buffer) != "tts" {
|
||
|
t.Errorf("Expected data (tts) does not equal Actual data (%s)", string(buffer))
|
||
|
}
|
||
|
})
|
||
|
|
||
|
t.Run("it retrieves data by chunk successfully", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
// Create file for retrieving data into
|
||
|
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
|
||
|
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
defer os.RemoveAll(retrievalFilePath)
|
||
|
defer retrievalFile.Close()
|
||
|
|
||
|
_, err = Retrieve(hash, retrievalFile, 3, 0, os.TempDir())
|
||
|
|
||
|
if err != nil {
|
||
|
if err != io.EOF {
|
||
|
t.Errorf("Retrieve Error: %s", err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
buffer := make([]byte, 3)
|
||
|
|
||
|
retrievalFile.Seek(0, 0)
|
||
|
_, _ = retrievalFile.Read(buffer)
|
||
|
|
||
|
fmt.Printf("Retrieved data: %s", string(buffer))
|
||
|
|
||
|
if string(buffer) != "but" {
|
||
|
t.Errorf("Expected data (but) does not equal Actual data (%s)", string(buffer))
|
||
|
}
|
||
|
})
|
||
|
|
||
|
// Test passing in negative offset
|
||
|
t.Run("it should return an error when retrieving with offset less 0", func(t *testing.T) {
|
||
|
assert := assert.New(t)
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
// Create file for retrieving data into
|
||
|
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
|
||
|
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
defer os.RemoveAll(retrievalFilePath)
|
||
|
defer retrievalFile.Close()
|
||
|
|
||
|
_, err = Retrieve(hash, retrievalFile, int64(fi.Size()), -1, os.TempDir())
|
||
|
assert.NotNil(err)
|
||
|
if err != nil {
|
||
|
assert.Equal("argError: Invalid offset: -1", err.Error(), err.Error())
|
||
|
}
|
||
|
})
|
||
|
|
||
|
// Test passing in negative length
|
||
|
t.Run("it should return the entire file successfully when retrieving with negative length", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
// Create file for retrieving data into
|
||
|
retrievalFilePath := path.Join(os.TempDir(), "retrieved.txt")
|
||
|
retrievalFile, err := os.OpenFile(retrievalFilePath, os.O_RDWR|os.O_CREATE, 0777)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error creating file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
defer os.RemoveAll(retrievalFilePath)
|
||
|
defer retrievalFile.Close()
|
||
|
|
||
|
_, err = Retrieve(hash, retrievalFile, -1, 0, os.TempDir())
|
||
|
|
||
|
if err != nil {
|
||
|
if err != io.EOF {
|
||
|
t.Errorf("Retrieve Error: %s", err.Error())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
buffer := make([]byte, 5)
|
||
|
|
||
|
retrievalFile.Seek(0, 0)
|
||
|
_, _ = retrievalFile.Read(buffer)
|
||
|
|
||
|
fmt.Printf("Retrieved data: %s", string(buffer))
|
||
|
|
||
|
if string(buffer) != "butts" {
|
||
|
t.Errorf("Expected data butts does not equal Actual data %s", string(buffer))
|
||
|
}
|
||
|
})
|
||
|
|
||
|
}
|
||
|
|
||
|
func TestDelete(t *testing.T) {
|
||
|
t.Run("it deletes data successfully", func(t *testing.T) {
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
folder1 := string(hash[0:2])
|
||
|
folder2 := string(hash[2:4])
|
||
|
fileName := string(hash[4:])
|
||
|
|
||
|
if _, err := os.Stat(path.Join(os.TempDir(), folder1, folder2, fileName)); err != nil {
|
||
|
t.Errorf("Failed to Store test file")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
Delete(hash, os.TempDir())
|
||
|
_, err = os.Stat(path.Join(os.TempDir(), folder1, folder2, fileName))
|
||
|
if err == nil {
|
||
|
t.Errorf("Failed to Delete test file")
|
||
|
return
|
||
|
}
|
||
|
})
|
||
|
|
||
|
// Test passing in a hash that doesn't exist
|
||
|
t.Run("it returns an error if hash doesn't exist", func(t *testing.T) {
|
||
|
assert := assert.New(t)
|
||
|
file, err := os.Open(tmpfile)
|
||
|
if err != nil {
|
||
|
t.Errorf("Error opening tmp file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
fi, err := file.Stat()
|
||
|
if err != nil {
|
||
|
t.Errorf("Could not stat test file: %s", err.Error())
|
||
|
return
|
||
|
}
|
||
|
|
||
|
defer file.Close()
|
||
|
|
||
|
hash := "0123456789ABCDEFGHIJ"
|
||
|
Store(hash, file, int64(fi.Size()), 0, os.TempDir())
|
||
|
|
||
|
folder1 := string(hash[0:2])
|
||
|
folder2 := string(hash[2:4])
|
||
|
fileName := string(hash[4:])
|
||
|
|
||
|
if _, err := os.Stat(path.Join(os.TempDir(), folder1, folder2, fileName)); err != nil {
|
||
|
t.Errorf("Failed to Store test file")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
falseHash := ""
|
||
|
|
||
|
err = Delete(falseHash, os.TempDir())
|
||
|
assert.NotNil(err)
|
||
|
if err != nil {
|
||
|
assert.NotEqual(err.Error(), "argError: Hash folder does not exist", "They should be equal")
|
||
|
}
|
||
|
})
|
||
|
}
|
||
|
|
||
|
func TestMain(m *testing.M) {
|
||
|
content := []byte("butts")
|
||
|
tmpfilePtr, err := ioutil.TempFile("", "api_test")
|
||
|
if err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
tmpfile = tmpfilePtr.Name()
|
||
|
defer os.Remove(tmpfile) // clean up
|
||
|
|
||
|
if _, err := tmpfilePtr.Write(content); err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
if err := tmpfilePtr.Close(); err != nil {
|
||
|
log.Fatal(err)
|
||
|
}
|
||
|
|
||
|
m.Run()
|
||
|
}
|