diff --git a/README.md b/README.md
index 08a1977..296c582 100644
--- a/README.md
+++ b/README.md
@@ -92,15 +92,17 @@ The above command will compile the Typescript source code into a `dist/` directo
### 4.1. Useful Commands
-| Command | Description |
-|-------------------|-------------------------------------------------------------------------------------|
-| `yarn build` | Builds the source code into the `dist/` directory. |
-| `yarn docs:build` | Builds the documentation into the `.docusaurus/` directory. |
-| `yarn docs:serve` | Serves the built documentation from the `.docusaurus/` directory. |
-| `yarn docs:start` | Builds and runs the documentation in a development environment with hot reloading. |
-| `yarn lint` | Runs the linter on `.js` and `.ts` files. |
-| `yarn prettier` | Runs the prettier on `.js` and `.ts` files. |
-| `yarn test` | Runs the tests. |
+| Command | Description |
+|-------------------|------------------------------------------------------------------------------------|
+| `yarn build` | Builds the source code into the `dist/` directory. |
+| `yarn docs:build` | Builds the documentation into the `.docusaurus/` directory. |
+| `yarn docs:serve` | Serves the built documentation from the `.docusaurus/` directory. |
+| `yarn docs:start` | Builds and runs the documentation in a development environment with hot reloading. |
+| `yarn lint` | Runs the linter on `.js` and `.ts` files. |
+| `yarn node:start` | Starts up a NEAR development node and runs it in the background. |
+| `yarn node:start` | Stops the NEAR development node that was started in `yarn node:start`. |
+| `yarn prettier` | Runs the prettier on `.js` and `.ts` files. |
+| `yarn test` | Starts a NEAR development node and runs the tests. |
[Back to top ^][table-of-contents]
diff --git a/jest.config.ts b/jest.config.ts
index 49a8587..3491434 100644
--- a/jest.config.ts
+++ b/jest.config.ts
@@ -5,6 +5,7 @@ import { pathsToModuleNameMapper } from 'ts-jest';
import { compilerOptions } from './tsconfig.json';
const config: Config = {
+ globalSetup: '/test/globalSetup.ts',
moduleNameMapper: {
...pathsToModuleNameMapper(compilerOptions.paths, {
prefix: '/',
diff --git a/scripts/start_node.sh b/scripts/start_node.sh
index 47bbbd1..53c9dfb 100755
--- a/scripts/start_node.sh
+++ b/scripts/start_node.sh
@@ -20,7 +20,7 @@ function main {
# start the node
near-sandbox --home ./.near run > /dev/null 2>&1 &
- sleep 1000
+ sleep 2s
printf "%b node started \n" "${INFO_PREFIX}"
diff --git a/scripts/stop_node.sh b/scripts/stop_node.sh
index b426492..7103469 100755
--- a/scripts/stop_node.sh
+++ b/scripts/stop_node.sh
@@ -5,7 +5,7 @@ SCRIPT_DIR=$(dirname "${0}")
source "${SCRIPT_DIR}"/set_vars.sh
-# Public: Gets the saved node PID from .near/node.pid and kills the background process.
+# Public: Kills the node's process.
#
# Examples
#
@@ -17,11 +17,13 @@ function main {
set_vars
- printf "%b killing node process \n" "${INFO_PREFIX}"
+ printf "%b stopping the node \n" "${INFO_PREFIX}"
# stop the node
pkill -f "near-sandbox"
+ printf "%b stopped node \n" "${INFO_PREFIX}"
+
exit 0
}
diff --git a/src/controllers/Social.get.test.ts b/src/controllers/Social.get.test.ts
index 9772d2e..ed2ba58 100644
--- a/src/controllers/Social.get.test.ts
+++ b/src/controllers/Social.get.test.ts
@@ -1,10 +1,7 @@
-import { Account, connect, Near } from 'near-api-js';
-import { NearAccount, Worker } from 'near-workspaces';
-import { resolve } from 'node:path';
-import { cwd } from 'node:process';
+import type { Account } from 'near-api-js';
-// constants
-import { NETWORK_ID } from '@test/constants';
+// credentials
+import { account_id as socialContractAccountId } from '@test/credentials/localnet/social.test.near.json';
// controllers
import Social from './Social';
@@ -13,35 +10,10 @@ import Social from './Social';
import createEphemeralAccount from '@test/helpers/createEphemeralAccount';
describe(`${Social.name}#get`, () => {
- let contractAccount: NearAccount;
- let near: Near;
let signer: Account;
- let worker: Worker;
-
- beforeAll(async () => {
- worker = await Worker.init({
- network: NETWORK_ID,
- });
-
- contractAccount = await worker.rootAccount.devDeploy(
- resolve(cwd(), 'test', 'contracts', 'social_db.wasm')
- );
- await worker.rootAccount.call(contractAccount.accountId, 'new', {});
-
- near = await connect({
- networkId: NETWORK_ID,
- nodeUrl: worker.provider.connection.url,
- });
- });
-
- afterAll(async () => {
- await worker.tearDown();
- });
beforeEach(async () => {
- const result = await createEphemeralAccount({
- worker,
- });
+ const result = await createEphemeralAccount();
signer = result.account;
});
@@ -49,7 +21,7 @@ describe(`${Social.name}#get`, () => {
it('should return an empty object when the contract does not know the account', async () => {
// arrange
const client = new Social({
- contractId: contractAccount.accountId,
+ contractId: socialContractAccountId,
});
// act
const result = await client.get({
diff --git a/src/controllers/Social.getVersion.test.ts b/src/controllers/Social.getVersion.test.ts
index e97c29a..b359857 100644
--- a/src/controllers/Social.getVersion.test.ts
+++ b/src/controllers/Social.getVersion.test.ts
@@ -1,10 +1,7 @@
-import { Account, connect, Near } from 'near-api-js';
-import { NearAccount, Worker } from 'near-workspaces';
-import { resolve } from 'node:path';
-import { cwd } from 'node:process';
+import type { Account } from 'near-api-js';
-// constants
-import { NETWORK_ID } from '@test/constants';
+// credentials
+import { account_id as socialContractAccountId } from '@test/credentials/localnet/social.test.near.json';
// controllers
import Social from './Social';
@@ -13,35 +10,10 @@ import Social from './Social';
import createEphemeralAccount from '@test/helpers/createEphemeralAccount';
describe(`${Social.name}#getVersion`, () => {
- let contractAccount: NearAccount;
- let near: Near;
let signer: Account;
- let worker: Worker;
-
- beforeAll(async () => {
- worker = await Worker.init({
- network: NETWORK_ID,
- });
-
- contractAccount = await worker.rootAccount.devDeploy(
- resolve(cwd(), 'test', 'contracts', 'social_db.wasm')
- );
- await worker.rootAccount.call(contractAccount.accountId, 'new', {});
-
- near = await connect({
- networkId: NETWORK_ID,
- nodeUrl: worker.provider.connection.url,
- });
- });
-
- afterAll(async () => {
- await worker.tearDown();
- });
beforeEach(async () => {
- const result = await createEphemeralAccount({
- worker,
- });
+ const result = await createEphemeralAccount();
signer = result.account;
});
@@ -49,7 +21,7 @@ describe(`${Social.name}#getVersion`, () => {
it('should return the version of the social contract', async () => {
// arrange
const client = new Social({
- contractId: contractAccount.accountId,
+ contractId: socialContractAccountId,
});
// act
const version = await client.getVersion({ signer });
diff --git a/src/controllers/Social.set.test.ts b/src/controllers/Social.set.test.ts
index 2817675..7c9f74f 100644
--- a/src/controllers/Social.set.test.ts
+++ b/src/controllers/Social.set.test.ts
@@ -1,11 +1,8 @@
import { Account, providers, transactions, utils } from 'near-api-js';
import { randomBytes } from 'node:crypto';
-import { NearAccount, Worker } from 'near-workspaces';
-import { resolve } from 'node:path';
-import { cwd } from 'node:process';
-// constants
-import { NETWORK_ID } from '@test/constants';
+// credentials
+import { account_id as socialContractAccountId } from '@test/credentials/localnet/social.test.near.json';
// controllers
import Social from './Social';
@@ -14,38 +11,16 @@ import Social from './Social';
import accountAccessKey, {
IAccessKeyResponse,
} from '@test/helpers/accountAccessKey';
-import createEphemeralAccount from '@test/helpers/createEphemeralAccount';
-
-// utils
import convertNEARToYoctoNEAR from '@app/utils/convertNEARToYoctoNEAR';
+import createEphemeralAccount from '@test/helpers/createEphemeralAccount';
describe(`${Social.name}#set`, () => {
- let contractAccount: NearAccount;
let keyPair: utils.KeyPairEd25519;
let signer: Account;
let signerAccessKeyResponse: IAccessKeyResponse;
- let worker: Worker;
-
- beforeAll(async () => {
- worker = await Worker.init({
- network: NETWORK_ID,
- });
-
- contractAccount = await worker.rootAccount.devDeploy(
- resolve(cwd(), 'test', 'contracts', 'social_db.wasm')
- );
- await worker.rootAccount.call(contractAccount.accountId, 'new', {});
- });
-
- afterAll(async () => {
- await worker.tearDown();
- });
beforeEach(async () => {
- const result = await createEphemeralAccount({
- initialBalanceInAtomicUnits: convertNEARToYoctoNEAR('100'),
- worker,
- });
+ const result = await createEphemeralAccount(convertNEARToYoctoNEAR('100'));
keyPair = result.keyPair;
signer = result.account;
@@ -54,7 +29,7 @@ describe(`${Social.name}#set`, () => {
it('should set storage and add the data', async () => {
// arrange
const client = new Social({
- contractId: contractAccount.accountId,
+ contractId: socialContractAccountId,
});
const data = {
[signer.accountId]: {
diff --git a/test/constants/Config.ts b/test/constants/Config.ts
index b943bd7..8ee5ca9 100644
--- a/test/constants/Config.ts
+++ b/test/constants/Config.ts
@@ -1,2 +1,2 @@
-export const NETWORK_ID = 'sandbox';
+export const NETWORK_ID = 'localnet';
export const NODE_URL = 'http://localhost:3030';
diff --git a/test/globalSetup.ts b/test/globalSetup.ts
new file mode 100644
index 0000000..29dc5d2
--- /dev/null
+++ b/test/globalSetup.ts
@@ -0,0 +1,75 @@
+import { Account, connect, keyStores, utils } from 'near-api-js';
+import { resolve } from 'node:path';
+import { cwd } from 'node:process';
+import { readFile } from 'node:fs/promises';
+
+// constants
+import { NETWORK_ID, NODE_URL } from './constants';
+
+// credentials
+import { account_id as genesisAccountId } from './credentials/localnet/test.near.json';
+import { account_id as socialContractAccountId } from './credentials/localnet/social.test.near.json';
+
+// helpers
+import createTestAccount from './helpers/createTestAccount';
+
+// utils
+import convertNEARToYoctoNEAR from '../src/utils/convertNEARToYoctoNEAR';
+
+export default async function globalSetup() {
+ const _functionName = 'globalSetup';
+ const near = await connect({
+ networkId: NETWORK_ID,
+ nodeUrl: NODE_URL,
+ keyStore: new keyStores.UnencryptedFileSystemKeyStore(
+ resolve(cwd(), 'test', 'credentials')
+ ),
+ });
+ const contract = await readFile(
+ resolve(cwd(), 'test', 'contracts', 'social_db.wasm')
+ );
+ let contractAccountPublicKey: utils.PublicKey;
+ let contractAccount: Account;
+ let genesisAccount: Account;
+
+ genesisAccount = await near.account(genesisAccountId);
+ contractAccountPublicKey = await near.connection.signer.getPublicKey(
+ socialContractAccountId,
+ NETWORK_ID
+ );
+
+ // create the contract account
+ contractAccount = await createTestAccount({
+ creatorAccount: genesisAccount,
+ initialBalanceInAtomicUnits: BigInt(convertNEARToYoctoNEAR('10')),
+ newAccountID: socialContractAccountId,
+ newAccountPublicKey: contractAccountPublicKey,
+ connection: near,
+ });
+
+ // deploy the account
+ await contractAccount.deployContract(contract);
+
+ try {
+ // initialize the contract
+ await genesisAccount.functionCall({
+ contractId: contractAccount.accountId,
+ methodName: 'new',
+ });
+ // set the contract to live
+ await contractAccount.functionCall({
+ contractId: contractAccount.accountId,
+ methodName: 'set_status',
+ args: {
+ status: 'Live',
+ },
+ });
+ } catch (error) {
+ // if the contract has already been initialized, just ignore
+ if (error.message.includes('The contract has already been initialized')) {
+ return;
+ }
+
+ console.error(`${_functionName}:`, JSON.stringify(error));
+ }
+}
diff --git a/test/helpers/createEphemeralAccount/createEphemeralAccount.ts b/test/helpers/createEphemeralAccount/createEphemeralAccount.ts
index d86d5f8..bdd2e8a 100644
--- a/test/helpers/createEphemeralAccount/createEphemeralAccount.ts
+++ b/test/helpers/createEphemeralAccount/createEphemeralAccount.ts
@@ -1,55 +1,56 @@
-import { connect, keyStores, utils, Near } from 'near-api-js';
-import { NearAccount, randomAccountId } from 'near-workspaces';
+import { connect, keyStores, utils, KeyPair, Near, Account } from 'near-api-js';
+import { randomBytes } from 'node:crypto';
// constants
-import { NETWORK_ID } from '@test/constants';
+import { NETWORK_ID, NODE_URL } from '@test/constants';
+
+// credentials
+import {
+ account_id as faucetAccountId,
+ secret_key as faucetSecretKey,
+} from '@test/credentials/localnet/test.near.json';
// types
-import type { IOptions, IResult } from './types';
+import type { IResult } from './types';
/**
- * Creates an ephemeral account with a randomised account ID.
- * @param {IOptions} options - the initial balance and the worker.
+ * Creates an ephemeral account with a randomised account ID of 8 lower case hexadecimal characters.
+ * @param {string} initialBalanceInAtomicUnits - [optional] the initial balance of the account in account units.
+ * Defaults to zero.
* @returns {Promise} a promise that resolves to the account and the account's access key pair.
*/
-export default async function createEphemeralAccount({
- initialBalanceInAtomicUnits,
- worker,
-}: IOptions): Promise {
- const rootAccountKeyPair = await worker.rootAccount.getKey(); // get the faucet key pair
+export default async function createEphemeralAccount(
+ initialBalanceInAtomicUnits?: string
+): Promise {
+ const accountId = `${randomBytes(8).toString('hex').toLowerCase()}.test.near`;
+ const faucetKeyPair = KeyPair.fromString(faucetSecretKey); // get the faucet key pair
const keyPair = utils.KeyPairEd25519.fromRandom(); // create the new access key to be used
const keyStore = new keyStores.InMemoryKeyStore();
- let account: NearAccount;
+ let faucetAccount: Account;
let near: Near;
- if (!rootAccountKeyPair) {
- throw new Error(
- `failed to get the root account "${worker.rootAccount.accountId}" access key`
- );
- }
-
- // create the new account with the initial balance
- account = await worker.rootAccount.createSubAccount(randomAccountId(), {
- initialBalance: initialBalanceInAtomicUnits,
- keyPair,
- });
-
// set the keys to the in-memory keystore
- await keyStore.setKey(
- NETWORK_ID,
- worker.rootAccount.accountId,
- rootAccountKeyPair
- );
- await keyStore.setKey(NETWORK_ID, account.accountId, keyPair);
+ await keyStore.setKey(NETWORK_ID, faucetAccountId, faucetKeyPair);
+ await keyStore.setKey(NETWORK_ID, accountId, keyPair);
near = await connect({
networkId: NETWORK_ID,
- nodeUrl: worker.provider.connection.url,
+ nodeUrl: NODE_URL,
keyStore,
});
+ faucetAccount = await near.account(faucetAccountId);
+
+ // create the new account with the initial balance
+ await faucetAccount.createAccount(
+ accountId,
+ keyPair.publicKey,
+ initialBalanceInAtomicUnits
+ ? BigInt(initialBalanceInAtomicUnits)
+ : BigInt('0')
+ );
return {
- account: await near.account(account.accountId),
+ account: await near.account(accountId),
keyPair,
};
}
diff --git a/test/helpers/createEphemeralAccount/types/IOptions.ts b/test/helpers/createEphemeralAccount/types/IOptions.ts
deleted file mode 100644
index 5b4b0f8..0000000
--- a/test/helpers/createEphemeralAccount/types/IOptions.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import type { Worker } from 'near-workspaces';
-
-interface IOptions {
- initialBalanceInAtomicUnits?: string;
- worker: Worker;
-}
-
-export default IOptions;
diff --git a/test/helpers/createEphemeralAccount/types/index.ts b/test/helpers/createEphemeralAccount/types/index.ts
index 8c186b3..37d8c72 100644
--- a/test/helpers/createEphemeralAccount/types/index.ts
+++ b/test/helpers/createEphemeralAccount/types/index.ts
@@ -1,2 +1 @@
-export type { default as IOptions } from './IOptions';
export type { default as IResult } from './IResult';
diff --git a/test/helpers/createTestAccount/createTestAccount.ts b/test/helpers/createTestAccount/createTestAccount.ts
new file mode 100644
index 0000000..5826f93
--- /dev/null
+++ b/test/helpers/createTestAccount/createTestAccount.ts
@@ -0,0 +1,38 @@
+import type { Account } from 'near-api-js';
+
+// types
+import type { IOptions } from './types';
+
+/**
+ * Creates a test account with an optional initial balance.
+ * @param {IOptions} options - the options needed to create the new account.
+ * @returns {Promise} a promise that resolves to the new account.
+ */
+export default async function createTestAccount({
+ connection,
+ creatorAccount,
+ initialBalanceInAtomicUnits,
+ newAccountID,
+ newAccountPublicKey,
+}: IOptions): Promise {
+ let newAccount = await connection.account(newAccountID);
+
+ try {
+ // this will error if the account doesn't exist
+ await newAccount.getAccountBalance();
+
+ // if the new account exists, return it
+ return newAccount;
+ } catch (error) {
+ // no account exists, create a new one
+ }
+
+ // create the new account
+ await creatorAccount.createAccount(
+ newAccountID,
+ newAccountPublicKey,
+ initialBalanceInAtomicUnits || BigInt('0')
+ );
+
+ return await connection.account(newAccountID);
+}
diff --git a/test/helpers/createTestAccount/index.ts b/test/helpers/createTestAccount/index.ts
new file mode 100644
index 0000000..6db817d
--- /dev/null
+++ b/test/helpers/createTestAccount/index.ts
@@ -0,0 +1,2 @@
+export { default } from './createTestAccount';
+export * from './types';
diff --git a/test/helpers/createTestAccount/types/IOptions.ts b/test/helpers/createTestAccount/types/IOptions.ts
new file mode 100644
index 0000000..c52c77d
--- /dev/null
+++ b/test/helpers/createTestAccount/types/IOptions.ts
@@ -0,0 +1,11 @@
+import { Account, Near, utils } from 'near-api-js';
+
+interface IOptions {
+ connection: Near;
+ creatorAccount: Account;
+ initialBalanceInAtomicUnits?: bigint;
+ newAccountID: string;
+ newAccountPublicKey: utils.PublicKey;
+}
+
+export default IOptions;
diff --git a/test/helpers/createTestAccount/types/index.ts b/test/helpers/createTestAccount/types/index.ts
new file mode 100644
index 0000000..68e7001
--- /dev/null
+++ b/test/helpers/createTestAccount/types/index.ts
@@ -0,0 +1 @@
+export type { default as IOptions } from './IOptions';