diff --git a/server/BUILD.bazel b/server/BUILD.bazel deleted file mode 100644 index f501d5be..00000000 --- a/server/BUILD.bazel +++ /dev/null @@ -1,47 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") - -package( - default_visibility = ["//visibility:public"], - licenses = ["notice"], # Apache 2.0 -) - -go_library( - name = "go_default_library", - srcs = [ - "server.go", - ], - importpath = "github.com/google/cel-go/server", - deps = [ - "//cel:go_default_library", - "//common/types:go_default_library", - "//common/types/ref:go_default_library", - "//ext:go_default_library", - "@com_google_cel_spec//proto/test/v1/proto2:test_all_types_go_proto", - "@com_google_cel_spec//proto/test/v1/proto3:test_all_types_go_proto", - "@org_golang_google_genproto_googleapis_api//expr/conformance/v1alpha1:go_default_library", - "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", - "@org_golang_google_genproto_googleapis_rpc//code:go_default_library", - "@org_golang_google_genproto_googleapis_rpc//status:go_default_library", - "@org_golang_google_protobuf//proto:go_default_library", - "@org_golang_google_protobuf//types/known/anypb:go_default_library", - ], -) - -go_test( - name = "go_default_test", - srcs = ["server_test.go"], - args = ["$(location //server/main:cel_server)"], - data = ["//server/main:cel_server"], - rundir = ".", - visibility = ["//visibility:public"], - deps = [ - ":go_default_library", - "//checker/decls:go_default_library", - "//common/operators:go_default_library", - "//test:go_default_library", - "@com_google_cel_spec//tools/celrpc:go_default_library", - "@org_golang_google_genproto_googleapis_api//expr/conformance/v1alpha1:go_default_library", - "@org_golang_google_genproto_googleapis_api//expr/v1alpha1:go_default_library", - "@org_golang_google_genproto_googleapis_rpc//status:go_default_library", - ], -) diff --git a/server/ct.sh b/server/ct.sh deleted file mode 100755 index 9d8ad4d3..00000000 --- a/server/ct.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -exec "$@" diff --git a/server/go.mod b/server/go.mod deleted file mode 100644 index f760fdfc..00000000 --- a/server/go.mod +++ /dev/null @@ -1,23 +0,0 @@ -module github.com/google/cel-go/server - -go 1.21 - -require ( - cel.dev/expr v0.16.1 - github.com/google/cel-go v0.0.0-00010101000000-000000000000 - google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 - google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 - google.golang.org/protobuf v1.34.2 -) - -require ( - github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/stoewer/go-strcase v1.2.0 // indirect - golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc // indirect - golang.org/x/net v0.26.0 // indirect - golang.org/x/sys v0.21.0 // indirect - golang.org/x/text v0.16.0 // indirect - google.golang.org/grpc v1.65.0 // indirect -) - -replace github.com/google/cel-go => ./.. diff --git a/server/go.sum b/server/go.sum deleted file mode 100644 index c86b1e4c..00000000 --- a/server/go.sum +++ /dev/null @@ -1,36 +0,0 @@ -cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= -cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= -github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= -github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/bazelbuild/rules_go v0.49.0 h1:5vCbuvy8Q11g41lseGJDc5vxhDjJtfxr6nM/IC4VmqM= -github.com/bazelbuild/rules_go v0.49.0/go.mod h1:Dhcz716Kqg1RHNWos+N6MlXNkjNP2EwZQ0LukRKJfMs= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc h1:mCRnTeVUjcrhlRmO0VK8a6k6Rrf6TF9htwo2pJVSjIU= -golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= -golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= -golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1:YcyjlL1PRr2Q17/I0dPk2JmYS5CDXfcdb2Z3YRioEbw= -google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/server/main/BUILD.bazel b/server/main/BUILD.bazel deleted file mode 100644 index 9cf60230..00000000 --- a/server/main/BUILD.bazel +++ /dev/null @@ -1,16 +0,0 @@ -load("@io_bazel_rules_go//go:def.bzl", "go_binary") - -package( - default_visibility = ["//visibility:public"], - licenses = ["notice"], # Apache 2.0 -) - -go_binary( - name = "cel_server", - srcs = ["main.go"], - out = "cel_server", - deps = [ - "//server:go_default_library", - "@com_google_cel_spec//tools/celrpc:go_default_library", - ], -) diff --git a/server/main/main.go b/server/main/main.go deleted file mode 100644 index 6a2f4137..00000000 --- a/server/main/main.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package main declares the executable entry point for the CEL server. -package main - -import ( - "github.com/google/cel-go/server" - - "cel.dev/expr/tools/celrpc" -) - -func main() { - celrpc.RunServer(&server.ConformanceServer{}) -} diff --git a/server/server.go b/server/server.go deleted file mode 100644 index 355bbb4c..00000000 --- a/server/server.go +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package server defines the gRPC conformance test server for CEL Go. -package server - -import ( - "context" - "fmt" - - "github.com/google/cel-go/cel" - "github.com/google/cel-go/common/types" - "github.com/google/cel-go/common/types/ref" - "github.com/google/cel-go/ext" - - test2pb "cel.dev/expr/proto/test/v1/proto2/test_all_types" - test3pb "cel.dev/expr/proto/test/v1/proto3/test_all_types" - - confpb "google.golang.org/genproto/googleapis/api/expr/conformance/v1alpha1" - exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" - codepb "google.golang.org/genproto/googleapis/rpc/code" - statuspb "google.golang.org/genproto/googleapis/rpc/status" - anypb "google.golang.org/protobuf/types/known/anypb" -) - -// ConformanceServer contains the server state. -type ConformanceServer struct{} - -// Parse implements ConformanceService.Parse. -func (s *ConformanceServer) Parse(ctx context.Context, in *confpb.ParseRequest) (*confpb.ParseResponse, error) { - if in.CelSource == "" { - return nil, invalidArgument("No source code.") - } - // NOTE: syntax_version isn't currently used - parseOptions := []cel.EnvOption{ - ext.Math(), - ext.Protos(), - ext.Bindings(), - } - if in.DisableMacros { - parseOptions = append(parseOptions, cel.ClearMacros()) - } - parseOptions = append(parseOptions, cel.OptionalTypes()) - env, _ := cel.NewEnv(parseOptions...) - past, iss := env.Parse(in.CelSource) - resp := confpb.ParseResponse{} - if iss == nil || iss.Err() == nil { - // Success - resp.ParsedExpr, _ = cel.AstToParsedExpr(past) - } else { - // Failure - appendErrors(iss.Errors(), &resp.Issues) - } - return &resp, nil -} - -// Check implements ConformanceService.Check. -func (s *ConformanceServer) Check(ctx context.Context, in *confpb.CheckRequest) (*confpb.CheckResponse, error) { - if in.ParsedExpr == nil { - return nil, invalidArgument("No parsed expression.") - } - if in.ParsedExpr.SourceInfo == nil { - return nil, invalidArgument("No source info.") - } - // Build the environment. - checkOptions := []cel.EnvOption{ - cel.StdLib(), - ext.Strings(), - ext.Math(), - ext.Encoders(), - } - if in.NoStdEnv { - checkOptions = []cel.EnvOption{} - } - checkOptions = append(checkOptions, cel.Container(in.Container)) - checkOptions = append(checkOptions, cel.Declarations(in.TypeEnv...)) - checkOptions = append(checkOptions, cel.Types(&test2pb.TestAllTypes{})) - checkOptions = append(checkOptions, cel.Types(&test2pb.Proto2ExtensionScopedMessage{})) - checkOptions = append(checkOptions, cel.Types(&test3pb.TestAllTypes{})) - checkOptions = append(checkOptions, cel.OptionalTypes()) - env, _ := cel.NewCustomEnv(checkOptions...) - - // Check the expression. - cast, iss := env.Check(cel.ParsedExprToAst(in.ParsedExpr)) - resp := confpb.CheckResponse{} - if iss == nil || iss.Err() == nil { - // Success - resp.CheckedExpr, _ = cel.AstToCheckedExpr(cast) - } else { - // Failure - appendErrors(iss.Errors(), &resp.Issues) - } - return &resp, nil -} - -// Eval implements ConformanceService.Eval. -func (s *ConformanceServer) Eval(ctx context.Context, in *confpb.EvalRequest) (*confpb.EvalResponse, error) { - env, _ := evalEnv.Extend(cel.Container(in.Container)) - var prg cel.Program - var err error - switch in.GetExprKind().(type) { - case *confpb.EvalRequest_ParsedExpr: - ast := cel.ParsedExprToAst(in.GetParsedExpr()) - prg, err = env.Program(ast) - if err != nil { - return nil, err - } - case *confpb.EvalRequest_CheckedExpr: - ast := cel.CheckedExprToAst(in.GetCheckedExpr()) - prg, err = env.Program(ast) - if err != nil { - return nil, err - } - default: - return nil, invalidArgument("No expression.") - } - args := make(map[string]any) - for name, exprValue := range in.Bindings { - refVal, err := ExprValueToRefValue(env.TypeAdapter(), exprValue) - if err != nil { - return nil, fmt.Errorf("can't convert binding %s: %s", name, err) - } - args[name] = refVal - } - // NOTE: the EvalState is currently discarded - res, _, err := prg.Eval(args) - resultExprVal, err := RefValueToExprValue(res, err) - if err != nil { - return nil, fmt.Errorf("can't convert result: %s", err) - } - return &confpb.EvalResponse{Result: resultExprVal}, nil -} - -// appendErrors converts the errors from errs to Status messages -// and appends them to the list of issues. -func appendErrors(errs []*cel.Error, issues *[]*statuspb.Status) { - for _, e := range errs { - status := ErrToStatus(e, confpb.IssueDetails_ERROR) - *issues = append(*issues, status) - } -} - -// ErrToStatus converts an Error to a Status message with the given severity. -func ErrToStatus(e *cel.Error, severity confpb.IssueDetails_Severity) *statuspb.Status { - detail := &confpb.IssueDetails{ - Severity: severity, - Position: &confpb.SourcePosition{ - Line: int32(e.Location.Line()), - Column: int32(e.Location.Column()), - }, - } - s := errToStatus(invalidArgument(e.Message)) - packed, err := anypb.New(detail) - if err != nil { - return s - } - s.Details = append(s.Details, packed) - return s -} - -// TODO(jimlarson): The following conversion code should be moved to -// common/types/provider.go and consolidated/refactored as appropriate. -// In particular, make judicious use of types.NativeToValue(). - -// RefValueToExprValue converts between ref.Val and exprpb.ExprValue. -func RefValueToExprValue(res ref.Val, err error) (*exprpb.ExprValue, error) { - if err != nil { - s := errToStatus(err) - return &exprpb.ExprValue{ - Kind: &exprpb.ExprValue_Error{ - Error: &exprpb.ErrorSet{ - Errors: []*statuspb.Status{s}, - }, - }, - }, nil - } - if types.IsUnknown(res) { - return &exprpb.ExprValue{ - Kind: &exprpb.ExprValue_Unknown{ - Unknown: &exprpb.UnknownSet{ - Exprs: res.Value().([]int64), - }, - }}, nil - } - v, err := cel.RefValueToValue(res) - if err != nil { - return nil, err - } - return &exprpb.ExprValue{ - Kind: &exprpb.ExprValue_Value{Value: v}}, nil -} - -// ExprValueToRefValue converts between exprpb.ExprValue and ref.Val. -func ExprValueToRefValue(adapter types.Adapter, ev *exprpb.ExprValue) (ref.Val, error) { - switch ev.Kind.(type) { - case *exprpb.ExprValue_Value: - return cel.ValueToRefValue(adapter, ev.GetValue()) - case *exprpb.ExprValue_Error: - // An error ExprValue is a repeated set of statuspb.Status - // messages, with no convention for the status details. - // To convert this to a types.Err, we need to convert - // these Status messages to a single string, and be - // able to decompose that string on output so we can - // round-trip arbitrary ExprValue messages. - // TODO(jimlarson) make a convention for this. - return types.NewErr("XXX add details later"), nil - case *exprpb.ExprValue_Unknown: - var unk *types.Unknown - for _, id := range ev.GetUnknown().GetExprs() { - if unk == nil { - unk = types.NewUnknown(id, nil) - } - unk = types.MergeUnknowns(types.NewUnknown(id, nil), unk) - } - return unk, nil - } - return nil, invalidArgument("unknown ExprValue kind") -} - -func errToStatus(err error) *statuspb.Status { - re, ok := err.(invalidArgErr) - if ok { - return &statuspb.Status{ - Code: int32(codepb.Code_INVALID_ARGUMENT), - Message: re.msg, - } - } - return &statuspb.Status{ - Code: int32(codepb.Code_UNKNOWN), - Message: err.Error(), - } -} - -func invalidArgument(msg string) error { - return invalidArgErr{msg: msg} -} - -type invalidArgErr struct { - msg string -} - -func (e invalidArgErr) Error() string { - return e.String() -} - -func (e invalidArgErr) String() string { - return fmt.Sprintf("rpc error: code = InvalidArgument desc = %s", e.msg) -} - -func (e invalidArgErr) Is(other error) bool { - otherErr, ok := other.(invalidArgErr) - return ok && e.msg == otherErr.msg -} - -var evalEnv *cel.Env - -func init() { - evalEnv, _ = cel.NewEnv( - ext.Strings(), - ext.Math(), - ext.Encoders(), - cel.Types(&test2pb.TestAllTypes{}, &test2pb.Proto2ExtensionScopedMessage{}, &test3pb.TestAllTypes{}), - cel.EagerlyValidateDeclarations(true), - cel.EnableErrorOnBadPresenceTest(true), - cel.OptionalTypes()) -} diff --git a/server/server_test.go b/server/server_test.go deleted file mode 100644 index 1b34e3be..00000000 --- a/server/server_test.go +++ /dev/null @@ -1,404 +0,0 @@ -// Copyright 2018 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package server - -import ( - "context" - "log" - "os" - "testing" - - "cel.dev/expr/tools/celrpc" - - "github.com/google/cel-go/checker/decls" - "github.com/google/cel-go/common/operators" - "github.com/google/cel-go/test" - - confpb "google.golang.org/genproto/googleapis/api/expr/conformance/v1alpha1" - exprpb "google.golang.org/genproto/googleapis/api/expr/v1alpha1" -) - -type serverTest struct { - client celrpc.ConfClient -} - -var ( - globals = serverTest{} -) - -// TestMain performs setup for testing. -func TestMain(m *testing.M) { - // Use a helper function to ensure we run shutdown() - // before calling os.Exit() - os.Exit(mainHelper(m)) -} - -func mainHelper(m *testing.M) int { - client, err := celrpc.NewGrpcClient(os.Args[1]) - globals.client = client - defer client.Shutdown() - if err != nil { - // testing.M doesn't have a logging method. hmm... - log.Fatal(err) - return 1 - } - return m.Run() -} - -var ( - parsed = &exprpb.ParsedExpr{ - Expr: test.ExprCall(1, operators.Add, - test.ExprLiteral(2, int64(1)), - test.ExprLiteral(3, int64(1))), - SourceInfo: &exprpb.SourceInfo{ - Location: "the location", - Positions: map[int64]int32{ - 1: 0, - 2: 0, - 3: 4, - }, - }, - } -) - -// TestParse tests the Parse method. -func TestParse(t *testing.T) { - req := confpb.ParseRequest{ - CelSource: "1 + 1", - } - res, err := globals.client.Parse(context.Background(), &req) - if err != nil { - t.Fatal(err) - } - if res == nil { - t.Fatal("Empty result") - } - if res.ParsedExpr == nil { - t.Fatal("Empty parsed expression in result") - } - // Could check against 'parsed' above, - // but the expression ids are arbitrary, - // and explicit comparison logic is about as - // much work as normalization would be. - if res.ParsedExpr.Expr == nil { - t.Fatal("Empty expression in result") - } - switch res.ParsedExpr.Expr.ExprKind.(type) { - case *exprpb.Expr_CallExpr: - c := res.ParsedExpr.Expr.GetCallExpr() - if c.Target != nil { - t.Error("Call has target", c) - } - if c.Function != "_+_" { - t.Error("Wrong function", c) - } - if len(c.Args) != 2 { - t.Error("Too many or few args", c) - } - for i, a := range c.Args { - switch a.ExprKind.(type) { - case *exprpb.Expr_ConstExpr: - l := a.GetConstExpr() - switch l.ConstantKind.(type) { - case *exprpb.Constant_Int64Value: - if l.GetInt64Value() != int64(1) { - t.Errorf("Arg %d wrong value: %v", i, a) - } - default: - t.Errorf("Arg %d not int: %v", i, a) - } - default: - t.Errorf("Arg %d not literal: %v", i, a) - } - } - default: - t.Error("Wrong expression type", res.ParsedExpr.Expr) - } -} - -// TestCheck tests the Check method. -func TestCheck(t *testing.T) { - // If TestParse() passes, it validates a good chunk - // of the server mechanisms for data conversion, so we - // won't be as fussy here.. - req := confpb.CheckRequest{ - ParsedExpr: parsed, - } - res, err := globals.client.Check(context.Background(), &req) - if err != nil { - t.Fatal(err) - } - if res == nil { - t.Fatal("Empty result") - } - if res.CheckedExpr == nil { - t.Fatal("No checked expression") - } - tp, present := res.CheckedExpr.TypeMap[int64(1)] - if !present { - t.Fatal("No type for top level expression", res) - } - switch tp.TypeKind.(type) { - case *exprpb.Type_Primitive: - if tp.GetPrimitive() != exprpb.Type_INT64 { - t.Error("Bad top-level type", tp) - } - default: - t.Error("Bad top-level type", tp) - } -} - -// TestEval tests the Eval method. -func TestEval(t *testing.T) { - req := confpb.EvalRequest{ - ExprKind: &confpb.EvalRequest_ParsedExpr{ParsedExpr: parsed}, - } - res, err := globals.client.Eval(context.Background(), &req) - if err != nil { - t.Fatal(err) - } - if res == nil || res.Result == nil { - t.Fatal("Nil result") - } - switch res.Result.Kind.(type) { - case *exprpb.ExprValue_Value: - v := res.Result.GetValue() - switch v.Kind.(type) { - case *exprpb.Value_Int64Value: - if v.GetInt64Value() != int64(2) { - t.Error("Wrong result for 1 + 1", v) - } - default: - t.Error("Wrong result value type", v) - } - default: - t.Fatal("Result not a value", res.Result) - } -} - -// TestFullUp tests Parse, Check, and Eval back-to-back. -func TestFullUp(t *testing.T) { - preq := confpb.ParseRequest{ - CelSource: "x + y", - } - pres, err := globals.client.Parse(context.Background(), &preq) - if err != nil { - t.Fatal(err) - } - parsedExpr := pres.ParsedExpr - if parsedExpr == nil { - t.Fatal("Empty parsed expression") - } - - creq := confpb.CheckRequest{ - ParsedExpr: parsedExpr, - TypeEnv: []*exprpb.Decl{ - decls.NewVar("x", decls.Int), - decls.NewVar("y", decls.Int), - }, - } - cres, err := globals.client.Check(context.Background(), &creq) - if err != nil { - t.Fatal(err) - } - if cres == nil { - t.Fatal("Empty check result") - } - checkedExpr := cres.CheckedExpr - if checkedExpr == nil { - t.Fatal("No checked expression") - } - tp, present := checkedExpr.TypeMap[int64(1)] - if !present { - t.Fatal("No type for top level expression", cres) - } - switch tp.TypeKind.(type) { - case *exprpb.Type_Primitive: - if tp.GetPrimitive() != exprpb.Type_INT64 { - t.Error("Bad top-level type", tp) - } - default: - t.Error("Bad top-level type", tp) - } - - ereq := confpb.EvalRequest{ - ExprKind: &confpb.EvalRequest_CheckedExpr{CheckedExpr: checkedExpr}, - Bindings: map[string]*exprpb.ExprValue{ - "x": exprValueInt64(1), - "y": exprValueInt64(2), - }, - } - eres, err := globals.client.Eval(context.Background(), &ereq) - if err != nil { - t.Fatal(err) - } - if eres == nil || eres.Result == nil { - t.Fatal("Nil result") - } - switch eres.Result.Kind.(type) { - case *exprpb.ExprValue_Value: - v := eres.Result.GetValue() - switch v.Kind.(type) { - case *exprpb.Value_Int64Value: - if v.GetInt64Value() != int64(3) { - t.Error("Wrong result for 1 + 2", v) - } - default: - t.Error("Wrong result value type", v) - } - default: - t.Fatal("Result not a value", eres.Result) - } -} - -func exprValueInt64(x int64) *exprpb.ExprValue { - return &exprpb.ExprValue{ - Kind: &exprpb.ExprValue_Value{ - Value: &exprpb.Value{ - Kind: &exprpb.Value_Int64Value{Int64Value: x}, - }, - }, - } -} - -// fullPipeline parses, checks, and evaluates the CEL expression in source -// and returns the result from the Eval call. -func fullPipeline(t *testing.T, source string) (*confpb.ParseResponse, *confpb.CheckResponse, *confpb.EvalResponse) { - t.Helper() - - // Parse - preq := confpb.ParseRequest{ - CelSource: source, - } - pres, err := globals.client.Parse(context.Background(), &preq) - if err != nil { - t.Fatal(err) - } - if pres == nil { - t.Fatal("Empty parse result") - } - parsedExpr := pres.ParsedExpr - if parsedExpr == nil { - t.Fatal("Empty parsed expression") - } - if parsedExpr.Expr == nil { - t.Fatal("Empty root expression") - } - - // Check - creq := confpb.CheckRequest{ - ParsedExpr: parsedExpr, - } - cres, err := globals.client.Check(context.Background(), &creq) - if err != nil { - t.Fatal(err) - } - if cres == nil { - t.Fatal("Empty check result") - } - checkedExpr := cres.CheckedExpr - if checkedExpr == nil { - t.Fatal("No checked expression") - } - - // Eval - ereq := confpb.EvalRequest{ - ExprKind: &confpb.EvalRequest_CheckedExpr{CheckedExpr: checkedExpr}, - } - eres, err := globals.client.Eval(context.Background(), &ereq) - if err != nil { - t.Fatal(err) - } - if eres == nil || eres.Result == nil { - t.Fatal("Nil result") - } - return pres, cres, eres -} - -// expectEvalTrue parses, checks, and evaluates the CEL expression in source -// and checks that the result is the boolean value 'true'. -func expectEvalTrue(t *testing.T, source string) { - t.Helper() - pres, cres, eres := fullPipeline(t, source) - - rootID := pres.ParsedExpr.Expr.Id - topType, present := cres.CheckedExpr.TypeMap[rootID] - if !present { - t.Fatal("No type for top level expression", cres) - } - switch topType.TypeKind.(type) { - case *exprpb.Type_Primitive: - if topType.GetPrimitive() != exprpb.Type_BOOL { - t.Error("Bad top-level type", topType) - } - default: - t.Error("Bad top-level type", topType) - } - - switch eres.Result.Kind.(type) { - case *exprpb.ExprValue_Value: - v := eres.Result.GetValue() - switch v.Kind.(type) { - case *exprpb.Value_BoolValue: - if !v.GetBoolValue() { - t.Error("Wrong result", v) - } - default: - t.Error("Wrong result value type", v) - } - default: - t.Fatal("Result not a value", eres.Result) - } -} - -// TestCondTrue tests true conditional behavior. -func TestCondTrue(t *testing.T) { - expectEvalTrue(t, "(true ? 'a' : 'b') == 'a'") -} - -// TestCondFalse tests false conditional behavior. -func TestCondFalse(t *testing.T) { - expectEvalTrue(t, "(false ? 'a' : 'b') == 'b'") -} - -// TestMapOrderInsignificant tests that maps with different order are equal. -func TestMapOrderInsignificant(t *testing.T) { - expectEvalTrue(t, "{1: 'a', 2: 'b'} == {2: 'b', 1: 'a'}") -} - -// FailsTestOneMetaType tests that types of different types are equal. -func FailsTestOneMetaType(t *testing.T) { - expectEvalTrue(t, "type(type(1)) == type(type('foo'))") -} - -// FailsTestTypeType tests that the meta-type is its own type. -func FailsTestTypeType(t *testing.T) { - expectEvalTrue(t, "type(type) == type") -} - -// FailsTestNullTypeName checks that the type of null is "null_type". -func FailsTestNullTypeName(t *testing.T) { - expectEvalTrue(t, "type(null) == null_type") -} - -// TestError ensures that errors are properly transmitted. -func TestError(t *testing.T) { - _, _, eres := fullPipeline(t, "1 / 0") - switch eres.Result.Kind.(type) { - case *exprpb.ExprValue_Error: - return - } - t.Fatalf("got %v, want division by zero error", eres.Result) -}