mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-12 19:32:48 -06:00
afb30d3f9d
Brace yourselves! For there are many touched files here. No changes in semantics however. Spent a long time trying out the various optional rules gocritic provides, and settled for a few of them. There are more I really like, but that would take many hours to address across the codebase. Perhaps others find gocritic too pedantic? If so, we can merge the fixes without enabling the rule. Signed-off-by: Anders Eknert <anders@styra.com>
1201 lines
26 KiB
Go
1201 lines
26 KiB
Go
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/open-policy-agent/opa/v1/util"
|
|
"github.com/open-policy-agent/opa/v1/util/test"
|
|
)
|
|
|
|
func TestParseExit0(t *testing.T) {
|
|
|
|
files := map[string]string{
|
|
"x.rego": `package x
|
|
|
|
p = 1
|
|
`,
|
|
}
|
|
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, &configuredParseParams)
|
|
if errc != 1 {
|
|
t.Fatalf("Expected exit code 1, got %v", errc)
|
|
}
|
|
if len(stderr) == 0 {
|
|
t.Fatalf("Expected output in stderr")
|
|
}
|
|
}
|
|
|
|
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.ReplaceAll(`{
|
|
"package": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 1,
|
|
"text": "cGFja2FnZQ=="
|
|
},
|
|
"path": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 9,
|
|
"text": "eA=="
|
|
},
|
|
"type": "var",
|
|
"value": "data"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 9,
|
|
"text": "eA=="
|
|
},
|
|
"type": "string",
|
|
"value": "x"
|
|
}
|
|
]
|
|
},
|
|
"rules": [
|
|
{
|
|
"body": [
|
|
{
|
|
"index": 0,
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 5,
|
|
"text": "MQ=="
|
|
},
|
|
"terms": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 5,
|
|
"text": "MQ=="
|
|
},
|
|
"type": "boolean",
|
|
"value": true
|
|
}
|
|
}
|
|
],
|
|
"head": {
|
|
"name": "p",
|
|
"value": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 5,
|
|
"text": "MQ=="
|
|
},
|
|
"type": "number",
|
|
"value": 1
|
|
},
|
|
"ref": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 1,
|
|
"text": "cA=="
|
|
},
|
|
"type": "var",
|
|
"value": "p"
|
|
}
|
|
],
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 1,
|
|
"text": "cCA9IDE="
|
|
}
|
|
},
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 1,
|
|
"text": "cCA9IDE="
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`, "TEMPDIR", tempDirPath)
|
|
|
|
gotLines := strings.Split(string(stdout), "\n")
|
|
wantLines := strings.Split(expectedOutput, "\n")
|
|
min := len(gotLines)
|
|
if len(wantLines) < min {
|
|
min = len(wantLines)
|
|
}
|
|
|
|
for i := range min {
|
|
if gotLines[i] != wantLines[i] {
|
|
t.Fatalf("Expected line %d to be\n%v\n, got\n%v", i, wantLines[i], gotLines[i])
|
|
}
|
|
}
|
|
|
|
if len(gotLines) != len(wantLines) {
|
|
t.Fatalf("Expected %d lines, got %d", len(wantLines), len(gotLines))
|
|
}
|
|
}
|
|
|
|
func TestParseRefsJSONOutput(t *testing.T) {
|
|
|
|
files := map[string]string{
|
|
"x.rego": `package x
|
|
|
|
a.b.c := true
|
|
`,
|
|
}
|
|
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": {
|
|
"value": {
|
|
"type": "boolean",
|
|
"value": true
|
|
},
|
|
"assign": true,
|
|
"ref": [
|
|
{
|
|
"type": "var",
|
|
"value": "a"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"value": "b"
|
|
},
|
|
{
|
|
"type": "string",
|
|
"value": "c"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
if got, want := string(stdout), expectedOutput; got != want {
|
|
t.Fatalf("Expected output\n%v\n, got\n%v", want, got)
|
|
}
|
|
}
|
|
|
|
func TestParseRefsJSONOutputWithLocations(t *testing.T) {
|
|
|
|
files := map[string]string{
|
|
"x.rego": `package x
|
|
|
|
a.b.c := true
|
|
`,
|
|
}
|
|
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.ReplaceAll(`{
|
|
"package": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 1,
|
|
"text": "cGFja2FnZQ=="
|
|
},
|
|
"path": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 9,
|
|
"text": "eA=="
|
|
},
|
|
"type": "var",
|
|
"value": "data"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 9,
|
|
"text": "eA=="
|
|
},
|
|
"type": "string",
|
|
"value": "x"
|
|
}
|
|
]
|
|
},
|
|
"rules": [
|
|
{
|
|
"body": [
|
|
{
|
|
"index": 0,
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 10,
|
|
"text": "dHJ1ZQ=="
|
|
},
|
|
"terms": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 10,
|
|
"text": "dHJ1ZQ=="
|
|
},
|
|
"type": "boolean",
|
|
"value": true
|
|
}
|
|
}
|
|
],
|
|
"head": {
|
|
"value": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 10,
|
|
"text": "dHJ1ZQ=="
|
|
},
|
|
"type": "boolean",
|
|
"value": true
|
|
},
|
|
"assign": true,
|
|
"ref": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 1,
|
|
"text": "YQ=="
|
|
},
|
|
"type": "var",
|
|
"value": "a"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 3,
|
|
"text": "Yg=="
|
|
},
|
|
"type": "string",
|
|
"value": "b"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 5,
|
|
"text": "Yw=="
|
|
},
|
|
"type": "string",
|
|
"value": "c"
|
|
}
|
|
],
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 1,
|
|
"text": "YS5iLmMgOj0gdHJ1ZQ=="
|
|
}
|
|
},
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 3,
|
|
"col": 1,
|
|
"text": "YS5iLmMgOj0gdHJ1ZQ=="
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`, "TEMPDIR", tempDirPath)
|
|
|
|
gotLines := strings.Split(string(stdout), "\n")
|
|
wantLines := strings.Split(expectedOutput, "\n")
|
|
min := len(gotLines)
|
|
if len(wantLines) < min {
|
|
min = len(wantLines)
|
|
}
|
|
|
|
for i := range min {
|
|
if gotLines[i] != wantLines[i] {
|
|
t.Fatalf("Expected line %d to be\n%v\n, got\n%v", i, wantLines[i], gotLines[i])
|
|
}
|
|
}
|
|
|
|
if len(gotLines) != len(wantLines) {
|
|
t.Fatalf("Expected %d lines, got %d", len(wantLines), len(gotLines))
|
|
}
|
|
}
|
|
func TestParseRulesBlockJSONOutputWithLocations(t *testing.T) {
|
|
|
|
files := map[string]string{
|
|
"x.rego": `package x
|
|
import rego.v1
|
|
|
|
default allow = false
|
|
allow = true if {
|
|
input.method == "GET"
|
|
input.path = ["getUser", user]
|
|
input.user == user
|
|
}
|
|
`,
|
|
}
|
|
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.ReplaceAll(`{
|
|
"package": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 1,
|
|
"text": "cGFja2FnZQ=="
|
|
},
|
|
"path": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 9,
|
|
"text": "eA=="
|
|
},
|
|
"type": "var",
|
|
"value": "data"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 1,
|
|
"col": 9,
|
|
"text": "eA=="
|
|
},
|
|
"type": "string",
|
|
"value": "x"
|
|
}
|
|
]
|
|
},
|
|
"imports": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 2,
|
|
"col": 1,
|
|
"text": "aW1wb3J0"
|
|
},
|
|
"path": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 2,
|
|
"col": 8,
|
|
"text": "cmVnby52MQ=="
|
|
},
|
|
"type": "ref",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 2,
|
|
"col": 8,
|
|
"text": "cmVnbw=="
|
|
},
|
|
"type": "var",
|
|
"value": "rego"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 2,
|
|
"col": 13,
|
|
"text": "djE="
|
|
},
|
|
"type": "string",
|
|
"value": "v1"
|
|
}
|
|
]
|
|
}
|
|
}
|
|
],
|
|
"rules": [
|
|
{
|
|
"body": [
|
|
{
|
|
"index": 0,
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 4,
|
|
"col": 1,
|
|
"text": "ZGVmYXVsdA=="
|
|
},
|
|
"terms": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 4,
|
|
"col": 1,
|
|
"text": "ZGVmYXVsdA=="
|
|
},
|
|
"type": "boolean",
|
|
"value": true
|
|
}
|
|
}
|
|
],
|
|
"default": true,
|
|
"head": {
|
|
"name": "allow",
|
|
"value": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 4,
|
|
"col": 17,
|
|
"text": "ZmFsc2U="
|
|
},
|
|
"type": "boolean",
|
|
"value": false
|
|
},
|
|
"ref": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 4,
|
|
"col": 9,
|
|
"text": "YWxsb3c="
|
|
},
|
|
"type": "var",
|
|
"value": "allow"
|
|
}
|
|
],
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 4,
|
|
"col": 9,
|
|
"text": "YWxsb3cgPSBmYWxzZQ=="
|
|
}
|
|
},
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 4,
|
|
"col": 1,
|
|
"text": "ZGVmYXVsdA=="
|
|
}
|
|
},
|
|
{
|
|
"body": [
|
|
{
|
|
"index": 0,
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 6,
|
|
"col": 3,
|
|
"text": "aW5wdXQubWV0aG9kID09ICJHRVQi"
|
|
},
|
|
"terms": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 6,
|
|
"col": 16,
|
|
"text": "PT0="
|
|
},
|
|
"type": "ref",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 6,
|
|
"col": 16,
|
|
"text": "PT0="
|
|
},
|
|
"type": "var",
|
|
"value": "equal"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 6,
|
|
"col": 3,
|
|
"text": "aW5wdXQubWV0aG9k"
|
|
},
|
|
"type": "ref",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 6,
|
|
"col": 3,
|
|
"text": "aW5wdXQ="
|
|
},
|
|
"type": "var",
|
|
"value": "input"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 6,
|
|
"col": 9,
|
|
"text": "bWV0aG9k"
|
|
},
|
|
"type": "string",
|
|
"value": "method"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 6,
|
|
"col": 19,
|
|
"text": "IkdFVCI="
|
|
},
|
|
"type": "string",
|
|
"value": "GET"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"index": 1,
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 3,
|
|
"text": "aW5wdXQucGF0aCA9IFsiZ2V0VXNlciIsIHVzZXJd"
|
|
},
|
|
"terms": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 14,
|
|
"text": "PQ=="
|
|
},
|
|
"type": "ref",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 14,
|
|
"text": "PQ=="
|
|
},
|
|
"type": "var",
|
|
"value": "eq"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 3,
|
|
"text": "aW5wdXQucGF0aA=="
|
|
},
|
|
"type": "ref",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 3,
|
|
"text": "aW5wdXQ="
|
|
},
|
|
"type": "var",
|
|
"value": "input"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 9,
|
|
"text": "cGF0aA=="
|
|
},
|
|
"type": "string",
|
|
"value": "path"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 16,
|
|
"text": "WyJnZXRVc2VyIiwgdXNlcl0="
|
|
},
|
|
"type": "array",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 17,
|
|
"text": "ImdldFVzZXIi"
|
|
},
|
|
"type": "string",
|
|
"value": "getUser"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 7,
|
|
"col": 28,
|
|
"text": "dXNlcg=="
|
|
},
|
|
"type": "var",
|
|
"value": "user"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"index": 2,
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 8,
|
|
"col": 3,
|
|
"text": "aW5wdXQudXNlciA9PSB1c2Vy"
|
|
},
|
|
"terms": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 8,
|
|
"col": 14,
|
|
"text": "PT0="
|
|
},
|
|
"type": "ref",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 8,
|
|
"col": 14,
|
|
"text": "PT0="
|
|
},
|
|
"type": "var",
|
|
"value": "equal"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 8,
|
|
"col": 3,
|
|
"text": "aW5wdXQudXNlcg=="
|
|
},
|
|
"type": "ref",
|
|
"value": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 8,
|
|
"col": 3,
|
|
"text": "aW5wdXQ="
|
|
},
|
|
"type": "var",
|
|
"value": "input"
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 8,
|
|
"col": 9,
|
|
"text": "dXNlcg=="
|
|
},
|
|
"type": "string",
|
|
"value": "user"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 8,
|
|
"col": 17,
|
|
"text": "dXNlcg=="
|
|
},
|
|
"type": "var",
|
|
"value": "user"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"head": {
|
|
"name": "allow",
|
|
"value": {
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 5,
|
|
"col": 9,
|
|
"text": "dHJ1ZQ=="
|
|
},
|
|
"type": "boolean",
|
|
"value": true
|
|
},
|
|
"ref": [
|
|
{
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 5,
|
|
"col": 1,
|
|
"text": "YWxsb3c="
|
|
},
|
|
"type": "var",
|
|
"value": "allow"
|
|
}
|
|
],
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 5,
|
|
"col": 1,
|
|
"text": "YWxsb3cgPSB0cnVl"
|
|
}
|
|
},
|
|
"location": {
|
|
"file": "TEMPDIR/x.rego",
|
|
"row": 5,
|
|
"col": 1,
|
|
"text": "YWxsb3cgPSB0cnVlIGlmIHsKICBpbnB1dC5tZXRob2QgPT0gIkdFVCIKICBpbnB1dC5wYXRoID0gWyJnZXRVc2VyIiwgdXNlcl0KICBpbnB1dC51c2VyID09IHVzZXIKfQ=="
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`, "TEMPDIR", tempDirPath)
|
|
|
|
gotLines := strings.Split(string(stdout), "\n")
|
|
wantLines := strings.Split(expectedOutput, "\n")
|
|
min := len(gotLines)
|
|
if len(wantLines) < min {
|
|
min = len(wantLines)
|
|
}
|
|
|
|
for i := range min {
|
|
if gotLines[i] != wantLines[i] {
|
|
t.Fatalf("Expected line %d to be\n%v\n, got\n%v", i, wantLines[i], gotLines[i])
|
|
}
|
|
}
|
|
|
|
if len(gotLines) != len(wantLines) {
|
|
t.Fatalf("Expected %d lines, got %d", len(wantLines), len(gotLines))
|
|
}
|
|
}
|
|
|
|
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))
|
|
}
|
|
}
|
|
|
|
func TestParse_DefaultRegoVersion(t *testing.T) {
|
|
tests := []struct {
|
|
note string
|
|
module string
|
|
expErrs []string
|
|
}{
|
|
{
|
|
note: "v0 module",
|
|
module: `package test
|
|
a[x] {
|
|
x := 42
|
|
}`,
|
|
expErrs: []string{
|
|
"`if` keyword is required before rule body",
|
|
"`contains` keyword is required for partial set rules",
|
|
},
|
|
},
|
|
{
|
|
note: "v1 module",
|
|
module: `package test
|
|
a contains x if {
|
|
x := 42
|
|
}`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.note, func(t *testing.T) {
|
|
files := map[string]string{
|
|
"test.rego": tc.module,
|
|
}
|
|
|
|
_, _, stderr, _ := testParse(t, files, &parseParams{
|
|
format: util.NewEnumFlag(parseFormatPretty, []string{parseFormatPretty, parseFormatJSON}),
|
|
})
|
|
|
|
if len(tc.expErrs) > 0 {
|
|
errs := string(stderr)
|
|
for _, expErr := range tc.expErrs {
|
|
if !strings.Contains(errs, expErr) {
|
|
t.Fatalf("Expected error:\n\n%q\n\ngot:\n\n%s", expErr, errs)
|
|
}
|
|
}
|
|
} else if len(stderr) > 0 {
|
|
t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseCompatibleFlags(t *testing.T) {
|
|
tests := []struct {
|
|
note string
|
|
v0Compatible bool
|
|
v1Compatible bool
|
|
policy string
|
|
expErrs []string
|
|
}{
|
|
{
|
|
note: "v0, keywords not used",
|
|
v0Compatible: true,
|
|
policy: `package test
|
|
p[v] {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
{
|
|
note: "v0, keywords not imported",
|
|
v0Compatible: true,
|
|
policy: `package test
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
expErrs: []string{
|
|
"var cannot be used for rule name",
|
|
},
|
|
},
|
|
{
|
|
note: "v0, keywords imported",
|
|
v0Compatible: true,
|
|
policy: `package test
|
|
import future.keywords
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
{
|
|
note: "v0, rego.v1 imported",
|
|
v0Compatible: true,
|
|
policy: `package test
|
|
import rego.v1
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
|
|
{
|
|
note: "v1, keywords not used",
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
p[v] {
|
|
v := input.x
|
|
}`,
|
|
expErrs: []string{
|
|
"`if` keyword is required before rule body",
|
|
"`contains` keyword is required for partial set rules",
|
|
},
|
|
},
|
|
{
|
|
note: "v1, keywords not imported",
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
{
|
|
note: "v1, keywords imported",
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
import future.keywords
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
{
|
|
note: "v1, rego.v1 imported",
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
import rego.v1
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
|
|
// v0 takes precedence over v1
|
|
{
|
|
note: "v0+v1, keywords not used",
|
|
v0Compatible: true,
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
p[v] {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
{
|
|
note: "v0+v1, keywords not imported",
|
|
v0Compatible: true,
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
expErrs: []string{
|
|
"var cannot be used for rule name",
|
|
},
|
|
},
|
|
{
|
|
note: "v0+1, keywords imported",
|
|
v0Compatible: true,
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
import future.keywords
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
{
|
|
note: "v0+v1, rego.v1 imported",
|
|
v0Compatible: true,
|
|
v1Compatible: true,
|
|
policy: `package test
|
|
import rego.v1
|
|
p contains v if {
|
|
v := input.x
|
|
}`,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.note, func(t *testing.T) {
|
|
files := map[string]string{
|
|
"policy.rego": tc.policy,
|
|
}
|
|
|
|
_, _, stderr, _ := testParse(t, files, &parseParams{
|
|
format: util.NewEnumFlag(parseFormatPretty, []string{parseFormatPretty, parseFormatJSON}),
|
|
v0Compatible: tc.v0Compatible,
|
|
v1Compatible: tc.v1Compatible,
|
|
})
|
|
|
|
if len(tc.expErrs) > 0 {
|
|
errs := string(stderr)
|
|
for _, expErr := range tc.expErrs {
|
|
if !strings.Contains(errs, expErr) {
|
|
t.Fatalf("Expected error:\n\n%q\n\ngot:\n\n%s", expErr, errs)
|
|
}
|
|
}
|
|
} else if len(stderr) > 0 {
|
|
t.Fatalf("Expected no stderr output, got:\n%s\n", string(stderr))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Runs parse and returns the exit code, stdout, and stderr contents
|
|
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, params, stdout, stderr)
|
|
|
|
tempDirUsed = path
|
|
})
|
|
|
|
return errc, stdout.Bytes(), stderr.Bytes(), tempDirUsed
|
|
}
|