Skip to content

Commit

Permalink
Merge c22e6d0 into fd0e98f
Browse files Browse the repository at this point in the history
  • Loading branch information
jchenche authored Sep 20, 2022
2 parents fd0e98f + c22e6d0 commit 6da61e6
Show file tree
Hide file tree
Showing 12 changed files with 333 additions and 4 deletions.
67 changes: 67 additions & 0 deletions taqueria-plugin-ligo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,73 @@ Lastly, `taq compile hello.mligo` will compile `hello.mligo` and emit `hello.tz`

None for now.

## The `taq test` Task

Basic usage is:

```shell
taq test <filename>
```

### Basic description
This task tests the LIGO source code and ouputs a result suggesting a failure or success. Normally you'd have a contract file and a separate test file that includes the contract's code, both within the `/contracts` directory.

For example, refer to the following 2 code snippets:
```ligo title="counter.mligo"
type storage = int
type parameter =
Increment of int
| Decrement of int
| Reset
type return = operation list * storage
// Two entrypoints
let add (store, delta : storage * int) : storage = store + delta
let sub (store, delta : storage * int) : storage = store - delta
(* Main access point that dispatches to the entrypoints according to
the smart contract parameter. *)
let main (action, store : parameter * storage) : return =
([] : operation list), // No operations
(match action with
Increment (n) -> add (store, n)
| Decrement (n) -> sub (store, n)
| Reset -> 0)
```

```ligo title="testCounter.mligo"
#include "counter.mligo"
let initial_storage = 42
let test_initial_storage =
let (taddr, _, _) = Test.originate main initial_storage 0tez in
assert (Test.get_storage taddr = initial_storage)
let test_increment =
let (taddr, _, _) = Test.originate main initial_storage 0tez in
let contr = Test.to_contract taddr in
let _ = Test.transfer_to_contract_exn contr (Increment 1) 1mutez in
assert (Test.get_storage taddr = initial_storage + 1)
```

By running `taq test testCounter.mligo`, you should get the following:
```
┌───────────────────┬──────────────────────────────────────────────┐
│ Contract │ Test Results │
├───────────────────┼──────────────────────────────────────────────┤
│ testCounter.mligo │ Everything at the top-level was executed. │
│ │ - test_initial_storage exited with value (). │
│ │ - test_increment exited with value (). │
│ │ │
│ │ 🎉 All tests passed 🎉 │
└───────────────────┴──────────────────────────────────────────────┘
```

## Template creation
The LIGO plugin also exposes a contract template via the `taq create contract <contractName>` task. This task will create a new LIGO contract in the `contracts` directory, insert some boilerplate LIGO contract code and will register the contract with Taqueria

Expand Down
67 changes: 67 additions & 0 deletions taqueria-plugin-ligo/_readme.eta
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,73 @@ Make sure you name it `hello.storages.mligo` and not `hello.storage.mligo` (note

None for now.

## The `taq test` Task

Basic usage is:

```shell
taq test <filename>
```

### Basic description
This task tests the LIGO source code and ouputs a result suggesting a failure or success. Normally you'd have a contract file and a separate test file that includes the contract's code, both within the `/contracts` directory.

For example, refer to the following 2 code snippets:
```ligo title="counter.mligo"
type storage = int

type parameter =
Increment of int
| Decrement of int
| Reset

type return = operation list * storage

// Two entrypoints

let add (store, delta : storage * int) : storage = store + delta
let sub (store, delta : storage * int) : storage = store - delta

(* Main access point that dispatches to the entrypoints according to
the smart contract parameter. *)

let main (action, store : parameter * storage) : return =
([] : operation list), // No operations
(match action with
Increment (n) -> add (store, n)
| Decrement (n) -> sub (store, n)
| Reset -> 0)
```

```ligo title="testCounter.mligo"
#include "counter.mligo"

let initial_storage = 42

let test_initial_storage =
let (taddr, _, _) = Test.originate main initial_storage 0tez in
assert (Test.get_storage taddr = initial_storage)

let test_increment =
let (taddr, _, _) = Test.originate main initial_storage 0tez in
let contr = Test.to_contract taddr in
let _ = Test.transfer_to_contract_exn contr (Increment 1) 1mutez in
assert (Test.get_storage taddr = initial_storage + 1)
```

By running `taq test testCounter.mligo`, you should get the following:
```
┌───────────────────┬──────────────────────────────────────────────┐
│ Contract │ Test Results │
├───────────────────┼──────────────────────────────────────────────┤
│ testCounter.mligo │ Everything at the top-level was executed. │
│ │ - test_initial_storage exited with value (). │
│ │ - test_increment exited with value (). │
│ │ │
│ │ 🎉 All tests passed 🎉 │
└───────────────────┴──────────────────────────────────────────────┘
```

## Template creation
The LIGO plugin also exposes a contract template via the `taq create contract <contractName>` task. This task will create a new LIGO contract in the `contracts` directory, insert some boilerplate LIGO contract code and will register the contract with Taqueria

Expand Down
3 changes: 1 addition & 2 deletions taqueria-plugin-ligo/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ const compileExprs = (parsedArgs: Opts, sourceFile: string, exprKind: ExprKind):
})
.then(mergeArtifactsOutput(sourceFile));

const compileContractWithStorageAndParameter = async (parsedArgs: Opts, sourceFile: string) => {
const compileContractWithStorageAndParameter = async (parsedArgs: Opts, sourceFile: string): Promise<TableRow[]> => {
const contractCompileResult = await compileContract(parsedArgs, sourceFile);
if (contractCompileResult.artifact === COMPILE_ERR_MSG) return [contractCompileResult];

Expand Down Expand Up @@ -226,7 +226,6 @@ const mergeArtifactsOutput = (sourceFile: string) =>

export const compile = (parsedArgs: Opts): Promise<void> => {
const sourceFile = parsedArgs.sourceFile;
if (!sourceFile) return sendAsyncErr('No source file specified.');
let p: Promise<TableRow[]>;
if (isStoragesFile(sourceFile)) p = compileExprs(parsedArgs, sourceFile, 'storage');
else if (isParametersFile(sourceFile)) p = compileExprs(parsedArgs, sourceFile, 'parameter');
Expand Down
7 changes: 7 additions & 0 deletions taqueria-plugin-ligo/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ Plugin.create(i18n => ({
handler: 'proxy',
encoding: 'json',
}),
Task.create({
task: 'test',
command: 'test <sourceFile>',
description: 'Test a smart contract written in LIGO',
handler: 'proxy',
encoding: 'json',
}),
],
templates: [
Template.create({
Expand Down
5 changes: 3 additions & 2 deletions taqueria-plugin-ligo/ligo.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { sendAsyncErr } from '@taqueria/node-sdk';
import { RequestArgs } from '@taqueria/node-sdk/types';
import compile from './compile';
import { test } from './test';

interface Opts extends RequestArgs.ProxyRequestArgs {
sourceFile: string;
Expand All @@ -10,8 +11,8 @@ export const ligo = (parsedArgs: Opts): Promise<void> => {
switch (parsedArgs.task) {
case 'compile':
return compile(parsedArgs);
// case 'test':
// return test(parsedArgs); // TODO: to be implemented in the future
case 'test':
return test(parsedArgs);
default:
return sendAsyncErr(`${parsedArgs.task} is not an understood task by the LIGO plugin`);
}
Expand Down
52 changes: 52 additions & 0 deletions taqueria-plugin-ligo/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { execCmd, getArch, sendAsyncErr, sendErr, sendJsonRes, sendWarn } from '@taqueria/node-sdk';
import { RequestArgs } from '@taqueria/node-sdk/types';
import { join } from 'path';

interface Opts extends RequestArgs.t {
sourceFile: string;
}

type TableRow = { contract: string; testResults: string };

const getInputFilename = (parsedArgs: Opts, sourceFile: string): string =>
join(parsedArgs.config.contractsDir, sourceFile);

const getTestContractCmd = (parsedArgs: Opts, sourceFile: string): string => {
const projectDir = process.env.PROJECT_DIR ?? parsedArgs.projectDir;
if (!projectDir) throw `No project directory provided`;
const baseCmd =
`DOCKER_DEFAULT_PLATFORM=linux/amd64 docker run --rm -v \"${projectDir}\":/project -w /project -u $(id -u):$(id -g) ligolang/ligo:next run test`;
const inputFile = getInputFilename(parsedArgs, sourceFile);
const cmd = `${baseCmd} ${inputFile}`;
return cmd;
};

const testContract = (parsedArgs: Opts, sourceFile: string): Promise<TableRow> =>
getArch()
.then(() => getTestContractCmd(parsedArgs, sourceFile))
.then(execCmd)
.then(({ stdout, stderr }) => {
if (stderr.length > 0) sendWarn(stderr);
const result = '🎉 All tests passed 🎉';
return {
contract: sourceFile,
testResults: stdout.length > 0 ? `${stdout}\n${result}` : result,
};
})
.catch(err => {
sendErr(`\n=== For ${sourceFile} ===`);
sendErr(err.message.replace(/Command failed.+?\n/, ''));
return {
contract: sourceFile,
testResults: 'Some tests failed :(',
};
});

export const test = (parsedArgs: Opts): Promise<void> => {
const sourceFile = parsedArgs.sourceFile;
return testContract(parsedArgs, sourceFile).then(result => [result]).then(sendJsonRes).catch(err =>
sendAsyncErr(err, false)
);
};

export default test;
13 changes: 13 additions & 0 deletions tests/e2e/data/hello-tacos-invalid-tests.mligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#include "hello-tacos.mligo"

let available_tacos = 100

let test_available_tacos =
let (taddr, _, _) = Test.originate main initial_storage 100tez in
assert (Test.get_storage taddr = available_tacos)

let test_buy_tacos =
let (taddr, _, _) = Test.originate main initial_storage 100tez in
let contr = Test.to_contract taddr in
let _ = Test.transfer_to_contract_exn contr 1mutez in
assert (Test.get_storage taddr = test_available_tacos - 1)
12 changes: 12 additions & 0 deletions tests/e2e/data/hello-tacos-tests.mligo
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "hello-tacos.mligo"

let available_tacos = 100n

let test_available_tacos =
let (taddr, _, _) = Test.originate main available_tacos 100tez in
assert (Test.get_storage taddr = available_tacos)

let test_buy_tacos =
let res = main(1n, available_tacos) in (* The nature of the contract we don't have entrypoints
// The only way to test it is to call function itself *)
assert (res.1 = 99n)
2 changes: 2 additions & 0 deletions tests/e2e/data/help-contents/help-contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Commands:
taq list-contracts List registered contracts
taq compile Provided by more than one plugin. The option -
-plugin is required.
taq test <sourceFile> Test a smart contract written in LIGO
taq create <template> Create files from pre-existing templates
Options:
Expand Down Expand Up @@ -96,6 +97,7 @@ Commands:
taq list-contracts List registered contracts
taq compile Provided by more than one plugin. The option -
-plugin is required.
taq test <sourceFile> Test a smart contract written in LIGO
taq create <template> Create files from pre-existing templates
Options:
Expand Down
1 change: 1 addition & 0 deletions tests/e2e/data/help-contents/ligo-contents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Commands:
tax to Michelson code, along with its associat
ed storages and parameters files if they are f
ound [aliases: c, compile-ligo]
taq test <sourceFile> Test a smart contract written in LIGO
taq create <template> Create files from pre-existing templates
Options:
Expand Down
40 changes: 40 additions & 0 deletions tests/e2e/taqueria-plugin-ligo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,46 @@ describe('E2E Testing for taqueria ligo plugin', () => {
}
});

test('Verify that taqueria ligo plugin can run ligo test using taq test <sourceFile> command', async () => {
try {
// 1. Copy contract and tests files from data folder to taqueria project folder
await exec(`cp e2e/data/hello-tacos-tests.mligo ${taqueriaProjectPath}/contracts`);

// 2. Run taq test ${testFileName}
const { stdout, stderr } = await exec(`taq test hello-tacos-tests.mligo`, { cwd: `./${taqueriaProjectPath}` });
expect(stdout).toContain('All tests passed');
} catch (error) {
throw new Error(`error: ${error}`);
}
});

test('Verify that taqueria ligo plugin will output proper error message running taq test <sourceFile> command against invalid test file', async () => {
try {
// 1. Copy contract and tests files from data folder to taqueria project folder
await exec(`cp e2e/data/hello-tacos-invalid-tests.mligo ${taqueriaProjectPath}/contracts`);

// 2. Run taq test ${testFileName}
// const output = await exec(`taq test hello-tacos-invalid-tests.mligo`, { cwd: `./${taqueriaProjectPath}` });
const { stdout, stderr } = await exec(`taq test hello-tacos-invalid-tests.mligo`, {
cwd: `./${taqueriaProjectPath}`,
});
expect(stdout).toContain('Some tests failed :(');
expect(stderr).toContain('Variable "initial_storage" not found.');
} catch (error) {
throw new Error(`error: ${error}`);
}
});

test('Verify that taqueria ligo plugin will output proper error message running taq test <sourceFile> command against non-existing file', async () => {
try {
// 1. Run taq test ${testFileName} against file that does not exist
const { stdout, stderr } = await exec(`taq test hello-tacos-test.mligo`, { cwd: `./${taqueriaProjectPath}` });
expect(stderr).toContain('contracts/hello-tacos-test.mligo: No such file or directory');
} catch (error) {
throw new Error(`error: ${error}`);
}
});

test.skip('Verify that the LIGO contract template is instantiated with the right content and registered', async () => {
try {
await exec(`taq create contract counter.mligo`, { cwd: `./${taqueriaProjectPath}` });
Expand Down
Loading

0 comments on commit 6da61e6

Please sign in to comment.