# Complete templates for common assertions

These examples supply the entire program and test document. Copy both files;
no placeholder scripts or module configuration are needed. Install `mooncram`
and a complete MoonBit toolchain, with its `bin` directory on `PATH`. The script
uses that installation's `moon` and `moonrun`, and pins `moonbitlang/async`
and `moonbitlang/x`; the first build needs access to download these dependencies.

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

## The program

Complete contents of `tests/demo.mbtx`:

```mbt
import {
  "moonbitlang/async@0.22.4",
  "moonbitlang/async@0.22.4/stdio",
  "moonbitlang/core/env",
  "moonbitlang/x@0.5.5/sys",
}

async fn main {
  match @env.args().get(1).unwrap_or("hello") {
    "hello" => @stdio.stdout.write("Hello, MoonBit!\n")
    "fail" => {
      @stdio.stderr.write("error: invalid argument\n")
      @sys.exit(2)
    }
    "warn" => {
      @stdio.stdout.write("progress\n")
      @stdio.stderr.write("warning: using default\n")
    }
    "version" => {
      let version = @env.get_env_var("DEMO_VERSION").unwrap_or("1.2.3")
      @stdio.stdout.write("version: \{version}\n")
    }
    "env" => {
      let name = @env.get_env_var("DEMO_NAME").unwrap_or("World")
      @stdio.stdout.write("Hello, \{name}!\n")
    }
    "copy" => @stdio.stdout.write(@stdio.stdin.read_all())
    "prompt" => @stdio.stdout.write("ready> ")
    _ => @sys.exit(3)
  }
}
```

## The test document

Complete contents of `tests/recipes.md` (omit the outer `markdown` wrapper):

````markdown
# CLI assertion examples

## Nonzero exit code, empty stdout and an exact error

```mooncram
$ moonx ./demo.mbtx fail
@STDOUT (empty)
@STDERR
error: invalid argument
[2]
```

## Assert stderr only

```mooncram
$ moonx ./demo.mbtx warn
@STDOUT (ignore)
@STDERR
warning: using default
```

## Require stderr to be empty

```mooncram
$ moonx ./demo.mbtx hello
@STDOUT
Hello, MoonBit!
@STDERR (empty)
```

## Match the changing field with a glob or regex

```mooncram
$ DEMO_VERSION=1.2.3 moonx ./demo.mbtx version
version: * (glob)

$ DEMO_VERSION=2.4.0 moonx ./demo.mbtx version
version: [0-9]+\.[0-9]+\.[0-9]+ (regex)
```

## Read a temporary environment variable

```mooncram
$ DEMO_NAME='Moon Bit' moonx ./demo.mbtx env
Hello, Moon Bit!
```

## Pipe one script's stdout into another's stdin

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

## Assert a prompt without a final newline

```mooncram
$ moonx ./demo.mbtx prompt
"ready> " (escaped) (no-eol)
```
````

From `demo`, run:

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

`check` prints `8 cases, 0 errors`; `test` prints
`8 cases, 0 failed, 0 errors`. Both exit with `0`, including the case whose
program intentionally exits with `2`.

## Adapt the templates

- Both stream markers are required when asserting stderr. `(ignore)` skips that
  stream's content assertion; `(empty)` requires zero characters, so even one
  newline fails. Without markers, only stdout is asserted. Exit status is still
  checked in all these forms, and defaults to `0`.
- Choose exact matching for fixed output. Use glob for an unconstrained dynamic
  field, or regex when the field's format matters. The version examples supply
  two values deterministically; in a real version test, use an exact expectation
  if changing the version should require an explicit test update. Keep the stable
  `version: ` prefix, and avoid replacing an entire meaningful line with `* (glob)`.
  Every pattern matches one whole line; it does not skip extra lines.
- The program reads `DEMO_NAME` itself. Assignments affect only that segment and
  its build, not later cases. `$DEMO_NAME` in a command argument is literal text;
  mooncram does not expand it.
- Every pipeline segment names `moonx`. Only the final stdout is asserted;
  stderr is concatenated in segment order. The exit status is the rightmost
  nonzero status, or `0` if all segments succeed. No shell `cat` is involved:
  the supplied `copy` mode reads stdin.
- The prompt's space is part of the expected output. JSON escaping makes that
  space visible, and `(no-eol)` requires no final newline. Omit `(no-eol)` when
  the program writes a newline.
