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

feat(account): Add TestCoin helper class for testing purposes #3647

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
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
67 changes: 67 additions & 0 deletions packages/account/src/test-utils/test-coin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { describe, expect, test } from 'vitest';
import { type Coin } from '@fuel-ts/account';
import { bn, type BN } from '@fuel-ts/math';
import { getRandomB256 } from '@fuel-ts/crypto';
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import { getRandomB256 } from '@fuel-ts/crypto';
import { getRandomB256 } from '@fuel-ts/address';


import { TestCoin } from './TestCoin';

/**
* @group node
*/
describe('TestCoin', () => {
test('constructor creates coin with default values', () => {
const testCoin = new TestCoin();
const coin = testCoin.toCoin();

expect(coin.id).toBeDefined();
expect(coin.id).toMatch(/^0x[a-f0-9]{64}$/);
expect(coin.owner).toBeDefined();
expect(coin.owner).toMatch(/^0x[a-f0-9]{64}$/);
expect(coin.amount).toBeDefined();
expect(coin.amount.toString()).toBe('1000000');
expect(coin.type).toBe(0);
});

test('constructor accepts custom values', () => {
const customParams = {
id: getRandomB256(),
owner: getRandomB256(),
amount: bn(500),
type: 1,
};

const testCoin = new TestCoin(customParams);
const coin = testCoin.toCoin();

expect(coin.id).toBe(customParams.id);
expect(coin.owner).toBe(customParams.owner);
expect(coin.amount).toBe(customParams.amount);
expect(coin.type).toBe(customParams.type);
});

test('many() creates specified number of coins', () => {
const count = 3;
const coins = TestCoin.many({}, count);

expect(coins).toHaveLength(count);
expect(coins[0].id).not.toBe(coins[1].id);
expect(coins[1].id).not.toBe(coins[2].id);
});

test('many() applies same base parameters to all coins', () => {
const baseParams = {
owner: getRandomB256(),
amount: bn(1000),
type: 2,
};

const coins = TestCoin.many(baseParams, 2);

expect(coins[0].owner).toBe(baseParams.owner);
expect(coins[0].amount).toBe(baseParams.amount);
expect(coins[0].type).toBe(baseParams.type);
expect(coins[1].owner).toBe(baseParams.owner);
expect(coins[1].amount).toBe(baseParams.amount);
expect(coins[1].type).toBe(baseParams.type);
});
});
51 changes: 51 additions & 0 deletions packages/account/src/test-utils/test-coin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { Coin } from '@fuel-ts/account';
import { bn, type BN } from '@fuel-ts/math';
import { getRandomB256 } from '@fuel-ts/crypto';
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
import { getRandomB256 } from '@fuel-ts/crypto';
import { getRandomB256 } from '@fuel-ts/address';


interface TestCoinSpecs {
id: string;
owner: string;
amount: BN;
type: number;
}

export class TestCoin {
public readonly id: string;
public readonly owner: string;
public readonly amount: BN;
public readonly type: number;

Comment on lines +12 to +17
Copy link
Contributor

Choose a reason for hiding this comment

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

The TestCoin should follow the same approach as the existent TestMessage:

export class TestMessage {
public readonly sender: Address;
public readonly recipient: Address;
public readonly nonce: string;
public readonly amount: number | BN;
public readonly data: string;
public readonly da_height: number;

It is intended to be used when setting up a local node, just like the TestMessage is used:

const { provider } = await launchTestNode({
nodeOptions: {
snapshotConfig: {
stateConfig: {
messages: [
new TestMessage({
nonce:
'0x381de90750098776c71544527fd253412908dec3d07ce9a7367bd1ba975908a0',
}).toChainMessage(),
],
},
},
},
});

Therefore, its properties should be the same ones as the Coin type for the stateConfig:

/**
* A helper class to create coins for testing purposes.
*/
constructor({
id = getRandomB256(),
owner = getRandomB256(),
amount = bn(1000000),
type = 0,
}: Partial<TestCoinSpecs> = {}) {
this.id = id;
this.owner = owner;
this.amount = amount;
this.type = type;
}

/**
* Creates a chain-compatible coin object
*/
toCoin(): Coin {
return {
id: this.id,
owner: this.owner,
amount: this.amount,
type: this.type,
Copy link
Contributor

Choose a reason for hiding this comment

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

The property type does not exist on the Coin type

};
}

/**
* Creates multiple test coins with the same base parameters
*/
static many(params: Partial<TestCoinSpecs> = {}, count = 1): Coin[] {
return Array.from({ length: count }, () => new TestCoin(params).toCoin());
}
}
45 changes: 44 additions & 1 deletion packages/fuel-gauge/src/transaction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
sleep,
TransactionType,
} from 'fuels';
import { ASSET_A, expectToThrowFuelError, launchTestNode, TestMessage } from 'fuels/test-utils';
import { ASSET_A, expectToThrowFuelError, launchTestNode, TestMessage, TestCoin } from 'fuels/test-utils';
import { bn } from 'fuels';

import { CallTestContractFactory } from '../test/typegen';

Expand Down Expand Up @@ -317,4 +318,46 @@ describe('Transaction', () => {
})
);
});

it('should handle transaction with TestCoin', async () => {
using launched = await launchTestNode();
const { provider, wallets: [wallet] } = launched;

const baseAssetId = await provider.getBaseAssetId();
const initialBalance = await wallet.getBalance(baseAssetId);

// Create test coins with specific parameters
const testCoin = new TestCoin({
amount: bn(1000),
owner: wallet.address.toB256(),
assetId: baseAssetId,
});
Comment on lines +323 to +334
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this test working? The transaction is being submitted with a coin not created on the local chain.


const request = new ScriptTransactionRequest({
gasLimit: 10000,
gasPrice: 1,
});

// Add test coin as input
request.addCoinInput(testCoin.toCoin());

// Add output to send coins back to wallet
request.addCoinOutput(wallet.address.toB256(), testCoin.amount, baseAssetId);

// Fund and send transaction
await request.estimateAndFund(wallet);
const tx = await wallet.sendTransaction(request);
const result = await tx.waitForResult();

// Verify transaction success
expect(result.isStatusSuccess).toBeTruthy();

// Verify balance changes
const finalBalance = await wallet.getBalance(baseAssetId);
expect(finalBalance.gte(initialBalance)).toBeTruthy();

// Verify coin was spent
const spentCoins = await wallet.getCoinsToSpend(baseAssetId);
expect(spentCoins.find((c) => c.id === testCoin.id)).toBeUndefined();
});
});