1// Copyright 2016 The OPA Authors. All rights reserved. 2// Use of this source code is governed by an Apache2 3// license that can be found in the LICENSE file. 4 5package repl 6 7import ( 8 "bytes" 9 "context" 10 "encoding/json" 11 "fmt" 12 "io" 13 "io/ioutil" 14 "os" 15 "path/filepath" 16 "reflect" 17 "sort" 18 "strings" 19 "testing" 20 21 "github.com/open-policy-agent/opa/ast" 22 "github.com/open-policy-agent/opa/internal/presentation" 23 "github.com/open-policy-agent/opa/storage" 24 "github.com/open-policy-agent/opa/storage/inmem" 25 "github.com/open-policy-agent/opa/util" 26) 27 28func TestFunction(t *testing.T) { 29 store := newTestStore() 30 ctx := context.Background() 31 txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) 32 33 mod1 := []byte(`package a.b.c 34 35foo(x) = y { 36 split(x, ".", y) 37} 38 39bar([x, y]) = z { 40 trim(x, y, z) 41} 42`) 43 44 mod2 := []byte(`package a.b.d 45 46baz(_) = y { 47 data.a.b.c.foo("barfoobar.bar", x) 48 data.a.b.c.bar(x, y) 49}`) 50 51 if err := store.UpsertPolicy(ctx, txn, "mod1", mod1); err != nil { 52 panic(err) 53 } 54 55 if err := store.UpsertPolicy(ctx, txn, "mod2", mod2); err != nil { 56 panic(err) 57 } 58 59 if err := store.Commit(ctx, txn); err != nil { 60 panic(err) 61 } 62 63 var buf bytes.Buffer 64 repl := newRepl(store, &buf) 65 repl.OneShot(ctx, "json") 66 repl.OneShot(ctx, "data.a.b.d.baz(null, x)") 67 exp := util.MustUnmarshalJSON([]byte(`{"result": [{"expressions": [{"text":"data.a.b.d.baz(null, x)", "value": true, "location": {"row": 1, "col": 1}}], "bindings": {"x": "foo"}}]}`)) 68 result := util.MustUnmarshalJSON(buf.Bytes()) 69 if !reflect.DeepEqual(exp, result) { 70 t.Fatalf("expected data.a.b.d.baz(x) to be %v, got %v", exp, result) 71 } 72 73 err := repl.OneShot(ctx, "p(x) = y { y = x+4 }") 74 if err != nil { 75 t.Fatalf("failed to compile repl function: %v", err) 76 } 77 78 buf.Reset() 79 repl.OneShot(ctx, "data.repl.p(5, y)") 80 exp = util.MustUnmarshalJSON([]byte(`{ 81 "result": [ 82 { 83 "expressions": [ 84 { 85 "text": "data.repl.p(5, y)", 86 "value": true, 87 "location": { 88 "col": 1, 89 "row": 1 90 } 91 } 92 ], 93 "bindings": { 94 "y": 9 95 } 96 } 97 ] 98 }`)) 99 result = util.MustUnmarshalJSON(buf.Bytes()) 100 if !reflect.DeepEqual(exp, result) { 101 t.Fatalf("expected datrepl.p(x) to be %v, got %v", exp, result) 102 } 103 104 repl.OneShot(ctx, "f(1, x) = y { y = x }") 105 repl.OneShot(ctx, "f(2, x) = y { y = x*2 }") 106 107 buf.Reset() 108 repl.OneShot(ctx, "data.repl.f(1, 2, y)") 109 exp = util.MustUnmarshalJSON([]byte(`{ 110 "result": [ 111 { 112 "expressions": [ 113 { 114 "text": "data.repl.f(1, 2, y)", 115 "location": { 116 "col": 1, 117 "row": 1 118 }, 119 "value": true 120 } 121 ], 122 "bindings": { 123 "y": 2 124 } 125 } 126 ] 127 }`)) 128 result = util.MustUnmarshalJSON(buf.Bytes()) 129 if !reflect.DeepEqual(exp, result) { 130 t.Fatalf("expected data.repl.f(1, 2, y) to be %v, got %v", exp, result) 131 } 132 buf.Reset() 133 repl.OneShot(ctx, "data.repl.f(2, 2, y)") 134 exp = util.MustUnmarshalJSON([]byte(`{ 135 "result": [ 136 { 137 "expressions": [ 138 { 139 "text": "data.repl.f(2, 2, y)", 140 "location": { 141 "col": 1, 142 "row": 1 143 }, 144 "value": true 145 } 146 ], 147 "bindings": { 148 "y": 4 149 } 150 } 151 ] 152 }`)) 153 result = util.MustUnmarshalJSON(buf.Bytes()) 154 if !reflect.DeepEqual(exp, result) { 155 t.Fatalf("expected data.repl.f(2, 2, y) to be %v, got %v", exp, result) 156 } 157} 158 159func TestComplete(t *testing.T) { 160 ctx := context.Background() 161 store := newTestStore() 162 txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) 163 164 mod1 := []byte(`package a.b.c 165 166p = 1 { true } 167q = 2 { true } 168q = 3 { false }`) 169 170 mod2 := []byte(`package a.b.d 171 172r = 3 { true }`) 173 174 if err := store.UpsertPolicy(ctx, txn, "mod1", mod1); err != nil { 175 panic(err) 176 } 177 178 if err := store.UpsertPolicy(ctx, txn, "mod2", mod2); err != nil { 179 panic(err) 180 } 181 182 if err := store.Commit(ctx, txn); err != nil { 183 panic(err) 184 } 185 186 var buf bytes.Buffer 187 repl := newRepl(store, &buf) 188 repl.OneShot(ctx, "s = 4") 189 buf.Reset() 190 191 result := repl.complete("") 192 expected := []string{ 193 "data.a.b.c.p", 194 "data.a.b.c.q", 195 "data.a.b.d.r", 196 "data.repl.s", 197 } 198 199 sort.Strings(result) 200 sort.Strings(expected) 201 202 if !reflect.DeepEqual(result, expected) { 203 t.Fatalf("Expected %v but got: %v", expected, result) 204 } 205 206 result = repl.complete("data.a.b") 207 expected = []string{ 208 "data.a.b.c.p", 209 "data.a.b.c.q", 210 "data.a.b.d.r", 211 } 212 213 sort.Strings(result) 214 sort.Strings(expected) 215 216 if !reflect.DeepEqual(result, expected) { 217 t.Fatalf("Expected %v but got: %v", expected, result) 218 } 219 220 result = repl.complete("data.a.b.c.p[x]") 221 expected = []string{} 222 223 if !reflect.DeepEqual(result, expected) { 224 t.Fatalf("Expected %v but got: %v", expected, result) 225 } 226 227 repl.OneShot(ctx, "import data.a.b.c.p as xyz") 228 repl.OneShot(ctx, "import data.a.b.d") 229 230 result = repl.complete("x") 231 expected = []string{ 232 "xyz", 233 } 234 235 if !reflect.DeepEqual(result, expected) { 236 t.Fatalf("Expected %v but got: %v", expected, result) 237 } 238} 239 240func TestDump(t *testing.T) { 241 ctx := context.Background() 242 input := `{"a": [1,2,3,4]}` 243 var data map[string]interface{} 244 err := util.UnmarshalJSON([]byte(input), &data) 245 if err != nil { 246 panic(err) 247 } 248 store := inmem.NewFromObject(data) 249 var buffer bytes.Buffer 250 repl := newRepl(store, &buffer) 251 repl.OneShot(ctx, "dump") 252 expectOutput(t, buffer.String(), "{\"a\":[1,2,3,4]}\n") 253} 254 255func TestDumpPath(t *testing.T) { 256 ctx := context.Background() 257 input := `{"a": [1,2,3,4]}` 258 var data map[string]interface{} 259 err := util.UnmarshalJSON([]byte(input), &data) 260 if err != nil { 261 panic(err) 262 } 263 store := inmem.NewFromObject(data) 264 var buffer bytes.Buffer 265 repl := newRepl(store, &buffer) 266 267 dir, err := ioutil.TempDir("", "dump-path-test") 268 if err != nil { 269 t.Fatal(err) 270 } 271 272 defer os.RemoveAll(dir) 273 file := filepath.Join(dir, "tmpfile") 274 repl.OneShot(ctx, fmt.Sprintf("dump %s", file)) 275 276 if buffer.String() != "" { 277 t.Errorf("Expected no output but got: %v", buffer.String()) 278 } 279 280 bs, err := ioutil.ReadFile(file) 281 if err != nil { 282 t.Fatalf("Expected file read to succeed but got: %v", err) 283 } 284 285 var result map[string]interface{} 286 if err := util.UnmarshalJSON(bs, &result); err != nil { 287 t.Fatalf("Expected json unmarshal to succeed but got: %v", err) 288 } 289 290 if !reflect.DeepEqual(data, result) { 291 t.Fatalf("Expected dumped json to equal %v but got: %v", data, result) 292 } 293} 294 295func TestHelp(t *testing.T) { 296 topics["deadbeef"] = topicDesc{ 297 fn: func(w io.Writer) error { 298 fmt.Fprintln(w, "blah blah blah") 299 return nil 300 }, 301 } 302 303 ctx := context.Background() 304 store := inmem.New() 305 var buffer bytes.Buffer 306 repl := newRepl(store, &buffer) 307 repl.OneShot(ctx, "help deadbeef") 308 309 expected := "blah blah blah\n" 310 311 if buffer.String() != expected { 312 t.Fatalf("Unexpected output from help topic: %v", buffer.String()) 313 } 314} 315 316func TestShowDebug(t *testing.T) { 317 ctx := context.Background() 318 store := inmem.New() 319 var buffer bytes.Buffer 320 repl := newRepl(store, &buffer) 321 repl.OneShot(ctx, "show debug") 322 323 var result replDebugState 324 325 if err := util.Unmarshal(buffer.Bytes(), &result); err != nil { 326 t.Fatal(err) 327 } 328 329 var exp replDebugState 330 exp.Explain = explainOff 331 332 if !reflect.DeepEqual(result, exp) { 333 t.Fatalf("Expected %+v but got %+v", exp, result) 334 } 335 336 buffer.Reset() 337 338 repl.OneShot(ctx, "trace") 339 repl.OneShot(ctx, "metrics") 340 repl.OneShot(ctx, "instrument") 341 repl.OneShot(ctx, "profile") 342 repl.OneShot(ctx, "show debug") 343 344 exp.Explain = explainFull 345 exp.Metrics = true 346 exp.Instrument = true 347 exp.Profile = true 348 349 if err := util.Unmarshal(buffer.Bytes(), &result); err != nil { 350 t.Fatal(err) 351 } 352 353 if !reflect.DeepEqual(result, exp) { 354 t.Fatalf("Expected %+v but got %+v", exp, result) 355 } 356} 357 358func TestShow(t *testing.T) { 359 ctx := context.Background() 360 store := inmem.New() 361 var buffer bytes.Buffer 362 repl := newRepl(store, &buffer) 363 364 repl.OneShot(ctx, `package repl_test`) 365 repl.OneShot(ctx, "show") 366 assertREPLText(t, buffer, "package repl_test\n") 367 buffer.Reset() 368 369 repl.OneShot(ctx, "import input.xyz") 370 repl.OneShot(ctx, "show") 371 372 expected := `package repl_test 373 374import input.xyz` + "\n" 375 assertREPLText(t, buffer, expected) 376 buffer.Reset() 377 378 repl.OneShot(ctx, "import data.foo as bar") 379 repl.OneShot(ctx, "show") 380 381 expected = `package repl_test 382 383import data.foo as bar 384import input.xyz` + "\n" 385 assertREPLText(t, buffer, expected) 386 buffer.Reset() 387 388 repl.OneShot(ctx, `p[1] { true }`) 389 buffer.Reset() 390 repl.OneShot(ctx, `p[2] { true }`) 391 buffer.Reset() 392 repl.OneShot(ctx, "show") 393 394 expected = `package repl_test 395 396import data.foo as bar 397import input.xyz 398 399p[1] 400 401p[2]` + "\n" 402 assertREPLText(t, buffer, expected) 403 buffer.Reset() 404 405 repl.OneShot(ctx, "package abc") 406 repl.OneShot(ctx, "show") 407 408 assertREPLText(t, buffer, "package abc\n") 409 buffer.Reset() 410 411 repl.OneShot(ctx, "package repl_test") 412 repl.OneShot(ctx, "show") 413 414 assertREPLText(t, buffer, expected) 415 buffer.Reset() 416} 417 418func TestTypes(t *testing.T) { 419 ctx := context.Background() 420 store := inmem.New() 421 var buffer bytes.Buffer 422 repl := newRepl(store, &buffer) 423 424 repl.OneShot(ctx, "types") 425 repl.OneShot(ctx, `p[x] = y { x := "a"; y := 1 }`) 426 repl.OneShot(ctx, `p[x]`) 427 428 output := strings.TrimSpace(buffer.String()) 429 430 exp := []string{ 431 "# data.repl.p[x]: number", 432 "# x: string", 433 } 434 435 for i := range exp { 436 if !strings.Contains(output, exp[i]) { 437 t.Fatalf("Expected output to contain %q but got: %v", exp[i], output) 438 } 439 } 440 441} 442 443func TestUnknown(t *testing.T) { 444 ctx := context.Background() 445 store := inmem.New() 446 var buffer bytes.Buffer 447 repl := newRepl(store, &buffer) 448 449 repl.OneShot(ctx, "xs = [1,2,3]") 450 buffer.Reset() 451 452 err := repl.OneShot(ctx, "unknown input") 453 if err != nil { 454 t.Fatal("Unexpected command error:", err) 455 } 456 457 repl.OneShot(ctx, "data.repl.xs[i] = x; input.x = x") 458 459 output := strings.TrimSpace(buffer.String()) 460 expected := strings.TrimSpace(` 461+---------+-------------+ 462| Query 1 | input.x = 1 | 463| | i = 0 | 464| | x = 1 | 465+---------+-------------+ 466| Query 2 | input.x = 2 | 467| | i = 1 | 468| | x = 2 | 469+---------+-------------+ 470| Query 3 | input.x = 3 | 471| | i = 2 | 472| | x = 3 | 473+---------+-------------+ 474`) 475 476 if output != expected { 477 t.Fatalf("Unexpected output. Expected:\n\n%v\n\nGot:\n\n%v", expected, output) 478 } 479} 480func TestUnknownMetrics(t *testing.T) { 481 ctx := context.Background() 482 store := inmem.New() 483 var buffer bytes.Buffer 484 repl := newRepl(store, &buffer) 485 486 repl.OneShot(ctx, "xs = [1,2,3]") 487 buffer.Reset() 488 489 err := repl.OneShot(ctx, "unknown input") 490 if err != nil { 491 t.Fatal("Unexpected command error:", err) 492 } 493 494 repl.OneShot(ctx, "metrics") 495 496 repl.OneShot(ctx, "data.repl.xs[i] = x; input.x = x") 497 498 output := strings.TrimSpace(buffer.String()) 499 expected := strings.TrimSpace(` 500+---------+-------------+ 501| Query 1 | input.x = 1 | 502| | i = 0 | 503| | x = 1 | 504+---------+-------------+ 505| Query 2 | input.x = 2 | 506| | i = 1 | 507| | x = 2 | 508+---------+-------------+ 509| Query 3 | input.x = 3 | 510| | i = 2 | 511| | x = 3 | 512+---------+-------------+ 513`) 514 515 if !strings.HasPrefix(output, expected) { 516 t.Fatalf("Unexpected partial eval results. Expected:\n\n%v\n\nGot:\n\n%v", expected, output) 517 } 518 519 if !strings.Contains(output, "timer_rego_partial_eval_ns") { 520 t.Fatal("Expected timer_rego_partial_eval_ns but got:\n\n", output) 521 } 522} 523 524func TestUnknownJSON(t *testing.T) { 525 ctx := context.Background() 526 store := inmem.New() 527 var buffer bytes.Buffer 528 repl := newRepl(store, &buffer) 529 530 repl.OneShot(ctx, "xs = [1,2,3]") 531 buffer.Reset() 532 533 err := repl.OneShot(ctx, "unknown input") 534 if err != nil { 535 t.Fatal("Unexpected command error:", err) 536 } 537 538 repl.OneShot(ctx, "json") 539 repl.OneShot(ctx, "data.repl.xs[i] = x; input.x = x") 540 541 var result presentation.Output 542 543 if err := json.NewDecoder(&buffer).Decode(&result); err != nil { 544 t.Fatal(err) 545 } 546 547 if len(result.Partial.Queries) != 3 { 548 t.Fatalf("Expected exactly 3 queries in partial evaluation output but got: %v", result) 549 } 550} 551 552func TestUnknownInvalid(t *testing.T) { 553 ctx := context.Background() 554 store := inmem.New() 555 var buffer bytes.Buffer 556 repl := newRepl(store, &buffer) 557 558 err := repl.OneShot(ctx, "unknown x-1") 559 if err == nil || !strings.Contains(err.Error(), "usage: unknown <input/data reference>") { 560 t.Fatal("expected error from setting bad unknown but got:", err) 561 } 562 563 // Ensure that partial evaluation has not been enabled. 564 buffer.Reset() 565 repl.OneShot(ctx, "1+2") 566 result := strings.TrimSpace(buffer.String()) 567 if result != "3" { 568 t.Fatal("want true but got:", result) 569 } 570} 571 572func TestUnset(t *testing.T) { 573 ctx := context.Background() 574 store := inmem.New() 575 var buffer bytes.Buffer 576 repl := newRepl(store, &buffer) 577 578 var err error 579 580 repl.OneShot(ctx, "magic = 23") 581 repl.OneShot(ctx, "p = 3.14") 582 repl.OneShot(ctx, "unset p") 583 584 err = repl.OneShot(ctx, "p") 585 586 if _, ok := err.(ast.Errors); !ok { 587 t.Fatalf("Expected AST error but got: %v", err) 588 } 589 590 buffer.Reset() 591 repl.OneShot(ctx, "p = 3.14") 592 repl.OneShot(ctx, `p = 3 { false }`) 593 repl.OneShot(ctx, "unset p") 594 595 err = repl.OneShot(ctx, "p") 596 if _, ok := err.(ast.Errors); !ok { 597 t.Fatalf("Expected AST error but got err: %v, output: %v", err, buffer.String()) 598 } 599 600 if err := repl.OneShot(ctx, "unset "); err == nil { 601 t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) 602 } 603 604 if err := repl.OneShot(ctx, "unset 1=1"); err == nil { 605 t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) 606 } 607 608 if err := repl.OneShot(ctx, `unset "p"`); err == nil { 609 t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) 610 } 611 612 buffer.Reset() 613 repl.OneShot(ctx, "p(x) = y { y = x }") 614 repl.OneShot(ctx, "unset p") 615 616 err = repl.OneShot(ctx, "data.repl.p(1, 2)") 617 if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined function data.repl.p` { 618 t.Fatalf("Expected eval error (undefined built-in) but got err: '%v'", err) 619 } 620 621 buffer.Reset() 622 repl.OneShot(ctx, "p(1, x) = y { y = x }") 623 repl.OneShot(ctx, "p(2, x) = y { y = x+1 }") 624 repl.OneShot(ctx, "unset p") 625 626 err = repl.OneShot(ctx, "data.repl.p(1, 2, 3)") 627 if err == nil || err.Error() != `1 error occurred: 1:1: rego_type_error: undefined function data.repl.p` { 628 t.Fatalf("Expected eval error (undefined built-in) but got err: '%v'", err) 629 } 630 631 buffer.Reset() 632 repl.OneShot(ctx, `unset q`) 633 if buffer.String() != "warning: no matching rules in current module\n" { 634 t.Fatalf("Expected unset error for missing rule but got: %v", buffer.String()) 635 } 636 637 buffer.Reset() 638 repl.OneShot(ctx, `unset q`) 639 if buffer.String() != "warning: no matching rules in current module\n" { 640 t.Fatalf("Expected unset error for missing function but got: %v", buffer.String()) 641 } 642 643 buffer.Reset() 644 repl.OneShot(ctx, `magic`) 645 if buffer.String() != "23\n" { 646 t.Fatalf("Expected magic to be defined but got: %v", buffer.String()) 647 } 648 649 buffer.Reset() 650 repl.OneShot(ctx, `package data.other`) 651 err = repl.OneShot(ctx, `unset magic`) 652 if buffer.String() != "warning: no matching rules in current module\n" { 653 t.Fatalf("Expected unset error for bad syntax but got: %v", buffer.String()) 654 } 655 656 repl.OneShot(ctx, `input = {}`) 657 658 if err := repl.OneShot(ctx, `unset input`); err != nil { 659 t.Fatalf("Expected unset to succeed for input: %v", err) 660 } 661 662 buffer.Reset() 663 repl.OneShot(ctx, `not input`) 664 665 if buffer.String() != "true\n" { 666 t.Fatalf("Expected unset input to remove input document: %v", buffer.String()) 667 } 668 669} 670 671func TestOneShotEmptyBufferOneExpr(t *testing.T) { 672 ctx := context.Background() 673 store := newTestStore() 674 var buffer bytes.Buffer 675 repl := newRepl(store, &buffer) 676 repl.OneShot(ctx, "data.a[i].b.c[j] = 2") 677 expectOutput(t, buffer.String(), "+---+---+\n| i | j |\n+---+---+\n| 0 | 1 |\n+---+---+\n") 678 buffer.Reset() 679 repl.OneShot(ctx, "data.a[i].b.c[j] = \"deadbeef\"") 680 expectOutput(t, buffer.String(), "undefined\n") 681} 682 683func TestOneShotEmptyBufferOneRule(t *testing.T) { 684 ctx := context.Background() 685 store := newTestStore() 686 var buffer bytes.Buffer 687 repl := newRepl(store, &buffer) 688 repl.OneShot(ctx, `p[x] { data.a[i] = x }`) 689 expectOutput(t, buffer.String(), "Rule 'p' defined in package repl. Type 'show' to see rules.\n") 690} 691 692func TestOneShotBufferedExpr(t *testing.T) { 693 ctx := context.Background() 694 store := newTestStore() 695 var buffer bytes.Buffer 696 repl := newRepl(store, &buffer) 697 repl.OneShot(ctx, "data.a[i].b.c[j] = ") 698 expectOutput(t, buffer.String(), "") 699 repl.OneShot(ctx, "2") 700 expectOutput(t, buffer.String(), "") 701 repl.OneShot(ctx, "") 702 expectOutput(t, buffer.String(), "+---+---+\n| i | j |\n+---+---+\n| 0 | 1 |\n+---+---+\n") 703} 704 705func TestOneShotBufferedRule(t *testing.T) { 706 ctx := context.Background() 707 store := newTestStore() 708 var buffer bytes.Buffer 709 repl := newRepl(store, &buffer) 710 repl.OneShot(ctx, "p[x] { ") 711 expectOutput(t, buffer.String(), "") 712 repl.OneShot(ctx, "data.a[i].b.c[1]") 713 expectOutput(t, buffer.String(), "") 714 repl.OneShot(ctx, " = ") 715 expectOutput(t, buffer.String(), "") 716 repl.OneShot(ctx, "x") 717 expectOutput(t, buffer.String(), "") 718 repl.OneShot(ctx, "}") 719 expectOutput(t, buffer.String(), "") 720 repl.OneShot(ctx, "") 721 expectOutput(t, buffer.String(), "Rule 'p' defined in package repl. Type 'show' to see rules.\n") 722 buffer.Reset() 723 repl.OneShot(ctx, "p[2]") 724 expectOutput(t, buffer.String(), "2\n") 725} 726 727func TestOneShotJSON(t *testing.T) { 728 ctx := context.Background() 729 store := newTestStore() 730 var buffer bytes.Buffer 731 repl := newRepl(store, &buffer) 732 repl.outputFormat = "json" 733 repl.OneShot(ctx, "data.a[i] = x") 734 var expected interface{} 735 if err := util.UnmarshalJSON([]byte(`{ 736 "result": [ 737 { 738 "expressions": [ 739 { 740 "value": true, 741 "text": "data.a[i] = x", 742 "location": { 743 "row": 1, 744 "col": 1 745 } 746 } 747 ], 748 "bindings": { 749 "i": 0, 750 "x": { 751 "b": { 752 "c": [ 753 true, 754 2, 755 false 756 ] 757 } 758 } 759 } 760 }, 761 { 762 "expressions": [ 763 { 764 "value": true, 765 "text": "data.a[i] = x", 766 "location": { 767 "row": 1, 768 "col": 1 769 } 770 } 771 ], 772 "bindings": { 773 "i": 1, 774 "x": { 775 "b": { 776 "c": [ 777 false, 778 true, 779 1 780 ] 781 } 782 } 783 } 784 } 785 ] 786 }`), &expected); err != nil { 787 panic(err) 788 } 789 790 var result interface{} 791 792 if err := util.UnmarshalJSON(buffer.Bytes(), &result); err != nil { 793 t.Errorf("Unexpected output format: %v", err) 794 return 795 } 796 797 if !reflect.DeepEqual(expected, result) { 798 t.Errorf("Expected %v but got: %v", expected, buffer.String()) 799 } 800} 801 802func TestEvalData(t *testing.T) { 803 ctx := context.Background() 804 store := newTestStore() 805 var buffer bytes.Buffer 806 repl := newRepl(store, &buffer) 807 testMod := []byte(`package ex 808 809p = [1, 2, 3] { true }`) 810 811 txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) 812 813 if err := store.UpsertPolicy(ctx, txn, "test", testMod); err != nil { 814 panic(err) 815 } 816 817 if err := store.Commit(ctx, txn); err != nil { 818 panic(err) 819 } 820 821 repl.OneShot(ctx, "data") 822 823 expected := parseJSON(` 824 { 825 "a": [ 826 { 827 "b": { 828 "c": [ 829 true, 830 2, 831 false 832 ] 833 } 834 }, 835 { 836 "b": { 837 "c": [ 838 false, 839 true, 840 1 841 ] 842 } 843 } 844 ], 845 "ex": { 846 "p": [ 847 1, 848 2, 849 3 850 ] 851 } 852 }`) 853 result := parseJSON(buffer.String()) 854 855 // Strip REPL documents out as these change depending on build settings. 856 data := result.(map[string]interface{}) 857 delete(data, "repl") 858 859 if !reflect.DeepEqual(result, expected) { 860 t.Fatalf("Expected:\n%v\n\nGot:\n%v", expected, result) 861 } 862} 863 864func TestEvalFalse(t *testing.T) { 865 ctx := context.Background() 866 store := newTestStore() 867 var buffer bytes.Buffer 868 repl := newRepl(store, &buffer) 869 repl.OneShot(ctx, "false") 870 result := buffer.String() 871 if result != "false\n" { 872 t.Errorf("Expected result to be false but got: %v", result) 873 } 874} 875 876func TestEvalConstantRule(t *testing.T) { 877 ctx := context.Background() 878 store := newTestStore() 879 var buffer bytes.Buffer 880 repl := newRepl(store, &buffer) 881 repl.OneShot(ctx, "pi = 3.14") 882 result := buffer.String() 883 if result != "Rule 'pi' defined in package repl. Type 'show' to see rules.\n" { 884 t.Errorf("Expected rule to be defined but got: %v", result) 885 return 886 } 887 buffer.Reset() 888 repl.OneShot(ctx, "pi") 889 result = buffer.String() 890 expected := "3.14\n" 891 if result != expected { 892 t.Errorf("Expected pi to evaluate to 3.14 but got: %v", result) 893 return 894 } 895 buffer.Reset() 896 err := repl.OneShot(ctx, "pi.deadbeef") 897 result = buffer.String() 898 if result != "" || !strings.Contains(err.Error(), "undefined ref: data.repl.pi.deadbeef") { 899 t.Fatalf("Expected pi.deadbeef to fail/error but got:\nresult: %q\nerr: %v", result, err) 900 } 901 buffer.Reset() 902 repl.OneShot(ctx, "pi > 3") 903 result = buffer.String() 904 if result != "true\n" { 905 t.Errorf("Expected pi > 3 to be true but got: %v", result) 906 return 907 } 908} 909 910func TestEvalConstantRuleDefaultRootDoc(t *testing.T) { 911 ctx := context.Background() 912 store := newTestStore() 913 var buffer bytes.Buffer 914 repl := newRepl(store, &buffer) 915 repl.OneShot(ctx, "input = 1") 916 buffer.Reset() 917 repl.OneShot(ctx, "input = 2") 918 assertREPLText(t, buffer, "undefined\n") 919 buffer.Reset() 920 repl.OneShot(ctx, "input = 1") 921 assertREPLText(t, buffer, "true\n") 922} 923 924func TestEvalConstantRuleAssignment(t *testing.T) { 925 ctx := context.Background() 926 store := newTestStore() 927 var buffer bytes.Buffer 928 929 defined := "Rule 'x' defined in package repl. Type 'show' to see rules.\n" 930 redefined := "Rule 'x' re-defined in package repl. Type 'show' to see rules.\n" 931 definedInput := "Rule 'input' defined in package repl. Type 'show' to see rules.\n" 932 redefinedInput := "Rule 'input' re-defined in package repl. Type 'show' to see rules.\n" 933 934 repl := newRepl(store, &buffer) 935 repl.OneShot(ctx, "x = 1") 936 assertREPLText(t, buffer, defined) 937 buffer.Reset() 938 repl.OneShot(ctx, "x := 2") 939 assertREPLText(t, buffer, redefined) 940 buffer.Reset() 941 942 repl.OneShot(ctx, "show") 943 assertREPLText(t, buffer, `package repl 944 945x := 2 946`) 947 buffer.Reset() 948 949 repl.OneShot(ctx, "x := 3") 950 assertREPLText(t, buffer, redefined) 951 buffer.Reset() 952 repl.OneShot(ctx, "x") 953 result := buffer.String() 954 if result != "3\n" { 955 t.Fatalf("Expected 3 but got: %v", result) 956 } 957 958 buffer.Reset() 959 repl.OneShot(ctx, "x = 3") 960 result = buffer.String() 961 if result != "true\n" { 962 t.Fatalf("Expected true but got: %v", result) 963 } 964 965 buffer.Reset() 966 repl.OneShot(ctx, "input = 0") 967 assertREPLText(t, buffer, definedInput) 968 buffer.Reset() 969 repl.OneShot(ctx, "input := 1") 970 assertREPLText(t, buffer, redefinedInput) 971 buffer.Reset() 972 repl.OneShot(ctx, "input") 973 result = buffer.String() 974 if result != "1\n" { 975 t.Fatalf("Expected 1 but got: %v", result) 976 } 977 978 buffer.Reset() 979 err := repl.OneShot(ctx, "assign()") 980 if err == nil || !strings.Contains(err.Error(), "too few arguments") { 981 t.Fatal("Expected type check error but got:", err) 982 } 983} 984 985func TestEvalSingleTermMultiValue(t *testing.T) { 986 ctx := context.Background() 987 store := newTestStore() 988 var buffer bytes.Buffer 989 repl := newRepl(store, &buffer) 990 repl.outputFormat = "json" 991 992 input := `{ 993 "result": [ 994 { 995 "expressions": [ 996 { 997 "value": true, 998 "text": "data.a[i].b.c[_]", 999 "location": { 1000 "row": 1, 1001 "col": 1 1002 } 1003 } 1004 ], 1005 "bindings": { 1006 "i": 0 1007 } 1008 }, 1009 { 1010 "expressions": [ 1011 { 1012 "value": 2, 1013 "text": "data.a[i].b.c[_]", 1014 "location": { 1015 "row": 1, 1016 "col": 1 1017 } 1018 } 1019 ], 1020 "bindings": { 1021 "i": 0 1022 } 1023 }, 1024 { 1025 "expressions": [ 1026 { 1027 "value": true, 1028 "text": "data.a[i].b.c[_]", 1029 "location": { 1030 "row": 1, 1031 "col": 1 1032 } 1033 } 1034 ], 1035 "bindings": { 1036 "i": 1 1037 } 1038 }, 1039 { 1040 "expressions": [ 1041 { 1042 "value": 1, 1043 "text": "data.a[i].b.c[_]", 1044 "location": { 1045 "row": 1, 1046 "col": 1 1047 } 1048 } 1049 ], 1050 "bindings": { 1051 "i": 1 1052 } 1053 } 1054 ] 1055 }` 1056 1057 var expected interface{} 1058 if err := util.UnmarshalJSON([]byte(input), &expected); err != nil { 1059 panic(err) 1060 } 1061 1062 repl.OneShot(ctx, "data.a[i].b.c[_]") 1063 var result interface{} 1064 if err := util.UnmarshalJSON(buffer.Bytes(), &result); err != nil { 1065 t.Errorf("Expected valid JSON document: %v: %v", err, buffer.String()) 1066 return 1067 } 1068 1069 if !reflect.DeepEqual(expected, result) { 1070 t.Errorf("Expected %v but got: %v", expected, buffer.String()) 1071 return 1072 } 1073 1074 buffer.Reset() 1075 1076 repl.OneShot(ctx, "data.deadbeef[x]") 1077 s := buffer.String() 1078 if s != "{}\n" { 1079 t.Errorf("Expected undefined from reference but got: %v", s) 1080 return 1081 } 1082 1083 buffer.Reset() 1084 1085 repl.OneShot(ctx, `p[x] { a = [1, 2, 3, 4]; a[_] = x }`) 1086 buffer.Reset() 1087 repl.OneShot(ctx, "p[x]") 1088 1089 input = ` 1090 { 1091 "result": [ 1092 { 1093 "expressions": [ 1094 { 1095 "value": 1, 1096 "text": "p[x]", 1097 "location": { 1098 "row": 1, 1099 "col": 1 1100 } 1101 } 1102 ], 1103 "bindings": { 1104 "x": 1 1105 } 1106 }, 1107 { 1108 "expressions": [ 1109 { 1110 "value": 2, 1111 "text": "p[x]", 1112 "location": { 1113 "row": 1, 1114 "col": 1 1115 } 1116 } 1117 ], 1118 "bindings": { 1119 "x": 2 1120 } 1121 }, 1122 { 1123 "expressions": [ 1124 { 1125 "value": 3, 1126 "text": "p[x]", 1127 "location": { 1128 "row": 1, 1129 "col": 1 1130 } 1131 } 1132 ], 1133 "bindings": { 1134 "x": 3 1135 } 1136 }, 1137 { 1138 "expressions": [ 1139 { 1140 "value": 4, 1141 "text": "p[x]", 1142 "location": { 1143 "row": 1, 1144 "col": 1 1145 } 1146 } 1147 ], 1148 "bindings": { 1149 "x": 4 1150 } 1151 } 1152 ] 1153 } 1154 ` 1155 1156 if err := util.UnmarshalJSON([]byte(input), &expected); err != nil { 1157 panic(err) 1158 } 1159 1160 if err := util.UnmarshalJSON(buffer.Bytes(), &result); err != nil { 1161 t.Errorf("Expected valid JSON document: %v: %v", err, buffer.String()) 1162 return 1163 } 1164 1165 if !reflect.DeepEqual(expected, result) { 1166 t.Errorf("Exepcted %v but got: %v", expected, buffer.String()) 1167 } 1168} 1169 1170func TestEvalSingleTermMultiValueSetRef(t *testing.T) { 1171 ctx := context.Background() 1172 store := newTestStore() 1173 var buffer bytes.Buffer 1174 repl := newRepl(store, &buffer) 1175 repl.outputFormat = "json" 1176 repl.OneShot(ctx, `p[1] { true }`) 1177 repl.OneShot(ctx, `p[2] { true }`) 1178 repl.OneShot(ctx, `q = {3, 4} { true }`) 1179 repl.OneShot(ctx, `r = [x, y] { x = {5, 6}; y = [7, 8] }`) 1180 1181 repl.OneShot(ctx, "p[x]") 1182 expected := parseJSON(`{ 1183 "result": [ 1184 { 1185 "expressions": [ 1186 { 1187 "value": 1, 1188 "text": "p[x]", 1189 "location": { 1190 "row": 1, 1191 "col": 1 1192 } 1193 } 1194 ], 1195 "bindings": { 1196 "x": 1 1197 } 1198 }, 1199 { 1200 "expressions": [ 1201 { 1202 "value": 2, 1203 "text": "p[x]", 1204 "location": { 1205 "row": 1, 1206 "col": 1 1207 } 1208 } 1209 ], 1210 "bindings": { 1211 "x": 2 1212 } 1213 } 1214 ] 1215 }`) 1216 result := parseJSON(buffer.String()) 1217 if !reflect.DeepEqual(result, expected) { 1218 t.Fatalf("Expected %v but got: %v", expected, buffer.String()) 1219 } 1220 1221 buffer.Reset() 1222 repl.OneShot(ctx, "q[x]") 1223 expected = parseJSON(`{ 1224 "result": [ 1225 { 1226 "expressions": [ 1227 { 1228 "value": 3, 1229 "text": "q[x]", 1230 "location": { 1231 "row": 1, 1232 "col": 1 1233 } 1234 } 1235 ], 1236 "bindings": { 1237 "x": 3 1238 } 1239 }, 1240 { 1241 "expressions": [ 1242 { 1243 "value": 4, 1244 "text": "q[x]", 1245 "location": { 1246 "row": 1, 1247 "col": 1 1248 } 1249 } 1250 ], 1251 "bindings": { 1252 "x": 4 1253 } 1254 } 1255 ] 1256 }`) 1257 result = parseJSON(buffer.String()) 1258 if !reflect.DeepEqual(result, expected) { 1259 t.Fatalf("Expected %v but got: %v", expected, buffer.String()) 1260 } 1261 1262 // Example below shows behavior for ref that iterates an embedded set. The 1263 // tricky part here is that r[_] may refer to multiple collection types. If 1264 // we eventually have a way of distinguishing between the bindings added for 1265 // refs to sets, then those bindings could be filtered out. For now this is 1266 // acceptable, as it should be an edge case. 1267 buffer.Reset() 1268 repl.OneShot(ctx, "r[_][x]") 1269 expected = parseJSON(`{ 1270 "result": [ 1271 { 1272 "expressions": [ 1273 { 1274 "value": 5, 1275 "text": "r[_][x]", 1276 "location": { 1277 "row": 1, 1278 "col": 1 1279 } 1280 } 1281 ], 1282 "bindings": { 1283 "x": 5 1284 } 1285 }, 1286 { 1287 "expressions": [ 1288 { 1289 "value": 6, 1290 "text": "r[_][x]", 1291 "location": { 1292 "row": 1, 1293 "col": 1 1294 } 1295 } 1296 ], 1297 "bindings": { 1298 "x": 6 1299 } 1300 }, 1301 { 1302 "expressions": [ 1303 { 1304 "value": 7, 1305 "text": "r[_][x]", 1306 "location": { 1307 "row": 1, 1308 "col": 1 1309 } 1310 } 1311 ], 1312 "bindings": { 1313 "x": 0 1314 } 1315 }, 1316 { 1317 "expressions": [ 1318 { 1319 "value": 8, 1320 "text": "r[_][x]", 1321 "location": { 1322 "row": 1, 1323 "col": 1 1324 } 1325 } 1326 ], 1327 "bindings": { 1328 "x": 1 1329 } 1330 } 1331 ] 1332 }`) 1333 result = parseJSON(buffer.String()) 1334 if !reflect.DeepEqual(result, expected) { 1335 t.Fatalf("Expected %v but got: %v", expected, buffer.String()) 1336 } 1337} 1338 1339func TestEvalRuleCompileError(t *testing.T) { 1340 ctx := context.Background() 1341 store := newTestStore() 1342 var buffer bytes.Buffer 1343 repl := newRepl(store, &buffer) 1344 err := repl.OneShot(ctx, `p[x] { true }`) 1345 expected := "x is unsafe" 1346 if !strings.Contains(err.Error(), expected) { 1347 t.Errorf("Expected error to contain %v but got: %v (err: %v)", expected, buffer.String(), err) 1348 return 1349 } 1350 buffer.Reset() 1351 err = repl.OneShot(ctx, `p = true { true }`) 1352 result := buffer.String() 1353 if err != nil || result != "Rule 'p' defined in package repl. Type 'show' to see rules.\n" { 1354 t.Errorf("Expected valid rule to compile (because state should be unaffected) but got: %v (err: %v)", result, err) 1355 } 1356} 1357 1358func TestEvalBodyCompileError(t *testing.T) { 1359 ctx := context.Background() 1360 store := newTestStore() 1361 var buffer bytes.Buffer 1362 repl := newRepl(store, &buffer) 1363 repl.outputFormat = "json" 1364 err := repl.OneShot(ctx, `x = 1; y > x`) 1365 if _, ok := err.(ast.Errors); !ok { 1366 t.Fatalf("Expected error message in output but got`: %v", buffer.String()) 1367 } 1368 buffer.Reset() 1369 repl.OneShot(ctx, `x = 1; y = 2; y > x`) 1370 result := util.MustUnmarshalJSON(buffer.Bytes()) 1371 exp := util.MustUnmarshalJSON([]byte(`{ 1372 "result": [ 1373 { 1374 "expressions": [ 1375 { 1376 "value": true, 1377 "text": "x = 1", 1378 "location": { 1379 "row": 1, 1380 "col": 1 1381 } 1382 }, 1383 { 1384 "value": true, 1385 "text": "y = 2", 1386 "location": { 1387 "row": 1, 1388 "col": 8 1389 } 1390 }, 1391 { 1392 "value": true, 1393 "text": "y \u003e x", 1394 "location": { 1395 "row": 1, 1396 "col": 15 1397 } 1398 } 1399 ], 1400 "bindings": { 1401 "x": 1, 1402 "y": 2 1403 } 1404 } 1405 ] 1406 }`)) 1407 if !reflect.DeepEqual(exp, result) { 1408 t.Errorf(`Expected %v but got: %v"`, exp, buffer.String()) 1409 return 1410 } 1411} 1412 1413func TestEvalBodyContainingWildCards(t *testing.T) { 1414 ctx := context.Background() 1415 store := newTestStore() 1416 var buffer bytes.Buffer 1417 repl := newRepl(store, &buffer) 1418 repl.OneShot(ctx, "data.a[_].b.c[_] = x") 1419 expected := strings.TrimSpace(` 1420+-------+ 1421| x | 1422+-------+ 1423| true | 1424| 2 | 1425| false | 1426| false | 1427| true | 1428| 1 | 1429+-------+`) 1430 result := strings.TrimSpace(buffer.String()) 1431 if result != expected { 1432 t.Errorf("Expected only a single column of output but got:\n%v", result) 1433 } 1434 1435} 1436 1437func TestEvalBodyInput(t *testing.T) { 1438 ctx := context.Background() 1439 store := newTestStore() 1440 var buffer bytes.Buffer 1441 repl := newRepl(store, &buffer) 1442 1443 repl.OneShot(ctx, `package repl`) 1444 repl.OneShot(ctx, `input["foo.bar"] = "hello" { true }`) 1445 repl.OneShot(ctx, `input["baz"] = data.a[0].b.c[2] { true }`) 1446 repl.OneShot(ctx, `package test`) 1447 repl.OneShot(ctx, "import input.baz") 1448 repl.OneShot(ctx, `p = true { input["foo.bar"] = "hello"; baz = false }`) 1449 buffer.Reset() 1450 repl.OneShot(ctx, "p") 1451 1452 result := buffer.String() 1453 if result != "true\n" { 1454 t.Fatalf("expected true but got: %v", result) 1455 } 1456} 1457 1458func TestEvalBodyInputComplete(t *testing.T) { 1459 ctx := context.Background() 1460 store := newTestStore() 1461 var buffer bytes.Buffer 1462 repl := newRepl(store, &buffer) 1463 1464 // Test that input can be defined completely: 1465 // https://github.com/open-policy-agent/opa/issues/231 1466 repl.OneShot(ctx, `package repl`) 1467 repl.OneShot(ctx, `input = 1`) 1468 buffer.Reset() 1469 repl.OneShot(ctx, `input`) 1470 1471 result := buffer.String() 1472 if result != "1\n" { 1473 t.Fatalf("Expected 1 but got: %v", result) 1474 } 1475 1476 buffer.Reset() 1477 1478 // Test that input is as expected 1479 repl.OneShot(ctx, `package ex1`) 1480 repl.OneShot(ctx, `x = input`) 1481 buffer.Reset() 1482 repl.OneShot(ctx, `x`) 1483 1484 result = buffer.String() 1485 if result != "1\n" { 1486 t.Fatalf("Expected 1 but got: %v", result) 1487 } 1488 1489 // Test that local input replaces other inputs 1490 repl.OneShot(ctx, `package ex2`) 1491 repl.OneShot(ctx, `input = 2`) 1492 buffer.Reset() 1493 repl.OneShot(ctx, `input`) 1494 1495 result = buffer.String() 1496 1497 if result != "2\n" { 1498 t.Fatalf("Expected 2 but got: %v", result) 1499 } 1500 1501 buffer.Reset() 1502 1503 // Test that original input is intact 1504 repl.OneShot(ctx, `package ex3`) 1505 repl.OneShot(ctx, `input`) 1506 1507 result = buffer.String() 1508 1509 if result != "1\n" { 1510 t.Fatalf("Expected 1 but got: %v", result) 1511 } 1512 1513 // Test that deferencing undefined input results in undefined 1514 buffer.Reset() 1515 1516 repl = newRepl(store, &buffer) 1517 repl.OneShot(ctx, `input.p`) 1518 result = buffer.String() 1519 if result != "undefined\n" { 1520 t.Fatalf("Expected undefined but got: %v", result) 1521 } 1522 1523 buffer.Reset() 1524 repl.OneShot(ctx, `input.p = false`) 1525 result = buffer.String() 1526 if result != "undefined\n" { 1527 t.Fatalf("Expected undefined but got: %v", result) 1528 } 1529 1530} 1531 1532func TestEvalBodyWith(t *testing.T) { 1533 ctx := context.Background() 1534 store := newTestStore() 1535 var buffer bytes.Buffer 1536 repl := newRepl(store, &buffer) 1537 1538 repl.OneShot(ctx, `p = true { input.foo = "bar" }`) 1539 buffer.Reset() 1540 repl.OneShot(ctx, "p") 1541 1542 if buffer.String() != "undefined\n" { 1543 t.Fatalf("Expected undefined but got: %v", buffer.String()) 1544 } 1545 1546 buffer.Reset() 1547 1548 repl.OneShot(ctx, `p with input.foo as "bar"`) 1549 1550 result := buffer.String() 1551 expected := "true\n" 1552 1553 if result != expected { 1554 t.Fatalf("Expected true but got: %v", result) 1555 } 1556} 1557 1558func TestEvalBodyRewrittenBuiltin(t *testing.T) { 1559 ctx := context.Background() 1560 store := newTestStore() 1561 var buffer bytes.Buffer 1562 repl := newRepl(store, &buffer) 1563 repl.OneShot(ctx, "json") 1564 repl.OneShot(ctx, `p[x] { a[x]; a = [1,2,3,4] }`) 1565 repl.OneShot(ctx, "p[x] > 1") 1566 result := util.MustUnmarshalJSON(buffer.Bytes()) 1567 expected := util.MustUnmarshalJSON([]byte(`{ 1568 "result": [ 1569 { 1570 "expressions": [ 1571 { 1572 "value": true, 1573 "text": "p[x] \u003e 1", 1574 "location": { 1575 "row": 1, 1576 "col": 1 1577 } 1578 } 1579 ], 1580 "bindings": { 1581 "x": 2 1582 } 1583 }, 1584 { 1585 "expressions": [ 1586 { 1587 "value": true, 1588 "text": "p[x] \u003e 1", 1589 "location": { 1590 "row": 1, 1591 "col": 1 1592 } 1593 } 1594 ], 1595 "bindings": { 1596 "x": 3 1597 } 1598 } 1599 ] 1600 }`)) 1601 if util.Compare(result, expected) != 0 { 1602 t.Fatalf("Expected %v but got: %v", expected, buffer.String()) 1603 } 1604} 1605 1606func TestEvalBodyRewrittenRef(t *testing.T) { 1607 ctx := context.Background() 1608 store := newTestStore() 1609 var buffer bytes.Buffer 1610 repl := newRepl(store, &buffer) 1611 repl.OneShot(ctx, "json") 1612 repl.OneShot(ctx, `i = 1`) 1613 repl.OneShot(ctx, `data.a[0].b.c[i]`) 1614 result := util.MustUnmarshalJSON(buffer.Bytes()) 1615 expected := util.MustUnmarshalJSON([]byte(`{ 1616 "result": [ 1617 { 1618 "expressions": [ 1619 { 1620 "value": 2, 1621 "text": "data.a[0].b.c[i]", 1622 "location": { 1623 "row": 1, 1624 "col": 1 1625 } 1626 } 1627 ] 1628 } 1629 ] 1630 }`)) 1631 if util.Compare(result, expected) != 0 { 1632 t.Fatalf("Expected %v but got: %v", expected, buffer.String()) 1633 } 1634 buffer.Reset() 1635 repl.OneShot(ctx, "p = {1,2,3}") 1636 repl.OneShot(ctx, "p") 1637 result = util.MustUnmarshalJSON(buffer.Bytes()) 1638 expected = util.MustUnmarshalJSON([]byte(`{ 1639 "result": [ 1640 { 1641 "expressions": [ 1642 { 1643 "value": [ 1644 1, 1645 2, 1646 3 1647 ], 1648 "text": "p", 1649 "location": { 1650 "row": 1, 1651 "col": 1 1652 } 1653 } 1654 ] 1655 } 1656 ] 1657 }`)) 1658 if util.Compare(result, expected) != 0 { 1659 t.Fatalf("Expected %v but got: %v", expected, buffer.String()) 1660 } 1661 buffer.Reset() 1662 repl.OneShot(ctx, "p[x]") 1663 result = util.MustUnmarshalJSON(buffer.Bytes()) 1664 expected = util.MustUnmarshalJSON([]byte(`{ 1665 "result": [ 1666 { 1667 "expressions": [ 1668 { 1669 "value": 1, 1670 "text": "p[x]", 1671 "location": { 1672 "row": 1, 1673 "col": 1 1674 } 1675 } 1676 ], 1677 "bindings": { 1678 "x": 1 1679 } 1680 }, 1681 { 1682 "expressions": [ 1683 { 1684 "value": 2, 1685 "text": "p[x]", 1686 "location": { 1687 "row": 1, 1688 "col": 1 1689 } 1690 } 1691 ], 1692 "bindings": { 1693 "x": 2 1694 } 1695 }, 1696 { 1697 "expressions": [ 1698 { 1699 "value": 3, 1700 "text": "p[x]", 1701 "location": { 1702 "row": 1, 1703 "col": 1 1704 } 1705 } 1706 ], 1707 "bindings": { 1708 "x": 3 1709 } 1710 } 1711 ] 1712 }`)) 1713 if util.Compare(result, expected) != 0 { 1714 t.Fatalf("Expected %v but got: %v", expected, buffer.String()) 1715 } 1716} 1717 1718func TestEvalBodySomeDecl(t *testing.T) { 1719 ctx := context.Background() 1720 store := newTestStore() 1721 var buffer bytes.Buffer 1722 repl := newRepl(store, &buffer) 1723 repl.OneShot(ctx, "json") 1724 repl.OneShot(ctx, "some x; x = 1") 1725 exp := util.MustUnmarshalJSON([]byte(`{ 1726 "result": [ 1727 { 1728 "expressions": [ 1729 { 1730 "value": true, 1731 "text": "x = 1", 1732 "location": { 1733 "row": 1, 1734 "col": 9 1735 } 1736 } 1737 ], 1738 "bindings": { 1739 "x": 1 1740 } 1741 } 1742 ] 1743 }`)) 1744 result := util.MustUnmarshalJSON(buffer.Bytes()) 1745 if util.Compare(result, exp) != 0 { 1746 t.Fatalf("Expected %v but got: %v", exp, result) 1747 } 1748} 1749 1750func TestEvalImport(t *testing.T) { 1751 ctx := context.Background() 1752 store := newTestStore() 1753 var buffer bytes.Buffer 1754 repl := newRepl(store, &buffer) 1755 repl.OneShot(ctx, "import data.a") 1756 if len(buffer.Bytes()) != 0 { 1757 t.Errorf("Expected no output but got: %v", buffer.String()) 1758 return 1759 } 1760 buffer.Reset() 1761 repl.OneShot(ctx, "a[0].b.c[0] = true") 1762 result := buffer.String() 1763 expected := "true\n" 1764 if result != expected { 1765 t.Errorf("Expected expression to evaluate successfully but got: %v", result) 1766 return 1767 } 1768 1769 // https://github.com/open-policy-agent/opa/issues/158 - re-run query to 1770 // make sure import is not lost 1771 buffer.Reset() 1772 repl.OneShot(ctx, "a[0].b.c[0] = true") 1773 result = buffer.String() 1774 expected = "true\n" 1775 if result != expected { 1776 t.Fatalf("Expected expression to evaluate successfully but got: %v", result) 1777 } 1778} 1779 1780func TestEvalPackage(t *testing.T) { 1781 ctx := context.Background() 1782 store := newTestStore() 1783 var buffer bytes.Buffer 1784 repl := newRepl(store, &buffer) 1785 repl.OneShot(ctx, `package foo.bar`) 1786 repl.OneShot(ctx, `p = true { true }`) 1787 repl.OneShot(ctx, `package baz.qux`) 1788 buffer.Reset() 1789 err := repl.OneShot(ctx, "p") 1790 if !strings.Contains(err.Error(), "p is unsafe") { 1791 t.Fatalf("Expected unsafe variable error but got: %v", err) 1792 } 1793 repl.OneShot(ctx, "import data.foo.bar.p") 1794 buffer.Reset() 1795 repl.OneShot(ctx, "p") 1796 if buffer.String() != "true\n" { 1797 t.Errorf("Expected expression to eval successfully but got: %v", buffer.String()) 1798 return 1799 } 1800} 1801 1802func TestMetrics(t *testing.T) { 1803 ctx := context.Background() 1804 store := newTestStore() 1805 var buffer bytes.Buffer 1806 1807 repl := newRepl(store, &buffer) 1808 repl.OneShot(ctx, "a = {[1,2], [3,4]}") 1809 repl.OneShot(ctx, "metrics") 1810 repl.OneShot(ctx, `[x | a[x]]`) 1811 if !strings.Contains(buffer.String(), "timer_rego_query_compile_ns") { 1812 t.Fatal("Expected output to contain well known metric key but got:", buffer.String()) 1813 } 1814 1815 buffer.Reset() 1816 repl.OneShot(ctx, `[x | a[x]]`) 1817 if !strings.Contains(buffer.String(), "timer_rego_query_compile_ns") { 1818 t.Fatal("Expected output to contain well known metric key but got:", buffer.String()) 1819 } 1820 1821 buffer.Reset() 1822 repl.OneShot(ctx, "metrics") 1823 repl.OneShot(ctx, `[x | a[x]]`) 1824 1825 expected := `[ 1826 [ 1827 1, 1828 2 1829 ], 1830 [ 1831 3, 1832 4 1833 ] 1834] 1835` 1836 1837 if expected != buffer.String() { 1838 t.Fatalf("Expected output to be exactly:\n%v\n\nGot:\n\n%v\n", expected, buffer.String()) 1839 } 1840} 1841 1842func TestProfile(t *testing.T) { 1843 store := newTestStore() 1844 ctx := context.Background() 1845 txn := storage.NewTransactionOrDie(ctx, store, storage.WriteParams) 1846 const numLines = 21 1847 1848 mod2 := []byte(`package rbac 1849 1850 input = { 1851 "subject": "bob", 1852 "resource": "foo123", 1853 "action": "write", 1854 } 1855 bindings = [ 1856 { 1857 "user": "alice", 1858 "roles": ["dev", "test"], 1859 }, 1860 { 1861 "user": "bob", 1862 "roles": ["test"], 1863 }, 1864] 1865 1866 roles = [ 1867 { 1868 "name": "dev", 1869 "permissions": [ 1870 {"resource": "foo123", "action": "write"}, 1871 {"resource": "foo123", "action": "read"}, 1872 ], 1873 }, 1874 { 1875 "name": "test", 1876 "permissions": [{"resource": "foo123", "action": "read"}], 1877 }, 1878] 1879 1880default allow = false 1881 1882 allow { 1883 user_has_role[role_name] 1884 role_has_permission[role_name] 1885 } 1886 1887 user_has_role[role_name] { 1888 binding := bindings[_] 1889 binding.user = input.subject 1890 role_name := binding.roles[_] 1891 } 1892 1893 role_has_permission[role_name] { 1894 role := roles[_] 1895 role_name := role.name 1896 perm := role.permissions[_] 1897 perm.resource = input.resource 1898 perm.action = input.action 1899 }`) 1900 1901 if err := store.UpsertPolicy(ctx, txn, "mod2", mod2); err != nil { 1902 panic(err) 1903 } 1904 if err := store.Commit(ctx, txn); err != nil { 1905 panic(err) 1906 } 1907 1908 var buffer bytes.Buffer 1909 repl := newRepl(store, &buffer) 1910 repl.OneShot(ctx, "profile") 1911 repl.OneShot(ctx, "data.rbac.allow") 1912 result := buffer.String() 1913 lines := strings.Split(result, "\n") 1914 if len(lines) != numLines { 1915 t.Fatal("Expected 21 lines, got :", len(lines)) 1916 } 1917 buffer.Reset() 1918} 1919 1920func TestInstrument(t *testing.T) { 1921 ctx := context.Background() 1922 store := newTestStore() 1923 var buffer bytes.Buffer 1924 1925 repl := newRepl(store, &buffer) 1926 1927 // Turn on instrumentation w/o turning on metrics. 1928 repl.OneShot(ctx, "instrument") 1929 repl.OneShot(ctx, "true") 1930 1931 result := buffer.String() 1932 1933 if !strings.Contains(result, "histogram_eval_op_plug") { 1934 t.Fatal("Expected plug histogram in output but got:", result) 1935 } 1936 1937 buffer.Reset() 1938 1939 // Turn off instrumentation. 1940 repl.OneShot(ctx, "instrument") 1941 repl.OneShot(ctx, "true") 1942 1943 result = buffer.String() 1944 1945 if strings.Contains(result, "histogram_eval_op_plug") { 1946 t.Fatal("Expected instrumentation to be turned off but got:", result) 1947 } 1948 1949 buffer.Reset() 1950 1951 // Turn on metrics and then turn on instrumentation. 1952 repl.OneShot(ctx, "metrics") 1953 repl.OneShot(ctx, "true") 1954 1955 result = buffer.String() 1956 1957 if strings.Contains(result, "histogram_eval_op_plug") { 1958 t.Fatal("Expected instrumentation to be turned off but got:", result) 1959 } 1960 1961 if !strings.Contains(result, "timer_rego_query_eval_ns") { 1962 t.Fatal("Expected metrics to be turned on but got:", result) 1963 } 1964 1965 buffer.Reset() 1966 1967 repl.OneShot(ctx, "instrument") 1968 repl.OneShot(ctx, "true") 1969 1970 result = buffer.String() 1971 1972 if !strings.Contains(result, "histogram_eval_op_plug") { 1973 t.Fatal("Expected instrumentation to be turned on but got:", result) 1974 } 1975 1976 if !strings.Contains(result, "timer_rego_query_eval_ns") { 1977 t.Fatal("Expected metrics to be turned on but got:", result) 1978 } 1979 1980} 1981 1982func TestEvalTrace(t *testing.T) { 1983 ctx := context.Background() 1984 store := newTestStore() 1985 var buffer bytes.Buffer 1986 repl := newRepl(store, &buffer) 1987 repl.OneShot(ctx, "trace") 1988 repl.OneShot(ctx, `data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1`) 1989 expected := strings.TrimSpace(` 1990query:1 Enter data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1 1991query:1 | Eval data.a[i].b.c[j] = x 1992query:1 | Eval data.a[k].b.c[x] = 1 1993query:1 | Fail data.a[k].b.c[x] = 1 1994query:1 | Redo data.a[i].b.c[j] = x 1995query:1 | Eval data.a[k].b.c[x] = 1 1996query:1 | Exit data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1 1997query:1 Redo data.a[i].b.c[j] = x; data.a[k].b.c[x] = 1 1998query:1 | Redo data.a[k].b.c[x] = 1 1999query:1 | Redo data.a[i].b.c[j] = x 2000query:1 | Eval data.a[k].b.c[x] = 1 2001query:1 | Fail data.a[k].b.c[x] = 1 2002query:1 | Redo data.a[i].b.c[j] = x 2003query:1 | Eval data.a[k].b.c[x] = 1 2004query:1 | Fail data.a[k].b.c[x] = 1 2005query:1 | Redo data.a[i].b.c[j] = x 2006query:1 | Eval data.a[k].b.c[x] = 1 2007query:1 | Fail data.a[k].b.c[x] = 1 2008query:1 | Redo data.a[i].b.c[j] = x 2009query:1 | Eval data.a[k].b.c[x] = 1 2010query:1 | Fail data.a[k].b.c[x] = 1 2011query:1 | Redo data.a[i].b.c[j] = x 2012+---+---+---+---+ 2013| i | j | k | x | 2014+---+---+---+---+ 2015| 0 | 1 | 1 | 2 | 2016+---+---+---+---+`) 2017 expected += "\n" 2018 2019 if expected != buffer.String() { 2020 t.Fatalf("Expected output to be exactly:\n%v\n\nGot:\n\n%v\n", expected, buffer.String()) 2021 } 2022} 2023 2024func TestEvalNotes(t *testing.T) { 2025 ctx := context.Background() 2026 store := newTestStore() 2027 var buffer bytes.Buffer 2028 repl := newRepl(store, &buffer) 2029 repl.OneShot(ctx, `p { a = [1,2,3]; a[i] = x; x > 1; trace(sprintf("x = %d", [x])) }`) 2030 repl.OneShot(ctx, "notes") 2031 buffer.Reset() 2032 repl.OneShot(ctx, "p") 2033 expected := strings.TrimSpace(`query:1 Enter data.repl.p = _ 2034query:1 | Enter data.repl.p 2035note | | Note "x = 2" 2036query:1 Redo data.repl.p = _ 2037query:1 | Redo data.repl.p 2038note | | Note "x = 3" 2039true`) 2040 expected += "\n" 2041 if expected != buffer.String() { 2042 t.Fatalf("Expected output to be exactly:\n%v\n\nGot:\n\n%v\n", expected, buffer.String()) 2043 } 2044} 2045 2046func TestTruncatePrettyOutput(t *testing.T) { 2047 ctx := context.Background() 2048 store := inmem.New() 2049 var buffer bytes.Buffer 2050 repl := newRepl(store, &buffer) 2051 repl.prettyLimit = 1000 // crank up limit to test repl command 2052 repl.OneShot(ctx, "pretty-limit 80") 2053 repl.OneShot(ctx, "data[x]") 2054 for _, line := range strings.Split(buffer.String(), "\n") { 2055 // | "repl" | {"version": <elided>... | 2056 if len(line) > 96 { 2057 t.Fatalf("Expected len(line) to be < 96 but got:\n\n%v", buffer) 2058 } 2059 } 2060 buffer.Reset() 2061 if err := repl.OneShot(ctx, "pretty-limit"); err == nil || !strings.Contains(err.Error(), "usage: pretty-limit <n>") { 2062 t.Fatalf("Expected usage error but got: %v", err) 2063 } 2064} 2065 2066func TestUnsetPackage(t *testing.T) { 2067 ctx := context.Background() 2068 store := inmem.New() 2069 var buffer bytes.Buffer 2070 repl := newRepl(store, &buffer) 2071 2072 repl.OneShot(ctx, "package a") 2073 if err := repl.OneShot(ctx, `unset-package 5`); err == nil { 2074 t.Fatalf("Expected package-unset error for bad package but got: %v", buffer.String()) 2075 } 2076 2077 buffer.Reset() 2078 2079 repl.OneShot(ctx, "package a") 2080 repl.OneShot(ctx, "unset-package b") 2081 if buffer.String() != "warning: no matching package\n" { 2082 t.Fatalf("Expected unset-package warning no matching package but got: %v", buffer.String()) 2083 } 2084 2085 buffer.Reset() 2086 2087 repl.OneShot(ctx, `package a`) 2088 if err := repl.OneShot(ctx, `unset-package b`); err != nil { 2089 t.Fatalf("Expected unset-package to succeed for input: %v", err) 2090 } 2091 2092 buffer.Reset() 2093 2094 repl.OneShot(ctx, "package a") 2095 repl.OneShot(ctx, "unset-package a") 2096 repl.OneShot(ctx, "show") 2097 if buffer.String() != "no rules defined\n" { 2098 t.Fatalf("Expected unset-package to return to default but got: %v", buffer.String()) 2099 } 2100} 2101 2102func assertREPLText(t *testing.T, buf bytes.Buffer, expected string) { 2103 t.Helper() 2104 result := buf.String() 2105 if result != expected { 2106 t.Fatalf("Expected:\n%v\n\nString:\n\n%v\nGot:\n%v\n\nString:\n\n%v", []byte(expected), expected, []byte(result), result) 2107 } 2108} 2109 2110func expectOutput(t *testing.T, output string, expected string) { 2111 t.Helper() 2112 if output != expected { 2113 t.Errorf("Repl output: expected %#v but got %#v", expected, output) 2114 } 2115} 2116 2117func newRepl(store storage.Store, buffer *bytes.Buffer) *REPL { 2118 repl := New(store, "", buffer, "", 0, "") 2119 return repl 2120} 2121 2122func newTestStore() storage.Store { 2123 input := ` 2124 { 2125 "a": [ 2126 { 2127 "b": { 2128 "c": [true,2,false] 2129 } 2130 }, 2131 { 2132 "b": { 2133 "c": [false,true,1] 2134 } 2135 } 2136 ] 2137 } 2138 ` 2139 var data map[string]interface{} 2140 err := util.UnmarshalJSON([]byte(input), &data) 2141 if err != nil { 2142 panic(err) 2143 } 2144 return inmem.NewFromObject(data) 2145} 2146 2147func parseJSON(s string) interface{} { 2148 var v interface{} 2149 if err := util.UnmarshalJSON([]byte(s), &v); err != nil { 2150 panic(err) 2151 } 2152 return v 2153} 2154