Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

various fixes & improvements #46

Merged
merged 1 commit into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions .github/workflows/run-nextjs-app-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ name: Run the ts-nextjs-app tests

on:
pull_request:
branches-ignore:
- '**'
push:
branches:
- main
branches-ignore:
- '**'

jobs:
build:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/run-ts-node-app-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ name: Run the ts-node-app tests

on:
pull_request:
branches-ignore:
- '**'
push:
branches:
- main
branches-ignore:
- '**'

jobs:
build:
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/run-vite-app-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ name: Run the ts-vite-app tests

on:
pull_request:
branches-ignore:
- '**'
push:
branches:
- main
branches-ignore:
- '**'

jobs:
build:
Expand Down
11 changes: 10 additions & 1 deletion workspace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,23 @@

Aptos Workspace is an integrated development environment designed to make innovation on Aptos easy and intuitive by removing unnecessary obstacles and lowering the barrier to entry.

> **_NOTE:_** This is an alpha, non-production-ready version. Breaking changes are expected.
> **_NOTE:_** This is an alpha, non-production-ready version. Some features have not been implemented yet and breaking changes are expected.

## Overview

Aptos Workspace provides a testing environment framework for Aptos developers to easily run integration tests for their dApps.

Aptos Workspace utilizes [mocha](https://mochajs.org/) as the testing framework and [chai](https://www.chaijs.com/) as the assertion framework.

## Prerequisites
Currently you'll need to have Docker installed on your system in order to use Aptos Workspace.
Docker is required because Aptos Workspace uses it to run a local indexer instance, which is part of the local network.

### Install Docker
- **Mac**: Download [Docker Desktop for macOS](https://docs.docker.com/desktop/setup/install/mac-install/) from the Docker website.
- **Windows**: Download [Docker Desktop for Windows](https://docs.docker.com/desktop/setup/install/windows-install/) from the Docker website.
- **Linux**: Download [Docker Desktop for Linux](https://docs.docker.com/desktop/setup/install/linux/), or install [Docker Engine](https://docs.docker.com/engine/install/).

## Getting Started

To start using Workspace you need to create an `npm project` by going to an empty folder (or `cd` into an existing one), and run:
Expand Down
11 changes: 9 additions & 2 deletions workspace/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion workspace/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aptos-labs/workspace",
"version": "0.0.17",
"version": "0.0.18",
"license": "Apache-2.0",
"bin": {
"aptos-workspace": "./dist/internal/cli.js",
Expand Down Expand Up @@ -51,6 +51,7 @@
"@types/fs-extra": "^5.1.0",
"@types/mocha": "^10.0.7",
"@types/node": "^20.14.11",
"@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"chai": "^4.2.0",
Expand Down
23 changes: 15 additions & 8 deletions workspace/src/internal/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,21 @@ function generateRandomId(length: number): string {
export class TestNode {
private static readonly TIMEOUT_SECONDS = 60;

private constructor(private info: NodeInfo | null) {}
private constructor(private info: NodeInfo | null) { }

public static async spawn(): Promise<TestNode> {
const id = generateRandomId(4);

const configVerbose = getUserConfigVerbose();

const cliCommand = "aptos-workspace-server";
const cliArgs: string[] = [];
const cliCommand = "npx";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be great to clean up a bit the output when running npx aptos workspace run on cli when docker is not available

Created test directory: /var/folders/9k/9m6fp1vj0sx79mys877nfvy00000gn/T/.tmpQ6w03p
Starting node..
Starting postgres..

One or more services failed to start, will run shutdown steps

Running shutdown steps
Removed docker volume aptos-workspace-0a56d71e-5a74-4d4a-a974-1814ce9e165b-postgres
details = """
panicked at config/src/config/network_config.rs:257:89:
called `Result::unwrap()` on an `Err` value: No such file or directory (os error 2)"""
backtrace = """
   0:        0x10345344c - __mh_execute_header
   1:        0x103453418 - __mh_execute_header
   2:        0x103455dfc - __mh_execute_header
   3:        0x103455d9c - __mh_execute_header
   4:        0x102a37184 - __mh_execute_header
   5:        0x1043038f8 - __mh_execute_header
   6:        0x104305c40 - __mh_execute_header
   7:        0x104305968 - __mh_execute_header
   8:        0x104303414 - __mh_execute_header
   9:        0x1048c6538 - __ZN7rocksdb6ribbon6detail34BandingConfigHelper1MaybeSupportedILNS0_25ConstructionFailureChanceE1ELy128ELb0ELb0ELb1EE11GetNumSlotsEj
  10:        0x1048c64c4 - __ZN7rocksdb6ribbon6detail34BandingConfigHelper1MaybeSupportedILNS0_25ConstructionFailureChanceE1ELy128ELb0ELb0ELb1EE11GetNumSlotsEj
  11:        0x10277d30c - __mh_execute_header
  12:        0x1030aa050 - __mh_execute_header
  13:        0x10314e4c8 - __mh_execute_header
  14:        0x103152ed4 - __mh_execute_header
  15:        0x103150980 - __mh_execute_header
  16:        0x103427a48 - __mh_execute_header
  17:        0x103428324 - __mh_execute_header
  18:        0x1042e2898 - __mh_execute_header
  19:        0x19d8e02e4 - __pthread_deallocate
"""

details = """
panicked at config/src/config/network_config.rs:257:89:
called `Result::unwrap()` on an `Err` value: No such file or directory (os error 2)"""
backtrace = """
   0:        0x10345344c - __mh_execute_header
   1:        0x103453418 - __mh_execute_header
   2:        0x103455dfc - __mh_execute_header
   3:        0x103455d9c - __mh_execute_header
   4:        0x102a37184 - __mh_execute_header
   5:        0x1043038f8 - __mh_execute_header
   6:        0x104305c40 - __mh_execute_header
   7:        0x104305968 - __mh_execute_header
   8:        0x104303414 - __mh_execute_header
   9:        0x1048c6538 - __ZN7rocksdb6ribbon6detail34BandingConfigHelper1MaybeSupportedILNS0_25ConstructionFailureChanceE1ELy128ELb0ELb0ELb1EE11GetNumSlotsEj
  10:        0x1048c64c4 - __ZN7rocksdb6ribbon6detail34BandingConfigHelper1MaybeSupportedILNS0_25ConstructionFailureChanceE1ELy128ELb0ELb0ELb1EE11GetNumSlotsEj
  11:        0x10277d30c - __mh_execute_header
  12:        0x10277deac - __mh_execute_header
  13:        0x1032ae384 - __mh_execute_header
  14:        0x1032b19c8 - __mh_execute_header
  15:        0x1032a52dc - __mh_execute_header
  16:        0x10329bb38 - __mh_execute_header
  17:        0x1032a64b0 - __mh_execute_header
  18:        0x1032a6678 - __mh_execute_header
  19:        0x1032a7c90 - __mh_execute_header
  20:        0x1043a964c - __mh_execute_header
  21:        0x1043a60cc - __mh_execute_header
  22:        0x104399b48 - __mh_execute_header
  23:        0x1043a7df8 - __mh_execute_header
  24:        0x1043a5b68 - __mh_execute_header
  25:        0x1043a9534 - __mh_execute_header
  26:        0x1043b2a4c - __mh_execute_header
  27:        0x1043aee2c - __mh_execute_header
  28:        0x104399410 - __mh_execute_header
  29:        0x1043b0d7c - __mh_execute_header
  30:        0x1043aff60 - __mh_execute_header
  31:        0x1043afcf8 - __mh_execute_header
  32:        0x1043b1a98 - __mh_execute_header
  33:        0x104398b74 - __mh_execute_header
  34:        0x10439a6ec - __mh_execute_header
  35:        0x1042e2898 - __mh_execute_header
  36:        0x19d8e02e4 - __pthread_deallocate
"""

{
  "Error": "Unexpected error: one or more services failed to start: failed to start indexer api server: one or more dependencies failed to start: failed to create docker network: Docker is not available, confirm it is installed and running. See https://aptos.dev/guides/local-development-network#faq for assistance. (version_home): error trying to connect: No such file or directory (os error 2): No such file or directory (os error 2)"

const cliArgs: string[] = ["aptos", "workspace", "run"];

const childProcess = spawn(cliCommand, cliArgs);
const childProcess = spawn(cliCommand, cliArgs, { detached: false, stdio: ['ignore', 'pipe', 'ignore'], shell: true });

const rl = readline.createInterface({
input: childProcess.stdout,
output: process.stdout,
output: undefined,
terminal: false,
});

Expand Down Expand Up @@ -123,12 +123,19 @@ export class TestNode {
public async stop() {
await new Promise((resolve, reject) => {
if (!this.info?.process?.pid) return;
kill(this.info.process.pid, (err) => {

const process = this.info.process;
kill(this.info.process.pid, 'SIGINT', (err) => {
if (err) {
reject(err);
} else {
resolve(true);
return;
}

process.on('exit', (code, signal) => {
resolve(true)
});

// TODO: timeout?
});
});
}
Expand Down
5 changes: 3 additions & 2 deletions workspace/src/internal/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { ChildProcessWithoutNullStreams } from "child_process";
import { ChildProcessByStdio, ChildProcessWithoutNullStreams } from "child_process";
import { Readable } from "stream";

export type NodeInfo = {
process: ChildProcessWithoutNullStreams;
process: ChildProcessByStdio<null, Readable, null>;
rest_api_port: number;
faucet_port: number;
indexer_port: number;
Expand Down
76 changes: 74 additions & 2 deletions workspace/src/tasks/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,91 @@ import {
} from "../internal/utils/source-files";
import { MochaOptions } from "mocha";
import { isTSProject } from "../internal/utils/typescript-support";
import * as os from 'os';
import { exec } from "child_process";
import semver from "semver";

export type TestOptionsArguments = {
timeout: string;
grep: string;
jobs: number;
};

function max_num_jobs(): number {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

running npx aptos-workspace test when docker is not available does not output the error reason (i.e docker is not available)

Aptos CLI version 6.0.2 detected.
Spinning up the server to run tests, hold on...


  1) "before all" hook: beforeAll in "{root}"

  2) "before all" hook: beforeAll in "{root}"

  0 passing (1m)
  2 failing

  1) "before all" hook: beforeAll in "{root}":
     Error: Failed to spawn test node: Timeout waiting for ports
      at Function.spawn (/Users/maayansavir/work/aptos_repos/workspace/workspace/src/internal/node.ts:57:43)
      at async exports.b (/Users/maayansavir/work/aptos_repos/workspace/workspace/src/internal/rootHook.ts:23:24)
      at async Context.beforeAll (/Users/maayansavir/work/aptos_repos/workspace/workspace/src/internal/rootHook.ts:10:5)

  2) "before all" hook: beforeAll in "{root}":
     Error: Failed to spawn test node: Timeout waiting for ports
      at Function.spawn (/Users/maayansavir/work/aptos_repos/workspace/workspace/src/internal/node.ts:57:43)
      at async exports.b (/Users/maayansavir/work/aptos_repos/workspace/workspace/src/internal/rootHook.ts:23:24)
      at async Context.beforeAll (/Users/maayansavir/work/aptos_repos/workspace/workspace/src/internal/rootHook.ts:10:5)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now I'll probably just add another check that tries to run the docker command once. Later we'll see if there's a better way to detect it.

const cpuCores = os.cpus().length;
const totalMemoryGB = os.totalmem() / (1024 ** 3);

return Math.max(1, Math.min(Math.floor(cpuCores / 4), Math.floor(totalMemoryGB / 4)));
}

async function checkAptosVersion(): Promise<void> {
return new Promise((resolve, reject) => {
exec("npx aptos --version", (error, stdout, stderr) => {
if (error) {
return reject(
new Error(
"Failed to run npx aptos, have you installed it properly?\n" +
"If your project uses `yarn`, you need to run `yarn add --dev @aptos-labs/ts-sdk`.\n" +
"If your project uses `pnpm`, you need to run `pnpm add -D @aptos-labs/aptos-cli`."
)
);
}

const versionMatch = stdout.match(/aptos (\d+\.\d+\.\d+)/);

if (!versionMatch) {
return reject(new Error("Could not determine the Aptos CLI version."));
}

const version = versionMatch[1];
const minimumVersion = "6.0.2";

if (semver.lt(version, minimumVersion)) {
return reject(
new Error(
`Aptos CLI version needs to be at least ${minimumVersion}, please run "npx aptos --update" to update.\n` +
"You might need to restart your shell in a new window for the update to take effect."
)
);
}

console.log(`Aptos CLI version ${version} detected.`);
resolve(); // Successfully completed the check
});
});
}

async function checkDockerVersion(): Promise<void> {
return new Promise((resolve, reject) => {
exec("docker -v", (error, stdout, stderr) => {
if (error) {
reject(new Error(
"An error occurred while checking the Docker version. Ensure Docker is installed and properly set up.\n\n" +
"If Docker is not installed, you can download and install it by following the instructions here: https://www.docker.com/get-started/"));
} else {
const match = stdout.trim().match(/Docker version (\d+\.\d+\.\d+)/);
if (match) {
console.log(`Docker version ${match[1]} detected.`);
resolve();
} else {
reject(new Error(`Failed to parse Docker version. Docker output: ${stdout}`));
}
}
});
});
}

export const test = async (options: TestOptionsArguments) => {
const userProjectPath = process.cwd();

isUserProjectTestsFolderExists(userProjectPath);
await checkAptosVersion();
await checkDockerVersion();

const { default: Mocha } = await import("mocha");

const mochaConfig: MochaOptions = {
timeout: options.timeout ?? 20000, // to support local testnet run, TODO improve performance
timeout: options.timeout ?? 60000, // to support local testnet run, TODO improve performance
require: [path.join(__dirname, "internal/rootHook.js")],
parallel: true,
grep: "",
Expand All @@ -43,6 +112,9 @@ export const test = async (options: TestOptionsArguments) => {
if (options.jobs !== undefined) {
mochaConfig.jobs = options.jobs;
}
else {
mochaConfig.jobs = max_num_jobs();
}

const mocha = new Mocha(mochaConfig);

Expand Down Expand Up @@ -75,7 +147,7 @@ export const test = async (options: TestOptionsArguments) => {
await mocha.loadFilesAsync();
}

console.log("Spinning up the server to run tests, hold on...");
console.log("\nSpinning up local networks to run tests, this may take a while...");
// run mocha
await new Promise<number>((resolve) => {
mocha.run(resolve);
Expand Down