Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests for RandomBeaconHistory contract #389

Merged
merged 6 commits into from
Nov 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
name: CI

on: [push]
on:
- push
- pull_request

jobs:
build:
Expand All @@ -16,7 +18,7 @@ jobs:
cache: 'npm'
cache-dependency-path: lib/js/test/package-lock.json
- name: Install Flow CLI
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/50058df8aa5f999a85cfd7afae6fc2661090078a/install.sh)" -- v0.41.2
run: sh -ci "$(curl -fsSL https://raw.githubusercontent.com/onflow/flow-cli/master/install.sh)" -- v1.5.0
- name: Flow cli Version
run: flow version
- name: Update PATH
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ test:
$(MAKE) generate -C lib/go
$(MAKE) test -C lib/go
$(MAKE) test -C lib/js/test
flow test --cover --covercode="contracts" tests/test_*.cdc

.PHONY: ci
ci:
$(MAKE) ci -C lib/go
$(MAKE) ci -C lib/js/test
flow test --cover --covercode="contracts" tests/test_*.cdc
132 changes: 104 additions & 28 deletions flow.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,106 @@
{
"contracts": {
"FlowClusterQC": "./contracts/epochs/FlowClusterQC.cdc",
"FlowContractAudits": "./contracts/FlowContractAudits.cdc",
"FlowDKG": "./contracts/epochs/FlowEpoch.cdc",
"FlowEpoch": "./contracts/epochs/FlowEpoch.cdc",
"FlowFees": "./contracts/FlowFees.cdc",
"FlowIDTableStaking": "./contracts/FlowIDTableStaking.cdc",
"FlowIDTableStaking_old": "./contracts/FlowIDTableStaking_old.cdc",
"FlowServiceAccount": "./contracts/FlowServiceAccount.cdc",
"FlowStakingCollection": "./contracts/FlowStakingCollection.cdc",
"FlowStorageFees": "./contracts/FlowStorageFees.cdc",
"FlowToken": "./contracts/FlowToken.cdc",
"LockedTokens": "./contracts/LockedTokens.cdc",
"NodeVersionBeacon": "./contracts/NodeVersionBeacon.cdc",
"RandomBeaconHistory": "./contracts/RandomBeaconHistory.cdc",
"StakingProxy": "./contracts/StakingProxy.cdc"
},
"networks": {
"emulator": "127.0.0.1:3569",
"mainnet": "access.mainnet.nodes.onflow.org:9000",
"testnet": "access.devnet.nodes.onflow.org:9000"
},
"accounts": {
"emulator-account": {
"address": "f8d6e0586b0a20c7",
"key": "7677f7c9410f8773b482737c778b5d7c6acfdbbae718d61e4727a07667f66004"
}
}
"contracts": {
"FlowClusterQC": {
"source": "./contracts/epochs/FlowClusterQC.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowContractAudits": {
"source": "./contracts/FlowContractAudits.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowDKG": {
"source": "./contracts/epochs/FlowEpoch.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowEpoch": {
"source": "./contracts/epochs/FlowEpoch.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowFees": {
"source": "./contracts/FlowFees.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowIDTableStaking": {
"source": "./contracts/FlowIDTableStaking.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowIDTableStaking_old": {
"source": "./contracts/FlowIDTableStaking_old.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowServiceAccount": {
"source": "./contracts/FlowServiceAccount.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowStakingCollection": {
"source": "./contracts/FlowStakingCollection.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowStorageFees": {
"source": "./contracts/FlowStorageFees.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"FlowToken": {
"source": "./contracts/FlowToken.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"LockedTokens": {
"source": "./contracts/LockedTokens.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"NodeVersionBeacon": {
"source": "./contracts/NodeVersionBeacon.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"RandomBeaconHistory": {
"source": "./contracts/RandomBeaconHistory.cdc",
"aliases": {
"testing": "0x0000000000000007"
}
},
"StakingProxy": {
"source": "./contracts/StakingProxy.cdc",
"aliases": {
"testing": "0x0000000000000007"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do they all have the same testing alias?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The testing alias is basically the account where the current contract will be deployed for usage in the Cadence testing framework. For the sake of completion & consistency, I had added it to all contracts in the flow.json config file. It does not need to be the same though. There are 10 predefined accounts that can be used as the testing alias.

}
}
},
"networks": {
"emulator": "127.0.0.1:3569",
"testing": "127.0.0.1:3569",
"mainnet": "access.mainnet.nodes.onflow.org:9000",
"testnet": "access.devnet.nodes.onflow.org:9000"
},
"accounts": {
"emulator-account": {
"address": "f8d6e0586b0a20c7",
"key": "7677f7c9410f8773b482737c778b5d7c6acfdbbae718d61e4727a07667f66004"
}
}
}
6 changes: 3 additions & 3 deletions lib/go/templates/internal/assets/assets.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions tests/scripts/get_lowest_height.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import "RandomBeaconHistory"

access(all) fun main(): UInt64 {
return RandomBeaconHistory.getLowestHeight()
}
160 changes: 160 additions & 0 deletions tests/test_random_beacon_history.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import Test
import BlockchainHelpers
import "RandomBeaconHistory"

access(all) let admin = Test.getAccount(0x0000000000000007)

access(all)
fun setup() {
let err = Test.deployContract(
name: "RandomBeaconHistory",
path: "../contracts/RandomBeaconHistory.cdc",
arguments: []
)
Test.expect(err, Test.beNil())
}

access(all)
fun testGetLowestHeightWhenNotInitialized() {
let scriptResult = executeScript(
"scripts/get_lowest_height.cdc",
[]
)
Test.expect(scriptResult, Test.beFailed())
Test.assertError(
scriptResult,
errorMessage: "History has not yet been initialized"
)
}

access(all)
fun testGetRandomSourceHistoryPageWithoutLowestHeightSet() {
let page: UInt64 = 1
let perPage: UInt64 = 10
let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_source_of_randomness_page.cdc",
[page, perPage]
)
Test.expect(scriptResult, Test.beFailed())
Test.assertError(
scriptResult,
errorMessage: "History has not yet been initialized",
)
}

access(all)
fun testGetSourceOfRandomnessWithoutLowestHeightSet() {
let atBlockHeight: UInt64 = 101
let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_source_of_randomness.cdc",
[atBlockHeight]
)
Test.expect(scriptResult, Test.beFailed())
Test.assertError(
scriptResult,
errorMessage: "History has not yet been initialized"
)
}

access(all)
fun testRecordRandomSource() {
let randomSource: [UInt8] = [0, 1, 1, 2, 3, 5, 8]
let txResult = executeTransaction(
"transactions/record_random_source.cdc",
[randomSource],
admin
)
Test.expect(txResult, Test.beSucceeded())

let page: UInt64 = 0
let perPage: UInt64 = 10
let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_source_of_randomness_page.cdc",
[page, perPage]
)
Test.expect(scriptResult, Test.beSucceeded())

let history = (scriptResult.returnValue as! RandomBeaconHistory.RandomSourceHistoryPage?)!
Test.assertEqual(randomSource, history.values[0]!.value)
}

access(all)
fun testGetSourceOfRandomnessForMissingBlockHeight() {
let atBlockHeight: UInt64 = 101
let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_source_of_randomness.cdc",
[atBlockHeight]
)
Test.expect(scriptResult, Test.beFailed())
Test.assertError(
scriptResult,
errorMessage: "Source of randomness not yet recorded"
)
}

access(all)
fun testGetSourceOfRandomnessPrecedingRecordedHistory() {
let lowestHeight = (executeScript(
"scripts/get_lowest_height.cdc",
[]
).returnValue as! UInt64?)!
let atBlockHeight: UInt64 = lowestHeight - 2

let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_source_of_randomness.cdc",
[atBlockHeight]
)
Test.expect(scriptResult, Test.beFailed())
Test.assertError(
scriptResult,
errorMessage: "Requested block height precedes recorded history"
)
}

access(all)
fun testGetSourceOfRandomnessFromPreviousBlockHeight() {
// Commit current block and advance to the next one
Test.commitBlock()

let atBlockHeight: UInt64 = getCurrentBlockHeight() - 1
let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_source_of_randomness.cdc",
[atBlockHeight]
)
Test.expect(scriptResult, Test.beSucceeded())

let randomSource = scriptResult.returnValue! as! RandomBeaconHistory.RandomSource
let value: [UInt8] = [0, 1, 1, 2, 3, 5, 8]
Test.assertEqual(atBlockHeight, randomSource.blockHeight)
Test.assertEqual(value, randomSource.value)
}

access(all)
fun testGetLatestSourceOfRandomness() {
let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_latest_source_of_randomness.cdc",
[]
)
Test.expect(scriptResult, Test.beSucceeded())

let randomSource = scriptResult.returnValue! as! RandomBeaconHistory.RandomSource
let atBlockHeight: UInt64 = getCurrentBlockHeight() - 1
let value: [UInt8] = [0, 1, 1, 2, 3, 5, 8]
Test.assertEqual(atBlockHeight, randomSource.blockHeight)
Test.assertEqual(value, randomSource.value)
}

access(all)
fun testGetRandomSourceHistoryPageExceedingLastPage() {
// There is only 1 page currently
let page: UInt64 = 11
let perPage: UInt64 = 10
let scriptResult = executeScript(
"../transactions/randomBeaconHistory/scripts/get_source_of_randomness_page.cdc",
[page, perPage]
)
Test.expect(scriptResult, Test.beSucceeded())

let history = (scriptResult.returnValue as! RandomBeaconHistory.RandomSourceHistoryPage?)!
Test.expect(history.values, Test.beEmpty())
}
11 changes: 11 additions & 0 deletions tests/transactions/record_random_source.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import "RandomBeaconHistory"

transaction(randomSource: [UInt8]) {
prepare(acct: AuthAccount) {
let heartbeat = acct.borrow<&RandomBeaconHistory.Heartbeat>(
from: RandomBeaconHistory.HeartbeatStoragePath
) ?? panic("Could not borrow heartbeat resource")

heartbeat.heartbeat(randomSourceHistory: randomSource)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import "RandomBeaconHistory"
/// Retrieves the source of randomness for the requested block height from the RandomBeaconHistory contract.
///
access(all) fun main(page: UInt64, perPage: UInt64): RandomBeaconHistory.RandomSourceHistoryPage {
return RandomBeaconHistory.getRandomSourceHistoryPage(page: page, perPage: perPage)
return RandomBeaconHistory.getRandomSourceHistoryPage(page, perPage: perPage)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @janezpodhostnik There was an error while testing this script, not how much important it is though.

Copy link
Collaborator

@janezpodhostnik janezpodhostnik Oct 30, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its not that important, as it is mostly as an example, but we should fix this. I'll open up a PR.

Thank you for finding this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fixed in this PR, so not sure there's a need to open up another PR.

}