# Quickstart: your first CLI test

Mooncram tests MoonBit command-line programs using commands and expected output
written in Markdown. It supports these targets:

| Target | Command inside a `mooncram` block | Prerequisites | Backend |
| --- | --- | --- | --- |
| Standalone script | `moonx ./hello.mbtx` | An existing `.mbtx` file, relative to the block's working directory or absolute. No module declaration is needed. | Always Wasm. |
| Local executable package | `moonx user/acme/cmd/tool` | A `mooncram user/acme` fence; launch from the directory whose `moon.mod` declares that exact name. The package's `moon.pkg` must declare `pkgtype(kind: "executable")` or `options("is-main": true)`. | Wasm by default; `--target native` selects native. |
| Remote executable package | `moonx user/tool[/package]@1.2.3` | A published executable with an exact SemVer version; `moonx` fetches and caches it. No module declaration is needed. | Always Wasm. |

The package names above illustrate the required form. Arbitrary binaries,
ordinary shell commands such as `echo` or `cat`, and `.mbt` files are not
supported targets. Every pipeline segment must start with `moonx` after any
temporary environment assignments. Commands do not run through a shell:
variables, wildcards and `~` are not expanded, and redirection is unsupported.

## Before executing cases

| Decision | Rule |
| --- | --- |
| Assertions | By default, assert stdout and program exit code `0`. Stderr needs explicit paired `@STDOUT` and `@STDERR` markers. |
| Paths | Local modules use `moon.mod` directly in the startup directory, without searching parents. Scripts and relative file arguments use the block's working directory: the canonical document's parent by default, or its `#cwd` directory. `#cwd` does not change module lookup. |
| Execution | `test`, `update`, and `update --dry-run` execute code with your permissions, without a sandbox or temporary workspace isolation. Dry-run leaves Markdown unchanged, but programs can still modify files. Use `check` to validate without execution. |
| Timeout | The default 60000 ms deadline is shared by each case's builds, remote fetching, execution, and output capture. |

## Create two files

Install `mooncram` and a complete MoonBit toolchain, with its `bin` directory on
`PATH`. Mooncram uses that installation's `moon` and `moonrun` for these scripts.
This example needs no repository checkout, module configuration or external
dependencies. Create the following files in any working directory:

```text
demo/
└── tests/
    ├── hello.mbtx
    └── hello.md
```

Complete contents of `tests/hello.mbtx`:

```mbt
fn main {
  println("Hello, MoonBit!")
}
```

Complete contents of `tests/hello.md`:

````markdown
# Greeting

The script greets the user.

```mooncram
$ moonx ./hello.mbtx
Hello, MoonBit!
```
````

The `$ ` line starts a case. Following lines assert stdout; the default expected
program exit code is `0`. Stderr is not asserted unless you explicitly use both
`@STDOUT` and `@STDERR` markers. Only backtick fences labeled `mooncram` are
executed; when creating `hello.md`, omit the outer four-backtick `markdown`
wrapper shown here.

## Check and run

From `demo`, run these commands in your terminal:

```sh
mooncram check tests/hello.md
mooncram test tests/hello.md
```

`check` prints the following summary and exits with `0`:

```text
1 cases, 0 errors
```

`test` builds and executes the script, prints the following summary and exits
with `0`:

```text
1 cases, 0 failed, 0 errors
```

There is no individual report for a passing case. The input `tests/hello.md`
is relative to the directory where you launch mooncram. The script
`./hello.mbtx` is relative to the document directory, `demo/tests`.
The startup directory need not contain `moon.mod` for this script-only test.

`check` validates structure and current targets, but does not compile or test
behavior. A script generated by an earlier case can be reported missing by
`check`; `test` resolves targets as cases run.

Mooncram's own exit code is independent of the expected program exit code.
A program returning `2` can pass a case ending in `[2]`. `test` returns `0`
when all cases pass, `1` for mismatches without errors, and `2` for errors or
no cases.

## Handle a changed result

If the greeting changes unexpectedly, fix the program and rerun `test`.
For an intentional change, keep these update rules in mind:

- Updates also process passing cases and may normalize expectations and fences.
  Review the full proposed diff.
- An execution error anywhere in a document prevents all its expectation
  updates and dry-run document diffs. Other documents can still update.
- `update --dry-run` can print `FAIL` and return `0`. This means the update
  preview completed without errors; use `test` for the CI pass/fail decision.

Preview the new expectations, review the diff, apply it, and verify again:

```sh
mooncram update tests/hello.md --dry-run
mooncram update tests/hello.md
mooncram test tests/hello.md
```

Keep any nondefault `--target` and `--timeout-ms` options the same for `test`,
dry-run and `update`. The final `test` must pass to confirm the updated examples.
