Skip to content

Commit

Permalink
feat(Test): Increase code covarage (#830)
Browse files Browse the repository at this point in the history
* feat(Test): Add more test for Chain

* Fix contract test

* test(Contract): More coverage

* test(Contract): More coverage

* test(Contract): Fix test

* test(Contract): More covarage

* test(Contract): Add log

* test(Contract): Run all tests

* test(Contract): Fix typo

* test(Contract): Improve jsonbig test

* test(Contract): More Contract test coverage

* test(Contract): More ACI test coverage

* test(Contract): More ACI test coverage

* chrome(codecov): trigger codecov

* test(Contract): More tests
  • Loading branch information
nduchak committed Dec 16, 2019
1 parent 475c2aa commit 6f760fb
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 24 deletions.
1 change: 1 addition & 0 deletions es/chain/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* PERFORMANCE OF THIS SOFTWARE.
*/
import * as R from 'ramda'

import Chain from './'
import Oracle from '../oracle/node'
import formatBalance from '../utils/amount-formatter'
Expand Down
4 changes: 2 additions & 2 deletions es/contract/compiler.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,11 @@ async function contractGetACI (code, options = {}) {

async function setCompilerUrl (url, { forceCompatibility = false } = {}) {
this.http.changeBaseUrl(url)
this.compilerVersion = null
this.compilerVersion = await this.getCompilerVersion().catch(e => null)
await this.checkCompatibility({ forceCompatibility })
}

async function checkCompatibility ({ force = false, forceCompatibility = false } = {}) {
this.compilerVersion = await this.getCompilerVersion().catch(e => null)
if (!this.compilerVersion && !force) throw new Error('Compiler do not respond')
if (!forceCompatibility && this.compilerVersion && !semverSatisfies(this.compilerVersion.split('-')[0], COMPILER_GE_VERSION, COMPILER_LT_VERSION)) {
const version = this.compilerVersion
Expand Down Expand Up @@ -127,6 +126,7 @@ function isInit () {
const ContractCompilerAPI = AsyncInit.compose(ContractBase, {
async init ({ compilerUrl = this.compilerUrl, forceCompatibility = false }) {
this.http = Http({ baseUrl: compilerUrl })
this.compilerVersion = await this.getCompilerVersion().catch(e => null)
await this.checkCompatibility({ force: true, forceCompatibility })
},
methods: {
Expand Down
41 changes: 39 additions & 2 deletions test/integration/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/

import { describe, it, before } from 'mocha'
import { configure, ready } from './'
import { generateKeyPair } from '../../es/utils/crypto'
Expand All @@ -38,7 +37,45 @@ describe('Node Chain', function () {
await client.awaitHeight(target, { attempts: 120 }).should.eventually.be.at.least(target)
return client.height().should.eventually.be.at.least(target)
})

it('Can verify transaction from broadcast error', async () => {
try {
await client.spend(0, publicKey, { fee: 100, verify: false })
} catch (e) {
const validation = await e.verifyTx()
validation.should.has.property('validation')
}
})
it('Get top block', async () => {
const top = await client.topBlock()
top.should.has.property('hash')
top.should.has.property('height')
})
it('Get pending transaction', async () => {
const mempool = await client.mempool()
mempool.should.has.property('transactions')
})
it('Get current generation', async () => {
const generation = await client.getCurrentGeneration()
generation.should.has.property('keyBlock')
})
it('Get key block', async () => {
const { keyBlock } = await client.getCurrentGeneration()
const keyBlockByHash = await client.getKeyBlock(keyBlock.hash)
const keyBlockByHeight = await client.getKeyBlock(keyBlock.height)
const keyBlockError = await client.getKeyBlock(false).catch(e => true)
keyBlockByHash.should.be.an('object')
keyBlockByHeight.should.be.an('object')
keyBlockError.should.be.equal(true)
})
it('Get generation', async () => {
const { keyBlock } = await client.getCurrentGeneration()
const genByHash = await client.getGeneration(keyBlock.hash)
const genByHeight = await client.getGeneration(keyBlock.height)
const genArgsError = await client.getGeneration(true).catch(e => true)
genByHash.should.be.an('object')
genByHeight.should.be.an('object')
genArgsError.should.be.equal(true)
})
it('polls for transactions', async () => {
const sender = await client.address()
const receiver = publicKey
Expand Down
117 changes: 102 additions & 15 deletions test/integration/contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/

import Compiler from '../../es/contract/compiler'
import { describe, it, before } from 'mocha'
import { BaseAe, configure, plan, ready } from './'
import { BaseAe, configure, plan, ready, compilerUrl } from './'
import { decode } from '../../es/tx/builder/helpers'

import * as R from 'ramda'
Expand All @@ -25,6 +25,12 @@ const identityContract = `
contract Identity =
entrypoint main(x : int) = x
`

const errorContract = `
contract Identity =
payable stateful entrypoint main(x : address) = Chain.spend(x, 1000000000)
`

const stateContract = `
contract StateContract =
record state = { value: string }
Expand Down Expand Up @@ -55,7 +61,8 @@ contract StateContract =
record yesEr = { t: number}
datatype dateUnit = Year | Month | Day
datatype one_or_both('a, 'b) = Left('a) | Right('b) | Both('a, 'b)
entrypoint init(value: string, key: int, testOption: option(string)) : state = { value = value, key = key, testOption = testOption }
entrypoint retrieve() : string*int = (state.value, state.key)
Expand Down Expand Up @@ -92,6 +99,11 @@ contract StateContract =
entrypoint usingExternalLib(s: int): int = Test.double(s)
entrypoint datTypeFn(s: dateUnit): dateUnit = s
entrypoint datTypeGFn(x : one_or_both(int, string)) : int =
switch(x)
Left(x) => x
Right(_) => abort("asdasd")
Both(x, _) => x
`

const encodedNumberSix = 'cb_DA6sWJo='
Expand Down Expand Up @@ -158,6 +170,7 @@ describe('Contract', function () {
const callRes = await contract.contractCall(identityContract, deployed.address, 'main', callDataCall)
callRes.result.should.have.property('gasUsed')
callRes.result.should.have.property('returnType')
callRes.result.should.have.property('returnType')
const decodedCallResult = await callRes.decode()
decodedCallResult.should.be.equal(callArg)
})
Expand All @@ -181,6 +194,23 @@ describe('Contract', function () {
res.result.should.have.property('returnType')
})

it('Call-Static deploy transaction on specific hash', async () => {
const { hash } = await contract.topBlock()
const compiled = bytecode.bytecode
const res = await contract.contractCallStatic(identityContract, null, 'init', [], { bytecode: compiled, top: hash })
res.result.should.have.property('gasUsed')
res.result.should.have.property('returnType')
})
it('Test handleError(Parse and check contract execution error)', async () => {
const code = await contract.contractCompile(errorContract)
const deployed = await code.deploy()
try {
await deployed.call('main', [await contract.address()])
} catch (e) {
e.message.indexOf('Invocation failed').should.not.be.equal(-1)
}
})

it('Dry-run without accounts', async () => {
const client = await BaseAe()
client.removeAccount('ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi')
Expand All @@ -204,6 +234,7 @@ describe('Contract', function () {
const result = await deployed.call('main', ['42'], { waitMined: false, verify: false })
Boolean(result.result === undefined).should.be.equal(true)
Boolean(result.txData === undefined).should.be.equal(true)
await contract.poll(result.hash)
})

it('calls deployed contracts static', async () => {
Expand Down Expand Up @@ -266,10 +297,23 @@ describe('Contract', function () {
})

describe('Sophia Compiler', function () {
let callData
let bytecode
it('Init un-compatible compiler version', async () => {
try {
// Init compiler
const compiler = await Compiler({ compilerUrl })
// Overwrite compiler version
compiler.compilerVersion = '1.0.0'
await compiler.checkCompatibility()
} catch (e) {
e.message.indexOf('Unsupported compiler version 1.0.0').should.not.be.equal(-1)
}
})
it('compile', async () => {
const code = await contract.compileContractAPI(identityContract)
const prefix = code.slice(0, 2)
const isString = typeof code === 'string'
bytecode = await contract.compileContractAPI(identityContract)
const prefix = bytecode.slice(0, 2)
const isString = typeof bytecode === 'string'
prefix.should.be.equal('cb')
isString.should.be.equal(true)
})
Expand All @@ -278,15 +322,34 @@ describe('Contract', function () {
aci.should.have.property('interface')
})
it('encode call-data', async () => {
const encoded = await contract.contractEncodeCallDataAPI(identityContract, 'init', [])
const prefix = encoded.slice(0, 2)
const isString = typeof encoded === 'string'
callData = await contract.contractEncodeCallDataAPI(identityContract, 'init', [])
const prefix = callData.slice(0, 2)
const isString = typeof callData === 'string'
prefix.should.be.equal('cb')
isString.should.be.equal(true)
})
it('decode call-data', async () => {
it('decode call result', async () => {
return contract.contractDecodeCallResultAPI(identityContract, 'main', encodedNumberSix, 'ok', { backend: 'fate' }).should.eventually.become(6)
})
it('Decode call-data using source', async () => {
const decodedCallData = await contract.contractDecodeCallDataBySourceAPI(identityContract, 'init', callData)
decodedCallData.arguments.should.be.an('array')
decodedCallData.arguments.length.should.be.equal(0)
decodedCallData.function.should.be.equal('init')
})
it('Decode call-data using bytecode', async () => {
const decodedCallData = await contract.contractDecodeCallDataByCodeAPI(bytecode, callData)
decodedCallData.arguments.should.be.an('array')
decodedCallData.arguments.length.should.be.equal(0)
decodedCallData.function.should.be.equal('init')
})
it('Decode data API', async () => {
const returnData = 'cb_bzvA9Af6'
return contract.contractDecodeDataAPI('string', returnData).catch(e => 1).should.eventually.become(1)
})
it('validate bytecode', async () => {
return contract.validateByteCodeAPI(bytecode, identityContract).should.eventually.become(true)
})
it('Use invalid compiler url', async () => {
try {
const cloned = R.clone(contract)
Expand Down Expand Up @@ -335,19 +398,17 @@ describe('Contract', function () {
result.should.have.property('returnType')
result.callerId.should.be.equal(onAccount)
})

it('Can deploy/call using AEVM', async () => {
await contractObject.compile({ backend: 'aevm' })
const deployStatic = await contractObject.methods.init.get('123', 1, 'hahahaha', { backend: 'aevm' })
deployStatic.should.be.an('object')
deployed = await contractObject.methods.init('123', 1, 'hahahaha', { backend: 'aevm' })
deployed = await contractObject.methods.init.send('123', 1, 'hahahaha', { backend: 'aevm' })
deployed.should.be.an('object')
const { result } = await contractObject.methods.intFn(123, { backend: 'aevm' })
result.should.have.property('gasUsed')
result.should.have.property('returnType')
await contractObject.compile()
})

it('Deploy contract before compile', async () => {
contractObject.compiled = null
await contractObject.methods.init('123', 1, 'hahahaha')
Expand All @@ -362,6 +423,7 @@ describe('Contract', function () {
const result = await contractObject.methods.intFn.send(2, { waitMined: false })
Boolean(result.result === undefined).should.be.equal(true)
Boolean(result.txData === undefined).should.be.equal(true)
await contract.poll(result.hash)
})
it('Generate ACI object with corresponding bytecode', async () => {
await contract.getContractInstance(testContract, { contractAddress: contractObject.deployInfo.address, filesystem, opt: { ttl: 0 } })
Expand Down Expand Up @@ -421,10 +483,23 @@ describe('Contract', function () {
}
})
it('Valid', async () => {
const { decodedResult } = await contractObject.methods.intFn(1)
const { decodedResult } = await contractObject.methods.intFn.get(1)
decodedResult.toString().should.be.equal('1')
})
})
describe('BOOL', function () {
it('Invalid', async () => {
try {
await contractObject.methods.boolFn({})
} catch (e) {
e.message.should.be.equal('"Argument" at position 0 fails because [Value "[[object Object]]" at path: [0] not a boolean]')
}
})
it('Valid', async () => {
const { decodedResult } = await contractObject.methods.boolFn.get(true)
decodedResult.should.be.equal(true)
})
})
describe('STRING', function () {
it('Invalid', async () => {
try {
Expand Down Expand Up @@ -538,7 +613,8 @@ describe('Contract', function () {
[address, ['someStringV', 324]]
]
)
const { decodedResult } = await contractObject.methods.mapFn(mapArg)
const objectFromMap = Array.from(mapArg.entries()).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})
const { decodedResult } = await contractObject.methods.mapFn(objectFromMap)
JSON.stringify(decodedResult).should.be.equal(JSON.stringify(Array.from(mapArg.entries())))
})
it('Map With Option Value', async () => {
Expand Down Expand Up @@ -656,6 +732,17 @@ describe('Contract', function () {
e.message.should.be.equal('"Argument" at position 0 fails because ["0" must be a string, "value" must contain at least one of [Year, Month, Day]]')
}
})
it('Call generic datatype', async () => {
const res = await contractObject.methods.datTypeGFn({ Left: [2] })
res.decodedResult.should.be.equal(2)
})
it('Invalid arguments length', async () => {
try {
await contractObject.methods.datTypeGFn()
} catch (e) {
e.message.should.be.equal('Function "datTypeGFn" require 1 arguments of types [{"StateContract.one_or_both":["int","string"]}] but get []')
}
})
it('Invalid variant', async () => {
try {
await contractObject.methods.datTypeFn('asdcxz')
Expand Down
4 changes: 2 additions & 2 deletions test/integration/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ chai.should()

const url = process.env.TEST_URL || 'http://localhost:3013'
const internalUrl = process.env.TEST_INTERNAL_URL || 'http://localhost:3113'
const compilerUrl = process.env.COMPILER_URL || 'http://localhost:3080'
export const compilerUrl = process.env.COMPILER_URL || 'http://localhost:3080'
const networkId = process.env.TEST_NETWORK_ID || 'ae_devnet'
const forceCompatibility = process.env.FORCE_COMPATIBILITY || false
export const account = Crypto.generateKeyPair()
export const account2 = Crypto.generateKeyPair()

const BaseAe = (params) => Ae.compose({
const BaseAe = (params) => Ae.waitMined(true).compose({
deepProps: { Swagger: { defaults: { debug: !!process.env['DEBUG'] } } },
props: { url, internalUrl, process, compilerUrl }
})({ ...params, forceCompatibility })
Expand Down
4 changes: 4 additions & 0 deletions test/integration/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ describe('Node client', function () {
e.message.should.be.equal('Invalid node instance object')
}
})
it('Can get network id', async () => {
const node = await NodePool()
node.getNetworkId().should.be.equal('ae_mainnet')
})
it('Throw error on using API without node', async () => {
const node = await NodePool()
try {
Expand Down
21 changes: 20 additions & 1 deletion test/integration/transaction.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import { describe, it, before } from 'mocha'
import { encodeBase58Check, encodeBase64Check, generateKeyPair, salt } from '../../es/utils/crypto'
import { ready, configure } from './index'
import { commitmentHash } from '../../es/tx/builder/helpers'
import { commitmentHash, isNameValid } from '../../es/tx/builder/helpers'

const nonce = 1
const nameTtl = 1
Expand Down Expand Up @@ -244,4 +244,23 @@ describe('Native Transaction', function () {
const orQuery = (await client.getOracleQuery(oracleId, queryId))
orQuery.response.should.be.equal(`or_${encodeBase64Check(queryResponse)}`)
})
it('Get next account nonce', async () => {
const accountId = await client.address()
const { nonce: accountNonce } = await client.api.getAccountByPubkey(accountId).catch(() => ({ nonce: 0 }))
const nonce = await client.getAccountNonce(await client.address())
nonce.should.be.equal(accountNonce + 1)
const nonceCustom = await client.getAccountNonce(await client.address(), 1)
nonceCustom.should.be.equal(1)
})
it('Is name valid', () => {
try {
isNameValid('asdasdasd.testDomain')
} catch (e) {
e.message.indexOf('AENS: Invalid name domain').should.not.be.equal(-1)
}
})
it('Destroy instance', () => {
client.destroyInstance()
console.log('Finish without error')
})
})
Loading

0 comments on commit 6f760fb

Please sign in to comment.