Skip to content
This repository has been archived by the owner on Sep 14, 2023. It is now read-only.

Implement integration test cases for rest of current contract entry points #7

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
7 changes: 7 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ on:
description: Run code coverage
required: false
default: true
smartContractVersion:
type: string
description: GitHub tag of the smart contract version to run the tests against
required: false
default: "main"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand Down Expand Up @@ -62,6 +67,8 @@ jobs:
java-version: '11'

- name: Build with Gradle
env:
SMART_CONTRACT_VERSION_TAG: ${{ github.event.inputs.smartContractVersion }}
run: ./gradlew clean build --refresh-dependencies -Pversion=$VERSION -x koverReport

- name: Generate code coverage reports
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tech.figure.validationoracle.client.client.base

import cosmos.tx.v1beta1.ServiceOuterClass.BroadcastTxResponse
import io.provenance.client.grpc.Signer
import tech.figure.validationoracle.client.domain.execute.ContractSettingsUpdate
import tech.figure.validationoracle.client.domain.execute.EntityCreationRequest
import tech.figure.validationoracle.client.domain.execute.EntityUpdateRequest
import tech.figure.validationoracle.client.domain.execute.ValidationDefinitionCreationRequest
Expand All @@ -20,7 +21,7 @@ import tech.figure.validationoracle.client.domain.execute.ValidationRequestUpdat
interface VOExecutor {
/**
* Executes the validation oracle smart contract to create a new validation definition to allow a new validation
* type to be used in the smart contract instance.
* type to be used.
*
* @param request The [ValidationDefinitionCreationRequest] payload that will be sent to the smart contract.
* @param signer Any implementation of [Signer] to sign the message programmatically.
Expand Down Expand Up @@ -125,7 +126,7 @@ interface VOExecutor {
): BroadcastTxResponse

/**
* Executes the validation oracle smart contract to updating an existing entity.
* Executes the validation oracle smart contract to update an existing entity.
*
* @param request The [EntityUpdateRequest] payload that will be sent to the smart contract.
* @param signer Any implementation of [Signer] to sign the message programmatically.
Expand All @@ -138,4 +139,19 @@ interface VOExecutor {
signer: Signer,
options: BroadcastOptions = BroadcastOptions(),
): BroadcastTxResponse

/**
* Executes the validation oracle smart contract to update its settings.
*
* @param request The [EntityUpdateRequest] payload that will be sent to the smart contract.
* @param signer Any implementation of [Signer] to sign the message programmatically.
* See [AccountSigner][tech.figure.validationoracle.util.wallet.AccountSigner] for a provided implementation.
* @param options Various options that alter how the transaction is broadcast.
* See [BroadcastOptions] for more details.
*/
fun updateContractSettings(
request: ContractSettingsUpdate,
signer: Signer,
options: BroadcastOptions = BroadcastOptions(),
): BroadcastTxResponse
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,8 @@ interface VOQuerier {
*/
fun queryValidationRequestsByValidator(validatorAddress: String): List<ValidationRequestOrder>

/**
* Retrieves information about an entity associated with the smart contract instance by the given address.
*/
fun queryEntityByAddress(address: String): EntityDetail?
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import io.provenance.client.protobuf.extensions.toTxBody
import tech.figure.validationoracle.client.client.base.BroadcastOptions
import tech.figure.validationoracle.client.client.base.VOExecutor
import tech.figure.validationoracle.client.client.base.VOQuerier
import tech.figure.validationoracle.client.domain.execute.ContractSettingsUpdate
import tech.figure.validationoracle.client.domain.execute.EntityCreationRequest
import tech.figure.validationoracle.client.domain.execute.EntityUpdateRequest
import tech.figure.validationoracle.client.domain.execute.ValidationDefinitionCreationRequest
Expand Down Expand Up @@ -82,6 +83,12 @@ class DefaultVOExecutor(
options: BroadcastOptions,
): BroadcastTxResponse = doExecute(generateMsg(request, signer.address()), signer, options)

override fun updateContractSettings(
request: ContractSettingsUpdate,
signer: Signer,
options: BroadcastOptions,
): BroadcastTxResponse = doExecute(generateMsg(request, signer.address()), signer, options)

/**
* Constructs a generic [MsgExecuteContract] from a provided [ContractExecuteInput] message,
* ensuring that the provided address is the signer.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package tech.figure.validationoracle.client.domain.execute

import com.fasterxml.jackson.databind.annotation.JsonSerialize
import tech.figure.validationoracle.client.domain.execute.base.ContractExecuteInput
import tech.figure.validationoracle.client.domain.serialization.ContractSettingsUpdateSerializer

@JsonSerialize(using = ContractSettingsUpdateSerializer::class)
data class ContractSettingsUpdate(
val newAdminAddress: String? = null,
) : ContractExecuteInput
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package tech.figure.validationoracle.client.domain.serialization

import com.fasterxml.jackson.core.JsonGenerator
import com.fasterxml.jackson.databind.JsonSerializer
import com.fasterxml.jackson.databind.SerializerProvider
import tech.figure.validationoracle.client.domain.execute.ContractSettingsUpdate

/**
* The JSON structure of a contract entry point is impossible to represent with vanilla Jackson annotation setups. This
* serializer enables the object to be serialized correctly with its multiple-nested nodes without enabling special
* ObjectMapper features, allowing the object to be more universally applicable to external ObjectMapper singleton
* instances.
*
* This serializer outputs the values in the following format:
* ```json
* {
* "update_settings": {
* "new_admin_address": ...
* }
* }
* ```
*/
class ContractSettingsUpdateSerializer : JsonSerializer<ContractSettingsUpdate>() {
override fun serialize(value: ContractSettingsUpdate, gen: JsonGenerator, provider: SerializerProvider?) {
SafeJsonGenerator(gen).apply {
jsonObject {
jsonObject("update_settings") {
jsonObject("update") {
gen.writeStringField("new_admin_address", value.newAdminAddress)
}
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package tech.figure.validationoracle.client.client

import io.grpc.StatusRuntimeException
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.Order
import io.kotest.core.spec.Spec
import io.kotest.inspectors.forAtLeastOne
import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import io.kotest.property.checkAll
import tech.figure.validationoracle.client.domain.execute.ContractSettingsUpdate
import tech.figure.validationoracle.client.test.IntegrationTestBase
import tech.figure.validationoracle.client.test.PrimitiveArbs.anyBech32TestnetAddress
import tech.figure.validationoracle.client.test.PrimitiveArbs.anyBlankString
import tech.figure.validationoracle.client.test.PrimitiveArbs.anyNonEmptyString

@Order(1)
Expand Down Expand Up @@ -48,14 +54,32 @@ class ContractMetadataIntegrationTest : IntegrationTestBase({
}
}
}
"Updating the contract's settings" xshould {
"fail when given an empty body" {
// TODO: Implement
}
"Updating the contract's settings" should {
"fail when given an invalid new admin address" {
// TODO: Implement
checkAll(
anyBlankString,
) { randomInvalidAddress ->
shouldThrow<StatusRuntimeException> {
voClient.updateContractSettings(
ContractSettingsUpdate(
newAdminAddress = randomInvalidAddress,
),
SpecTestContainer.Provenance.contractAdminAccount,
)
}.let { exception ->
listOf(
"invalid request: failed to execute message; message index: 0: " +
"Invalid request: new_admin_address was empty: execute wasm contract failed",
// TODO: In the contract code, see if error below can be captured into the error above
"invalid request: failed to execute message; message index: 0: Generic error: addr_validate " +
"errored: empty address string is not allowed: execute wasm contract failed",
).forAtLeastOne { errorMessage ->
exception.message shouldContain errorMessage
}
}
}
}
"succeed when given a valid request" {
"!succeed when given a valid request" {
// TODO: Implement
}
}
Expand All @@ -66,7 +90,6 @@ class ContractMetadataIntegrationTest : IntegrationTestBase({
* TODO: this function would ideally belong in the parent class and be invoked at "before all specs" instead
*/
override suspend fun beforeSpec(spec: Spec) {
instance.stop()
instance.withServices(
*specTestContainers.map { container -> container.serviceName }.toTypedArray(),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package tech.figure.validationoracle.client.client

import io.grpc.StatusRuntimeException
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.core.spec.Order
import io.kotest.matchers.string.shouldContain
import io.kotest.matchers.string.shouldContainIgnoringCase
import io.kotest.property.Arb
import io.kotest.property.arbitrary.boolean
import io.kotest.property.arbitrary.orNull
Expand All @@ -10,36 +13,58 @@ import tech.figure.validationoracle.client.domain.execute.ValidationDefinitionCr
import tech.figure.validationoracle.client.test.IntegrationTestBase
import tech.figure.validationoracle.client.test.PrimitiveArbs.anyBlankString
import tech.figure.validationoracle.client.test.PrimitiveArbs.anyNonEmptyString
import tech.figure.validationoracle.util.enums.ProvenanceNetworkType
import tech.figure.validationoracle.util.wallet.AccountSigner

@Order(2)
class ValidationDefinitionIntegrationTest : IntegrationTestBase({
val voClient = getVoClient()
"Creating a new validation definition" xshould {
"fail on an invalid validation type" {
"Creating a new validation definition" should {
"!fail on an invalid validation type" {
checkAll(
anyBlankString,
anyNonEmptyString.orNull(),
Arb.boolean().orNull(),
Arb.boolean().orNull(),
) { invalidValidationType, displayName, enabled, bindName ->
shouldThrow<Exception> { // TODO: Check for a more specific type
shouldThrow<StatusRuntimeException> {
voClient.createValidationDefinition(
ValidationDefinitionCreationRequest(
validationType = invalidValidationType,
displayName = displayName,
enabled = enabled,
bindName = bindName,
),
AccountSigner.fromMnemonic("", ProvenanceNetworkType.TESTNET), // TODO: Set mnemonic
SpecTestContainer.Provenance.contractAdminAccount,
)
}.let { exception ->
// TODO: Validate the exception message
// TODO: Re-enable this test case once contract is updated to require a non-empty validation type
exception.message shouldContain "invalid request: failed to execute message; "
}
}
}
"succeed with a valid request body" {
"fail if executed by a party other than the contract admin" {
checkAll(
anyNonEmptyString,
anyNonEmptyString.orNull(),
Arb.boolean().orNull(),
Arb.boolean().orNull(),
) { validationType, displayName, enabled, bindName ->
shouldThrow<StatusRuntimeException> {
voClient.createValidationDefinition(
ValidationDefinitionCreationRequest(
validationType = validationType,
displayName = displayName,
enabled = enabled,
bindName = bindName,
),
SpecTestContainer.Provenance.party1Account,
)
}.let { exception ->
exception.message shouldContainIgnoringCase "invalid request: failed to execute message; " +
"message index: 0: Unauthorized: must be the contract admin: execute wasm contract failed"
}
}
}
"!succeed with a valid request body" {
// TODO: Implement
}
}
Expand Down
Loading