@ultraos/ultratest2
v0.0.18
Published
`ultratest2` is a command line interface application designed to run on any environment by utilizing a mix of EOS binaries and binaries inside a Docker container. It abstracts the chain startup process to be configurable for any Antelope-based chain, allo
Downloads
76
Readme
Ultratest2
ultratest2
is a command line interface application designed to run on any environment by utilizing a mix of EOS binaries and binaries inside a Docker container. It abstracts the chain startup process to be configurable for any Antelope-based chain, allowing for a broader audience.
Table of Contents
Installation
Install Node 18+.
Install ultratest2 Globally
npm i -g @ultraos/ultratest2
Install ultratest2 Locally
Clone this repository.
Navigate into the folder created.
Run
npm install
.Run
npm link .
.Run
ultratest2 -h
.Create
$HOME/ultratest
directory if missing. Runmkdir $HOME/ultratest
.
Usage
CLI Options
ultratest2 --help
List of available options:
-b <binaries_path>
: Path where binaries are located on a Mac or Linux system (default: "/usr/local/bin")-d <url>
: Docker Image URL (preset: "quay.io/ultra.io/3rdparty-devtools")--docker-exec-path <path>
: Docker Executable Path (default: "docker")--docker-host-mount-path <path>
: Docker Host Mount Path (default: "/home/user/ultratest")--docker-container-mount-path <path>
: Docker Container Mount Path (default: "/opt/ro_configs")--docker-container-data-dir <path>
: Docker container data directory (default: "/opt/rw_data")--docker-container-prefix <prefix>
: Prefix for container name (default: "ultratest")-s <snapshot_path>
: Snapshot file to use-c <config_folder>
: Path where configurations should exist (default: "/home/user/ultratest")-t <test_folder_or_single_test>
: Specify a test folder, or single test--exclude <test_folder_or_file_pattern>
: Exclude specific tests from running--nodeos-config-path
: Nodeos config path (default: "/home/user/ultratest/config.ini")--data-dir-path
: Nodeos data dir path (default: "/home/user/ultratest/data-dir")--log-conf-path
: Nodeos logging config path (default: "/home/user/ultratest/logging.json")--log-test-path
: Test logging path (default: "/home/user/ultratest/test.log")--contracts-dir-path
: Path to directory where contracts are located (default: "/home/user/ultra/eosio.contracts/build/contracts")--create-test <file_path>
: Path to create a new test--create-plugin <name>
: Name of the plugin to create--prepare <file_path>
: Path to prepare/install dependencies for a test file/directory--github-token <token>
: GitHub token to clone private repositories--keep-alive
: Keep nodeos running after test execution (default: false)-h, --help
: Display help for command
Creating Tests
To create a new test using ultratest2
, use the --create-test
option followed by the desired name of your test. This will generate a test skeleton in the current directory.
Example
ultratest2 --create-test tests/demo
This command will create a file named demo.spec.ts
in the tests
directory with the following content:
import { assert, assertAsync, assertAsyncThrow, sleep } from '@ultraos/ultratest/apis/testApi';
import { UltraTest, UltraTestAPI } from '@ultraos/ultratest/interfaces/test';
import { genesis } from 'ultratest-genesis-plugin/genesis';
import { system, SystemAPI } from 'ultratest-system-plugin/system';
export default class Test extends UltraTest {
constructor() {
super();
}
// handled by genesis plugin
// requiredProducers() {
// return 1;
// }
async onChainStart(ultra: UltraTestAPI) {
ultra.addPlugins([genesis(ultra), system(ultra)]);
}
async tests(ultra: UltraTestAPI) {
const systemAPI = new SystemAPI(ultra);
return {
'Check chain info': async () => {
const info = await systemAPI.getInfo();
ultra.test.assert(info.head_block_num > 1, 'Head block num not progressing');
},
};
}
}
You can also execute the skeleton test
ultratest2 -t tests/demo.spec.ts
Creating Plugins
To create a new plugin skeleton in ./src/plugins/custom/demo
using ultratest2
, use the --create-plugin
option followed by the name of your plugin.
Example
ultratest2 --create-plugin demo
This command will create a plugin skeleton in ./src/plugins/custom/demo
with the basic structure for you to add customized functionality. Plugins are always stored in the ultratest2
source code directory and cannot be moved.
Plugin Skeleton
Your demo plugin skeleton index.ts
might look like this:
import { assert, assertAsync, assertAsyncThrow, sleep, HTTP_API, INodeos } from '@ultraos/ultratest/apis/pluginApi';
import { Plugin, argsToParams } from '@ultraos/ultratest/interfaces/plugin';
import { UltraTest, UltraTestAPI } from '@ultraos/ultratest/interfaces/test';
import { config, keychain } from '@ultraos/ultratest/services';
import { logger } from '@ultraos/ultratest/utility';
export class PluginAPI {
public logger: typeof logger;
private api: HTTP_API;
constructor(ultra: UltraTestAPI, customNodeos: INodeos = undefined) {
this.api = ultra.api;
this.logger = ultra.logger;
if (customNodeos) this.api = new HTTP_API(customNodeos);
}
}
export function plugin(): Plugin {
return {
name: 'plugin',
params: () => { return argsToParams(arguments[Symbol.iterator]()) },
initCallback: async (plugin: Plugin, restoredFromSnapshot: boolean, ultra: UltraTestAPI) => {
const activeTestState = ultra.activeTestState;
const activeTestFile = ultra.activeTestState.file;
const pluginAPI = new PluginAPI(ultra);
// Put your plugin logic here
},
stopCallback: async () => {},
};
}
This skeleton provides a starting point for creating and integrating custom plugins into your ultratest2 framework. You can expand upon this skeleton to implement specific functionality required for your testing or application needs.
Key points to keep in mind when writing a plugin
params
field of your plugin is used to hash the initial configuration of your plugin. This is used to determine if the loading of the plugin can be skipped if there is a snapshot with a matching hash that was created in a previous test. Two useful functions to hash the arguments are:argsToParams
,testMethodsToParams
,pluginsMethodsToParams
andcollectFromPluginAndTestMethods
.- Even if your plugin is restored from snapshot, the
initCallback
will still be called withrestoredFromSnapshot
set to true. It is your responsibility to decide whether you need to perform any actions in this case or not. - You may add additional methods to your plugin that other plugins or tests can call by accessing your plugin object using the
activeTestState
object. Example in existing plugins:requiredAccounts
stopCallback
is called after all the tests are executed, it does not occur at the end of each test.- Plugin does not necessarily need to be created using the
--create-plugin
option, instead you can simply have a method that returns aPlugin
object anywhere in your code (including your test) and then pass this object toultra.addPlugins
of your test.
Package.json Configuration
In the tests directory, the package.json
file manages dependencies and scripts necessary for running tests with ultratest2
. For details on its configuration, refer to tests/package.json.
dependencies: Lists required dependencies for the testing environment. They are populated automatically based on plugins used:
ultratest-genesis-plugin
,ultratest-system-plugin
,ultratest-ultra-contracts-plugin
,ultratest-ultra-startup-plugin
: Local dependencies specified with version^1.0.0
.ultratest-ultra-contracts-plugin-pinned
,ultratest-ultra-startup-plugin-pinned
: External plugins specified with GitHub repository URLs and branches (main
).
ultratestPlugins: Configuration section specifying plugins used by
ultratest2
:native plugins: Directly reference plugin names and specify them as
"native"
. The name must match the internal name used for those plugins so only the options in tests/package.json are available as native plugins.pinned plugins: Specify GitHub repositories (
url
andref
) for custom plugins that are not part of the local repository. The name of the plugin and repository can be arbitrary in this case. Refer toUltra Contracts Plugin
orUltra Startup Plugin
for an example of how to write a plugin.
Boot Process
The boot process for ultratest2
involves initializing the blockchain environment by running a series of plugins that set up the necessary nodes, accounts, contracts, and configurations. The boot process is structured as follows:
Genesis Plugin
The genesis plugin is the first step in the boot process. It sets up the initial state of the blockchain by performing the following tasks:
Starting the Genesis Node:
- The genesis node is the initial node that starts the blockchain. It includes all necessary configurations to bootstrap the network.
Preactivate Feature:
- This step activates the preactivate feature necessary for future protocol upgrades. If the test explicitly requires this feature to be inactive, it won't be activated.
Deploying the BIOS Contract:
- The BIOS contract is deployed to the
eosio
account. This contract is responsible for the initial configuration and deployment of the core system contracts.
- The BIOS contract is deployed to the
For more details, you can refer to the genesis.ts file.
System Plugin
The system plugin is responsible for setting up the core system accounts, deploying system contracts, and activating necessary protocol features. The key steps involved are:
Creating Required Accounts:
- Essential accounts required for the blockchain operations are created.
Creating Producer Accounts:
- Producer accounts (
prodacc1
,prodacc2
,prodacc3
, etc.) are created to participate in block production.
- Producer accounts (
Activating Protocol Features:
- All necessary protocol features are activated to ensure the blockchain operates with the required functionalities.
Deploying System Contracts:
eosio.token
andeosio.system
contracts are deployed to manage the token and system functionalities respectively.
Creating and Issuing Native Tokens:
- A native token is created and issued to the
eosio
account. Initial balances are also set up.
- A native token is created and issued to the
Initializing the System Contract:
- The system contract is initialized to start the core blockchain functionalities.
For more details, you can refer to the system.ts file.
Ultra Contracts Plugin
The ultra contracts plugin is responsible for deploying additional contracts required for Ultra's specific functionalities. This plugin is available as a native
plugin (default) or you can add it using a URL to its repository if you need a specific version. The key steps involved are:
Deploying Ultra Contracts:
- Specific contracts required by the Ultra platform are deployed.
Removing KYC Requirement:
- The requirement for KYC to deploy smart contracts is removed.
Ultra Startup Plugin
The ultra startup plugin performs additional setup required for the Ultra platform, including creating various accounts, setting permissions, and initializing additional functionalities. This plugin is available as a native
plugin (default) or you can add it using a URL to its repository if you need a specific version. The key steps involved are:
Creating Required Accounts and Setting Balances:
- Additional accounts required by the Ultra platform are created and initial balances are set.
Registering the Token Faucet:
- The token faucet is registered to allow users to obtain tokens for testing purposes.
Converting Unlimited Accounts:
- Accounts are configured to have unlimited permissions where required.
Setting System Account Permissions:
- Permissions for system accounts are set according to the required configurations.
Deploying Custom Contracts:
- Custom contracts specific to Ultra's functionalities are deployed.
For more details, you can refer to the respective plugin files.
This structured boot process ensures that the blockchain environment is set up correctly with all the necessary configurations and contracts deployed, ready for use.
Future Plans
- Parallel Chains for Testing
- Multiple reruns of the same test