Write state diff to CSV #2

Merged
elizabethengelman merged 47 commits from ee-state-diff into statediff-for-archive-node 2019-01-28 21:31:02 +00:00
7 changed files with 494 additions and 472 deletions
Showing only changes of commit f4ca756812 - Show all commits

View File

@ -1,13 +0,0 @@
package builder_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestBuilder(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Builder Suite")
}

View File

@ -1,36 +1,18 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains a batch of utility type declarations used by the tests. As the node
// operates on unique types, a lot of them are needed to check various features.
package builder_test
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
b "github.com/ethereum/go-ethereum/statediff/builder"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
"testing"
"math/big"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/common"
b "github.com/ethereum/go-ethereum/statediff/builder"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/consensus/ethash"
"bytes"
"reflect"
)
var (
@ -116,8 +98,7 @@ func testChainGen(i int, block *core.BlockGen) {
}
}
var _ = ginkgo.Describe("", func() {
var (
var (
block0Hash, block1Hash, block2Hash, block3Hash common.Hash
block0, block1, block2, block3 *types.Block
builder b.Builder
@ -125,9 +106,9 @@ var _ = ginkgo.Describe("", func() {
burnAddress = common.HexToAddress("0x0")
diff *b.StateDiff
err error
)
)
ginkgo.BeforeEach(func() {
func TestBuilder(t *testing.T) {
_, blocks := makeChain(3, 0, genesis)
block0Hash = common.HexToHash("0xd1721cfd0b29c36fd7a68f25c128e86413fb666a6e1d68e89b875bd299262661")
block1Hash = common.HexToHash("0x47c398dd688eaa4dd11b006888156783fe32df83d59b197c0fcd303408103d39")
@ -139,9 +120,16 @@ var _ = ginkgo.Describe("", func() {
block2 = blocks[block2Hash]
block3 = blocks[block3Hash]
builder = b.NewBuilder(testdb)
})
ginkgo.It("returns empty account diff collections when the state root hasn't changed", func() {
testEmptyDiff(t)
testBlock1(t)
testBlock2(t)
testBlock3(t)
}
var errorString = "Actual and expected do not match"
func testEmptyDiff(t *testing.T) {
expectedDiff := b.StateDiff{
BlockNumber: block0.Number().Int64(),
BlockHash: block0Hash,
@ -151,41 +139,41 @@ var _ = ginkgo.Describe("", func() {
}
diff, err := builder.BuildStateDiff(block0.Root(), block0.Root(), block0.Number().Int64(), block0Hash)
if err != nil {
t.Error(err)
}
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(diff).To(gomega.Equal(&expectedDiff))
})
if !equals(diff, &expectedDiff) {
t.Errorf("Actual and expected do not match:\nActual: %+v\nExpected: %+v", diff, expectedDiff)
}
}
ginkgo.Context("Block 1", func() {
func testBlock1(t *testing.T) {
//10000 transferred from testBankAddress to account1Addr
var balanceChange = int64(10000)
ginkgo.BeforeEach(func() {
diff, err = builder.BuildStateDiff(block0.Root(), block1.Root(), block1.Number().Int64(), block1Hash)
if err != nil {
t.Error(err)
}
gomega.Expect(err).NotTo(gomega.HaveOccurred())
})
//it includes the block number and hash
if !equals(diff.BlockNumber, block1.Number().Int64()) { t.Errorf(errorString) }
if !equals(diff.BlockHash, block1Hash) { t.Errorf(errorString)}
ginkgo.It("includes the block number and hash", func() {
gomega.Expect(diff.BlockNumber).To(gomega.Equal(block1.Number().Int64()))
gomega.Expect(diff.BlockHash).To(gomega.Equal(block1Hash))
})
//it returns an empty collection for deleted accounts
if !equals(diff.DeletedAccounts, emptyAccountDiffEventualMap) { t.Errorf(errorString)}
ginkgo.It("returns an empty collection for deleted accounts", func() {
gomega.Expect(diff.DeletedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
})
ginkgo.It("returns balance diffs for updated accounts", func() {
//it returns balance diffs for updated accounts
expectedBankBalanceDiff := b.DiffBigInt{
NewValue: big.NewInt(testBankFunds.Int64() - balanceChange),
OldValue: testBankFunds,
}
gomega.Expect(len(diff.UpdatedAccounts)).To(gomega.Equal(1))
gomega.Expect(diff.UpdatedAccounts[testBankAddress].Balance).To(gomega.Equal(expectedBankBalanceDiff))
})
if !equals(len(diff.UpdatedAccounts), 1) { t.Errorf(errorString) }
if !equals(diff.UpdatedAccounts[testBankAddress].Balance, expectedBankBalanceDiff) { t.Errorf(errorString) }
ginkgo.It("returns balance diffs for new accounts", func() {
//it returns balance diffs for new accounts
expectedAccount1BalanceDiff := b.DiffBigInt{
NewValue: big.NewInt(balanceChange),
OldValue: nil,
@ -196,13 +184,12 @@ var _ = ginkgo.Describe("", func() {
OldValue: nil,
}
gomega.Expect(len(diff.CreatedAccounts)).To(gomega.Equal(2))
gomega.Expect(diff.CreatedAccounts[account1Addr].Balance).To(gomega.Equal(expectedAccount1BalanceDiff))
gomega.Expect(diff.CreatedAccounts[burnAddress].Balance).To(gomega.Equal(expectedBurnAddrBalanceDiff))
})
})
if !equals(len(diff.CreatedAccounts), 2) {}
if !equals(diff.CreatedAccounts[account1Addr].Balance, expectedAccount1BalanceDiff) { t.Errorf(errorString) }
if !equals(diff.CreatedAccounts[burnAddress].Balance, expectedBurnAddrBalanceDiff) { t.Errorf(errorString) }
}
ginkgo.Context("Block 2", func() {
func testBlock2(t *testing.T) {
//1000 transferred from testBankAddress to account1Addr
//1000 transferred from account1Addr to account2Addr
var (
@ -211,22 +198,18 @@ var _ = ginkgo.Describe("", func() {
block1Account1Balance = int64(10000)
)
ginkgo.BeforeEach(func() {
diff, err = builder.BuildStateDiff(block1.Root(), block2.Root(), block2.Number().Int64(), block2Hash)
if err != nil {
t.Error(err)
}
//it includes the block number and hash
if !equals(diff.BlockNumber, block2.Number().Int64()) { t.Errorf(errorString)}
if !equals(diff.BlockHash, block2Hash) {}
gomega.Expect(err).NotTo(gomega.HaveOccurred())
})
//it returns an empty collection for deleted accounts
if !equals(diff.DeletedAccounts, emptyAccountDiffEventualMap) { t.Errorf(errorString) }
ginkgo.It("includes the block number and hash", func() {
gomega.Expect(diff.BlockNumber).To(gomega.Equal(block2.Number().Int64()))
gomega.Expect(diff.BlockHash).To(gomega.Equal(block2Hash))
})
ginkgo.It("returns an empty collection for deleted accounts", func() {
gomega.Expect(diff.DeletedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
})
ginkgo.It("returns balance diffs for updated accounts", func() {
//it returns balance diffs for updated accounts
expectedBankBalanceDiff := b.DiffBigInt{
NewValue: big.NewInt(block1BankBalance - balanceChange),
OldValue: big.NewInt(block1BankBalance),
@ -242,13 +225,12 @@ var _ = ginkgo.Describe("", func() {
OldValue: big.NewInt(miningReward),
}
gomega.Expect(len(diff.UpdatedAccounts)).To(gomega.Equal(3))
gomega.Expect(diff.UpdatedAccounts[testBankAddress].Balance).To(gomega.Equal(expectedBankBalanceDiff))
gomega.Expect(diff.UpdatedAccounts[account1Addr].Balance).To(gomega.Equal(expectedAccount1BalanceDiff))
gomega.Expect(diff.UpdatedAccounts[burnAddress].Balance).To(gomega.Equal(expectedBurnBalanceDiff))
})
if !equals(len(diff.UpdatedAccounts), 3) { t.Errorf(errorString) }
if !equals(diff.UpdatedAccounts[testBankAddress].Balance, expectedBankBalanceDiff) { t.Errorf(errorString) }
if !equals(diff.UpdatedAccounts[account1Addr].Balance, expectedAccount1BalanceDiff) { t.Errorf(errorString) }
if !equals(diff.UpdatedAccounts[burnAddress].Balance, expectedBurnBalanceDiff) { t.Errorf(errorString) }
ginkgo.It("returns balance diffs for new accounts", func() {
//it returns balance diffs for new accounts
expectedAccount2BalanceDiff := b.DiffBigInt{
NewValue: big.NewInt(balanceChange),
OldValue: nil,
@ -259,35 +241,28 @@ var _ = ginkgo.Describe("", func() {
OldValue: nil,
}
gomega.Expect(len(diff.CreatedAccounts)).To(gomega.Equal(2))
gomega.Expect(diff.CreatedAccounts[account2Addr].Balance).To(gomega.Equal(expectedAccount2BalanceDiff))
gomega.Expect(diff.CreatedAccounts[contractAddr].Balance).To(gomega.Equal(expectedContractBalanceDiff))
})
})
if !equals(len(diff.CreatedAccounts), 2) { t.Errorf(errorString) }
if !equals(diff.CreatedAccounts[account2Addr].Balance, expectedAccount2BalanceDiff) { t.Errorf(errorString) }
if !equals(diff.CreatedAccounts[contractAddr].Balance, expectedContractBalanceDiff) { t.Errorf(errorString) }
}
ginkgo.Context("Block 3", func() {
//the contract's storage is changed
//and the block is mined by account 2
ginkgo.BeforeEach(func() {
func testBlock3(t *testing.T) {
diff, err = builder.BuildStateDiff(block2.Root(), block3.Root(), block3.Number().Int64(), block3Hash)
if err != nil {
t.Error(err)
}
gomega.Expect(err).NotTo(gomega.HaveOccurred())
})
//it includes the block number and hash
if !equals(diff.BlockNumber, block3.Number().Int64()) { t.Errorf(errorString) }
if !equals(diff.BlockHash, block3Hash) { t.Errorf(errorString) }
ginkgo.It("includes the block number and hash", func() {
gomega.Expect(diff.BlockNumber).To(gomega.Equal(block3.Number().Int64()))
gomega.Expect(diff.BlockHash).To(gomega.Equal(block3Hash))
})
//it returns an empty collection for deleted accounts
if !equals(diff.DeletedAccounts, emptyAccountDiffEventualMap) { t.Errorf(errorString) }
ginkgo.It("returns an empty collection for deleted accounts", func() {
gomega.Expect(diff.DeletedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
})
//it returns an empty collection for created accounts
if !equals(diff.CreatedAccounts, emptyAccountDiffEventualMap) { t.Errorf(errorString) }
ginkgo.It("returns an empty collection for created accounts", func() {
gomega.Expect(diff.CreatedAccounts).To(gomega.Equal(emptyAccountDiffEventualMap))
})
ginkgo.It("returns balance, storage and nonce diffs for updated accounts", func() {
//it returns balance, storage and nonce diffs for updated accounts
block2Account2Balance := int64(1000)
expectedAcct2BalanceDiff := b.DiffBigInt{
NewValue: big.NewInt(block2Account2Balance + miningReward),
@ -310,10 +285,18 @@ var _ = ginkgo.Describe("", func() {
OldValue: &oldNonce,
}
gomega.Expect(len(diff.UpdatedAccounts)).To(gomega.Equal(3))
gomega.Expect(diff.UpdatedAccounts[account2Addr].Balance).To(gomega.Equal(expectedAcct2BalanceDiff))
gomega.Expect(diff.UpdatedAccounts[contractAddr].Storage).To(gomega.Equal(expectedContractStorageDiff))
gomega.Expect(diff.UpdatedAccounts[testBankAddress].Nonce).To(gomega.Equal(expectedBankNonceDiff))
})
})
})
if !equals(len(diff.UpdatedAccounts), 3) { t.Errorf(errorString) }
if !equals(diff.UpdatedAccounts[account2Addr].Balance, expectedAcct2BalanceDiff) { t.Errorf(errorString) }
if !equals(diff.UpdatedAccounts[contractAddr].Storage, expectedContractStorageDiff) { t.Errorf(errorString) }
if !equals(diff.UpdatedAccounts[testBankAddress].Nonce, expectedBankNonceDiff) { t.Errorf(errorString) }
}
func equals(actual, expected interface{}) (success bool) {
if actualByteSlice, ok := actual.([]byte); ok {
if expectedByteSlice, ok := expected.([]byte); ok {
return bytes.Equal(actualByteSlice, expectedByteSlice)
}
}
return reflect.DeepEqual(actual, expected)
}

View File

@ -1,13 +0,0 @@
package extractor_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestExtractor(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Extractor Suite")
}

View File

@ -1,49 +1,32 @@
// Copyright 2015 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains a batch of utility type declarations used by the tests. As the node
// operates on unique types, a lot of them are needed to check various features.
package extractor_test
import (
"github.com/ethereum/go-ethereum/core/types"
b "github.com/ethereum/go-ethereum/statediff/builder"
e "github.com/ethereum/go-ethereum/statediff/extractor"
"testing"
"github.com/ethereum/go-ethereum/statediff/testhelpers"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
"math/big"
"math/rand"
b "github.com/ethereum/go-ethereum/statediff/builder"
e "github.com/ethereum/go-ethereum/statediff/extractor"
"github.com/ethereum/go-ethereum/core/types"
"bytes"
"reflect"
)
var _ = ginkgo.Describe("Extractor", func() {
var publisher testhelpers.MockPublisher
var builder testhelpers.MockBuilder
var currentBlockNumber *big.Int
var parentBlock, currentBlock *types.Block
var expectedStateDiff b.StateDiff
var extractor e.Extractor
var err error
var publisher testhelpers.MockPublisher
var builder testhelpers.MockBuilder
var currentBlockNumber *big.Int
var parentBlock, currentBlock *types.Block
var expectedStateDiff b.StateDiff
var extractor e.Extractor
var err error
ginkgo.BeforeEach(func() {
func TestExtractor(t *testing.T) {
publisher = testhelpers.MockPublisher{}
builder = testhelpers.MockBuilder{}
extractor, err = e.NewExtractor(&builder, &publisher)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
blockNumber := rand.Int63()
parentBlockNumber := big.NewInt(blockNumber - int64(1))
@ -58,44 +41,68 @@ var _ = ginkgo.Describe("Extractor", func() {
DeletedAccounts: nil,
UpdatedAccounts: nil,
}
})
ginkgo.It("builds a state diff struct", func() {
testBuildStateDiffStruct(t)
testBuildStateDiffErrorHandling(t)
testPublishingStateDiff(t)
testPublisherErrorHandling(t)
}
func testBuildStateDiffStruct(t *testing.T) {
builder.SetStateDiffToBuild(&expectedStateDiff)
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
if err != nil {
t.Error(err)
}
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(builder.OldStateRoot).To(gomega.Equal(parentBlock.Root()))
gomega.Expect(builder.NewStateRoot).To(gomega.Equal(currentBlock.Root()))
gomega.Expect(builder.BlockNumber).To(gomega.Equal(currentBlockNumber.Int64()))
gomega.Expect(builder.BlockHash).To(gomega.Equal(currentBlock.Hash()))
})
if !equals(builder.OldStateRoot, parentBlock.Root()) { t.Error()}
if !equals(builder.NewStateRoot, currentBlock.Root()) { t.Error()}
if !equals(builder.BlockNumber,currentBlockNumber.Int64()) { t.Error()}
if !equals(builder.BlockHash, currentBlock.Hash()) { t.Error()}
}
ginkgo.It("returns an error if building the state diff fails", func() {
func testBuildStateDiffErrorHandling(t *testing.T) {
builder.SetBuilderError(testhelpers.MockError)
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
if err == nil {
t.Error(err)
}
gomega.Expect(err).To(gomega.HaveOccurred())
gomega.Expect(err).To(gomega.MatchError(testhelpers.MockError))
})
if !equals(err, testhelpers.MockError) { t.Error() }
builder.SetBuilderError(nil)
}
ginkgo.It("publishes the state diff struct", func() {
func testPublishingStateDiff(t *testing.T) {
builder.SetStateDiffToBuild(&expectedStateDiff)
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
if err != nil {
t.Error(err)
}
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(publisher.StateDiff).To(gomega.Equal(&expectedStateDiff))
})
if !equals(publisher.StateDiff, &expectedStateDiff) { t.Error() }
}
ginkgo.It("returns an error if publishing the diff fails", func() {
func testPublisherErrorHandling(t *testing.T) {
publisher.SetPublisherError(testhelpers.MockError)
_, err = extractor.ExtractStateDiff(*parentBlock, *currentBlock)
if err == nil {
t.Error("Expected an error, but it didn't occur.")
}
if !equals(err, testhelpers.MockError) { t.Error() }
gomega.Expect(err).To(gomega.HaveOccurred())
gomega.Expect(err).To(gomega.MatchError(testhelpers.MockError))
})
})
publisher.SetPublisherError(nil)
}
func equals(actual, expected interface{}) (success bool) {
if actualByteSlice, ok := actual.([]byte); ok {
if expectedByteSlice, ok := expected.([]byte); ok {
return bytes.Equal(actualByteSlice, expectedByteSlice)
}
}
return reflect.DeepEqual(actual, expected)
}

View File

@ -1,13 +0,0 @@
package publisher_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
func TestPublisher(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Publisher Suite")
}

View File

@ -1,29 +1,30 @@
package publisher_test
import (
"encoding/csv"
"github.com/ethereum/go-ethereum/statediff"
"github.com/ethereum/go-ethereum/statediff/builder"
p "github.com/ethereum/go-ethereum/statediff/publisher"
"github.com/ethereum/go-ethereum/statediff/testhelpers"
"github.com/onsi/ginkgo"
"github.com/onsi/gomega"
"io/ioutil"
"testing"
"os"
"path/filepath"
"strconv"
"github.com/ethereum/go-ethereum/statediff/testhelpers"
p "github.com/ethereum/go-ethereum/statediff/publisher"
"io/ioutil"
"github.com/ethereum/go-ethereum/statediff"
"encoding/csv"
"path/filepath"
"bytes"
"reflect"
"github.com/ethereum/go-ethereum/statediff/builder"
"github.com/pkg/errors"
)
var _ = ginkgo.Describe("Publisher", func() {
var (
var (
tempDir = os.TempDir()
testFilePrefix = "test-statediff"
publisher p.Publisher
dir string
err error
)
)
var expectedCreatedAccountRow = []string{
var expectedCreatedAccountRow = []string{
strconv.FormatInt(testhelpers.BlockNumber, 10),
testhelpers.BlockHash,
"created",
@ -36,9 +37,9 @@ var _ = ginkgo.Describe("Publisher", func() {
testhelpers.ContractRoot,
testhelpers.ContractRoot,
testhelpers.StoragePath,
}
}
var expectedUpdatedAccountRow = []string{
var expectedUpdatedAccountRow = []string{
strconv.FormatInt(testhelpers.BlockNumber, 10),
testhelpers.BlockHash,
"updated",
@ -51,9 +52,9 @@ var _ = ginkgo.Describe("Publisher", func() {
testhelpers.ContractRoot,
testhelpers.ContractRoot,
testhelpers.StoragePath,
}
}
var expectedDeletedAccountRow = []string{
var expectedDeletedAccountRow = []string{
strconv.FormatInt(testhelpers.BlockNumber, 10),
testhelpers.BlockHash,
"deleted",
@ -66,104 +67,171 @@ var _ = ginkgo.Describe("Publisher", func() {
testhelpers.ContractRoot,
testhelpers.ContractRoot,
testhelpers.StoragePath,
}
}
ginkgo.BeforeEach(func() {
func TestPublisher(t *testing.T) {
dir, err = ioutil.TempDir(tempDir, testFilePrefix)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
config := statediff.Config{
Path: dir,
Mode: statediff.CSV,
}
publisher, err = p.NewPublisher(config)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
})
if err != nil {
t.Error(err)
}
ginkgo.AfterEach(func() {
os.RemoveAll(dir)
})
type Test func(t *testing.T)
ginkgo.It("persists the column headers to a CSV file", func() {
var tests = []Test{testColumnHeaders,
testAccountDiffs,
testWhenNoDiff,
testDefaultPublisher,
testDefaultDirectory,
}
for _, test := range tests {
test(t)
removeFilesFromDir(dir, t)
}
}
func removeFilesFromDir(dir string, t *testing.T) {
files, err := filepath.Glob(filepath.Join(dir, "*"))
if err != nil {
t.Error()
}
for _, file := range files {
err = os.RemoveAll(file)
if err !=nil {
t.Error()
}
}
}
func testColumnHeaders(t *testing.T) {
_, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
file, err := getTestDiffFile(dir)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
lines, err := csv.NewReader(file).ReadAll()
gomega.Expect(len(lines) > 1).To(gomega.BeTrue())
gomega.Expect(lines[0]).To(gomega.Equal(p.Headers))
})
if err != nil {
t.Error(err)
}
if len(lines) <= 1 { t.Error() }
ginkgo.It("persists the created, upated and deleted account diffs to a CSV file", func() {
if !equals(lines[0], p.Headers) { t.Error() }
}
func testAccountDiffs(t *testing.T) {
// it persists the created, updated and deleted account diffs to a CSV file
_, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
file, err := getTestDiffFile(dir)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
lines, err := csv.NewReader(file).ReadAll()
gomega.Expect(len(lines) > 3).To(gomega.BeTrue())
gomega.Expect(lines[1]).To(gomega.Equal(expectedCreatedAccountRow))
gomega.Expect(lines[2]).To(gomega.Equal(expectedUpdatedAccountRow))
gomega.Expect(lines[3]).To(gomega.Equal(expectedDeletedAccountRow))
})
if err != nil {
t.Error(err)
}
if len(lines) <= 3 { t.Error() }
if !equals(lines[1], expectedCreatedAccountRow) { t.Error() }
if !equals(lines[2], expectedUpdatedAccountRow) { t.Error()}
if !equals(lines[3], expectedDeletedAccountRow) { t.Error()}
}
ginkgo.It("creates an empty CSV when there is no diff", func() {
func testWhenNoDiff(t *testing.T) {
//it creates an empty CSV when there is no diff", func() {
emptyDiff := builder.StateDiff{}
_, err = publisher.PublishStateDiff(&emptyDiff)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
file, err := getTestDiffFile(dir)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil {
t.Error(err)
}
lines, err := csv.NewReader(file).ReadAll()
gomega.Expect(len(lines)).To(gomega.Equal(1))
})
if err != nil {
t.Error(err)
}
if !equals(len(lines), 1) { t.Error() }
}
ginkgo.It("defaults to publishing state diffs to a CSV file when no mode is configured", func() {
func testDefaultPublisher(t *testing.T) {
//it defaults to publishing state diffs to a CSV file when no mode is configured
config := statediff.Config{Path: dir}
publisher, err = p.NewPublisher(config)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil { t.Error(err) }
_, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil { t.Error(err) }
file, err := getTestDiffFile(dir)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil { t.Error(err) }
lines, err := csv.NewReader(file).ReadAll()
gomega.Expect(len(lines)).To(gomega.Equal(4))
gomega.Expect(lines[0]).To(gomega.Equal(p.Headers))
})
if err != nil { t.Error(err) }
if !equals(len(lines), 4) { t.Error()}
if !equals(lines[0],p.Headers) { t.Error()}
}
ginkgo.It("defaults to publishing CSV files in the current directory when no path is configured", func() {
func testDefaultDirectory(t *testing.T) {
//it defaults to publishing CSV files in the current directory when no path is configured
config := statediff.Config{}
publisher, err = p.NewPublisher(config)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil { t.Error(err) }
err := os.Chdir(dir)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil { t.Error(err) }
_, err = publisher.PublishStateDiff(&testhelpers.TestStateDiff)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil { t.Error(err) }
file, err := getTestDiffFile(dir)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
if err != nil { t.Error(err) }
lines, err := csv.NewReader(file).ReadAll()
gomega.Expect(len(lines)).To(gomega.Equal(4))
gomega.Expect(lines[0]).To(gomega.Equal(p.Headers))
})
})
if err != nil { t.Error(err) }
if !equals(len(lines), 4) { t.Error() }
if !equals(lines[0], p.Headers) { t.Error() }
}
func getTestDiffFile(dir string) (*os.File, error) {
files, err := ioutil.ReadDir(dir)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Expect(len(files) > 0).To(gomega.BeTrue())
if err != nil { return nil, err }
if len(files) == 0 { return nil, errors.New("There are 0 files.") }
fileName := files[0].Name()
filePath := filepath.Join(dir, fileName)
return os.Open(filePath)
}
func equals(actual, expected interface{}) (success bool) {
if actualByteSlice, ok := actual.([]byte); ok {
if expectedByteSlice, ok := expected.([]byte); ok {
return bytes.Equal(actualByteSlice, expectedByteSlice)
}
}
return reflect.DeepEqual(actual, expected)
}

View File

@ -11,3 +11,6 @@ func TestStatediff(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Statediff Suite")
}
//convert this over to use built in golang library
//only save the new value, and have a pointer to the old value - not sure how this pointer will work for the CSV version