Skip to content
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
14 changes: 11 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,18 +102,26 @@ morphic compile openapi.yaml -o api.ir.json # ...or to a file

```
usage:
morphic compile <spec-file> [-o out.json] [--fail-on error|warning] [--skip-validate]
morphic <command> [flags]
Comment thread
OmarAlJarrah marked this conversation as resolved.
morphic compile <spec-file> [flags]
```

`morphic`, `morphic help`, and `morphic` with a help flag (`-h`, `--help` or `-help`) print the
command list. `morphic help compile` and `morphic compile --help` print a command's flags. Help
always prints to stdout and exits `0`.

The flags below are `compile`'s:

| Flag | Meaning |
|---|---|
| `-o <file>` | Write IR JSON to `<file>` instead of stdout. |
| `--fail-on error\|warning` | Exit non-zero when a diagnostic at or above this severity is emitted (default `error`). |
| `--skip-validate` | Skip the referential-integrity `validate` pass. |
| `--explain <json-pointer>` | Report what compiling produced at this source coordinate instead of writing the document. |

Diagnostics print one per line as `<severity> <code> <path>#<pointer>: <message>`. Exit codes:
`0` clean, `1` a diagnostic reached the `--fail-on` threshold (or the spec could not be lowered),
`2` a usage or I/O error.
`0` clean (and for any help request), `1` a diagnostic reached the `--fail-on` threshold (or the
spec could not be lowered), `2` a usage or I/O error.

### Library

Expand Down
61 changes: 61 additions & 0 deletions cmd/morphic/help_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/stretchr/testify/require"

"github.com/dexpace/morphic/internal/testspec"
"github.com/dexpace/morphic/ir/irtest"
)

func TestRun_HelpForms(t *testing.T) {
Expand Down Expand Up @@ -131,3 +132,63 @@ func TestRootHelp_ListsEveryCommand(t *testing.T) {
assert.Contains(t, got, c.summary)
}
}

// compareHelpGolden compares got against the golden file at path, or rewrites
// it when -update is passed to go test.
//
// The -update flag comes from irtest rather than being declared here. A second
// flag.Bool("update", ...) in this package would not merely shadow the first:
// both register on the same command-line FlagSet, so the binary panics at init
// the moment anything pulls irtest into these tests.
func compareHelpGolden(t *testing.T, path, got string) {
t.Helper()

if irtest.Update() {
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
require.NoError(t, os.WriteFile(path, []byte(got), 0o644))
return
}

want, err := os.ReadFile(path)
require.NoError(t, err, "read golden %s (run with -update to create)", path)
assert.Empty(t, cmp.Diff(string(want), got), "golden mismatch for %s", path)
}

// TestHelp_MatchesGolden pins every text the CLI renders. The two help texts
// are the ones a user asks for; the two misuse texts are the ones a user gets
// by accident, and nothing else holds their wording — writeCommandUsage and the
// root help that misuse prints to stderr could otherwise drift in silence.
//
// The wantStderr column doubles as the stream-discipline assertion: help must
// never reach stderr, misuse must never reach stdout, and whichever stream is
// not under test must be empty.
func TestHelp_MatchesGolden(t *testing.T) {
// Not parallel: the -update path writes files.
tests := []struct {
name string
args []string
golden string
wantCode int
wantStderr bool
}{
{"root", nil, "root-help.txt", 0, false},
{"compile", []string{"help", "compile"}, "compile-help.txt", 0, false},
{"compile misuse", []string{"compile"}, "compile-usage.txt", 2, true},
{"unknown command", []string{"bogus"}, "unknown-command.txt", 2, true},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var stdout, stderr bytes.Buffer
require.Equal(t, tt.wantCode, run(tt.args, &stdout, &stderr))

got, quiet := stdout.String(), stderr.String()
if tt.wantStderr {
got, quiet = quiet, got
}
require.Empty(t, quiet, "nothing may reach the stream this text does not use")

compareHelpGolden(t, filepath.Join("testdata", tt.golden), got)
})
}
}
19 changes: 19 additions & 0 deletions cmd/morphic/testdata/compile-help.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
usage:
morphic compile <spec-file> [flags]

Lower an API spec (OpenAPI 3.x) into Morphic IR JSON on stdout, and write
diagnostics to stderr.

--explain reports what compiling produced at one source coordinate — the
type node interned there, the coordinates interned beneath it, and the
diagnostics stamped at it — instead of writing the document.

flags:
-explain string
report what compiling produced at this source pointer instead of writing IR JSON
-fail-on string
fail (exit 1) on diagnostics at or above this severity: error|warning (default "error")
-o string
write IR JSON to this file instead of stdout
-skip-validate
skip the referential-integrity validate pass
4 changes: 4 additions & 0 deletions cmd/morphic/testdata/compile-usage.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
morphic: compile requires exactly one spec file
usage:
morphic compile <spec-file> [flags]
run "morphic help compile" for details.
9 changes: 9 additions & 0 deletions cmd/morphic/testdata/root-help.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
usage:
morphic <command> [flags]

morphic lowers an API spec into Morphic IR.

commands:
compile lower an API spec (OpenAPI 3.x) into Morphic IR JSON

run "morphic help <command>" for command details.
10 changes: 10 additions & 0 deletions cmd/morphic/testdata/unknown-command.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
morphic: unknown command "bogus"
usage:
morphic <command> [flags]

morphic lowers an API spec into Morphic IR.

commands:
compile lower an API spec (OpenAPI 3.x) into Morphic IR JSON

run "morphic help <command>" for command details.
Loading