mirror of
https://github.com/open-policy-agent/opa.git
synced 2026-08-13 03:42:35 -06:00
db035b09fc
Makes OPA build and pass its tests on Go 1.27, while keeping Go 1.25 and 1.26 working. JSON output is unchanged on every supported version. Go 1.27 json package honours `encoding.TextAppender`. Many v1 ast types implement AppendText to build their Rego string cheaply, so on 1.27 they would have marshalled as Rego text. Files built only with 1.27 now implement MarshalJSONTo. Library users should keep using `json.Marshal` etc. The MarshalJSONTo methods are implementation details, are absent from 1.25 and 1.26 builds, and may change. --------- Signed-off-by: Anders Eknert <anders.eknert@apple.com> Signed-off-by: Charlie Egan <charlie_egan@apple.com> Co-authored-by: Charlie Egan <charlie_egan@apple.com>
1571 lines
38 KiB
Go
1571 lines
38 KiB
Go
//go:build !go1.27
|
|
|
|
// Copyright 2020 The OPA Authors. All rights reserved.
|
|
// Use of this source code is governed by an Apache2
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"maps"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/open-policy-agent/opa/cmd/formats"
|
|
"github.com/open-policy-agent/opa/internal/presentation"
|
|
"github.com/open-policy-agent/opa/v1/ast"
|
|
"github.com/open-policy-agent/opa/v1/bundle"
|
|
"github.com/open-policy-agent/opa/v1/rego"
|
|
"github.com/open-policy-agent/opa/v1/util"
|
|
"github.com/open-policy-agent/opa/v1/util/test"
|
|
)
|
|
|
|
// Minimize the number of tests that *actually* run the benchmarks, they are pretty slow.
|
|
// Have one test that exercises the whole flow.
|
|
func TestRunBenchmark(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
|
|
args := []string{"1 + 1"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
|
|
// Expect a json serialized benchmark result with histogram fields
|
|
var br testing.BenchmarkResult
|
|
err = util.UnmarshalJSON(buf.Bytes(), &br)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error unmarshalling output: %s", err)
|
|
}
|
|
|
|
if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 {
|
|
t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br)
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain histogram_timer_rego_query_eval_ns_count, got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N")
|
|
}
|
|
}
|
|
|
|
func TestRunBenchmarkWithQueryImport(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
// We add the rego.v1 import ..
|
|
params.imports = newrepeatedStringFlag([]string{"rego.v1"})
|
|
|
|
// .. which provides the 'in' keyword
|
|
args := []string{`"a" in ["a", "b", "c"]`}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
|
|
// Expect a json serialized benchmark result with histogram fields
|
|
var br testing.BenchmarkResult
|
|
err = util.UnmarshalJSON(buf.Bytes(), &br)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error unmarshalling output: %s", err)
|
|
}
|
|
|
|
if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 {
|
|
t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br)
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain histogram_timer_rego_query_eval_ns_count, got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N")
|
|
}
|
|
}
|
|
|
|
func TestRunBenchmarkE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
|
|
args := []string{"1 + 1"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
|
|
// Expect a json serialized benchmark result with histogram fields
|
|
var br testing.BenchmarkResult
|
|
err = util.UnmarshalJSON(buf.Bytes(), &br)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error unmarshalling output: %s", err)
|
|
}
|
|
|
|
if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 {
|
|
t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br)
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain 'histogram_timer_rego_query_eval_ns_count', got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N")
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_server_handler_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain 'histogram_timer_server_handler_ns_count', got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_server_handler_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_server_handler_ns_count' to be equal to N")
|
|
}
|
|
}
|
|
|
|
func TestRunBenchmarkE2EWithOPAConfigFile(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
fs := map[string]string{
|
|
"/config.yaml": `{"decision_logs": {"console": true}}`,
|
|
}
|
|
|
|
test.WithTempFS(fs, func(testDirRoot string) {
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
params.configFile = filepath.Join(testDirRoot, "config.yaml")
|
|
|
|
args := []string{"1 + 1"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
|
|
// Expect a json serialized benchmark result with histogram fields
|
|
var br testing.BenchmarkResult
|
|
err = util.UnmarshalJSON(buf.Bytes(), &br)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error unmarshalling output: %s", err)
|
|
}
|
|
|
|
if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 {
|
|
t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br)
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_rego_query_eval_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain 'histogram_timer_rego_query_eval_ns_count', got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_rego_query_eval_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_rego_query_eval_ns_count' to be equal to N")
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_server_handler_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain 'histogram_timer_server_handler_ns_count', got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_server_handler_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_server_handler_ns_count' to be equal to N")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRunBenchmarkFailFastE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.fail = true // configured to fail on undefined results
|
|
params.e2e = true
|
|
|
|
args := []string{"a := 1; a > 2"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
|
|
// Expect a json serialized benchmark result with histogram fields
|
|
var pr presentation.Output
|
|
err = util.UnmarshalJSON(buf.Bytes(), &pr)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error unmarshalling output: %s", err)
|
|
}
|
|
|
|
if len(pr.Errors) != 1 {
|
|
t.Fatalf("Expected 1 error in result, got:\n\n%s\n", buf.String())
|
|
}
|
|
}
|
|
|
|
func TestBenchPartialE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.partial = true
|
|
params.fail = true
|
|
params.e2e = true
|
|
params.unknowns = []string{"input"}
|
|
args := []string{"input.x > 0"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
|
|
var br testing.BenchmarkResult
|
|
err = util.UnmarshalJSON(buf.Bytes(), &br)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error unmarshalling output: %s", err)
|
|
}
|
|
|
|
if br.N == 0 || br.T == 0 || br.MemAllocs == 0 || br.MemBytes == 0 {
|
|
t.Fatalf("Expected benchmark results to be non-zero, got: %+v", br)
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_rego_partial_eval_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain 'histogram_timer_rego_partial_eval_ns_count', got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_rego_partial_eval_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_rego_partial_eval_ns_count' to be equal to N")
|
|
}
|
|
|
|
if _, ok := br.Extra["histogram_timer_server_handler_ns_count"]; !ok {
|
|
t.Fatalf("Expected benchmark results to contain 'histogram_timer_server_handler_ns_count', got: %+v", br)
|
|
}
|
|
|
|
if float64(br.N) != br.Extra["histogram_timer_server_handler_ns_count"] {
|
|
t.Fatalf("Expected 'histogram_timer_server_handler_ns_count' to be equal to N")
|
|
}
|
|
}
|
|
|
|
func TestRunBenchmarkPartialFailFastE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.partial = true
|
|
params.unknowns = []string{}
|
|
params.fail = true
|
|
params.e2e = true
|
|
args := []string{"1 == 2"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
|
|
actual := buf.String()
|
|
expected := `{
|
|
"errors": [
|
|
{
|
|
"message": "undefined result"
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
if actual != expected {
|
|
t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestRunBenchmarkFailFast(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.fail = true // configured to fail on undefined results
|
|
|
|
args := []string{"a := 1; a > 2"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
|
|
// Expect a json serialized benchmark result with histogram fields
|
|
var pr presentation.Output
|
|
err = util.UnmarshalJSON(buf.Bytes(), &pr)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error unmarshalling output: %s", err)
|
|
}
|
|
|
|
if len(pr.Errors) != 1 {
|
|
t.Fatalf("Expected 1 error in result, got:\n\n%s\n", buf.String())
|
|
}
|
|
}
|
|
|
|
// mockBenchRunner lets us test the bench CLI operations without having to wait ~10 seconds
|
|
// while the actual benchmark runner does its thing.
|
|
type mockBenchRunner struct {
|
|
onRun func(ctx context.Context, ectx *evalContext, params benchmarkCommandParams, f func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error)
|
|
}
|
|
|
|
func (r *mockBenchRunner) run(ctx context.Context, ectx *evalContext, params benchmarkCommandParams, f func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) {
|
|
if r.onRun != nil {
|
|
return r.onRun(ctx, ectx, params, f)
|
|
}
|
|
return testing.BenchmarkResult{}, nil
|
|
}
|
|
|
|
func TestBenchPartial(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.partial = true
|
|
params.fail = true
|
|
args := []string{"input=1"}
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &mockBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
}
|
|
|
|
func TestBenchMainErrPreparing(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
args := []string{"???"} // query compile error
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &mockBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
}
|
|
|
|
func TestBenchMainErrRunningBenchmark(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
args := []string{"1+1"}
|
|
var buf bytes.Buffer
|
|
|
|
mockRunner := &mockBenchRunner{}
|
|
mockRunner.onRun = func(_ context.Context, _ *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) {
|
|
return testing.BenchmarkResult{}, errors.New("error error error")
|
|
}
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, mockRunner)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
}
|
|
|
|
func TestBenchMainWithCount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
args := []string{"1+1"}
|
|
var buf bytes.Buffer
|
|
|
|
mockRunner := &mockBenchRunner{}
|
|
|
|
params.count = 25
|
|
actualCount := 0
|
|
mockRunner.onRun = func(_ context.Context, _ *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) {
|
|
actualCount++
|
|
return testing.BenchmarkResult{}, nil
|
|
}
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, mockRunner)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
|
|
if actualCount != params.count {
|
|
t.Fatalf("Expected benchmark to be run %d times, only ran %d", params.count, actualCount)
|
|
}
|
|
}
|
|
|
|
func TestBenchMainWithNegativeCount(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
args := []string{"1+1"}
|
|
var buf bytes.Buffer
|
|
|
|
mockRunner := &mockBenchRunner{}
|
|
|
|
params.count = -1
|
|
actualCount := 0
|
|
mockRunner.onRun = func(_ context.Context, _ *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) {
|
|
actualCount++
|
|
return testing.BenchmarkResult{}, nil
|
|
}
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, mockRunner)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
|
|
if actualCount != 0 {
|
|
t.Fatalf("Expected benchmark to not be run, instead ran %d times", actualCount)
|
|
}
|
|
}
|
|
|
|
func validateBenchMainPrep(t *testing.T, args []string, params benchmarkCommandParams) {
|
|
t.Helper()
|
|
|
|
var buf bytes.Buffer
|
|
|
|
mockRunner := &mockBenchRunner{}
|
|
|
|
mockRunner.onRun = func(ctx context.Context, ectx *evalContext, _ benchmarkCommandParams, _ func(context.Context, ...rego.EvalOption) error) (testing.BenchmarkResult, error) {
|
|
|
|
// cheat and use the ectx to evalute the query to ensure the input setup on it was valid
|
|
r := rego.New(ectx.regoArgs...)
|
|
pq, err := r.PrepareForEval(ctx)
|
|
if err != nil {
|
|
return testing.BenchmarkResult{}, err
|
|
}
|
|
|
|
rs, err := pq.Eval(ctx, ectx.evalArgs...)
|
|
if err != nil {
|
|
return testing.BenchmarkResult{}, err
|
|
}
|
|
|
|
if len(rs) == 0 {
|
|
return testing.BenchmarkResult{}, errors.New("expected result, got none")
|
|
}
|
|
|
|
return testing.BenchmarkResult{}, nil
|
|
}
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, mockRunner)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
}
|
|
|
|
func TestBenchMainWithJSONInputFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
files := map[string]string{
|
|
"/input.json": `{"x": 42}`,
|
|
}
|
|
args := []string{"input.x == 42"}
|
|
test.WithTempFS(files, func(path string) {
|
|
params.inputPath = filepath.Join(path, "input.json")
|
|
|
|
validateBenchMainPrep(t, args, params)
|
|
})
|
|
}
|
|
|
|
func TestBenchMainWithYAMLInputFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
files := map[string]string{
|
|
"/input.yaml": `x: 42`,
|
|
}
|
|
args := []string{"input.x == 42"}
|
|
test.WithTempFS(files, func(path string) {
|
|
params.inputPath = filepath.Join(path, "input.yaml")
|
|
|
|
validateBenchMainPrep(t, args, params)
|
|
})
|
|
}
|
|
|
|
func TestBenchMainInvalidInputFile(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
files := map[string]string{
|
|
"/input.yaml": `x: 42`,
|
|
}
|
|
args := []string{"1+1"}
|
|
test.WithTempFS(files, func(path string) {
|
|
params.inputPath = filepath.Join(path, "definitely", "not", "input.yaml")
|
|
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &mockBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBenchMainWithJSONInputFileE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
files := map[string]string{
|
|
"/input.json": `{"x": 42}`,
|
|
}
|
|
args := []string{"input.x == 42"}
|
|
test.WithTempFS(files, func(path string) {
|
|
params.inputPath = filepath.Join(path, "input.json")
|
|
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBenchMainWithYAMLInputFileE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
files := map[string]string{
|
|
"/input.yaml": `x: 42`,
|
|
}
|
|
args := []string{"input.x == 42"}
|
|
test.WithTempFS(files, func(path string) {
|
|
params.inputPath = filepath.Join(path, "input.yaml")
|
|
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBenchMainInvalidInputFileE2E(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
files := map[string]string{
|
|
"/input.yaml": `x: 42`,
|
|
}
|
|
args := []string{"1+1"}
|
|
test.WithTempFS(files, func(path string) {
|
|
params.inputPath = filepath.Join(path, "definitely", "not", "input.yaml")
|
|
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBenchMainWithBundleData(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
|
|
b := testBundle()
|
|
|
|
files := map[string]string{
|
|
"bundle.tar.gz": "",
|
|
}
|
|
|
|
test.WithTempFS(files, func(path string) {
|
|
bundlePath := filepath.Join(path, "bundle.tar.gz")
|
|
f, err := os.OpenFile(bundlePath, os.O_WRONLY, os.ModePerm)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
err = bundle.Write(f, b)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
err = params.bundlePaths.Set(bundlePath)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
args := []string{"data.a.b.x"}
|
|
|
|
validateBenchMainPrep(t, args, params)
|
|
})
|
|
}
|
|
|
|
func TestBenchMainWithBundleDataE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
|
|
b := testBundle()
|
|
|
|
files := map[string]string{
|
|
"bundle.tar.gz": "",
|
|
}
|
|
|
|
test.WithTempFS(files, func(path string) {
|
|
bundlePath := filepath.Join(path, "bundle.tar.gz")
|
|
f, err := os.OpenFile(bundlePath, os.O_WRONLY, os.ModePerm)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
err = bundle.Write(f, b)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
err = params.bundlePaths.Set(bundlePath)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
args := []string{"data.a.b.x"}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBenchMainWithDataE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
|
|
mod := `package a.b
|
|
import rego.v1
|
|
|
|
x if {
|
|
data.a.b.c == 42
|
|
}
|
|
`
|
|
|
|
files := map[string]string{
|
|
"p.rego": mod,
|
|
}
|
|
|
|
test.WithTempFS(files, func(path string) {
|
|
err := params.dataPaths.Set(filepath.Join(path, "p.rego"))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
args := []string{"data.a.b.x"}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBenchMainBadQueryE2E(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.e2e = true
|
|
args := []string{"foo.bar"}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
rc, err := benchMain(args, params, &buf, nil, &goBenchRunner{})
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
if rc != 1 {
|
|
t.Fatalf("Unexpected return code %d, expected 1", rc)
|
|
}
|
|
}
|
|
|
|
func TestBenchMain_DefaultRegoVersion(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
note string
|
|
module string
|
|
query string
|
|
expErrs []string
|
|
}{
|
|
// These tests are slow, so we're not being completely exhaustive here.
|
|
{
|
|
note: "v0 module",
|
|
module: `package test
|
|
a[x] {
|
|
x := 42
|
|
}`,
|
|
query: `data.test.a`,
|
|
expErrs: []string{
|
|
"mod.rego:2: rego_parse_error: `if` keyword is required before rule body",
|
|
"mod.rego:2: rego_parse_error: `contains` keyword is required for partial set rules",
|
|
},
|
|
},
|
|
{
|
|
note: "v1 module",
|
|
module: `package test
|
|
a contains x if {
|
|
x := 42
|
|
}`,
|
|
query: `data.test.a`,
|
|
},
|
|
}
|
|
|
|
modes := []struct {
|
|
name string
|
|
e2e bool
|
|
}{
|
|
{
|
|
name: "run",
|
|
},
|
|
{
|
|
name: "e2e",
|
|
e2e: true,
|
|
},
|
|
}
|
|
|
|
for _, mode := range modes {
|
|
for _, tc := range tests {
|
|
t.Run(fmt.Sprintf("%s, %s", tc.note, mode.name), func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := map[string]string{
|
|
"mod.rego": tc.module,
|
|
}
|
|
|
|
test.WithTempFS(files, func(path string) {
|
|
params := testBenchParams()
|
|
_ = params.outputFormat.Set(formats.Pretty)
|
|
params.e2e = mode.e2e
|
|
|
|
for n := range files {
|
|
err := params.dataPaths.Set(filepath.Join(path, n))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
}
|
|
|
|
args := []string{tc.query}
|
|
|
|
var buf, errBuf bytes.Buffer
|
|
rc, err := benchMain(args, params, &buf, &errBuf, &goBenchRunner{})
|
|
|
|
if len(tc.expErrs) > 0 {
|
|
if rc == 0 {
|
|
t.Fatalf("Expected non-zero return code")
|
|
}
|
|
|
|
output := errBuf.String()
|
|
for _, expErr := range tc.expErrs {
|
|
if !strings.Contains(output, expErr) {
|
|
t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, output)
|
|
}
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBenchMainCompatibleFlags(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
note string
|
|
v0Compatible bool
|
|
v1Compatible bool
|
|
module string
|
|
query string
|
|
expErrs []string
|
|
}{
|
|
// These tests are slow, so we're not being completely exhaustive here.
|
|
{
|
|
note: "v0, keywords not used",
|
|
v0Compatible: true,
|
|
module: `package test
|
|
a[4] {
|
|
1 == 1
|
|
}`,
|
|
query: `data.test.a`,
|
|
},
|
|
{
|
|
note: "v0, no keywords imported",
|
|
v0Compatible: true,
|
|
module: `package test
|
|
a contains 4 if {
|
|
1 == 1
|
|
}`,
|
|
query: `data.test.a`,
|
|
expErrs: []string{
|
|
"rego_parse_error: var cannot be used for rule name",
|
|
"rego_parse_error: number cannot be used for rule name",
|
|
},
|
|
},
|
|
{
|
|
note: "v1, keywords not used",
|
|
v1Compatible: true,
|
|
module: `package test
|
|
a[4] {
|
|
1 == 1
|
|
}`,
|
|
query: `data.test.a`,
|
|
expErrs: []string{
|
|
"rego_parse_error: `if` keyword is required before rule body",
|
|
"rego_parse_error: `contains` keyword is required for partial set rules",
|
|
},
|
|
},
|
|
{
|
|
note: "v1, no keywords imported",
|
|
v1Compatible: true,
|
|
module: `package test
|
|
a contains 4 if {
|
|
1 == 1
|
|
}`,
|
|
query: `data.test.a`,
|
|
},
|
|
{
|
|
note: "v0+v1, keywords not used (v0 takes precedence)",
|
|
v0Compatible: true,
|
|
v1Compatible: true,
|
|
module: `package test
|
|
a[4] {
|
|
1 == 1
|
|
}`,
|
|
query: `data.test.a`,
|
|
},
|
|
}
|
|
|
|
modes := []struct {
|
|
name string
|
|
e2e bool
|
|
}{
|
|
{
|
|
name: "run",
|
|
},
|
|
{
|
|
name: "e2e",
|
|
e2e: true,
|
|
},
|
|
}
|
|
|
|
for _, mode := range modes {
|
|
for _, tc := range tests {
|
|
t.Run(fmt.Sprintf("%s, %s", tc.note, mode.name), func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := map[string]string{
|
|
"mod.rego": tc.module,
|
|
}
|
|
|
|
test.WithTempFS(files, func(path string) {
|
|
params := testBenchParams()
|
|
_ = params.outputFormat.Set(formats.Pretty)
|
|
params.v0Compatible = tc.v0Compatible
|
|
params.v1Compatible = tc.v1Compatible
|
|
params.e2e = mode.e2e
|
|
|
|
for n := range files {
|
|
err := params.dataPaths.Set(filepath.Join(path, n))
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
}
|
|
|
|
args := []string{tc.query}
|
|
|
|
var buf, errBuf bytes.Buffer
|
|
rc, err := benchMain(args, params, &buf, &errBuf, &goBenchRunner{})
|
|
|
|
if len(tc.expErrs) > 0 {
|
|
if rc == 0 {
|
|
t.Fatalf("Expected non-zero return code")
|
|
}
|
|
|
|
output := errBuf.String()
|
|
for _, expErr := range tc.expErrs {
|
|
if !strings.Contains(output, expErr) {
|
|
t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, output)
|
|
}
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBenchMainWithBundleRegoVersion(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("too slow for testing.Short")
|
|
}
|
|
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
note string
|
|
bundleRegoVersion int
|
|
bundleFileRegoVersions map[string]int
|
|
modules map[string]string
|
|
query string
|
|
expErrs []string
|
|
}{
|
|
// These tests are slow, so we're not being completely exhaustive here.
|
|
{
|
|
note: "v0 bundle",
|
|
bundleRegoVersion: 0,
|
|
modules: map[string]string{
|
|
"test.rego": `package test
|
|
a[4] {
|
|
1 == 1
|
|
}`,
|
|
},
|
|
query: `data.test.a`,
|
|
},
|
|
{
|
|
note: "v0 bundle, no keywords imported",
|
|
bundleRegoVersion: 0,
|
|
modules: map[string]string{
|
|
"test.rego": `package test
|
|
a contains 4 if {
|
|
1 == 1
|
|
}`,
|
|
},
|
|
query: `data.test.a`,
|
|
expErrs: []string{
|
|
"rego_parse_error: var cannot be used for rule name",
|
|
"rego_parse_error: number cannot be used for rule name",
|
|
},
|
|
},
|
|
{
|
|
note: "v0 bundle, v1 per-file override",
|
|
bundleRegoVersion: 0,
|
|
bundleFileRegoVersions: map[string]int{
|
|
"*/test2.rego": 1,
|
|
},
|
|
modules: map[string]string{
|
|
"test1.rego": `package test
|
|
a[4] {
|
|
1 == 1
|
|
}`,
|
|
"test2.rego": `package test
|
|
b contains 4 if {
|
|
1 == 1
|
|
}`,
|
|
},
|
|
query: `data.test.a`,
|
|
},
|
|
{
|
|
note: "v1 bundle, keywords not used",
|
|
bundleRegoVersion: 1,
|
|
modules: map[string]string{
|
|
"test.rego": `package test
|
|
a[4] {
|
|
1 == 1
|
|
}`,
|
|
},
|
|
query: `data.test.a`,
|
|
expErrs: []string{
|
|
"rego_parse_error: `if` keyword is required before rule body",
|
|
"rego_parse_error: `contains` keyword is required for partial set rules",
|
|
},
|
|
},
|
|
{
|
|
note: "v1, no keywords imported",
|
|
bundleRegoVersion: 1,
|
|
modules: map[string]string{
|
|
"test.rego": `package test
|
|
a contains 4 if {
|
|
1 == 1
|
|
}`,
|
|
},
|
|
query: `data.test.a`,
|
|
},
|
|
}
|
|
|
|
bundleTypeCases := []struct {
|
|
note string
|
|
tar bool
|
|
}{
|
|
{
|
|
"bundle dir", false,
|
|
},
|
|
{
|
|
"bundle tar", true,
|
|
},
|
|
}
|
|
|
|
modes := []struct {
|
|
name string
|
|
e2e bool
|
|
}{
|
|
{
|
|
name: "run",
|
|
},
|
|
{
|
|
name: "e2e",
|
|
e2e: true,
|
|
},
|
|
}
|
|
|
|
for _, bundleType := range bundleTypeCases {
|
|
for _, mode := range modes {
|
|
for _, tc := range tests {
|
|
t.Run(fmt.Sprintf("%s, %s, %s", bundleType.note, tc.note, mode.name), func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
files := map[string]string{}
|
|
|
|
if bundleType.tar {
|
|
files["bundle.tar.gz"] = ""
|
|
} else {
|
|
maps.Copy(files, tc.modules)
|
|
|
|
manifest := bundle.Manifest{
|
|
RegoVersion: &tc.bundleRegoVersion,
|
|
FileRegoVersions: tc.bundleFileRegoVersions,
|
|
}
|
|
manifest.Init()
|
|
if b, err := json.Marshal(manifest); err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
} else {
|
|
files[".manifest"] = string(b)
|
|
}
|
|
}
|
|
|
|
test.WithTempFS(files, func(root string) {
|
|
p := root
|
|
if bundleType.tar {
|
|
b := bundle.Bundle{
|
|
Manifest: bundle.Manifest{
|
|
RegoVersion: &tc.bundleRegoVersion,
|
|
FileRegoVersions: tc.bundleFileRegoVersions,
|
|
},
|
|
Data: map[string]any{},
|
|
}
|
|
for k, v := range tc.modules {
|
|
b.Modules = append(b.Modules, bundle.ModuleFile{
|
|
Path: k,
|
|
Raw: []byte(v),
|
|
})
|
|
}
|
|
p = filepath.Join(root, "bundle.tar.gz")
|
|
f, err := os.OpenFile(p, os.O_WRONLY, os.ModePerm)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
err = bundle.Write(f, b)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
}
|
|
|
|
params := testBenchParams()
|
|
_ = params.outputFormat.Set(formats.Pretty)
|
|
|
|
params.e2e = mode.e2e
|
|
err := params.bundlePaths.Set(p)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
args := []string{tc.query}
|
|
|
|
var buf, errBuf bytes.Buffer
|
|
rc, err := benchMain(args, params, &buf, &errBuf, &goBenchRunner{})
|
|
|
|
if len(tc.expErrs) > 0 {
|
|
if rc == 0 {
|
|
t.Fatalf("Expected non-zero return code")
|
|
}
|
|
|
|
output := errBuf.String()
|
|
for _, expErr := range tc.expErrs {
|
|
if !strings.Contains(output, expErr) {
|
|
t.Fatalf("Expected error:\n\n%s\n\ngot:\n\n%s", expErr, output)
|
|
}
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
if rc != 0 {
|
|
t.Fatalf("Unexpected return code %d, expected 0", rc)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRenderBenchmarkResultJSONOutput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
err := params.outputFormat.Set(formats.JSON)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
br := fakeBenchResults()
|
|
|
|
var buf bytes.Buffer
|
|
renderBenchmarkResult(params, br, &buf)
|
|
|
|
actual := buf.String()
|
|
|
|
expected := `{
|
|
"N": 134844,
|
|
"T": 1088294120,
|
|
"Bytes": 0,
|
|
"MemAllocs": 8360721,
|
|
"MemBytes": 449906736,
|
|
"Extra": {
|
|
"histogram_timer_rego_query_eval_ns_75%": 4953.75,
|
|
"histogram_timer_rego_query_eval_ns_90%": 6309.6,
|
|
"histogram_timer_rego_query_eval_ns_95%": 7872.55,
|
|
"histogram_timer_rego_query_eval_ns_99%": 14947.34000000001,
|
|
"histogram_timer_rego_query_eval_ns_99.9%": 174377.08200000023,
|
|
"histogram_timer_rego_query_eval_ns_99.99%": 176301,
|
|
"histogram_timer_rego_query_eval_ns_count": 134844,
|
|
"histogram_timer_rego_query_eval_ns_max": 176301,
|
|
"histogram_timer_rego_query_eval_ns_mean": 5118.3706225680935,
|
|
"histogram_timer_rego_query_eval_ns_median": 4312,
|
|
"histogram_timer_rego_query_eval_ns_min": 3553,
|
|
"histogram_timer_rego_query_eval_ns_stddev": 6587.830963916497
|
|
}
|
|
}
|
|
`
|
|
if actual != expected {
|
|
t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestRenderBenchmarkResultPrettyOutput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.benchMem = false
|
|
err := params.outputFormat.Set(formats.Pretty)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
br := fakeBenchResults()
|
|
|
|
var buf bytes.Buffer
|
|
renderBenchmarkResult(params, br, &buf)
|
|
|
|
actual := buf.String()
|
|
|
|
expected := `┌───────────────────────────────────────────┬────────┐
|
|
│ samples │ 134844 │
|
|
│ ns/op │ 8071 │
|
|
│ histogram_timer_rego_query_eval_ns_75% │ 4954 │
|
|
│ histogram_timer_rego_query_eval_ns_90% │ 6310 │
|
|
│ histogram_timer_rego_query_eval_ns_95% │ 7873 │
|
|
│ histogram_timer_rego_query_eval_ns_99% │ 14947 │
|
|
│ histogram_timer_rego_query_eval_ns_99.9% │ 174377 │
|
|
│ histogram_timer_rego_query_eval_ns_99.99% │ 176301 │
|
|
│ histogram_timer_rego_query_eval_ns_count │ 134844 │
|
|
│ histogram_timer_rego_query_eval_ns_max │ 176301 │
|
|
│ histogram_timer_rego_query_eval_ns_mean │ 5118 │
|
|
│ histogram_timer_rego_query_eval_ns_median │ 4312 │
|
|
│ histogram_timer_rego_query_eval_ns_min │ 3553 │
|
|
│ histogram_timer_rego_query_eval_ns_stddev │ 6588 │
|
|
└───────────────────────────────────────────┴────────┘
|
|
`
|
|
if actual != expected {
|
|
t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestRenderBenchmarkResultPrettyOutputShowAllocs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.benchMem = true
|
|
err := params.outputFormat.Set(formats.Pretty)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
br := fakeBenchResults()
|
|
|
|
var buf bytes.Buffer
|
|
renderBenchmarkResult(params, br, &buf)
|
|
|
|
actual := buf.String()
|
|
|
|
expected := `┌───────────────────────────────────────────┬────────┐
|
|
│ samples │ 134844 │
|
|
│ ns/op │ 8071 │
|
|
│ B/op │ 3336 │
|
|
│ allocs/op │ 62 │
|
|
│ histogram_timer_rego_query_eval_ns_75% │ 4954 │
|
|
│ histogram_timer_rego_query_eval_ns_90% │ 6310 │
|
|
│ histogram_timer_rego_query_eval_ns_95% │ 7873 │
|
|
│ histogram_timer_rego_query_eval_ns_99% │ 14947 │
|
|
│ histogram_timer_rego_query_eval_ns_99.9% │ 174377 │
|
|
│ histogram_timer_rego_query_eval_ns_99.99% │ 176301 │
|
|
│ histogram_timer_rego_query_eval_ns_count │ 134844 │
|
|
│ histogram_timer_rego_query_eval_ns_max │ 176301 │
|
|
│ histogram_timer_rego_query_eval_ns_mean │ 5118 │
|
|
│ histogram_timer_rego_query_eval_ns_median │ 4312 │
|
|
│ histogram_timer_rego_query_eval_ns_min │ 3553 │
|
|
│ histogram_timer_rego_query_eval_ns_stddev │ 6588 │
|
|
└───────────────────────────────────────────┴────────┘
|
|
`
|
|
if actual != expected {
|
|
t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestRenderBenchmarkResultGoBenchOutputShowAllocs(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
params.benchMem = true
|
|
err := params.outputFormat.Set(formats.GoBench)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
br := fakeBenchResults()
|
|
|
|
var buf bytes.Buffer
|
|
renderBenchmarkResult(params, br, &buf)
|
|
|
|
actual := buf.String()
|
|
|
|
if !strings.HasPrefix(actual, "Benchmark") {
|
|
t.Fatalf("Expected line output to start with 'Benchmark', got: \n\n%s\n", actual)
|
|
}
|
|
|
|
if len(strings.Split(strings.TrimSpace(actual), "\n")) != 1 {
|
|
t.Fatalf("Expected only a single line of output")
|
|
}
|
|
}
|
|
|
|
func TestRenderBenchmarkErrorJSONOutput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
err := params.outputFormat.Set(formats.JSON)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
|
|
_, err = ast.ParseBody("???")
|
|
|
|
err = renderBenchmarkError(params, err, &buf, nil)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error %v", err)
|
|
}
|
|
|
|
actual := buf.String()
|
|
expected := `{
|
|
"errors": [
|
|
{
|
|
"message": "illegal token",
|
|
"code": "rego_parse_error",
|
|
"location": {
|
|
"file": "",
|
|
"row": 1,
|
|
"col": 1
|
|
},
|
|
"details": {
|
|
"line": "???",
|
|
"idx": 0
|
|
}
|
|
}
|
|
]
|
|
}
|
|
`
|
|
|
|
if actual != expected {
|
|
t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func TestRenderBenchmarkErrorPrettyOutput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
err := params.outputFormat.Set(formats.Pretty)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
testPrettyBenchmarkOutput(t, params)
|
|
}
|
|
|
|
func TestRenderBenchmarkErrorGoBenchOutput(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
params := testBenchParams()
|
|
err := params.outputFormat.Set(formats.GoBench)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error: %s", err)
|
|
}
|
|
|
|
testPrettyBenchmarkOutput(t, params)
|
|
}
|
|
|
|
func testPrettyBenchmarkOutput(t *testing.T, params benchmarkCommandParams) {
|
|
var buf bytes.Buffer
|
|
|
|
_, err := ast.ParseBody("???")
|
|
|
|
err = renderBenchmarkError(params, err, &buf, &buf)
|
|
if err != nil {
|
|
t.Fatalf("Unexpected error %v", err)
|
|
}
|
|
|
|
actual := buf.String()
|
|
expected := `1 error occurred: 1:1: rego_parse_error: illegal token
|
|
???
|
|
^
|
|
`
|
|
if actual != expected {
|
|
t.Fatalf("\nExpected:\n%s\n\nGot:\n%s\n", expected, actual)
|
|
}
|
|
}
|
|
|
|
func testBenchParams() benchmarkCommandParams {
|
|
params := newBenchmarkEvalParams()
|
|
params.benchMem = true
|
|
params.metrics = true
|
|
_ = params.outputFormat.Set(formats.JSON)
|
|
params.count = 1
|
|
return params
|
|
}
|
|
|
|
func fakeBenchResults() testing.BenchmarkResult {
|
|
return testing.BenchmarkResult{
|
|
N: 134844,
|
|
T: 1088294120,
|
|
Bytes: 0,
|
|
MemAllocs: 8360721,
|
|
MemBytes: 449906736,
|
|
Extra: map[string]float64{
|
|
"histogram_timer_rego_query_eval_ns_75%": 4953.75,
|
|
"histogram_timer_rego_query_eval_ns_90%": 6309.6,
|
|
"histogram_timer_rego_query_eval_ns_95%": 7872.55,
|
|
"histogram_timer_rego_query_eval_ns_99%": 14947.34000000001,
|
|
"histogram_timer_rego_query_eval_ns_99.9%": 174377.08200000023,
|
|
"histogram_timer_rego_query_eval_ns_99.99%": 176301,
|
|
"histogram_timer_rego_query_eval_ns_count": 134844,
|
|
"histogram_timer_rego_query_eval_ns_max": 176301,
|
|
"histogram_timer_rego_query_eval_ns_mean": 5118.3706225680935,
|
|
"histogram_timer_rego_query_eval_ns_median": 4312,
|
|
"histogram_timer_rego_query_eval_ns_min": 3553,
|
|
"histogram_timer_rego_query_eval_ns_stddev": 6587.830963916497,
|
|
},
|
|
}
|
|
}
|
|
|
|
func testBundle() bundle.Bundle {
|
|
mod := `package a.b
|
|
import rego.v1
|
|
|
|
x if {
|
|
data.a.b.c == 42
|
|
}
|
|
`
|
|
|
|
return bundle.Bundle{
|
|
Manifest: bundle.Manifest{},
|
|
Data: map[string]any{
|
|
"a": map[string]any{
|
|
"b": map[string]any{
|
|
"c": 42,
|
|
},
|
|
},
|
|
},
|
|
Modules: []bundle.ModuleFile{
|
|
{
|
|
Path: "/a/b/policy.rego",
|
|
Raw: []byte(mod),
|
|
Parsed: ast.MustParseModule(mod),
|
|
},
|
|
},
|
|
}
|
|
}
|