From a5af8afd8931368bb56ba858947185b9407b688c Mon Sep 17 00:00:00 2001 From: Javier Peletier Date: Tue, 18 Dec 2018 00:34:34 +0100 Subject: [PATCH] swarm/testutil: Testing framework demo --- swarm/api/http/test_server.go | 35 +-- swarm/storage/feed/handler_test.go | 282 ++++++------------ swarm/storage/feed/request_test.go | 191 ++++-------- .../firstrequest.json | 11 + .../results.json | 3 + .../secondrequest.json | 12 + .../feed/testdata/TestTopic/results.json | 3 + .../feed/testdata/TestTopic/topic.json | 1 + swarm/storage/feed/testutil.go | 30 +- swarm/storage/feed/topic_test.go | 42 +-- swarm/testutil/file.go | 2 + swarm/testutil/swarmtestservices.go | 20 ++ swarm/testutil/testtools.go | 21 ++ 13 files changed, 256 insertions(+), 397 deletions(-) create mode 100644 swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/firstrequest.json create mode 100644 swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/results.json create mode 100644 swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/secondrequest.json create mode 100644 swarm/storage/feed/testdata/TestTopic/results.json create mode 100644 swarm/storage/feed/testdata/TestTopic/topic.json create mode 100644 swarm/testutil/swarmtestservices.go create mode 100644 swarm/testutil/testtools.go diff --git a/swarm/api/http/test_server.go b/swarm/api/http/test_server.go index 9245c9c5b8..0e31331261 100644 --- a/swarm/api/http/test_server.go +++ b/swarm/api/http/test_server.go @@ -17,12 +17,12 @@ package http import ( - "io/ioutil" "net/http" "net/http/httptest" - "os" "testing" + "github.com/ethereum/go-ethereum/swarm/testutil" + "github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage/feed" @@ -32,33 +32,21 @@ type TestServer interface { ServeHTTP(http.ResponseWriter, *http.Request) } -func NewTestSwarmServer(t *testing.T, serverFunc func(*api.API) TestServer, resolver api.Resolver) *TestSwarmServer { - dir, err := ioutil.TempDir("", "swarm-storage-test") - if err != nil { - t.Fatal(err) - } +func NewTestSwarmServer(tx *testing.T, serverFunc func(*api.API) TestServer, resolver api.Resolver) *TestSwarmServer { + // hacked this function for the testing framework demo. Disregard changes here. + t := testutil.BeginTest(tx, false) + dir := t.Services.NewTempDir() + storeparams := storage.NewDefaultLocalStoreParams() storeparams.DbCapacity = 5000000 storeparams.CacheCapacity = 5000 storeparams.Init(dir) localStore, err := storage.NewLocalStore(storeparams, nil) - if err != nil { - os.RemoveAll(dir) - t.Fatal(err) - } + t.Ok(err) + fileStore := storage.NewFileStore(localStore, storage.NewFileStoreParams()) - // Swarm feeds test setup - feedsDir, err := ioutil.TempDir("", "swarm-feeds-test") - if err != nil { - t.Fatal(err) - } - - rhparams := &feed.HandlerParams{} - rh, err := feed.NewTestHandler(feedsDir, rhparams) - if err != nil { - t.Fatal(err) - } + rh := feed.NewTestHandler(t, "") a := api.NewAPI(fileStore, resolver, rh.Handler, nil) srv := httptest.NewServer(serverFunc(a)) @@ -70,8 +58,7 @@ func NewTestSwarmServer(t *testing.T, serverFunc func(*api.API) TestServer, reso cleanup: func() { srv.Close() rh.Close() - os.RemoveAll(dir) - os.RemoveAll(feedsDir) + t.FinishTest() }, CurrentTime: 42, } diff --git a/swarm/storage/feed/handler_test.go b/swarm/storage/feed/handler_test.go index 2f8a524535..d7c1cd5026 100644 --- a/swarm/storage/feed/handler_test.go +++ b/swarm/storage/feed/handler_test.go @@ -17,11 +17,9 @@ package feed import ( - "bytes" "context" "flag" "fmt" - "io/ioutil" "os" "testing" "time" @@ -31,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/swarm/chunk" "github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage/feed/lookup" + "github.com/ethereum/go-ethereum/swarm/testutil" ) var ( @@ -71,7 +70,9 @@ func (f *fakeTimeProvider) Now() Timestamp { } // make updates and retrieve them based on periods and versions -func TestFeedsHandler(t *testing.T) { +func TestFeedsHandler(tx *testing.T) { + t := testutil.BeginTest(tx, false) // set to true to generate results + defer t.FinishTest() // make fake timeProvider clock := &fakeTimeProvider{ @@ -80,12 +81,7 @@ func TestFeedsHandler(t *testing.T) { // signer containing private key signer := newAliceSigner() - - feedsHandler, datadir, teardownTest, err := setupTest(clock, signer) - if err != nil { - t.Fatal(err) - } - defer teardownTest() + feedsHandler := setupTest(t, clock) // create a new feed ctx, cancel := context.WithCancel(context.Background()) @@ -109,86 +105,68 @@ func TestFeedsHandler(t *testing.T) { chunkAddress := make(map[string]storage.Address) data := []byte(updates[0]) request.SetData(data) - if err := request.Sign(signer); err != nil { - t.Fatal(err) - } + + err := request.Sign(signer) + t.Ok(err) + chunkAddress[updates[0]], err = feedsHandler.Update(ctx, request) - if err != nil { - t.Fatal(err) - } + t.Ok(err) // move the clock ahead 21 seconds clock.FastForward(21) // t=4221 request, err = feedsHandler.NewRequest(ctx, &request.Feed) // this timestamps the update at t = 4221 - if err != nil { - t.Fatal(err) - } - if request.Epoch.Base() != 0 || request.Epoch.Level != lookup.HighestLevel-1 { - t.Fatalf("Suggested epoch BaseTime should be 0 and Epoch level should be %d", lookup.HighestLevel-1) - } + t.Ok(err) + t.Assert(request.Epoch.Base() == 0 && request.Epoch.Level == lookup.HighestLevel-1, "Suggested epoch BaseTime should be 0 and Epoch level should be %d", lookup.HighestLevel-1) request.Epoch.Level = lookup.HighestLevel // force level 25 instead of 24 to make it fail data = []byte(updates[1]) request.SetData(data) - if err := request.Sign(signer); err != nil { - t.Fatal(err) - } + err = request.Sign(signer) + t.Ok(err) + chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request) - if err == nil { - t.Fatal("Expected update to fail since an update in this epoch already exists") - } + t.MustFail(err, "Expected update to fail since an update in this epoch already exists") // move the clock ahead 21 seconds clock.FastForward(21) // t=4242 request, err = feedsHandler.NewRequest(ctx, &request.Feed) - if err != nil { - t.Fatal(err) - } + t.Ok(err) + request.SetData(data) - if err := request.Sign(signer); err != nil { - t.Fatal(err) - } + err = request.Sign(signer) + t.Ok(err) + chunkAddress[updates[1]], err = feedsHandler.Update(ctx, request) - if err != nil { - t.Fatal(err) - } + t.Ok(err) // move the clock ahead 42 seconds clock.FastForward(42) // t=4284 request, err = feedsHandler.NewRequest(ctx, &request.Feed) - if err != nil { - t.Fatal(err) - } + t.Ok(err) + data = []byte(updates[2]) request.SetData(data) - if err := request.Sign(signer); err != nil { - t.Fatal(err) - } + err = request.Sign(signer) + t.Ok(err) + chunkAddress[updates[2]], err = feedsHandler.Update(ctx, request) - if err != nil { - t.Fatal(err) - } + t.Ok(err) // move the clock ahead 1 second clock.FastForward(1) // t=4285 request, err = feedsHandler.NewRequest(ctx, &request.Feed) - if err != nil { - t.Fatal(err) - } - if request.Epoch.Base() != 0 || request.Epoch.Level != 22 { - t.Fatalf("Expected epoch base time to be %d, got %d. Expected epoch level to be %d, got %d", 0, request.Epoch.Base(), 22, request.Epoch.Level) - } + t.Ok(err) + t.Assert(request.Epoch.Base() == 0 && request.Epoch.Level == 22, "Expected epoch base time to be %d, got %d. Expected epoch level to be %d, got %d", 0, request.Epoch.Base(), 22, request.Epoch.Level) + data = []byte(updates[3]) request.SetData(data) - if err := request.Sign(signer); err != nil { - t.Fatal(err) - } + err = request.Sign(signer) + t.Ok(err) + chunkAddress[updates[3]], err = feedsHandler.Update(ctx, request) - if err != nil { - t.Fatal(err) - } + t.Ok(err) time.Sleep(time.Second) feedsHandler.Close() @@ -196,47 +174,29 @@ func TestFeedsHandler(t *testing.T) { // check we can retrieve the updates after close clock.FastForward(2000) // t=6285 - feedParams := &HandlerParams{} - - feedsHandler2, err := NewTestHandler(datadir, feedParams) - if err != nil { - t.Fatal(err) - } + feedsHandler2 := NewTestHandler(t, feedsHandler.dataDir) + t.Ok(err) update2, err := feedsHandler2.Lookup(ctx, NewQueryLatest(&request.Feed, lookup.NoClue)) - if err != nil { - t.Fatal(err) - } + t.Ok(err) // last update should be "clyde" - if !bytes.Equal(update2.data, []byte(updates[len(updates)-1])) { - t.Fatalf("feed update data was %v, expected %v", string(update2.data), updates[len(updates)-1]) - } - if update2.Level != 22 { - t.Fatalf("feed update epoch level was %d, expected 22", update2.Level) - } - if update2.Base() != 0 { - t.Fatalf("feed update epoch base time was %d, expected 0", update2.Base()) - } + t.Equals([]byte(updates[len(updates)-1]), update2.data) + t.Assert(update2.Level == 22, "feed update epoch level was %d, expected 22", update2.Level) + t.Assert(update2.Base() == 0, "feed update epoch base time was %d, expected 0", update2.Base()) + log.Debug("Latest lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data) // specific point in time update, err := feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, 4284, lookup.NoClue)) - if err != nil { - t.Fatal(err) - } - // check data - if !bytes.Equal(update.data, []byte(updates[2])) { - t.Fatalf("feed update data (historical) was %v, expected %v", string(update2.data), updates[2]) - } + t.Ok(err) + t.Equals([]byte(updates[2]), update.data) + log.Debug("Historical lookup", "epoch base time", update2.Base(), "epoch level", update2.Level, "data", update2.data) // beyond the first should yield an error update, err = feedsHandler2.Lookup(ctx, NewQuery(&request.Feed, startTime.Time-1, lookup.NoClue)) - if err == nil { - t.Fatalf("expected previous to fail, returned epoch %s data %v", update.Epoch.String(), update.data) - } - + t.MustFail(err, "expected previous to fail") } const Day = 60 * 60 * 24 @@ -247,7 +207,9 @@ func generateData(x uint64) []byte { return []byte(fmt.Sprintf("%d", x)) } -func TestSparseUpdates(t *testing.T) { +func TestSparseUpdates(tx *testing.T) { + t := testutil.BeginTest(tx, false) // set to true to generate results + defer t.FinishTest() // make fake timeProvider timeProvider := &fakeTimeProvider{ @@ -256,13 +218,7 @@ func TestSparseUpdates(t *testing.T) { // signer containing private key signer := newAliceSigner() - - rh, datadir, teardownTest, err := setupTest(timeProvider, signer) - if err != nil { - t.Fatal(err) - } - defer teardownTest() - defer os.RemoveAll(datadir) + rh := setupTest(t, timeProvider) // create a new feed ctx, cancel := context.WithCancel(context.Background()) @@ -281,33 +237,24 @@ func TestSparseUpdates(t *testing.T) { request := NewFirstRequest(fd.Topic) request.Epoch = lookup.GetNextEpoch(epoch, T) request.data = generateData(T) // this generates some data that depends on T, so we can check later - request.Sign(signer) - if err != nil { - t.Fatal(err) - } + err := request.Sign(signer) + t.Ok(err) + + _, err = rh.Update(ctx, request) + t.Ok(err) - if _, err := rh.Update(ctx, request); err != nil { - t.Fatal(err) - } epoch = request.Epoch lastUpdateTime = T } query := NewQuery(&fd, today, lookup.NoClue) - _, err = rh.Lookup(ctx, query) - if err != nil { - t.Fatal(err) - } + _, err := rh.Lookup(ctx, query) + t.Ok(err) _, content, err := rh.GetContent(&fd) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(generateData(lastUpdateTime), content) { - t.Fatalf("Expected to recover last written value %d, got %s", lastUpdateTime, string(content)) - } + t.Ok(err) + t.Equals(generateData(lastUpdateTime), content) // lookup the closest update to 35*Year + 6* Month (~ June 2005): // it should find the update we put on 35*Year, since we were updating every 5 years. @@ -315,21 +262,16 @@ func TestSparseUpdates(t *testing.T) { query.TimeLimit = 35*Year + 6*Month _, err = rh.Lookup(ctx, query) - if err != nil { - t.Fatal(err) - } + t.Ok(err) _, content, err = rh.GetContent(&fd) - if err != nil { - t.Fatal(err) - } - - if !bytes.Equal(generateData(35*Year), content) { - t.Fatalf("Expected to recover %d, got %s", 35*Year, string(content)) - } + t.Ok(err) + t.Equals(generateData(35*Year), content) } -func TestValidator(t *testing.T) { +func TestValidator(tx *testing.T) { + t := testutil.BeginTest(tx, false) // set to true to generate results + defer t.FinishTest() // make fake timeProvider timeProvider := &fakeTimeProvider{ @@ -338,13 +280,8 @@ func TestValidator(t *testing.T) { // signer containing private key. Alice will be the good girl signer := newAliceSigner() - // set up sim timeProvider - rh, _, teardownTest, err := setupTest(timeProvider, signer) - if err != nil { - t.Fatal(err) - } - defer teardownTest() + rh := setupTest(t, timeProvider) // create new feed topic, _ := NewTopic(subtopicName, nil) @@ -357,33 +294,27 @@ func TestValidator(t *testing.T) { // chunk with address data := []byte("foo") mr.SetData(data) - if err := mr.Sign(signer); err != nil { - t.Fatalf("sign fail: %v", err) - } + err := mr.Sign(signer) + t.Ok(err) chunk, err := mr.toChunk() - if err != nil { - t.Fatal(err) - } - if !rh.Validate(chunk) { - t.Fatal("Chunk validator fail on update chunk") - } + t.Ok(err) + t.Assert(rh.Validate(chunk), "Chunk validator fail on update chunk") address := chunk.Address() // mess with the address address[0] = 11 address[15] = 99 - - if rh.Validate(storage.NewChunk(address, chunk.Data())) { - t.Fatal("Expected Validate to fail with false chunk address") - } + t.Assert(!rh.Validate(storage.NewChunk(address, chunk.Data())), "Expected Validate to fail with false chunk address") } // tests that the content address validator correctly checks the data // tests that feed update chunks are passed through content address validator // there is some redundancy in this test as it also tests content addressed chunks, // which should be evaluated as invalid chunks by this validator -func TestValidatorInStore(t *testing.T) { +func TestValidatorInStore(tx *testing.T) { + t := testutil.BeginTest(tx, false) // set to true to generate results + defer t.FinishTest() // make fake timeProvider TimestampProvider = &fakeTimeProvider{ @@ -392,24 +323,16 @@ func TestValidatorInStore(t *testing.T) { // signer containing private key signer := newAliceSigner() - // set up localstore - datadir, err := ioutil.TempDir("", "storage-testfeedsvalidator") - if err != nil { - t.Fatal(err) - } - defer os.RemoveAll(datadir) + datadir := t.Services.NewTempDir() handlerParams := storage.NewDefaultLocalStoreParams() handlerParams.Init(datadir) store, err := storage.NewLocalStore(handlerParams, nil) - if err != nil { - t.Fatal(err) - } + t.Ok(err) // set up Swarm feeds handler and add is as a validator to the localstore - fhParams := &HandlerParams{} - fh := NewHandler(fhParams) + fh := NewHandler(&HandlerParams{}) store.Validators = append(store.Validators, fh) // create content addressed chunks, one good, one faulty @@ -439,55 +362,28 @@ func TestValidatorInStore(t *testing.T) { r.Update.ID = id r.data = data - r.Sign(signer) + err = r.Sign(signer) + t.Ok(err) uglyChunk, err := r.toChunk() - if err != nil { - t.Fatal(err) - } + t.Ok(err) // put the chunks in the store and check their error status err = store.Put(context.Background(), goodChunk) - if err == nil { - t.Fatal("expected error on good content address chunk with feed update validator only, but got nil") - } + t.MustFail(err, "expected error on good content address chunk with feed update validator only, but got nil") + err = store.Put(context.Background(), badChunk) - if err == nil { - t.Fatal("expected error on bad content address chunk with feed update validator only, but got nil") - } - err = store.Put(context.Background(), uglyChunk) - if err != nil { - t.Fatalf("expected no error on feed update chunk with feed update validator only, but got: %s", err) - } + t.MustFail(err, "expected error on bad content address chunk with feed update validator only, but got nil") + + err = store.Put(context.Background(), uglyChunk) // feed update chunk with feed update validator only + t.Ok(err) } // create rpc and feeds Handler -func setupTest(timeProvider timestampProvider, signer Signer) (fh *TestHandler, datadir string, teardown func(), err error) { - - var fsClean func() - var rpcClean func() - cleanF = func() { - if fsClean != nil { - fsClean() - } - if rpcClean != nil { - rpcClean() - } - } - - // temp datadir - datadir, err = ioutil.TempDir("", "fh") - if err != nil { - return nil, "", nil, err - } - fsClean = func() { - os.RemoveAll(datadir) - } - +func setupTest(t *testutil.SwarmTestTools, timeProvider timestampProvider) *TestHandler { TimestampProvider = timeProvider - fhParams := &HandlerParams{} - fh, err = NewTestHandler(datadir, fhParams) - return fh, datadir, cleanF, err + fh := NewTestHandler(t, "") + return fh } func newAliceSigner() *GenericSigner { diff --git a/swarm/storage/feed/request_test.go b/swarm/storage/feed/request_test.go index c30158fddf..b42165ab25 100644 --- a/swarm/storage/feed/request_test.go +++ b/swarm/storage/feed/request_test.go @@ -17,38 +17,27 @@ package feed import ( - "bytes" "encoding/binary" "encoding/json" - "fmt" "reflect" "testing" + "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/swarm/storage" "github.com/ethereum/go-ethereum/swarm/storage/feed/lookup" + "github.com/ethereum/go-ethereum/swarm/testutil" ) -func areEqualJSON(s1, s2 string) (bool, error) { - //credit for the trick: turtlemonvh https://gist.github.com/turtlemonvh/e4f7404e28387fadb8ad275a99596f67 - var o1 interface{} - var o2 interface{} - - err := json.Unmarshal([]byte(s1), &o1) - if err != nil { - return false, fmt.Errorf("Error mashalling string 1 :: %s", err.Error()) - } - err = json.Unmarshal([]byte(s2), &o2) - if err != nil { - return false, fmt.Errorf("Error mashalling string 2 :: %s", err.Error()) - } - - return reflect.DeepEqual(o1, o2), nil -} - // TestEncodingDecodingUpdateRequests ensures that requests are serialized properly // while also checking cryptographically that only the owner of a feed can update it. -func TestEncodingDecodingUpdateRequests(t *testing.T) { +func TestEncodingDecodingUpdateRequests(tx *testing.T) { + t := testutil.BeginTest(tx, false) // set to true to generate results + defer t.FinishTest() + + TimestampProvider = &fakeTimeProvider{ + currentTime: startTime.Time, // clock starts at t=4200 + } charlie := newCharlieSigner() //Charlie bob := newBobSigner() //Bob @@ -58,28 +47,12 @@ func TestEncodingDecodingUpdateRequests(t *testing.T) { firstRequest := NewFirstRequest(topic) firstRequest.User = charlie.Address() - // We now encode the create message to simulate we send it over the wire - messageRawData, err := firstRequest.MarshalJSON() - if err != nil { - t.Fatalf("Error encoding first feed update request: %s", err) - } + t.TestJSONMarshaller("firstrequest.json", firstRequest) - // ... the message arrives and is decoded... - var recoveredFirstRequest Request - if err := recoveredFirstRequest.UnmarshalJSON(messageRawData); err != nil { - t.Fatalf("Error decoding first feed update request: %s", err) - } - - // ... but verification should fail because it is not signed! - if err := recoveredFirstRequest.Verify(); err == nil { - t.Fatal("Expected Verify to fail since the message is not signed") - } - - // We now assume that the feed ypdate was created and propagated. - - const expectedSignature = "0x7235b27a68372ddebcf78eba48543fa460864b0b0e99cb533fcd3664820e603312d29426dd00fb39628f5299480a69bf6e462838d78de49ce0704c754c9deb2601" - const expectedJSON = `{"feed":{"topic":"0x6120676f6f6420746f706963206e616d65000000000000000000000000000000","user":"0x876a8936a7cd0b79ef0735ad0896c1afe278781c"},"epoch":{"time":1000,"level":1},"protocolVersion":0,"data":"0x5468697320686f75722773207570646174653a20537761726d2039392e3020686173206265656e2072656c656173656421"}` + // but verification should fail because it is not signed! + t.MustFail(firstRequest.Verify(), "Expected Verify to fail since the message is not signed") + // We now assume that the feed update was created and propagated. //Put together an unsigned update request that we will serialize to send it to the signer. data := []byte("This hour's update: Swarm 99.0 has been released!") request := &Request{ @@ -95,96 +68,74 @@ func TestEncodingDecodingUpdateRequests(t *testing.T) { }, } - messageRawData, err = request.MarshalJSON() - if err != nil { - t.Fatalf("Error encoding update request: %s", err) - } - - equalJSON, err := areEqualJSON(string(messageRawData), expectedJSON) - if err != nil { - t.Fatalf("Error decoding update request JSON: %s", err) - } - if !equalJSON { - t.Fatalf("Received a different JSON message. Expected %s, got %s", expectedJSON, string(messageRawData)) - } + messageRawData, err := request.MarshalJSON() + t.Ok(err) + t.JSONBytesEqualsFile("secondrequest.json", messageRawData) // now the encoded message messageRawData is sent over the wire and arrives to the signer //Attempt to extract an UpdateRequest out of the encoded message var recoveredRequest Request - if err := recoveredRequest.UnmarshalJSON(messageRawData); err != nil { - t.Fatalf("Error decoding update request: %s", err) - } + err = recoveredRequest.UnmarshalJSON(messageRawData) + t.Ok(err) //sign the request and see if it matches our predefined signature above. - if err := recoveredRequest.Sign(charlie); err != nil { - t.Fatalf("Error signing request: %s", err) - } - - compareByteSliceToExpectedHex(t, "signature", recoveredRequest.Signature[:], expectedSignature) + err = recoveredRequest.Sign(charlie) + t.Ok(err) + t.EqualsKey("signature", hexutil.Encode(recoveredRequest.Signature[:])) // mess with the signature and see what happens. To alter the signature, we briefly decode it as JSON // to alter the signature field. var j updateRequestJSON - if err := json.Unmarshal([]byte(expectedJSON), &j); err != nil { - t.Fatal("Error unmarshalling test json, check expectedJSON constant") - } + err = json.Unmarshal([]byte(messageRawData), &j) + t.Ok(err) + j.Signature = "Certainly not a signature" corruptMessage, _ := json.Marshal(j) // encode the message with the bad signature var corruptRequest Request - if err = corruptRequest.UnmarshalJSON(corruptMessage); err == nil { - t.Fatal("Expected DecodeUpdateRequest to fail when trying to interpret a corrupt message with an invalid signature") - } + err = corruptRequest.UnmarshalJSON(corruptMessage) + t.MustFail(err, "Expected DecodeUpdateRequest to fail when trying to interpret a corrupt message with an invalid signature") // Now imagine Bob wants to create an update of his own about the same feed, // signing a message with his private key - if err := request.Sign(bob); err != nil { - t.Fatalf("Error signing: %s", err) - } + err = request.Sign(bob) + t.Ok(err) // Now Bob encodes the message to send it over the wire... messageRawData, err = request.MarshalJSON() - if err != nil { - t.Fatalf("Error encoding message:%s", err) - } + t.Ok(err) // ... the message arrives to our Swarm node and it is decoded. recoveredRequest = Request{} - if err := recoveredRequest.UnmarshalJSON(messageRawData); err != nil { - t.Fatalf("Error decoding message:%s", err) - } + err = recoveredRequest.UnmarshalJSON(messageRawData) + t.Ok(err) // Before checking what happened with Bob's update, let's see what would happen if we mess // with the signature big time to see if Verify catches it savedSignature := *recoveredRequest.Signature // save the signature for later binary.LittleEndian.PutUint64(recoveredRequest.Signature[5:], 556845463424) // write some random data to break the signature - if err = recoveredRequest.Verify(); err == nil { - t.Fatal("Expected Verify to fail on corrupt signature") - } + err = recoveredRequest.Verify() + t.MustFail(err, "Expected Verify to fail on corrupt signature") // restore the Bob's signature from corruption *recoveredRequest.Signature = savedSignature // Now the signature is not corrupt - if err = recoveredRequest.Verify(); err != nil { - t.Fatal(err) - } + err = recoveredRequest.Verify() + t.Ok(err) // Reuse object and sign with our friend Charlie's private key - if err := recoveredRequest.Sign(charlie); err != nil { - t.Fatalf("Error signing with the correct private key: %s", err) - } + err = recoveredRequest.Sign(charlie) + t.Ok(err) // And now, Verify should work since this update now belongs to Charlie - if err = recoveredRequest.Verify(); err != nil { - t.Fatalf("Error verifying that Charlie, can sign a reused request object:%s", err) - } + err = recoveredRequest.Verify() + t.Ok(err) // mess with the lookup key to make sure Verify fails: recoveredRequest.Time = 77999 // this will alter the lookup key - if err = recoveredRequest.Verify(); err == nil { - t.Fatalf("Expected Verify to fail since the lookup key has been altered") - } + err = recoveredRequest.Verify() + t.MustFail(err, "Expected Verify to fail since the lookup key has been altered") } func getTestRequest() *Request { @@ -233,28 +184,18 @@ func TestUpdateChunkSerializationErrorChecking(t *testing.T) { } // check that signature address matches update signer address -func TestReverse(t *testing.T) { +func TestReverse(tx *testing.T) { + t := testutil.BeginTest(tx, false) // set to true to generate results + defer t.FinishTest() epoch := lookup.Epoch{ Time: 7888, Level: 6, } - // make fake timeProvider - timeProvider := &fakeTimeProvider{ - currentTime: startTime.Time, - } - // signer containing private key signer := newAliceSigner() - // set up rpc and create feeds handler - _, _, teardownTest, err := setupTest(timeProvider, signer) - if err != nil { - t.Fatal(err) - } - defer teardownTest() - topic, _ := NewTopic("Cervantes quotes", nil) fd := Feed{ Topic: topic, @@ -271,42 +212,30 @@ func TestReverse(t *testing.T) { // generate a chunk key for this request key := request.Addr() - if err = request.Sign(signer); err != nil { - t.Fatal(err) - } + err := request.Sign(signer) + t.Ok(err) chunk, err := request.toChunk() - if err != nil { - t.Fatal(err) - } + t.Ok(err) // check that we can recover the owner account from the update chunk's signature var checkUpdate Request - if err := checkUpdate.fromChunk(chunk); err != nil { - t.Fatal(err) - } + err = checkUpdate.fromChunk(chunk) + t.Ok(err) + checkdigest, err := checkUpdate.GetDigest() - if err != nil { - t.Fatal(err) - } - recoveredAddr, err := getUserAddr(checkdigest, *checkUpdate.Signature) - if err != nil { - t.Fatalf("Retrieve address from signature fail: %v", err) - } + t.Ok(err) + + recoveredAddr, err := getUserAddr(checkdigest, *checkUpdate.Signature) //Retrieve address from signature + t.Ok(err) + originalAddr := crypto.PubkeyToAddress(signer.PrivKey.PublicKey) // check that the metadata retrieved from the chunk matches what we gave it - if recoveredAddr != originalAddr { - t.Fatalf("addresses dont match: %x != %x", originalAddr, recoveredAddr) - } + t.Assert(recoveredAddr == originalAddr, "addresses dont match: %x != %x", originalAddr, recoveredAddr) + t.Equals(chunk.Address()[:], key[:]) - if !bytes.Equal(key[:], chunk.Address()[:]) { - t.Fatalf("Expected chunk key '%x', was '%x'", key, chunk.Address()) - } - if epoch != checkUpdate.Epoch { - t.Fatalf("Expected epoch to be '%s', was '%s'", epoch.String(), checkUpdate.Epoch.String()) - } - if !bytes.Equal(data, checkUpdate.data) { - t.Fatalf("Expected data '%x', was '%x'", data, checkUpdate.data) - } + t.Assert(epoch == checkUpdate.Epoch, "Expected epoch to be '%s', was '%s'", epoch.String(), checkUpdate.Epoch.String()) + + t.Equals(checkUpdate.data, data) } diff --git a/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/firstrequest.json b/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/firstrequest.json new file mode 100644 index 0000000000..184cf4a3e2 --- /dev/null +++ b/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/firstrequest.json @@ -0,0 +1,11 @@ +{ + "feed": { + "topic": "0x6120676f6f6420746f706963206e616d65000000000000000000000000000000", + "user": "0x876a8936a7cd0b79ef0735ad0896c1afe278781c" + }, + "epoch": { + "time": 4200, + "level": 25 + }, + "protocolVersion": 0 +} \ No newline at end of file diff --git a/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/results.json b/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/results.json new file mode 100644 index 0000000000..d7be519a03 --- /dev/null +++ b/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/results.json @@ -0,0 +1,3 @@ +{ + "signature": "0x7235b27a68372ddebcf78eba48543fa460864b0b0e99cb533fcd3664820e603312d29426dd00fb39628f5299480a69bf6e462838d78de49ce0704c754c9deb2601" +} \ No newline at end of file diff --git a/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/secondrequest.json b/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/secondrequest.json new file mode 100644 index 0000000000..c4b7e6703b --- /dev/null +++ b/swarm/storage/feed/testdata/TestEncodingDecodingUpdateRequests/secondrequest.json @@ -0,0 +1,12 @@ +{ + "feed": { + "topic": "0x6120676f6f6420746f706963206e616d65000000000000000000000000000000", + "user": "0x876a8936a7cd0b79ef0735ad0896c1afe278781c" + }, + "epoch": { + "time": 1000, + "level": 1 + }, + "protocolVersion": 0, + "data": "0x5468697320686f75722773207570646174653a20537761726d2039392e3020686173206265656e2072656c656173656421" +} \ No newline at end of file diff --git a/swarm/storage/feed/testdata/TestTopic/results.json b/swarm/storage/feed/testdata/TestTopic/results.json new file mode 100644 index 0000000000..dcf0c54599 --- /dev/null +++ b/swarm/storage/feed/testdata/TestTopic/results.json @@ -0,0 +1,3 @@ +{ + "hex": "0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789" +} \ No newline at end of file diff --git a/swarm/storage/feed/testdata/TestTopic/topic.json b/swarm/storage/feed/testdata/TestTopic/topic.json new file mode 100644 index 0000000000..7212c845f8 --- /dev/null +++ b/swarm/storage/feed/testdata/TestTopic/topic.json @@ -0,0 +1 @@ +"0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789" \ No newline at end of file diff --git a/swarm/storage/feed/testutil.go b/swarm/storage/feed/testutil.go index b513fa1f2f..98457d218c 100644 --- a/swarm/storage/feed/testutil.go +++ b/swarm/storage/feed/testutil.go @@ -18,20 +18,16 @@ package feed import ( "context" - "fmt" - "path/filepath" "sync" "github.com/ethereum/go-ethereum/p2p/enode" "github.com/ethereum/go-ethereum/swarm/storage" -) - -const ( - testDbDirName = "feeds" + "github.com/ethereum/go-ethereum/swarm/testutil" ) type TestHandler struct { *Handler + dataDir string } func (t *TestHandler) Close() { @@ -50,22 +46,24 @@ func newFakeNetFetcher(context.Context, storage.Address, *sync.Map) storage.NetF } // NewTestHandler creates Handler object to be used for testing purposes. -func NewTestHandler(datadir string, params *HandlerParams) (*TestHandler, error) { - path := filepath.Join(datadir, testDbDirName) - fh := NewHandler(params) +func NewTestHandler(t *testutil.SwarmTestTools, path string) *TestHandler { + if path == "" { + path = t.Services.NewTempDir() + } + fh := NewHandler(&HandlerParams{}) localstoreparams := storage.NewDefaultLocalStoreParams() localstoreparams.Init(path) localStore, err := storage.NewLocalStore(localstoreparams, nil) - if err != nil { - return nil, fmt.Errorf("localstore create fail, path %s: %v", path, err) - } + t.Ok(err) localStore.Validators = append(localStore.Validators, storage.NewContentAddressValidator(storage.MakeHashFunc(feedsHashAlgorithm))) localStore.Validators = append(localStore.Validators, fh) netStore, err := storage.NewNetStore(localStore, nil) - if err != nil { - return nil, err - } + t.Ok(err) netStore.NewNetFetcherFunc = newFakeNetFetcher fh.SetStore(netStore) - return &TestHandler{fh}, nil + th := &TestHandler{ + Handler: fh, + dataDir: path, + } + return th } diff --git a/swarm/storage/feed/topic_test.go b/swarm/storage/feed/topic_test.go index 0403204f7f..3fd2a59d90 100644 --- a/swarm/storage/feed/topic_test.go +++ b/swarm/storage/feed/topic_test.go @@ -4,47 +4,23 @@ import ( "testing" "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/swarm/testutil" ) -func TestTopic(t *testing.T) { +func TestTopic(tx *testing.T) { + t := testutil.BeginTest(tx, false) // set to true to generate results + defer t.FinishTest() + related, _ := hexutil.Decode("0xabcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789") topicName := "test-topic" topic, _ := NewTopic(topicName, related) hex := topic.Hex() - expectedHex := "0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789" - if hex != expectedHex { - t.Fatalf("Expected %s, got %s", expectedHex, hex) - } + t.EqualsKey("hex", hex) var topic2 Topic topic2.FromHex(hex) - if topic2 != topic { - t.Fatal("Expected recovered topic to be equal to original one") - } - - if topic2.Name(related) != topicName { - t.Fatal("Retrieved name does not match") - } - - bytes, err := topic2.MarshalJSON() - if err != nil { - t.Fatal(err) - } - expectedJSON := `"0xdfa89c750e3108f9c2aeef0123456789abcdef0123456789abcdef0123456789"` - equal, err := areEqualJSON(expectedJSON, string(bytes)) - if err != nil { - t.Fatal(err) - } - if !equal { - t.Fatalf("Expected JSON to be %s, got %s", expectedJSON, string(bytes)) - } - - err = topic2.UnmarshalJSON(bytes) - if err != nil { - t.Fatal(err) - } - if topic2 != topic { - t.Fatal("Expected recovered topic to be equal to original one") - } + t.Equals(topic, topic2) + t.Equals(topicName, topic2.Name(related)) + t.TestJSONMarshaller("topic.json", &topic2) } diff --git a/swarm/testutil/file.go b/swarm/testutil/file.go index 70732aa92e..20ea96406e 100644 --- a/swarm/testutil/file.go +++ b/swarm/testutil/file.go @@ -26,6 +26,8 @@ import ( "testing" ) +//TODO: refactor these as TestServices + // TempFileWithContent is a helper function that creates a temp file that contains the following string content then closes the file handle // it returns the complete file path func TempFileWithContent(t *testing.T, content string) string { diff --git a/swarm/testutil/swarmtestservices.go b/swarm/testutil/swarmtestservices.go new file mode 100644 index 0000000000..c28139d5f4 --- /dev/null +++ b/swarm/testutil/swarmtestservices.go @@ -0,0 +1,20 @@ +package testutil + +import ( + "github.com/epiclabs-io/ut" +) + +// SwarmTestServices groups all third-party dependencies to provide easy instantiation +// and cleanup of these. By default, it provides temporary files and directories +type swarmTestServices struct { + tt *SwarmTestTools + *ut.FileServices +} + +// Instantiate Test services +func newSwarmTestServices(tt *SwarmTestTools) *swarmTestServices { + return &swarmTestServices{ + tt: tt, + FileServices: ut.NewFileServices(tt.TestTools), + } +} diff --git a/swarm/testutil/testtools.go b/swarm/testutil/testtools.go new file mode 100644 index 0000000000..9d5f1020e5 --- /dev/null +++ b/swarm/testutil/testtools.go @@ -0,0 +1,21 @@ +package testutil + +import ( + "testing" + + "github.com/epiclabs-io/ut" +) + +// SwarmTestTools are Swarm-specific test tools +type SwarmTestTools struct { + *ut.TestTools + Services *swarmTestServices +} + +// BeginTest returns a project-specific test toolbox +func BeginTest(tb testing.TB, generateResults bool) *SwarmTestTools { + ett := new(SwarmTestTools) + ett.TestTools = ut.BeginTest(tb, generateResults) + ett.Services = newSwarmTestServices(ett) + return ett +}