diff --git a/sdk/typescript/rooch-sdk/package.json b/sdk/typescript/rooch-sdk/package.json index 4c47234322..bbb28af701 100644 --- a/sdk/typescript/rooch-sdk/package.json +++ b/sdk/typescript/rooch-sdk/package.json @@ -16,10 +16,10 @@ "vitest": "vitest", "test": "pnpm test:unit && pnpm test:e2e", "test:unit": "vitest run src", - "test:e2e": "pnpm prepare:e2e && wait-on tcp:0.0.0.0:6767 -l --timeout 180000 && vitest run e2e; pnpm stop:e2e", + "test:e2e": "pnpm prepare:e2e && wait-on tcp:0.0.0.0:6767 -l --timeout 180000 && vitest run e2e || exit 1; pnpm stop:e2e", "test:e2e:nowait": "vitest run e2e", "prepare:e2e": "nohup cargo run --bin rooch server start -n local -d TMP --port 6767 > /dev/null 2>&1 &", - "stop:e2e": "lsof -ti:6767 | xargs kill", + "stop:e2e": "lsof -ti:6767 | tee /dev/stderr | xargs -r kill -9", "prepublishOnly": "pnpm build", "size": "size-limit", "analyze": "size-limit --why", diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts index 89003bd8e1..a0ec9ff67f 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/bitcoin.test.ts @@ -15,13 +15,15 @@ describe('Bitcoin Assets API', () => { await testBox.loadRoochEnv() }) - afterAll(() => { - testBox.unloadContainer() + afterAll(async () => { + testBox.cleanEnv() }) it('query utxo should be success', async () => { - const addr = testBox.keypair.getSchnorrPublicKey().buildAddress(1, BitcoinNetowkType.Regtest).toStr() - console.log(addr) + const addr = testBox.keypair + .getSchnorrPublicKey() + .buildAddress(1, BitcoinNetowkType.Regtest) + .toStr() const result = await testBox.bitcoinContainer?.executeRpcCommand('generatetoaddress', [ '50', addr, @@ -81,17 +83,11 @@ describe('Bitcoin Assets API', () => { }) expect(utxos.data.length).toBeGreaterThan(0) - // first make sure don’t break - try { - const inscriptions = await testBox.getClient().queryInscriptions({ - filter: { - owner: addr, - }, - }) - // TODO: check inscriptions result - expect(inscriptions).toBeDefined() - } catch (e) { - expect(e).toBeUndefined() - } + const inscriptions = await testBox.getClient().queryInscriptions({ + filter: { + owner: addr, + }, + }) + expect(inscriptions.data.length).toBeGreaterThan(0) }) }) diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/checkpoint.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/checkpoint.test.ts index 9e98505c1a..9e6678251c 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/checkpoint.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/checkpoint.test.ts @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -import { beforeAll, describe, expect, it } from 'vitest' +import { beforeAll, describe, expect, it, afterAll } from 'vitest' import { Args } from '../../src/bcs/index.js' import { Secp256k1Keypair } from '../../src/keypairs/index.js' import { BitcoinAddress, BitcoinNetowkType } from '../../src/address/index.js' @@ -17,6 +17,10 @@ describe('Checkpoints Reading API', () => { testBox = TestBox.setup() }) + afterAll(async ()=> { + testBox.cleanEnv() + }) + it('Get latest rpc version eq local version', async () => { const resultSN = await testBox.getClient().getRpcApiVersion() @@ -147,6 +151,8 @@ describe('Checkpoints Reading API', () => { expect(await testBox.signAndExecuteTransaction(tx)).toBeTruthy() + await testBox.delay(3) + const result1 = await testBox.getClient().getEvents({ eventHandleType: `${await testBox.defaultCmdAddress()}::event_test::WithdrawEvent`, limit: '1', diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/coin.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/coin.test.ts index cefa0709f9..0cd925e0e5 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/coin.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/coin.test.ts @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -import { beforeAll, describe, expect, it } from 'vitest' +import { beforeAll, describe, expect, it, afterAll } from 'vitest' import { TestBox } from '../setup.js' import { Transaction } from '../../src/transactions/index.js' import { Secp256k1Keypair } from '../../src/keypairs/index.js' @@ -14,6 +14,10 @@ describe('Checkpoints Coin API', () => { testBox = TestBox.setup() }) + afterAll(async ()=> { + testBox.cleanEnv() + }) + it('Cmd publish package should be success', async () => { const result = await testBox.cmdPublishPackage('../../../examples/coins', { namedAddresses: 'coins=default', @@ -22,26 +26,40 @@ describe('Checkpoints Coin API', () => { expect(result).toBeTruthy() }) - it('Cmd publish package should be success', async () => { - testBox.roochCommand( - 'move run --function default::fixed_supply_coin::faucet --args object:default::fixed_supply_coin::Treasury', - ) + it('Check balances should be success', async () => { - let result = await testBox.getClient().getBalances({ - owner: await testBox.defaultCmdAddress(), - limit: '1', + const tx = new Transaction() + tx.callFunction({ + target: `${await testBox.defaultCmdAddress()}::fixed_supply_coin::faucet`, + args: [ + Args.object({ + address: await testBox.defaultCmdAddress(), + module: 'fixed_supply_coin', + name:'Treasury' + }) + ] }) - expect(result.has_next_page).toBeTruthy() + let result = await testBox.signAndExecuteTransaction(tx) + expect(result).toBeTruthy() + + await testBox.delay(3) let result1 = await testBox.getClient().getBalances({ - owner: await testBox.defaultCmdAddress(), + owner: testBox.address().toHexAddress(), + limit: '1', + }) + + expect(result1.has_next_page).toBeTruthy() + + let result2 = await testBox.getClient().getBalances({ + owner: testBox.address().toHexAddress(), limit: '1', - cursor: result.next_cursor, + cursor: result1.next_cursor, }) - expect(result1.has_next_page).toBeFalsy() - expect(result1.data.length === 1).toBeTruthy() + expect(result2.has_next_page).toBeFalsy() + expect(result2.data.length === 1).toBeTruthy() }) it('Transfer gas coin should be success', async () => { diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/example-entry-function.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/example-entry-function.test.ts index e6b4442810..736a26532e 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/example-entry-function.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/example-entry-function.test.ts @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -import { beforeAll, describe, expect, it } from 'vitest' +import { beforeAll, describe, expect, it, afterAll } from 'vitest' import { TestBox } from '../setup.js' import { Args } from '../../src/bcs/index.js' import { Transaction } from '../../src/transactions/index.js' @@ -14,6 +14,10 @@ describe('Checkpoints Example Entry Function', () => { testBox = TestBox.setup() }) + afterAll(async ()=> { + testBox.cleanEnv() + }) + it('Cmd publish package should be success', async () => { const result = await testBox.cmdPublishPackage('../../../examples/entry_function_arguments/') diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/module-abi.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/module-abi.test.ts index 87a5dd9b4d..a3161a7435 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/module-abi.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/module-abi.test.ts @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -import { beforeAll, describe, it, expect } from 'vitest' +import { beforeAll, describe, it, expect, afterAll } from 'vitest' import { TestBox } from '../setup.js' @@ -12,6 +12,10 @@ describe('Module Abi API', () => { testBox = TestBox.setup() }) + afterAll(async ()=> { + testBox.cleanEnv() + }) + it('Get module abi', async () => { const result = await testBox.getClient().getModuleAbi({ moduleAddr: '0x3', diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/session.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/session.test.ts index 24c43eb317..0f18323f0e 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/session.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/session.test.ts @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -import { beforeAll, describe, expect, it } from 'vitest' +import { beforeAll, describe, expect, it, afterAll } from 'vitest' import { TestBox } from '../setup.js' import { Transaction } from '../../src/transactions/index.js' @@ -12,6 +12,10 @@ describe('Checkpoints Session API', () => { testBox = TestBox.setup() }) + afterAll(async ()=> { + testBox.cleanEnv() + }) + it('Create session should be success', async () => { const session = await testBox.getClient().createSession({ sessionArgs: { diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/transaction.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/transaction.test.ts index 5dd349ec3e..3060d4190a 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/transaction.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/transaction.test.ts @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -import { beforeAll, describe, expect, it } from 'vitest' +import { beforeAll, describe, expect, it, afterAll } from 'vitest' import { TestBox } from '../setup.js' import { Transaction } from '../../src/transactions/index.js' @@ -12,6 +12,10 @@ describe('Checkpoints Transaction API', () => { testBox = TestBox.setup() }) + afterAll(async ()=> { + testBox.cleanEnv() + }) + it('Call function with bitcoin auth should be success', async () => { const tx = new Transaction() tx.callFunction({ diff --git a/sdk/typescript/rooch-sdk/test-e2e/case/transfer.test.ts b/sdk/typescript/rooch-sdk/test-e2e/case/transfer.test.ts index 01a8e74433..41d373d55b 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/case/transfer.test.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/case/transfer.test.ts @@ -1,7 +1,7 @@ // Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 -import { beforeAll, describe, expect, it } from 'vitest' +import { beforeAll, describe, expect, it, afterAll } from 'vitest' import { TestBox } from '../setup.js' import { Transaction } from '../../src/transactions/index.js' import { Secp256k1Keypair } from '../../src/keypairs/index.js' @@ -14,6 +14,10 @@ describe('Checkpoints Transfer API', () => { testBox = TestBox.setup() }) + afterAll(async ()=> { + testBox.cleanEnv() + }) + it('Transfer gas coin should be success', async () => { const amount = BigInt(10000000) const coinType = '0x3::gas_coin::GasCoin' diff --git a/sdk/typescript/rooch-sdk/test-e2e/setup.ts b/sdk/typescript/rooch-sdk/test-e2e/setup.ts index 51f00854ba..3fa3dfe2bc 100644 --- a/sdk/typescript/rooch-sdk/test-e2e/setup.ts +++ b/sdk/typescript/rooch-sdk/test-e2e/setup.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import * as fs from 'fs' -import tmp from 'tmp' import { RoochAddress } from '../src/address/index.js' import { getRoochNodeUrl, RoochClient } from '../src/client/index.js' import { Secp256k1Keypair } from '../src/keypairs/index.js' @@ -14,12 +13,13 @@ import { TestBox as TestBoxA, RoochContainer } from '@roochnetwork/test-suite' export const DEFAULT_NODE_URL = import.meta.env.VITE_FULLNODE_URL ?? getRoochNodeUrl('localnet') export class TestBox extends TestBoxA { - private client?: RoochClient + private client: RoochClient keypair: Secp256k1Keypair constructor(keypair: Secp256k1Keypair) { super() this.keypair = keypair + this.client = new RoochClient({ url: DEFAULT_NODE_URL }) } static setup(): TestBox { @@ -32,26 +32,14 @@ export class TestBox extends TestBoxA { port: number = 6768, ): Promise { await super.loadRoochEnv(target, port) - this.client = new RoochClient({ url: `http://127.0.0.1:${port}`, }) return } - getClient(url = DEFAULT_NODE_URL): RoochClient { - if (url === DEFAULT_NODE_URL) { - if (!this.client) { - this.client = new RoochClient({ - url, - }) - } - return this.client - } - - return new RoochClient({ - url, - }) + getClient(): RoochClient { + return this.client } address(): RoochAddress { @@ -76,20 +64,17 @@ export class TestBox extends TestBoxA { namedAddresses: 'rooch_examples=default', }, ) { - tmp.setGracefulCleanup() - - const tmpDir = tmp.dirSync({ unsafeCleanup: true }) const namedAddresses = options.namedAddresses.replaceAll( 'default', box.address().toHexAddress(), ) this.roochCommand( - `move build -p ${packagePath} --named-addresses ${namedAddresses} --install-dir ${tmpDir.name} --export --json`, + `move build -p ${packagePath} --named-addresses ${namedAddresses} --install-dir ${this.tmpDir.name} --export --json`, ) let fileBytes: Uint8Array try { - fileBytes = fs.readFileSync(tmpDir.name + '/package.blob') + fileBytes = fs.readFileSync(this.tmpDir.name + '/package.blob') const tx = new Transaction() tx.callFunction({ target: '0x2::module_store::publish_modules_entry', diff --git a/sdk/typescript/rooch-sdk/vite.config.ts b/sdk/typescript/rooch-sdk/vitest.config.js similarity index 100% rename from sdk/typescript/rooch-sdk/vite.config.ts rename to sdk/typescript/rooch-sdk/vitest.config.js diff --git a/sdk/typescript/test-suite/src/testbox.ts b/sdk/typescript/test-suite/src/testbox.ts index 73486b02e6..3ee0141d60 100644 --- a/sdk/typescript/test-suite/src/testbox.ts +++ b/sdk/typescript/test-suite/src/testbox.ts @@ -4,10 +4,13 @@ import tmp, { DirResult } from 'tmp' import { execSync } from 'child_process' import { Network, StartedNetwork } from 'testcontainers' +import { spawn } from 'child_process' import { OrdContainer, StartedOrdContainer } from './container/ord.js' import { RoochContainer, StartedRoochContainer } from './container/rooch.js' import { BitcoinContainer, StartedBitcoinContainer } from './container/bitcoin.js' +import path from 'node:path' +import fs from 'fs' const ordNetworkAlias = 'ord' const bitcoinNetworkAlias = 'bitcoind' @@ -19,10 +22,15 @@ export class TestBox { ordContainer?: StartedOrdContainer bitcoinContainer?: StartedBitcoinContainer roochContainer?: StartedRoochContainer | number + roochDir: string constructor() { tmp.setGracefulCleanup() this.tmpDir = tmp.dirSync({ unsafeCleanup: true }) + this.roochDir = path.join(this.tmpDir.name, '.rooch_test') + fs.mkdirSync(this.roochDir, { recursive: true }) + this.roochCommand(['init', '--config-dir', `${this.roochDir}`, '--skip-password']) + this.roochCommand(['env', 'switch', '--config-dir', `${this.roochDir}`, '--alias', 'local']) } async loadBitcoinEnv(customContainer?: BitcoinContainer) { @@ -86,13 +94,18 @@ export class TestBox { this.bitcoinContainer.getRpcUser(), '--btc-rpc-password', this.bitcoinContainer.getRpcPass(), + '--btc-sync-block-interval', + '1', ], ) } - cmds.push(`> ${this.tmpDir.name}/rooch.log 2>&1 & echo $!`) + // cmds.push(`> ${this.tmpDir.name}/rooch.log 2>&1 & echo $!`) - const result = this.roochCommand(cmds) + const result: string = await this.roochAsyncCommand( + cmds, + `JSON-RPC HTTP Server start listening 0.0.0.0:${port}`, + ) this.roochContainer = parseInt(result.toString().trim(), 10) await this.delay(5) @@ -119,7 +132,7 @@ export class TestBox { this.roochContainer = await container.start() } - unloadContainer() { + cleanEnv() { this.bitcoinContainer?.stop() this.ordContainer?.stop() @@ -142,12 +155,60 @@ export class TestBox { }) } + private buildRoochCommand(args: string[] | string) { + const root = this.findRootDir('pnpm-workspace.yaml') + const roochDir = path.join(root!, 'target', 'debug') + return `${roochDir}/./rooch ${typeof args === 'string' ? args : args.join(' ')}` + } + + // TODO: support container roochCommand(args: string[] | string): string { - return execSync(`cargo run --bin rooch ${typeof args === 'string' ? args : args.join(' ')}`, { + return execSync(this.buildRoochCommand(args), { encoding: 'utf-8', }) } + // TODO: support container + async roochAsyncCommand(args: string[] | string, waitFor: string): Promise { + return new Promise((resolve, reject) => { + const command = this.buildRoochCommand(args) + const child = spawn(command, { shell: true }) + + let output = '' + let pidOutput = '' + + child.on('spawn', () => { + if (child.pid) { + pidOutput = child.pid.toString() + } else { + reject(new Error('Failed to obtain PID of the process')) + } + }) + + child.stdout.on('data', (data) => { + output += data.toString() + + if (output.includes(waitFor)) { + resolve(pidOutput.trim()) + } + }) + + child.stderr.on('data', (data) => { + process.stderr.write(data) + }) + + child.on('error', (error) => { + reject(error) + }) + + child.on('close', () => { + if (!output.includes(waitFor)) { + reject(new Error('Expected output not found')) + } + }) + }) + } + async cmdPublishPackage( packagePath: string, options: { @@ -157,7 +218,7 @@ export class TestBox { }, ) { const result = this.roochCommand( - `move publish -p ${packagePath} --named-addresses ${options.namedAddresses} --json`, + `move publish -p ${packagePath} --config-dir ${this.roochDir} --named-addresses ${options.namedAddresses} --json`, ) const { execution_info } = JSON.parse(result) @@ -175,7 +236,9 @@ export class TestBox { */ async defaultCmdAddress(): Promise { if (!_defaultCmdAddress) { - const accounts = JSON.parse(this.roochCommand(['account', 'list', '--json'])) + const accounts = JSON.parse( + this.roochCommand(['account', 'list', '--config-dir', this.roochDir, '--json']), + ) if (Array.isArray(accounts)) { for (const account of accounts) { @@ -202,4 +265,18 @@ export class TestBox { } return this.network } + + private findRootDir(targetName: string) { + let currentDir = process.cwd() + + while (currentDir !== path.parse(currentDir).root) { + const targetPath = path.join(currentDir, targetName) + if (fs.existsSync(targetPath)) { + return currentDir + } + currentDir = path.dirname(currentDir) + } + + return null + } }