Skip to content

Adamant-im/botfactory-e2e

Repository files navigation

ADAMANT Botfactory E2E

botfactory-e2e is an independent testing framework for adamant-botfactory that uses Fluent interface.

Installation

To use this framework, you would need to install botfactory-e2e via your favorite package manager, e.g. npm:

npm install botfactory-e2e

For a better experience, you may also want to install another testing framework. This documentation uses Jest in its examples, but you can use any other testing framework you prefer.

Getting Started

Let's get started by writing a test for a hypothetical bot that greetings a user. First, create a bot.js file:

import {createBot} from 'adamant-botfactory';

const bot = createBot(process.env.PASSPHRASE, {
  nodes: [
    /* ... */
  ],
});

bot.command('start', usr => usr.reply(`Hello, ${usr.address}!`));

export {bot};

As you can see, we don't start the bot in the same file as we create it because it would interfere with our tests. You might want to create a separate file named start.js to run the bot.

Then, create a file named bot.test.js. This will contain our actual test:

import {createTestBot, createMockNode} from 'botfactory-e2e';
import {bot} from './bot.js';

const node = createMockNode('my-test-node');

describe('Greeting Bot', () => {
  const testBot = createTestBot(bot, {node});

  it('should greet a user with the correct address', done => {
    const passphrase =
      'angry special raise embark animal you ball million bronze science crater review';
    const user = createFakeUser(passphrase);

    user
      .interactsWith(testBot)
      .sends({command: 'start'})
      .shouldReceive(({message}) =>
        expect(message.text).toBe('Hello, U14581577999389437648!')
      )
      .then(done);
  });
});

afterAll(() => {
  node.shutdown();
});

There's much going on, so let's break this test down line by line. Firstly, we need to create a fake node:

const node = createMockNode('my-test-node');

This will start a local server to fake an ADAMANT node with mock responses. We must pass a unique id for the fake node to separate it from others and allow for future reuse.

Next, we create a test bot:

const testBot = createTestBot(bot, {node});

What it does is simply make a copy of the bot with a mock passphrase and connect it to the fake node we created earlier.

Finally, we can create an interface to test the bot using mock user:

const user = createFakeUser(passphrase);

Now, as we ready, we can tell the mock user which bot it should interact with:

user.interactsWith(testBot);

Then, we are going to test start command, so we generate and send a mock message to the bot with the /start command:

user.sends({command: 'start'});

To test the bot's response, we can use Jest's built-in assertion function inside the shouldReceive method's callback. First argument of the callback is the next transaction our bot will send to the user:

user.shouldReceive(({message}) =>
  expect(message.text).toBe('Hello, U14581577999389437648!')
);

As we finish the testing, we can let Jest know that we're done:

user.then(done);

Usage

async/await

You can use await with the mock user instance as it's a thenable class:

await user.interactsWith(myBot);
await user.sends({message: 'Hello!'});

Mock Nodes

A mock node is a fake ADAMANT Node that doesn't perform any calculations but returns mock data, which you are free to modify.

When creating your first mock node, botfactory-e2e will try to find a free port starting from 36668. Once it finds a free port, it will create a single server for all the features of the mock nodes:

const node1 = createMockNode('first-node');
console.log(node1.url); // http://localhost:36668/first-node

const node2 = createMockNode('second-node');
console.log(node2.url); // http://localhost:36668/second-node

When creating a mock node, you must specify the fake node's ID, which you can use to clear the old node's data and reuse it:

let node;

beforeEach(() => {
  node = createMockNode('test-node');
});

Then, you can pass the created nodes into createTestBot:

const testBot = createTestBot(bot, {
  nodes: [node1, node2],
});

// Or for a single node, you can use

const testBot = createTestBot(bot, {
  node: node1,
});

Configuration

Mock nodes can also be configured to include starting data, node information, and more.

Most of the properties can be set in the constructor of createMockNode and edited using node.info():

const node = createMockNode('test-syncing-node', {
  syncing: true,
  blocks: 90,
  consensus: 60,
});

// change the node status to synced
node.info({
  syncing: false,
  blocks: 0,
  consensus: 100,
});
online

You can set whether the node is considered alive. If you set online to false, the fake node will return a 500 error status:

const myNode = createMockNode('my-node', {
  online: false,
});

// get whether node is online
const status = myNode.online(); // false

// set node to online
myNode.online(true);
ping

To test the bot's ability to find the fastest node, you can set the ping property the same way you would with online:

const myNode = createMockNode('my-node', {
  ping: 5000, // 5 seconds
});

// get node's ping
const ping = myNode.ping(); // 5000

// set default ping
myNode.ping(60);

This will only affect /api/loader/status/ping and /api/node/status endpoints, as it's used by adamant-api-jsclient to determine the fastest node.

Mock data

In addition to node information, you can also manage blocks, accounts, and transactions using mock nodes.

setCurrentBlock

You can set the latest block using the setCurrentBlock method, which will automatically set the height and timestamp of the block and connect it to the previous block:

const block = node.setCurrentBlock({
  id: '11288856488163258860',
  version: 0,
  numberOfTransactions: 0,
  // ...
  totalForged: '20000000',
});

console.log(block.previousBlock); // 165438318875405182
newAccount

If your bot depends on external account information, you can mock an account using the newAccount method. This way, your bot will be able to retrieve information about the account, such as its balance:

import {createMockNode} from 'botfactory-e2e';
import {createKeypairFromPassphrase} from 'adamant-api';

const node = createMockNode('test-account-balance');

const {publicKey} = createKeypairFromPassphrase('apple banana...');
const myAccount = node.newAccount(publicKey);

myAccount.balance = {
  balance: '100000',
  unconfirmedBalance: '0',
};

// ...

const account = node.getAccount({address: myAccount.address});
console.log(`${account.address}: ${account.balance}`);

Test bot

Test bot simply copies all the handlers from your bot and connects it with the fake nodes you provided, adding its keypair to the fake nodes data:

const testBot = createTestBot(bot, {
  node,
  passphrase:
    'apple banana cherry date elderberry fig grape hazelnut iris juniper kiwi lemon',
});

const {publicKey} = node.getAccount(testBot.address);

// db299bb7fd288b387b0b94b539e2689c46f980ea7cfa0a53a26842f84f3c32bf
console.log(publicKey);

Fake users

To interact with your bot from a user point of view, you can use fake user:

const keyPair = createKeypairFromPassphrase('my secret passphrase');
const user = createFakeUser(keyPair);

Setting up

To mock the account data to the bot's node and set the bot to which the user will send messages, use interactsWith method:

// bot.js
const bot = createBot('...', {
  /* ... */
});

bot.command('start', async usr => {
  usr.reply(JSON.stringify(await usr.balance()));
});

export {bot};
// bot.test.js
import {bot} from './bot.js';

// prepare local server to mock node and copy of the bot
const node = createMockNode('test-balance');
const testBot = createTestBot(bot, {node});

// construct the user
const keyPair = createKeypairFromPassphrase('apple banana...');
const user = createFakeUser(keyPair);

user
  .interactsWith(testBot)
  .sends({command: 'start'})
  .shouldReceive(({message}) =>
    expect(JSON.parse(message)).toMatchObject({
      balance: '0',
      unconfirmedBalance: '0',
    })
  );

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Sponsor this project

Packages

No packages published