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

test: add build transaction unit tests #110

Merged
merged 5 commits into from
Apr 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 2 additions & 2 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"root": true,
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": ["tsconfig.json"]
"project": ["tsconfig.json"],
"exclude": "commitlint.config.js"
},
"exclude": "commitlint.config.js",
"plugins": ["@typescript-eslint", "prettier", "unicorn", "import"],
"extends": [
"eslint:recommended",
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

214 changes: 214 additions & 0 deletions src/stellar-plus/core/pipelines/build-transaction/index.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/* eslint-disable import/order */
import { Horizon, TransactionBuilder } from '@stellar/stellar-sdk'
import { AccountResponse } from '@stellar/stellar-sdk/lib/horizon'
import { Asset, Operation } from '@stellar/stellar-base'
import { testnet } from 'stellar-plus/constants'
import {
BuildTransactionPipelineInput as BTInput,
BuildTransactionPipelineOutput as BTOutput,
} from 'stellar-plus/core/pipelines/build-transaction/types'
import { HorizonHandler } from 'stellar-plus/horizon/types'

import { BuildTransactionPipeline } from '.'

jest.mock('@stellar/stellar-sdk', () => ({
Horizon: {
Server: jest.fn(),
},
Account: jest.fn(),
TransactionBuilder: jest.fn(() => {
return {
addOperation: jest.fn(),
setTimeout: jest.fn(),
setSorobanData: jest.fn(() => {
return {}
}),
build: jest.fn(() => {
return {}
}),
}
}),
}))

export function createMockedHorizonHandler(): jest.Mocked<HorizonHandler> {
return {
loadAccount: jest.fn().mockResolvedValue({} as AccountResponse),
server: new Horizon.Server(testnet.horizonUrl),
}
}

const mockedHorizonHandler = createMockedHorizonHandler()

const MOCKED_BT_INPUT: BTInput = {
header: {
source: 'source',
fee: '100',
timeout: 2,
},
horizonHandler: mockedHorizonHandler,
operations: [],
networkPassphrase: 'networkPassphrase',
}
const MOCKED_BT_OUTPUT: BTOutput = {} as BTOutput

describe('BuildTransactionPipeline', () => {
let buildTransactionPipeline: BuildTransactionPipeline

beforeEach(() => {
jest.clearAllMocks()
mockedHorizonHandler.loadAccount.mockClear()
})

describe('Load Account', () => {
beforeEach(() => {
buildTransactionPipeline = new BuildTransactionPipeline()
jest.clearAllMocks()
})

it('should load account successfully', async () => {
await buildTransactionPipeline.execute(MOCKED_BT_INPUT)

expect(mockedHorizonHandler.loadAccount).toHaveBeenCalledWith('source')
expect(mockedHorizonHandler.loadAccount).toHaveBeenCalledTimes(1)
})

it('should throw error', async () => {
mockedHorizonHandler.loadAccount.mockRejectedValueOnce(new Error('error'))

await expect(buildTransactionPipeline.execute(MOCKED_BT_INPUT)).rejects.toThrow('Could not load account!')
expect(mockedHorizonHandler.loadAccount).toHaveBeenCalledWith('source')
expect(mockedHorizonHandler.loadAccount).toHaveBeenCalledTimes(1)
})
})

describe('Build Envelope', () => {
const transactionBuilderOptions = {
fee: MOCKED_BT_INPUT.header.fee,
networkPassphrase: MOCKED_BT_INPUT.networkPassphrase,
}

beforeEach(() => {
buildTransactionPipeline = new BuildTransactionPipeline()
jest.clearAllMocks()
})

it('should create envelope successfully', async () => {
await buildTransactionPipeline.execute(MOCKED_BT_INPUT)

expect(TransactionBuilder).toHaveBeenCalledWith({}, transactionBuilderOptions)
})

it('should add sorobanData successfully', async () => {
await buildTransactionPipeline.execute({
...MOCKED_BT_INPUT,
sorobanData: 'sorobanData',
})

expect(TransactionBuilder).toHaveBeenCalledWith({}, transactionBuilderOptions)
})

it('should add single operation successfully', async () => {
await buildTransactionPipeline.execute({
...MOCKED_BT_INPUT,
operations: [
Operation.payment({
destination: 'GB3MXH633VRECLZRUAR3QCLQJDMXNYNHKZCO6FJEWXVWSUEIS7NU376P',
asset: Asset.native(),
amount: '100',
}),
],
})

expect(TransactionBuilder).toHaveBeenCalledWith({}, transactionBuilderOptions)
})

it('should add multiple operation successfully', async () => {
await buildTransactionPipeline.execute({
...MOCKED_BT_INPUT,
operations: [
Operation.payment({
destination: 'GB3MXH633VRECLZRUAR3QCLQJDMXNYNHKZCO6FJEWXVWSUEIS7NU376P',
asset: Asset.native(),
amount: '100',
}),
Operation.payment({
destination: 'GB3MXH633VRECLZRUAR3QCLQJDMXNYNHKZCO6FJEWXVWSUEIS7NU376P',
asset: Asset.native(),
amount: '100',
}),
Operation.payment({
destination: 'GB3MXH633VRECLZRUAR3QCLQJDMXNYNHKZCO6FJEWXVWSUEIS7NU376P',
asset: Asset.native(),
amount: '100',
}),
],
})

expect(TransactionBuilder).toHaveBeenCalledWith({}, transactionBuilderOptions)
})

it('should throw error', async () => {
;(TransactionBuilder as unknown as jest.Mock).mockImplementationOnce(() => {
throw new Error('error')
})

await expect(buildTransactionPipeline.execute(MOCKED_BT_INPUT)).rejects.toThrow(
'Could not create transaction builder!'
)
expect(TransactionBuilder).toHaveBeenCalledWith({}, transactionBuilderOptions)
})

it('should throw error adding sorobanData', async () => {
;(TransactionBuilder as unknown as jest.Mock).mockImplementationOnce(() => {
return {
setSorobanData: jest.fn(() => {
throw new Error('error')
}),
}
})

await expect(
buildTransactionPipeline.execute({
...MOCKED_BT_INPUT,
sorobanData: 'sorobanData',
})
).rejects.toThrow('Could not set Soroban data!')
expect(TransactionBuilder).toHaveBeenCalledWith({}, transactionBuilderOptions)
})

it('should throw error adding operation', async () => {
;(TransactionBuilder as unknown as jest.Mock).mockImplementationOnce(() => {
return {
addOperation: jest.fn(() => {
throw new Error('error')
}),
}
})

await expect(
buildTransactionPipeline.execute({
...MOCKED_BT_INPUT,
operations: [
Operation.payment({
destination: 'GB3MXH633VRECLZRUAR3QCLQJDMXNYNHKZCO6FJEWXVWSUEIS7NU376P',
asset: Asset.native(),
amount: '100',
}),
],
})
).rejects.toThrow('Could not add operations!')
expect(TransactionBuilder).toHaveBeenCalledWith({}, transactionBuilderOptions)
})
})

describe('Process', () => {
beforeEach(() => {
buildTransactionPipeline = new BuildTransactionPipeline()
jest.clearAllMocks()
})

it('should build transaction successfully', async () => {
await expect(buildTransactionPipeline.execute(MOCKED_BT_INPUT)).resolves.toEqual(MOCKED_BT_OUTPUT)
})
})
})
2 changes: 1 addition & 1 deletion src/stellar-plus/core/pipelines/build-transaction/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { xdr } from '@stellar/stellar-sdk'

import { HorizonHandler } from 'stellar-plus'
import { HorizonHandler } from 'stellar-plus/horizon/types'
import { EnvelopeHeader, Transaction } from 'stellar-plus/types'
import { BeltPluginType, GenericPlugin } from 'stellar-plus/utils/pipeline/conveyor-belts/types'

Expand Down
2 changes: 1 addition & 1 deletion src/stellar-plus/horizon/index.ts
Brunonascdev marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { HorizonHandler } from 'stellar-plus/horizon/types'
import { NetworkConfig } from 'stellar-plus/types'

export class HorizonHandlerClient implements HorizonHandler {
private networkConfig: NetworkConfig
public networkConfig: NetworkConfig
public server: Horizon.Server

/**
Expand Down