// Copyright 2018 Vulcanize // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package every_block_test import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" "github.com/vulcanize/vulcanizedb/examples/erc20_watcher/every_block" "github.com/vulcanize/vulcanizedb/examples/test_helpers" "github.com/vulcanize/vulcanizedb/pkg/core" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres" "github.com/vulcanize/vulcanizedb/pkg/datastore/postgres/repositories" "github.com/vulcanize/vulcanizedb/test_config" "math/rand" ) var _ = Describe("ERC20 Token Repository", func() { var db *postgres.DB var blockId int64 var blockNumber int64 var repository every_block.ERC20TokenRepository var blockRepository repositories.BlockRepository testAddress := "abc" BeforeEach(func() { db = test_helpers.CreateNewDatabase() repository = every_block.ERC20TokenRepository{DB: db} _, err := db.Query(`DELETE FROM token_supply`) Expect(err).NotTo(HaveOccurred()) blockRepository = *repositories.NewBlockRepository(db) blockNumber = rand.Int63() blockId = test_helpers.CreateBlock(blockNumber, blockRepository) }) Describe("Create", func() { It("creates a token supply record", func() { supply := supplyModel(blockNumber, testAddress, "100") err := repository.CreateSupply(supply) Expect(err).NotTo(HaveOccurred()) dbResult := test_helpers.TokenSupplyDBRow{} expectedTokenSupply := test_helpers.TokenSupplyDBRow{ Supply: int64(100), BlockID: blockId, TokenAddress: testAddress, } var count int err = repository.DB.QueryRowx(`SELECT count(*) FROM token_supply`).Scan(&count) Expect(err).NotTo(HaveOccurred()) Expect(count).To(Equal(1)) err = repository.DB.QueryRowx(`SELECT * FROM token_supply`).StructScan(&dbResult) Expect(err).NotTo(HaveOccurred()) Expect(dbResult.Supply).To(Equal(expectedTokenSupply.Supply)) Expect(dbResult.BlockID).To(Equal(expectedTokenSupply.BlockID)) Expect(dbResult.TokenAddress).To(Equal(expectedTokenSupply.TokenAddress)) }) It("returns an error if fetching the block's id from the database fails", func() { errorSupply := supplyModel(-1, "", "") err := repository.CreateSupply(errorSupply) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("sql")) Expect(err.Error()).To(ContainSubstring("block number -1")) }) It("returns an error if inserting the token_supply fails", func() { errorSupply := supplyModel(blockNumber, "", "") err := repository.CreateSupply(errorSupply) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("pq")) Expect(err.Error()).To(ContainSubstring("token_supply for block number")) }) }) Describe("When there are multiple nodes", func() { var node2DB *postgres.DB var node2BlockRepo *repositories.BlockRepository var node2BlockId int64 var node2TokenSupplyRepo every_block.ERC20TokenRepository var tokenSupply every_block.TokenSupply BeforeEach(func() { node2DB = createDbForAnotherNode() //create another block with the same number on node2 node2BlockRepo = repositories.NewBlockRepository(node2DB) node2BlockId = test_helpers.CreateBlock(blockNumber, *node2BlockRepo) tokenSupply = supplyModel(blockNumber, "abc", "100") node2TokenSupplyRepo = every_block.ERC20TokenRepository{DB: node2DB} }) It("only creates token_supply records for the current node (node2)", func() { err := node2TokenSupplyRepo.CreateSupply(tokenSupply) Expect(err).NotTo(HaveOccurred()) var tokenSupplies []test_helpers.TokenSupplyDBRow err = node2TokenSupplyRepo.DB.Select(&tokenSupplies, `SELECT * FROM token_supply`) Expect(err).NotTo(HaveOccurred()) Expect(len(tokenSupplies)).To(Equal(1)) Expect(tokenSupplies[0].BlockID).To(Equal(node2BlockId)) }) It("only includes missing block numbers for the current node", func() { //create token_supply on original node err := repository.CreateSupply(tokenSupply) Expect(err).NotTo(HaveOccurred()) originalNodeMissingBlocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(err).NotTo(HaveOccurred()) Expect(len(originalNodeMissingBlocks)).To(Equal(0)) node2MissingBlocks, err := node2TokenSupplyRepo.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(err).NotTo(HaveOccurred()) Expect(len(node2MissingBlocks)).To(Equal(1)) }) }) Describe("MissingBlocks", func() { It("returns the block numbers for which an associated TokenSupply record hasn't been created", func() { createTokenSupplyFor(repository, blockNumber) newBlockNumber := blockNumber + 1 test_helpers.CreateBlock(newBlockNumber, blockRepository) blocks, err := repository.MissingSupplyBlocks(blockNumber, newBlockNumber, testAddress) Expect(blocks).To(ConsistOf(newBlockNumber)) Expect(err).NotTo(HaveOccurred()) }) It("only returns blocks within the given range", func() { newBlockNumber := blockNumber + 1 test_helpers.CreateBlock(newBlockNumber, blockRepository) blocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(blocks).NotTo(ConsistOf(newBlockNumber)) Expect(err).NotTo(HaveOccurred()) }) It("does not return numbers that already have an associated TokenSupply record", func() { createTokenSupplyFor(repository, blockNumber) blocks, err := repository.MissingSupplyBlocks(blockNumber, blockNumber, testAddress) Expect(blocks).To(BeEmpty()) Expect(err).NotTo(HaveOccurred()) }) }) It("deletes the token supply record when the associated block is deleted", func() { err := repository.CreateSupply(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) Expect(err).NotTo(HaveOccurred()) var count int err = repository.DB.QueryRowx(`SELECT count(*) FROM token_supply`).Scan(&count) Expect(err).NotTo(HaveOccurred()) Expect(count).To(Equal(1)) _, err = db.Query(`DELETE FROM blocks`) Expect(err).NotTo(HaveOccurred()) err = repository.DB.QueryRowx(`SELECT count(*) FROM token_supply`).Scan(&count) Expect(err).NotTo(HaveOccurred()) Expect(count).To(Equal(0)) }) }) func supplyModel(blockNumber int64, tokenAddress string, supplyValue string) every_block.TokenSupply { return every_block.TokenSupply{ Value: supplyValue, TokenAddress: tokenAddress, BlockNumber: int64(blockNumber), } } func createTokenSupplyFor(repository every_block.ERC20TokenRepository, blockNumber int64) { err := repository.CreateSupply(every_block.TokenSupply{BlockNumber: blockNumber, Value: "0"}) Expect(err).NotTo(HaveOccurred()) } func createDbForAnotherNode() *postgres.DB { anotherNode := core.Node{ GenesisBlock: "GENESIS", NetworkID: 1, ID: "testNodeId", ClientName: "Geth/v1.7.2-stable-1db4ecdc/darwin-amd64/go1.9", } return test_config.NewTestDBWithoutDeletingRecords(anotherNode) }