Skip to content

Commit

Permalink
chore: init commit
Browse files Browse the repository at this point in the history
  • Loading branch information
martiliones committed Mar 7, 2024
0 parents commit bf49ba7
Show file tree
Hide file tree
Showing 35 changed files with 8,326 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
2 changes: 2 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
build/
dist/
3 changes: 3 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "./node_modules/gts/"
}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
build/
dist/
example.mts
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules/
build/
dist/
3 changes: 3 additions & 0 deletions .prettierrc.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
...require('gts/.prettierrc.json'),
};
192 changes: 192 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# ADAMANT Botfactory E2E

`botfactory-e2e` is an independent testing framework for [adamant-botfactory](https://github.com/adamant-im/adamant-botfactory) that uses [Fluent interface](https://en.wikipedia.org/wiki/Fluent_interface).

## Installation

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

```js
npm install botfactory-e2e
```

For a better experience, you may also want to install another testing framework. This documentation uses [`Jest`](https://github.com/jestjs/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:

```js
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:

```js
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:

```js
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:

```js
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:

```js
const user = createFakeUser(passphrase);
```

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

```js
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:

```js
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:

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

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

```js
user.then(done);
```

## Usage

### async/await

You can use `await` with the mock user instance as it's a [thenable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables) class:

```js
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:

```js
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:

```js
let node;

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

Then, you can pass the created nodes into `createTestBot`:

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

// Or for a single node, you can use

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

### 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:

```js
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, you can use fake user.

```js
user
.interactsWith(testBot)
.sends({command: 'start'})
.shouldReceive(({message}) =>
expect(message.text).toBe('Hello, U14581577999389437648!')
)
.then(done);
```
3 changes: 3 additions & 0 deletions examples/_setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import {globalNode} from 'adamant-botfactory-e2e';

globalNode.version('v0.6.0');
39 changes: 39 additions & 0 deletions examples/config.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import {createFakeNode, createFakeUser} from 'adamant-botfactory-e2e';
import {bot} from '../bot.js';

let node: Node;

beforeEach(() => {
// Clear the node data before each test
node = createMockNode('config-commands-test', {
version: '0.6.0',
});
});

it('should greet the users with their address', async () => {
const testBot = createTestBot(bot, {
nodes,
});

const user = createFakeUser('my passphrase');

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

const {message} = await user.shouldReceive();

await createFakeUser('my passphrase')
.interactsWith(testBot)
.sends({
command: 'start',
})
.shouldReceive(({message}) => {
expect(message).toBe('Welcome to test bot');
})
.transfers([0.5, 'BTC'], {
withMessage: 'Take it',
})
.shouldReceive(({message}) => {
expect(message).toBe('Thanks for the donation!');
});
});
22 changes: 22 additions & 0 deletions examples/greeting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import {node, createFakeUser} from 'adamant-botfactory-e2e';
import {bot} from '../bot.js';

const nodeVersion = 'v0.6.0';

beforeAll(() => {
node.version(nodeVersion);
});

it('should greet the users with their address', done => {
const user = createFakeUser('my passphrase', {node});

user
.interactsWith(bot)
.sends({command: 'start'})
.shouldReceive(({message}) => {
expect(message).toBe(
`Hello, ${user}. I am an example bot, I am connected to node ${nodeVersion}`
);
})
.then(done);
});
6 changes: 6 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/** @type {import('ts-jest').JestConfigWithTsJest} */
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'node',
transformIgnorePatterns: ['node_modules/.pnpm/(?!adamant-botfactory)'],
};
43 changes: 43 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "botfactory-e2e",
"version": "0.0.1",
"description": "End-to-end testing framework for adamant-botfactory",
"main": "dist/index.js",
"type": "module",
"scripts": {
"lint": "gts lint",
"test": "jest",
"clean": "gts clean",
"compile": "tsup-node src/index.ts",
"fix": "gts fix",
"prepare": "npm run compile",
"preinstall": "npx only-allow pnpm",
"format": "prettier --write ./src"
},
"keywords": [
"ADAMANT"
],
"author": "",
"license": "MIT",
"files": [
"dist/"
],
"peerDependencies": {
"adamant-botfactory": ">=0.2.1"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/jest": "^29.5.8",
"@types/node": "20.8.2",
"adamant-api": "^2.1.0",
"gts": "^5.2.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.1",
"tsup": "^7.2.0",
"typescript": "~5.2.0"
},
"dependencies": {
"axios": "^1.6.2",
"express": "^4.18.2"
}
}
Loading

0 comments on commit bf49ba7

Please sign in to comment.