-
Notifications
You must be signed in to change notification settings - Fork 15
/
BlockDaoSpec.scala
153 lines (129 loc) · 6.42 KB
/
BlockDaoSpec.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The 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 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 library. If not, see <http://www.gnu.org/licenses/>.
package org.alephium.explorer.persistence.dao
import scala.concurrent.ExecutionContext
import scala.io.Source
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Gen
import org.scalatest.concurrent.{Eventually, ScalaFutures}
import org.scalatest.time.{Minutes, Span}
import org.alephium.api.{model, ApiModelCodec}
import org.alephium.explorer.{AlephiumSpec, Generators}
import org.alephium.explorer.api.model.{BlockEntry, Pagination}
import org.alephium.explorer.persistence.{DatabaseFixture, DBRunner}
import org.alephium.explorer.persistence.model._
import org.alephium.explorer.persistence.schema._
import org.alephium.explorer.service.BlockFlowClient
import org.alephium.explorer.util.TestUtils._
import org.alephium.json.Json._
import org.alephium.util.{Duration, TimeStamp}
class BlockDaoSpec extends AlephiumSpec with ScalaFutures with Generators with Eventually {
implicit val executionContext: ExecutionContext = ExecutionContext.global
override implicit val patienceConfig = PatienceConfig(timeout = Span(1, Minutes))
it should "updateMainChainStatus correctly" in new Fixture {
import config.profile.api._
forAll(Gen.oneOf(blockEntities), arbitrary[Boolean]) {
case (block, mainChainInput) =>
blockDao.insertSQL(block).futureValue
blockDao.updateMainChainStatus(block.hash, mainChainInput).futureValue
val fetchedBlock = blockDao.get(block.hash).futureValue.get
fetchedBlock.hash is block.hash
fetchedBlock.mainChain is mainChainInput
val inputQuery = inputsTable.filter(_.blockHash === block.hash).map(_.mainChain).result
val outputQuery = outputsTable.filter(_.blockHash === block.hash).map(_.mainChain).result
val inputs: Seq[Boolean] = run(inputQuery).futureValue
val outputs: Seq[Boolean] = run(outputQuery).futureValue
inputs.size is block.inputs.size
outputs.size is block.outputs.size
(inputs ++ outputs).foreach(isMainChain => isMainChain is mainChainInput)
}
}
it should "not insert a block twice" in new Fixture {
import config.profile.api._
forAll(Gen.oneOf(blockEntities)) { block =>
blockDao.insertSQL(block).futureValue
blockDao.insertSQL(block).futureValue
val blockheadersQuery = blockHeadersTable.filter(_.hash === block.hash).map(_.hash).result
val headerHash: Seq[BlockEntry.Hash] = run(blockheadersQuery).futureValue
headerHash.size is 1
headerHash.foreach(_.is(block.hash))
block.transactions.nonEmpty is true
val inputQuery = inputsTable.filter(_.blockHash === block.hash).result
val outputQuery = outputsTable.filter(_.blockHash === block.hash).result
val blockDepsQuery = blockDepsTable.filter(_.hash === block.hash).map(_.dep).result
val transactionsQuery = transactionsTable.filter(_.blockHash === block.hash).result
val queries = Seq(inputQuery, outputQuery, blockDepsQuery, transactionsQuery)
val dbInputs = Seq(block.inputs, block.outputs, block.deps, block.transactions)
def checkDuplicates[T](dbInput: Seq[T], dbOutput: Seq[T]) = {
dbOutput.size is dbInput.size
dbOutput.foreach(output => dbInput.contains(output) is true)
}
dbInputs
.zip(queries)
.foreach { case (dbInput, query) => checkDuplicates(dbInput, run(query).futureValue) }
}
}
it should "list block headers via SQL and typed should return same result" in new Fixture {
val blocksCount = 30 //number of blocks to create
forAll(Gen.listOfN(blocksCount, blockHeaderTransactionEntityGen),
Gen.choose(0, blocksCount),
Gen.choose(0, blocksCount),
arbitrary[Boolean]) {
case (blocks, pageNum, pageLimit, reverse) =>
import config.profile.api._
//clear test data
run(blockHeadersTable.delete).futureValue
run(transactionsTable.delete).futureValue
//create test data
run(blockHeadersTable ++= blocks.map(_._1)).futureValue
run(transactionsTable ++= blocks.flatten(_._2)).futureValue
//Assert results returned by typed and SQL query are the same
def runAssert(page: Pagination) = {
val sqlResult = blockDao.listMainChainSQL(page).futureValue
val typedResult = blockDao.listMainChain(page).futureValue
sqlResult is typedResult
}
runAssert(Pagination.unsafe(0, pageLimit, reverse)) //First page test
runAssert(Pagination.unsafe(pageNum, pageLimit, reverse)) //Random page test
runAssert(Pagination.unsafe(blocks.size, pageLimit, reverse)) //Last page test
}
}
it should "Recreate issue #162 - not throw exception when inserting a big block" in new Fixture {
using(Source.fromResource("big_block.json")) { source =>
val rawBlock = source.getLines().mkString
val blockEntry = read[model.BlockEntry](rawBlock)
val block = BlockFlowClient.blockProtocolToEntity(blockEntry)
blockDao.insertSQL(block).futureValue is ()
}
}
trait Fixture
extends InputSchema
with OutputSchema
with BlockHeaderSchema
with BlockDepsSchema
with TransactionSchema
with DatabaseFixture
with DBRunner
with ApiModelCodec {
override val config = databaseConfig
val blockflowFetchMaxAge: Duration = Duration.ofMinutesUnsafe(30)
val blockDao = BlockDao(groupNum, databaseConfig)
val blockflow: Seq[Seq[model.BlockEntry]] =
blockFlowGen(maxChainSize = 5, startTimestamp = TimeStamp.now()).sample.get
val blocksProtocol: Seq[model.BlockEntry] = blockflow.flatten
val blockEntities: Seq[BlockEntity] = blocksProtocol.map(BlockFlowClient.blockProtocolToEntity)
}
}