Skip to content

Commit

Permalink
Improve parse command tests
Browse files Browse the repository at this point in the history
This adds tests for:
* pretty output
* json output
* json location and comment flags

This required the parse function to be adjusted to make the options
configurable in test cases.

Signed-off-by: Charlie Egan <charlie@styra.com>
  • Loading branch information
charlieegan3 committed Mar 2, 2023
1 parent b8f26e0 commit 2c571c2
Show file tree
Hide file tree
Showing 2 changed files with 218 additions and 14 deletions.
20 changes: 11 additions & 9 deletions cmd/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ const (
parseFormatJSON = "json"
)

var parseParams = struct {
type parseParams struct {
format *util.EnumFlag
jsonInclude string
}{
}

var configuredParseParams = parseParams{
format: util.NewEnumFlag(parseFormatPretty, []string{parseFormatPretty, parseFormatJSON}),
jsonInclude: "",
}
Expand All @@ -43,18 +45,18 @@ var parseCommand = &cobra.Command{
return nil
},
Run: func(_ *cobra.Command, args []string) {
os.Exit(parse(args, os.Stdout, os.Stderr))
os.Exit(parse(args, &configuredParseParams, os.Stdout, os.Stderr))
},
}

func parse(args []string, stdout io.Writer, stderr io.Writer) int {
func parse(args []string, params *parseParams, stdout io.Writer, stderr io.Writer) int {
if len(args) == 0 {
return 0
}

exposeLocation := false
exposeComments := true
for _, opt := range strings.Split(parseParams.jsonInclude, ",") {
for _, opt := range strings.Split(params.jsonInclude, ",") {
value := true
if strings.HasPrefix(opt, "-") {
value = false
Expand Down Expand Up @@ -100,15 +102,15 @@ func parse(args []string, stdout io.Writer, stderr io.Writer) int {
result.Parsed.Comments = nil
}

switch parseParams.format.String() {
switch params.format.String() {
case parseFormatJSON:
bs, err := json.MarshalIndent(result.Parsed, "", " ")
if err != nil {
_ = pr.JSON(stderr, pr.Output{Errors: pr.NewOutputErrors(err)})
return 1
}

fmt.Println(string(bs))
fmt.Fprint(stdout, string(bs)+"\n")
default:
if err != nil {
fmt.Fprintln(stderr, err)
Expand All @@ -121,8 +123,8 @@ func parse(args []string, stdout io.Writer, stderr io.Writer) int {
}

func init() {
parseCommand.Flags().VarP(parseParams.format, "format", "f", "set output format")
parseCommand.Flags().StringVarP(&parseParams.jsonInclude, "json-include", "", "", "select optional elements, current options: locations, comments. E.g. --json-include locations,-comments will include locations and exclude comments.")
parseCommand.Flags().VarP(configuredParseParams.format, "format", "f", "set output format")
parseCommand.Flags().StringVarP(&configuredParseParams.jsonInclude, "json-include", "", "", "select optional elements, current options: locations, comments. E.g. --json-include locations,-comments will include locations and exclude comments.")

RootCommand.AddCommand(parseCommand)
}
212 changes: 207 additions & 5 deletions cmd/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package cmd
import (
"bytes"
"path/filepath"
"strings"
"testing"

"github.com/open-policy-agent/opa/util"
"github.com/open-policy-agent/opa/util/test"
)

Expand All @@ -16,21 +18,40 @@ func TestParseExit0(t *testing.T) {
p = 1
`,
}
errc, _, stderr := testParse(t, files)
errc, stdout, stderr, _ := testParse(t, files, &configuredParseParams)
if errc != 0 {
t.Fatalf("Expected exit code 0, got %v", errc)
}
if len(stderr) > 0 {
t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr))
}

expectedOutput := `module
package
ref
data
"x"
rule
head
ref
p
1
body
expr index=0
true
`

if got, want := string(stdout), expectedOutput; got != want {
t.Fatalf("Expected output\n%v\n, got\n%v", want, got)
}
}

func TestParseExit1(t *testing.T) {

files := map[string]string{
"x.rego": `???`,
}
errc, _, stderr := testParse(t, files)
errc, _, stderr, _ := testParse(t, files, &configuredParseParams)
if errc != 1 {
t.Fatalf("Expected exit code 1, got %v", errc)
}
Expand All @@ -39,21 +60,202 @@ func TestParseExit1(t *testing.T) {
}
}

func TestParseJSONOutput(t *testing.T) {

files := map[string]string{
"x.rego": `package x
p = 1
`,
}
errc, stdout, stderr, _ := testParse(t, files, &parseParams{
format: util.NewEnumFlag(parseFormatJSON, []string{parseFormatPretty, parseFormatJSON}),
})
if errc != 0 {
t.Fatalf("Expected exit code 0, got %v", errc)
}
if len(stderr) > 0 {
t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr))
}

expectedOutput := `{
"package": {
"path": [
{
"type": "var",
"value": "data"
},
{
"type": "string",
"value": "x"
}
]
},
"rules": [
{
"body": [
{
"index": 0,
"terms": {
"type": "boolean",
"value": true
}
}
],
"head": {
"name": "p",
"value": {
"type": "number",
"value": 1
},
"ref": [
{
"type": "var",
"value": "p"
}
]
}
}
]
}
`

if got, want := string(stdout), expectedOutput; got != want {
t.Fatalf("Expected output\n%v\n, got\n%v", want, got)
}
}

func TestParseJSONOutputWithLocations(t *testing.T) {

files := map[string]string{
"x.rego": `package x
p = 1
`,
}
errc, stdout, stderr, tempDirPath := testParse(t, files, &parseParams{
format: util.NewEnumFlag(parseFormatJSON, []string{parseFormatPretty, parseFormatJSON}),
jsonInclude: "locations",
})
if errc != 0 {
t.Fatalf("Expected exit code 0, got %v", errc)
}
if len(stderr) > 0 {
t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr))
}

expectedOutput := strings.Replace(`{
"package": {
"location": {
"file": "TEMPDIR/x.rego",
"row": 1,
"col": 1
},
"path": [
{
"location": {
"file": "TEMPDIR/x.rego",
"row": 1,
"col": 9
},
"type": "var",
"value": "data"
},
{
"location": {
"file": "TEMPDIR/x.rego",
"row": 1,
"col": 9
},
"type": "string",
"value": "x"
}
]
},
"rules": [
{
"body": [
{
"index": 0,
"terms": {
"type": "boolean",
"value": true
}
}
],
"head": {
"name": "p",
"value": {
"location": {
"file": "TEMPDIR/x.rego",
"row": 3,
"col": 7
},
"type": "number",
"value": 1
},
"ref": [
{
"type": "var",
"value": "p"
}
]
}
}
]
}
`, "TEMPDIR", tempDirPath, -1)

if got, want := string(stdout), expectedOutput; got != want {
t.Fatalf("Expected output\n%v\n, got\n%v", want, got)
}
}

func TestParseJSONOutputComments(t *testing.T) {

files := map[string]string{
"x.rego": `package x
# comment
p = 1
`,
}
errc, stdout, stderr, _ := testParse(t, files, &parseParams{
format: util.NewEnumFlag(parseFormatJSON, []string{parseFormatPretty, parseFormatJSON}),
jsonInclude: "comments",
})
if errc != 0 {
t.Fatalf("Expected exit code 0, got %v", errc)
}
if len(stderr) > 0 {
t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr))
}

expectedCommentTextValue := "IGNvbW1lbnQ="

if !strings.Contains(string(stdout), expectedCommentTextValue) {
t.Fatalf("Comment text value %q missing in output: %s", expectedCommentTextValue, string(stdout))
}
}

// Runs parse and returns the exit code, stdout, and stderr contents
func testParse(t *testing.T, files map[string]string) (int, []byte, []byte) {
func testParse(t *testing.T, files map[string]string, params *parseParams) (int, []byte, []byte, string) {
t.Helper()

stdout := new(bytes.Buffer)
stderr := new(bytes.Buffer)
var errc int

var tempDirUsed string
test.WithTempFS(files, func(path string) {
var args []string
for file := range files {
args = append(args, filepath.Join(path, file))
}
errc = parse(args, stdout, stderr)
errc = parse(args, params, stdout, stderr)

tempDirUsed = path
})

return errc, stdout.Bytes(), stderr.Bytes()
return errc, stdout.Bytes(), stderr.Bytes(), tempDirUsed
}

0 comments on commit 2c571c2

Please sign in to comment.