Files
releases/runtime/runtime_test.go
T
Torin Sandall 6ee2d92a11 Fix test case to wait for correct error
We were seeing intermittent failures on Travis CI for this test case.
The error being delivered was an empty file parse error. With these
changes, the test will wait for up to 1 second to observe the expected
type error.

Signed-off-by: Torin Sandall <torinsandall@gmail.com>
2018-03-22 10:57:32 -07:00

265 lines
5.6 KiB
Go

// Copyright 2016 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 runtime
import (
"bytes"
"context"
"io/ioutil"
"os"
"path"
"reflect"
"strings"
"testing"
"time"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/storage"
"github.com/open-policy-agent/opa/util"
"github.com/open-policy-agent/opa/util/test"
)
func TestInit(t *testing.T) {
ctx := context.Background()
tmp1, err := ioutil.TempFile("", "docFile")
if err != nil {
panic(err)
}
defer os.Remove(tmp1.Name())
doc1 := `{"foo": "bar", "x": {"y": {"z": [1]}}}`
if _, err := tmp1.Write([]byte(doc1)); err != nil {
panic(err)
}
if err := tmp1.Close(); err != nil {
panic(err)
}
tmp2, err := ioutil.TempFile("", "policyFile")
if err != nil {
panic(err)
}
defer os.Remove(tmp2.Name())
mod1 := `package a.b.c
import data.foo
p = true { foo = "bar" }
p = true { 1 = 2 }`
if _, err := tmp2.Write([]byte(mod1)); err != nil {
panic(err)
}
if err := tmp2.Close(); err != nil {
panic(err)
}
params := NewParams()
params.Paths = []string{tmp1.Name(), tmp2.Name()}
rt, err := NewRuntime(ctx, params)
if err != nil {
t.Fatal(err)
}
txn := storage.NewTransactionOrDie(ctx, rt.Store)
node, err := rt.Store.Read(ctx, txn, storage.MustParsePath("/foo"))
if util.Compare(node, "bar") != 0 || err != nil {
t.Errorf("Expected %v but got %v (err: %v)", "bar", node, err)
return
}
ids, err := rt.Store.ListPolicies(ctx, txn)
if err != nil {
t.Fatal(err)
}
result, err := rt.Store.GetPolicy(ctx, txn, ids[0])
if err != nil || string(result) != mod1 {
t.Fatalf("Expected %v but got: %v (err: %v)", mod1, result, err)
}
}
func TestWatchPaths(t *testing.T) {
fs := map[string]string{
"/foo/bar/baz.json": "true",
}
expected := []string{
"/foo", "/foo/bar", "/foo/bar/baz.json",
}
test.WithTempFS(fs, func(rootDir string) {
paths, err := getWatchPaths([]string{"prefix:" + rootDir + "/foo"})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
result := []string{}
for _, path := range paths {
result = append(result, strings.TrimPrefix(path, rootDir))
}
if !reflect.DeepEqual(expected, result) {
t.Fatalf("Expected %v but got: %v", expected, result)
}
})
}
func TestRuntimeProcessWatchEvents(t *testing.T) {
ctx := context.Background()
fs := map[string]string{
"/some/data.json": `{
"hello": "world"
}`,
}
test.WithTempFS(fs, func(rootDir string) {
params := NewParams()
params.Paths = []string{rootDir}
rt, err := NewRuntime(ctx, params)
if err != nil {
t.Fatal(err)
}
var buf bytes.Buffer
if err := rt.startWatcher(ctx, params.Paths, onReloadPrinter(&buf)); err != nil {
t.Fatalf("Unexpected watcher init error: %v", err)
}
expected := map[string]interface{}{
"hello": "world-2",
}
if err := ioutil.WriteFile(path.Join(rootDir, "some/data.json"), util.MustMarshalJSON(expected), 0644); err != nil {
panic(err)
}
t0 := time.Now()
path := storage.MustParsePath("/some")
// In practice, reload takes ~100us on development machine.
maxWaitTime := time.Second * 1
var val interface{}
for time.Since(t0) < maxWaitTime {
time.Sleep(1 * time.Millisecond)
txn := storage.NewTransactionOrDie(ctx, rt.Store)
var err error
val, err = rt.Store.Read(ctx, txn, path)
if err != nil {
panic(err)
}
rt.Store.Abort(ctx, txn)
if reflect.DeepEqual(val, expected) {
return // success
}
}
t.Fatalf("Did not see expected change in %v, last value: %v, buf: %v", maxWaitTime, val, buf.String())
})
}
func TestRuntimeProcessWatchEventPolicyError(t *testing.T) {
ctx := context.Background()
fs := map[string]string{
"/x.rego": `package test
default x = 1
`,
}
test.WithTempFS(fs, func(rootDir string) {
params := NewParams()
params.Paths = []string{rootDir}
rt, err := NewRuntime(ctx, params)
if err != nil {
t.Fatal(err)
}
storage.Txn(ctx, rt.Store, storage.WriteParams, func(txn storage.Transaction) error {
return rt.Store.UpsertPolicy(ctx, txn, "out-of-band.rego", []byte(`package foo`))
})
ch := make(chan error)
testFunc := func(d time.Duration, err error) {
ch <- err
}
if err := rt.startWatcher(ctx, params.Paths, testFunc); err != nil {
t.Fatalf("Unexpected watcher init error: %v", err)
}
newModule := []byte(`package test
default x = 2`)
if err := ioutil.WriteFile(path.Join(rootDir, "y.rego"), newModule, 0644); err != nil {
t.Fatal(err)
}
// Wait for up to 1 second before considering test failed. On Linux we
// observe multiple events on write (e.g., create -> write) which
// triggers two errors instead of one, whereas on Darwin only a single
// event (e.g., create) is sent. Same as below.
maxWait := time.Second
timer := time.NewTimer(maxWait)
// Expect type error.
func() {
for {
select {
case result := <-ch:
if errs, ok := result.(ast.Errors); ok {
if errs[0].Code == ast.TypeErr {
err = nil
return
}
}
err = result
case <-timer.C:
return
}
}
}()
if err != nil {
t.Fatalf("Expected specific failure before %v. Last error: %v", maxWait, err)
}
if err := os.Remove(path.Join(rootDir, "x.rego")); err != nil {
t.Fatal(err)
}
timer = time.NewTimer(maxWait)
// Expect no error.
func() {
for {
select {
case result := <-ch:
if result == nil {
err = nil
return
}
err = result
case <-timer.C:
return
}
}
}()
if err != nil {
t.Fatalf("Expected result to succeed before %v. Last error: %v", maxWait, err)
}
})
}