package main
import (
+ "bytes"
"fmt"
"io/ioutil"
"os"
var TEST_HASH = "e4d909c290d0fb1ca068ffaddf22cbd0"
var BAD_BLOCK = []byte("The magic words are squeamish ossifrage.")
+// TODO(twp): Tests still to be written
+//
+// * PutBlockFull
+// - test that PutBlock returns 503 Full if the filesystem is full.
+// (must mock FreeDiskSpace or Statfs? use a tmpfs?)
+//
+// * PutBlockWriteErr
+// - test the behavior when Write returns an error.
+// - Possible solutions: use a small tmpfs and a high
+// MIN_FREE_KILOBYTES to trick PutBlock into attempting
+// to write a block larger than the amount of space left
+// - use an interface to mock ioutil.TempFile with a File
+// object that always returns an error on write
+//
// ========================================
// GetBlock tests.
// ========================================
// Check that GetBlock returns failure.
result, err := GetBlock(TEST_HASH)
- if err == nil {
- t.Errorf("GetBlock incorrectly returned success: ", result)
+ if err != NotFoundError {
+ t.Errorf("Expected NotFoundError, got %v", result)
}
}
// Check that GetBlock returns failure.
result, err := GetBlock(TEST_HASH)
- if err == nil {
- t.Errorf("GetBlock incorrectly returned success: %s", result)
+ if err != CorruptError {
+ t.Errorf("Expected CorruptError, got %v", result)
}
}
result, err := GetBlock(TEST_HASH)
if err != nil {
- t.Fatalf("GetBlock: %s", err.Error())
+ t.Fatalf("GetBlock returned error: %v", err)
}
if string(result) != string(TEST_BLOCK) {
t.Error("PutBlock/GetBlock mismatch")
result, err := GetBlock(TEST_HASH)
if err != nil {
- t.Fatalf("GetBlock: %s", err.Error())
+ t.Fatalf("GetBlock: %v", err)
}
if string(result) != string(TEST_BLOCK) {
t.Error("PutBlock/GetBlock mismatch")
}
}
-// TestPutBlockCorrupt
+// TestPutBlockMD5Fail
// Check that PutBlock returns an error if passed a block and hash that
// do not match.
//
-func TestPutBlockCorrupt(t *testing.T) {
+func TestPutBlockMD5Fail(t *testing.T) {
defer teardown()
// Create two test Keep volumes.
// Check that PutBlock returns the expected error when the hash does
// not match the block.
- if err := PutBlock(BAD_BLOCK, TEST_HASH); err == nil {
- t.Error("PutBlock succeeded despite a block mismatch")
- } else {
- ke := err.(*KeepError)
- if ke.HTTPCode != 401 || ke.Err.Error() != "MD5Fail" {
- t.Errorf("PutBlock returned the wrong error (%v)", ke)
- }
+ if err := PutBlock(BAD_BLOCK, TEST_HASH); err != MD5Error {
+ t.Error("Expected MD5Error, got %v", err)
}
// Confirm that GetBlock fails to return anything.
- if result, err := GetBlock(TEST_HASH); err == nil {
- t.Errorf("GetBlock succeded after a corrupt block store, returned '%s'",
- string(result))
+ if result, err := GetBlock(TEST_HASH); err != NotFoundError {
+ t.Errorf("GetBlock succeeded after a corrupt block store (result = %s, err = %v)",
+ string(result), err)
+ }
+}
+
+// TestPutBlockCorrupt
+// PutBlock should overwrite corrupt blocks on disk when given
+// a PUT request with a good block.
+//
+func TestPutBlockCorrupt(t *testing.T) {
+ defer teardown()
+
+ // Create two test Keep volumes.
+ KeepVolumes = setup(t, 2)
+
+ // Store a corrupted block under TEST_HASH.
+ store(t, KeepVolumes[0], TEST_HASH, BAD_BLOCK)
+ if err := PutBlock(TEST_BLOCK, TEST_HASH); err != nil {
+ t.Errorf("PutBlock: %v", err)
+ }
+
+ // The block on disk should now match TEST_BLOCK.
+ if block, err := GetBlock(TEST_HASH); err != nil {
+ t.Errorf("GetBlock: %v", err)
+ } else if bytes.Compare(block, TEST_BLOCK) != 0 {
+ t.Errorf("GetBlock returned: '%s'", string(block))
}
}
+// PutBlockCollision
+// PutBlock returns a 400 Collision error when attempting to
+// store a block that collides with another block on disk.
+//
+func TestPutBlockCollision(t *testing.T) {
+ defer teardown()
+
+ // These blocks both hash to the MD5 digest cee9a457e790cf20d4bdaa6d69f01e41.
+ var b1 = []byte("\x0e0eaU\x9a\xa7\x87\xd0\x0b\xc6\xf7\x0b\xbd\xfe4\x04\xcf\x03e\x9epO\x854\xc0\x0f\xfbe\x9cL\x87@\xcc\x94/\xeb-\xa1\x15\xa3\xf4\x15\\\xbb\x86\x07Is\x86em}\x1f4\xa4 Y\xd7\x8fZ\x8d\xd1\xef")
+ var b2 = []byte("\x0e0eaU\x9a\xa7\x87\xd0\x0b\xc6\xf7\x0b\xbd\xfe4\x04\xcf\x03e\x9etO\x854\xc0\x0f\xfbe\x9cL\x87@\xcc\x94/\xeb-\xa1\x15\xa3\xf4\x15\xdc\xbb\x86\x07Is\x86em}\x1f4\xa4 Y\xd7\x8fZ\x8d\xd1\xef")
+ var locator = "cee9a457e790cf20d4bdaa6d69f01e41"
+
+ // Prepare two test Keep volumes. Store one block,
+ // then attempt to store the other.
+ KeepVolumes = setup(t, 2)
+ store(t, KeepVolumes[1], locator, b1)
+
+ if err := PutBlock(b2, locator); err == nil {
+ t.Error("PutBlock did not report a collision")
+ } else if err != CollisionError {
+ t.Errorf("PutBlock returned %v", err)
+ }
+}
+
+// ========================================
+// FindKeepVolumes tests.
+// ========================================
+
// TestFindKeepVolumes
// Confirms that FindKeepVolumes finds tmpfs volumes with "/keep"
// directories at the top level.
for _, vol := range KeepVolumes {
os.RemoveAll(path.Dir(vol))
}
+ KeepVolumes = nil
}
// store
// Low-level code to write Keep blocks directly to disk for testing.
//
-func store(t *testing.T, keepdir string, filename string, block []byte) error {
+func store(t *testing.T, keepdir string, filename string, block []byte) {
blockdir := fmt.Sprintf("%s/%s", keepdir, filename[:3])
if err := os.MkdirAll(blockdir, 0755); err != nil {
t.Fatal(err)
} else {
t.Fatal(err)
}
-
- return nil
}