cedar_policy_core/
evaluator.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! This module contains the Cedar evaluator.
18
19use crate::ast::*;
20use crate::entities::{Dereference, Entities};
21use crate::extensions::Extensions;
22use crate::parser::Loc;
23#[cfg(feature = "partial-eval")]
24use std::collections::BTreeMap;
25use std::sync::Arc;
26
27mod err;
28#[cfg(feature = "tolerant-ast")]
29use crate::evaluator::EvaluationError::ASTErrorExpr;
30pub use err::evaluation_errors;
31pub use err::EvaluationError;
32pub(crate) use err::*;
33use evaluation_errors::*;
34use itertools::{Either, Itertools};
35use nonempty::nonempty;
36use smol_str::SmolStr;
37
38const REQUIRED_STACK_SPACE: usize = 1024 * 100;
39
40#[cfg(feature = "partial-eval")]
41type UnknownsMapper<'e> = Box<dyn Fn(&str) -> Option<Value> + 'e>;
42
43// PANIC SAFETY `Name`s in here are valid `Name`s
44#[allow(clippy::expect_used)]
45mod names {
46    use super::Name;
47    use std::sync::LazyLock;
48
49    pub static ANY_ENTITY_TYPE: LazyLock<Name> = LazyLock::new(|| {
50        Name::parse_unqualified_name("any_entity_type").expect("valid identifier")
51    });
52}
53
54/// Apply a `UnaryOp` to `arg` of type `Value`
55pub fn unary_app(op: UnaryOp, arg: Value, loc: Option<&Loc>) -> Result<Value> {
56    match op {
57        UnaryOp::Not => match arg.get_as_bool()? {
58            true => Ok(false.into()),
59            false => Ok(true.into()),
60        },
61        UnaryOp::Neg => {
62            let i = arg.get_as_long()?;
63            match i.checked_neg() {
64                Some(v) => Ok(v.into()),
65                None => Err(IntegerOverflowError::UnaryOp(UnaryOpOverflowError {
66                    op,
67                    arg,
68                    source_loc: loc.cloned(),
69                })
70                .into()),
71            }
72        }
73        UnaryOp::IsEmpty => {
74            let s = arg.get_as_set()?;
75            Ok(s.is_empty().into())
76        }
77    }
78}
79
80/// Evaluate binary relations (i.e., `BinaryOp::Eq`, `BinaryOp::Less`, and `BinaryOp::LessEq`)
81pub fn binary_relation(
82    op: BinaryOp,
83    arg1: &Value,
84    arg2: &Value,
85    extensions: &Extensions<'_>,
86) -> Result<Value> {
87    match op {
88        BinaryOp::Eq => Ok((arg1 == arg2).into()),
89        // comparison and arithmetic operators, which only work on Longs
90        BinaryOp::Less | BinaryOp::LessEq => {
91            let long_op = if matches!(op, BinaryOp::Less) {
92                |x, y| x < y
93            } else {
94                |x, y| x <= y
95            };
96            let ext_op = if matches!(op, BinaryOp::Less) {
97                |x, y| x < y
98            } else {
99                |x, y| x <= y
100            };
101            match (arg1.value_kind(), arg2.value_kind()) {
102                (ValueKind::Lit(Literal::Long(x)), ValueKind::Lit(Literal::Long(y))) => {
103                    Ok(long_op(x, y).into())
104                }
105                (ValueKind::ExtensionValue(x), ValueKind::ExtensionValue(y))
106                    if x.supports_operator_overloading()
107                        && y.supports_operator_overloading()
108                        && x.typename() == y.typename() =>
109                {
110                    Ok(ext_op(x, y).into())
111                }
112                // throw type errors
113                (ValueKind::Lit(Literal::Long(_)), _) => {
114                    Err(EvaluationError::type_error_single(Type::Long, arg2))
115                }
116                (_, ValueKind::Lit(Literal::Long(_))) => {
117                    Err(EvaluationError::type_error_single(Type::Long, arg1))
118                }
119                (ValueKind::ExtensionValue(x), _) if x.supports_operator_overloading() => {
120                    Err(EvaluationError::type_error_single(
121                        Type::Extension { name: x.typename() },
122                        arg2,
123                    ))
124                }
125                (_, ValueKind::ExtensionValue(y)) if y.supports_operator_overloading() => {
126                    Err(EvaluationError::type_error_single(
127                        Type::Extension { name: y.typename() },
128                        arg1,
129                    ))
130                }
131                _ => {
132                    let expected_types = valid_comparison_op_types(extensions);
133                    Err(EvaluationError::type_error_with_advice(
134                        expected_types.clone(),
135                        arg1,
136                        format!(
137                            "Only types {} support comparison",
138                            expected_types.into_iter().sorted().join(", ")
139                        ),
140                    ))
141                }
142            }
143        }
144        // PANIC SAFETY `op` is checked by the caller
145        #[allow(clippy::unreachable)]
146        _ => {
147            unreachable!("Should have already checked that op was one of these")
148        }
149    }
150}
151
152/// Evaluate binary arithmetic operations (i.e., `BinaryOp::Add`, `BinaryOp::Sub`, and `BinaryOp::Mul`)
153pub fn binary_arith(op: BinaryOp, arg1: Value, arg2: Value, loc: Option<&Loc>) -> Result<Value> {
154    let i1 = arg1.get_as_long()?;
155    let i2 = arg2.get_as_long()?;
156    match op {
157        BinaryOp::Add => match i1.checked_add(i2) {
158            Some(sum) => Ok(sum.into()),
159            None => Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
160                op,
161                arg1,
162                arg2,
163                source_loc: loc.cloned(),
164            })
165            .into()),
166        },
167        BinaryOp::Sub => match i1.checked_sub(i2) {
168            Some(diff) => Ok(diff.into()),
169            None => Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
170                op,
171                arg1,
172                arg2,
173                source_loc: loc.cloned(),
174            })
175            .into()),
176        },
177        BinaryOp::Mul => match i1.checked_mul(i2) {
178            Some(prod) => Ok(prod.into()),
179            None => Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
180                op,
181                arg1,
182                arg2,
183                source_loc: loc.cloned(),
184            })
185            .into()),
186        },
187        // PANIC SAFETY `op` is checked by the caller
188        #[allow(clippy::unreachable)]
189        _ => {
190            unreachable!("Should have already checked that op was one of these")
191        }
192    }
193}
194
195/// Evaluator object.
196///
197/// Conceptually keeps the evaluation environment as part of its internal state,
198/// because we will be repeatedly invoking the evaluator on every policy in a
199/// Slice.
200pub struct Evaluator<'e> {
201    /// `Principal` for the current request
202    principal: EntityUIDEntry,
203    /// `Action` for the current request
204    action: EntityUIDEntry,
205    /// `Resource` for the current request
206    resource: EntityUIDEntry,
207    /// `Context` for the current request; this will be a Record type
208    context: PartialValue,
209    /// Entities which we use to resolve entity references.
210    ///
211    /// This is a reference, because the `Evaluator` doesn't need ownership of
212    /// (or need to modify) the `Entities`. One advantage of this is that you
213    /// could create multiple `Evaluator`s without copying the `Entities`.
214    entities: &'e Entities,
215    /// Extensions which are active for this evaluation
216    extensions: &'e Extensions<'e>,
217    /// Mapper of unknown values into concrete ones, if recognized
218    #[cfg(feature = "partial-eval")]
219    unknowns_mapper: UnknownsMapper<'e>,
220}
221
222/// Evaluator for "restricted" expressions. See notes on `RestrictedExpr`.
223#[derive(Debug)]
224pub struct RestrictedEvaluator<'e> {
225    /// Extensions which are active for this evaluation
226    extensions: &'e Extensions<'e>,
227}
228
229impl<'e> RestrictedEvaluator<'e> {
230    /// Create a fresh evaluator for evaluating "restricted" expressions
231    pub fn new(extensions: &'e Extensions<'e>) -> Self {
232        Self { extensions }
233    }
234
235    /// Interpret a `RestrictedExpr` into a `Value` in this evaluation environment.
236    ///
237    /// May return an error, for instance if an extension function returns an error
238    pub fn interpret(&self, e: BorrowedRestrictedExpr<'_>) -> Result<Value> {
239        match self.partial_interpret(e)? {
240            PartialValue::Value(v) => Ok(v),
241            PartialValue::Residual(r) => Err(EvaluationError::non_value(r)),
242        }
243    }
244
245    /// Interpret a `RestrictedExpr` into a `Value` in this evaluation environment.
246    ///
247    /// May return an error, for instance if an extension function returns an error
248    ///
249    /// INVARIANT: If this returns a residual, the residual expression must be a valid restricted expression.
250    pub fn partial_interpret(&self, expr: BorrowedRestrictedExpr<'_>) -> Result<PartialValue> {
251        stack_size_check()?;
252
253        let res = self.partial_interpret_internal(expr);
254
255        // set the returned value's source location to the same source location
256        // as the input expression had.
257        // we do this here so that we don't have to set/propagate the source
258        // location in every arm of the big `match` in `partial_interpret_internal()`.
259        // also, if there is an error, set its source location to the source
260        // location of the input expression as well, unless it already had a
261        // more specific location
262        res.map(|pval| pval.with_maybe_source_loc(expr.source_loc().cloned()))
263            .map_err(|err| {
264                if err.source_loc().is_none() {
265                    err.with_maybe_source_loc(expr.source_loc().cloned())
266                } else {
267                    err
268                }
269            })
270    }
271
272    /// Internal function to interpret a `RestrictedExpr`. (External callers,
273    /// use `interpret()` or `partial_interpret()`.)
274    ///
275    /// Part of the reason this exists, instead of inlining this into
276    /// `partial_interpret()`, is so that we can use `?` inside this function
277    /// without immediately shortcircuiting into a return from
278    /// `partial_interpret()` -- ie, so we can make sure the source locations of
279    /// all errors are set properly before returning them from
280    /// `partial_interpret()`.
281    ///
282    /// INVARIANT: If this returns a residual, the residual expression must be a valid restricted expression.
283    fn partial_interpret_internal(&self, expr: BorrowedRestrictedExpr<'_>) -> Result<PartialValue> {
284        match expr.as_ref().expr_kind() {
285            ExprKind::Lit(lit) => Ok(lit.clone().into()),
286            ExprKind::Set(items) => {
287                let vals = items
288                    .iter()
289                    .map(|item| self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(item))) // assuming the invariant holds for `e`, it will hold here
290                    .collect::<Result<Vec<_>>>()?;
291                match split(vals) {
292                    Either::Left(values) => Ok(Value::set(values, expr.source_loc().cloned()).into()),
293                    Either::Right(residuals) => Ok(Expr::set(residuals).into()),
294                }
295            }
296            ExprKind::Unknown(u) => Ok(PartialValue::unknown(u.clone())),
297            ExprKind::Record(map) => {
298                let map = map
299                    .iter()
300                    .map(|(k, v)| Ok((k.clone(), self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(v))?))) // assuming the invariant holds for `e`, it will hold here
301                    .collect::<Result<Vec<_>>>()?;
302                let (names, attrs) : (Vec<_>, Vec<_>) = map.into_iter().unzip();
303                match split(attrs) {
304                    Either::Left(values) => Ok(Value::record(names.into_iter().zip(values), expr.source_loc().cloned()).into()),
305                    Either::Right(residuals) => {
306                        // PANIC SAFETY: can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`
307                        #[allow(clippy::expect_used)]
308                        Ok(
309                            Expr::record(names.into_iter().zip(residuals))
310                                .expect("can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`")
311                                .into()
312                        )
313                    }
314                }
315            }
316            ExprKind::ExtensionFunctionApp { fn_name, args } => {
317                let args = args
318                    .iter()
319                    .map(|arg| self.partial_interpret(BorrowedRestrictedExpr::new_unchecked(arg))) // assuming the invariant holds for `e`, it will hold here
320                    .collect::<Result<Vec<_>>>()?;
321                match split(args) {
322                    Either::Left(values) => {
323                        let values : Vec<_> = values.collect();
324                        let efunc = self.extensions.func(fn_name)?;
325                        efunc.call(&values)
326                    },
327                    Either::Right(residuals) => Ok(Expr::call_extension_fn(fn_name.clone(), residuals.collect()).into()),
328                }
329            },
330            // PANIC SAFETY Unreachable via invariant on restricted expressions
331            #[allow(clippy::unreachable)]
332            expr => unreachable!("internal invariant violation: BorrowedRestrictedExpr somehow contained this expr case: {expr:?}"),
333        }
334    }
335}
336
337pub(crate) fn valid_comparison_op_types(extensions: &Extensions<'_>) -> nonempty::NonEmpty<Type> {
338    let mut expected_types = nonempty::NonEmpty::singleton(Type::Long);
339    expected_types.extend(
340        extensions
341            .types_with_operator_overloading()
342            .map(|n| Type::Extension { name: n.clone() }),
343    );
344    expected_types
345}
346
347impl<'e> Evaluator<'e> {
348    /// Create a fresh `Evaluator` for the given `request`, which uses the given
349    /// `Entities` to resolve entity references. Use the given `Extension`s when
350    /// evaluating.
351    pub fn new(q: Request, entities: &'e Entities, extensions: &'e Extensions<'e>) -> Self {
352        Self {
353            principal: q.principal,
354            action: q.action,
355            resource: q.resource,
356            context: {
357                match q.context {
358                    None => PartialValue::unknown(Unknown::new_untyped("context")),
359                    Some(ctx) => ctx.into(),
360                }
361            },
362            entities,
363            extensions,
364            #[cfg(feature = "partial-eval")]
365            unknowns_mapper: Box::new(|_: &str| -> Option<Value> { None }),
366        }
367    }
368
369    // Constructs an Evaluator for a given unknowns mapper function.
370    #[cfg(feature = "partial-eval")]
371    pub(crate) fn with_unknowns_mapper(self, unknowns_mapper: UnknownsMapper<'e>) -> Self {
372        Self {
373            principal: self.principal,
374            action: self.action,
375            resource: self.resource,
376            context: self.context,
377            entities: self.entities,
378            extensions: self.extensions,
379            unknowns_mapper,
380        }
381    }
382
383    /// Evaluate the given `Policy`, returning either a bool or an error.
384    /// The bool indicates whether the policy applies, ie, "is satisfied" for the
385    /// current `request`.
386    /// This is _different than_ "if the current `request` should be allowed" --
387    /// it doesn't consider whether we're processing a `Permit` policy or a
388    /// `Forbid` policy.
389    pub fn evaluate(&self, p: &Policy) -> Result<bool> {
390        self.interpret(&p.condition(), p.env())?.get_as_bool()
391    }
392
393    /// Partially evaluate the given `Policy`, returning one of:
394    /// 1) A boolean, if complete evaluation was possible
395    /// 2) An error, if the policy is guaranteed to error
396    /// 3) A residual, if complete evaluation was impossible
397    ///    The bool indicates whether the policy applies, ie, "is satisfied" for the
398    ///    current `request`.
399    ///    This is _different than_ "if the current `request` should be allowed" --
400    ///    it doesn't consider whether we're processing a `Permit` policy or a
401    ///    `Forbid` policy.
402    pub fn partial_evaluate(&self, p: &Policy) -> Result<Either<bool, Expr>> {
403        match self.partial_interpret(&p.condition(), p.env())? {
404            PartialValue::Value(v) => v.get_as_bool().map(Either::Left),
405            PartialValue::Residual(e) => Ok(Either::Right(e)),
406        }
407    }
408
409    /// Interpret an `Expr` into a `Value` in this evaluation environment.
410    ///
411    /// Ensures the result is not a residual.
412    /// May return an error, for instance if the `Expr` tries to access an
413    /// attribute that doesn't exist.
414    pub fn interpret(&self, e: &Expr, slots: &SlotEnv) -> Result<Value> {
415        match self.partial_interpret(e, slots)? {
416            PartialValue::Value(v) => Ok(v),
417            PartialValue::Residual(r) => Err(EvaluationError::non_value(r)),
418        }
419    }
420
421    /// Interpret an `Expr` into a `Value` in this evaluation environment.
422    ///
423    /// May return a residual expression, if the input expression is symbolic.
424    /// May return an error, for instance if the `Expr` tries to access an
425    /// attribute that doesn't exist.
426    pub fn partial_interpret(&self, expr: &Expr, slots: &SlotEnv) -> Result<PartialValue> {
427        stack_size_check()?;
428
429        let res = self.partial_interpret_internal(expr, slots);
430
431        // set the returned value's source location to the same source location
432        // as the input expression had.
433        // we do this here so that we don't have to set/propagate the source
434        // location in every arm of the big `match` in `partial_interpret_internal()`.
435        // also, if there is an error, set its source location to the source
436        // location of the input expression as well, unless it already had a
437        // more specific location
438        res.map(|pval| pval.with_maybe_source_loc(expr.source_loc().cloned()))
439            .map_err(|err| {
440                if err.source_loc().is_none() {
441                    err.with_maybe_source_loc(expr.source_loc().cloned())
442                } else {
443                    err
444                }
445            })
446    }
447
448    /// Internal function to interpret an `Expr`. (External callers, use
449    /// `interpret()` or `partial_interpret()`.)
450    ///
451    /// Part of the reason this exists, instead of inlining this into
452    /// `partial_interpret()`, is so that we can use `?` inside this function
453    /// without immediately shortcircuiting into a return from
454    /// `partial_interpret()` -- ie, so we can make sure the source locations of
455    /// all errors are set properly before returning them from
456    /// `partial_interpret()`.
457    #[allow(clippy::cognitive_complexity)]
458    fn partial_interpret_internal(&self, expr: &Expr, slots: &SlotEnv) -> Result<PartialValue> {
459        let loc = expr.source_loc(); // the `loc` describing the location of the entire expression
460        match expr.expr_kind() {
461            ExprKind::Lit(lit) => Ok(lit.clone().into()),
462            ExprKind::Slot(id) => slots
463                .get(id)
464                .ok_or_else(|| err::EvaluationError::unlinked_slot(*id, loc.cloned()))
465                .map(|euid| PartialValue::from(euid.clone())),
466            ExprKind::Var(v) => match v {
467                Var::Principal => Ok(self.principal.evaluate(*v)),
468                Var::Action => Ok(self.action.evaluate(*v)),
469                Var::Resource => Ok(self.resource.evaluate(*v)),
470                Var::Context => Ok(self.context.clone()),
471            },
472            ExprKind::Unknown(u) => self.unknown_to_partialvalue(u),
473            ExprKind::If {
474                test_expr,
475                then_expr,
476                else_expr,
477            } => self.eval_if(test_expr, then_expr, else_expr, slots),
478            ExprKind::And { left, right } => {
479                match self.partial_interpret(left, slots)? {
480                    // PE Case
481                    PartialValue::Residual(e) => {
482                        Ok(PartialValue::Residual(Expr::and(e, right.as_ref().clone())))
483                    }
484                    // Full eval case
485                    PartialValue::Value(v) => {
486                        if v.get_as_bool()? {
487                            match self.partial_interpret(right, slots)? {
488                                // you might think that `true && <residual>` can be optimized to `<residual>`, but this isn't true because
489                                // <residual> must be boolean, or else it needs to type error. So return `true && <residual>` to ensure
490                                // type check happens
491                                PartialValue::Residual(right) => {
492                                    Ok(PartialValue::Residual(Expr::and(Expr::val(true), right)))
493                                }
494                                // If it's an actual value, compute and
495                                PartialValue::Value(v) => Ok(v.get_as_bool()?.into()),
496                            }
497                        } else {
498                            // We can short circuit here
499                            Ok(false.into())
500                        }
501                    }
502                }
503            }
504            ExprKind::Or { left, right } => {
505                match self.partial_interpret(left, slots)? {
506                    // PE cases
507                    PartialValue::Residual(r) => {
508                        Ok(PartialValue::Residual(Expr::or(r, right.as_ref().clone())))
509                    }
510                    // Full eval case
511                    PartialValue::Value(lhs) => {
512                        if lhs.get_as_bool()? {
513                            // We can short circuit here
514                            Ok(true.into())
515                        } else {
516                            match self.partial_interpret(right, slots)? {
517                                PartialValue::Residual(rhs) =>
518                                // you might think that `false || <residual>` can be optimized to `<residual>`, but this isn't true because
519                                // <residual> must be boolean, or else it needs to type error. So return `false || <residual>` to ensure
520                                // type check happens
521                                {
522                                    Ok(PartialValue::Residual(Expr::or(Expr::val(false), rhs)))
523                                }
524                                PartialValue::Value(v) => Ok(v.get_as_bool()?.into()),
525                            }
526                        }
527                    }
528                }
529            }
530            ExprKind::UnaryApp { op, arg } => match self.partial_interpret(arg, slots)? {
531                PartialValue::Value(arg) => unary_app(*op, arg, loc).map(Into::into),
532                // NOTE, there was a bug here found during manual review. (I forgot to wrap in unary_app call)
533                // Could be a nice target for fault injection
534                PartialValue::Residual(r) => Ok(PartialValue::Residual(Expr::unary_app(*op, r))),
535            },
536            ExprKind::BinaryApp { op, arg1, arg2 } => {
537                // NOTE: There are more precise partial eval opportunities here, esp w/ typed unknowns
538                // Current limitations:
539                //   Operators are not partially evaluated, except in a few 'simple' cases when comparing a concrete value with an unknown of known type
540                //   implemented in short_circuit_*
541                let (arg1, arg2) = match (
542                    self.partial_interpret(arg1, slots)?,
543                    self.partial_interpret(arg2, slots)?,
544                ) {
545                    (PartialValue::Value(v1), PartialValue::Value(v2)) => (v1, v2),
546                    (PartialValue::Value(v1), PartialValue::Residual(e2)) => {
547                        if let Some(val) = self.short_circuit_value_and_residual(&v1, &e2, *op) {
548                            return Ok(val);
549                        }
550                        return Ok(PartialValue::Residual(Expr::binary_app(*op, v1.into(), e2)));
551                    }
552                    (PartialValue::Residual(e1), PartialValue::Value(v2)) => {
553                        if let Some(val) = self.short_circuit_residual_and_value(&e1, &v2, *op) {
554                            return Ok(val);
555                        }
556                        return Ok(PartialValue::Residual(Expr::binary_app(*op, e1, v2.into())));
557                    }
558                    (PartialValue::Residual(e1), PartialValue::Residual(e2)) => {
559                        if let Some(val) = self.short_circuit_two_typed_residuals(&e1, &e2, *op) {
560                            return Ok(val);
561                        }
562                        return Ok(PartialValue::Residual(Expr::binary_app(*op, e1, e2)));
563                    }
564                };
565                match op {
566                    BinaryOp::Eq | BinaryOp::Less | BinaryOp::LessEq => {
567                        binary_relation(*op, &arg1, &arg2, self.extensions).map(Into::into)
568                    }
569                    BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul => {
570                        binary_arith(*op, arg1, arg2, loc).map(Into::into)
571                    }
572                    // hierarchy membership operator; see note on `BinaryOp::In`
573                    BinaryOp::In => {
574                        let uid1 = arg1.get_as_entity().map_err(|mut e|
575                            {
576                                // If arg1 is not an entity and arg2 is a set, then possibly
577                                // the user intended `arg2.contains(arg1)` rather than `arg1 in arg2`.
578                                // If arg2 is a record, then possibly they intended `arg2 has arg1`.
579                                if let EvaluationError::TypeError(TypeError { advice, .. }) = &mut e {
580                                    match arg2.type_of() {
581                                        Type::Set => *advice = Some("`in` is for checking the entity hierarchy; use `.contains()` to test set membership".into()),
582                                        Type::Record => *advice = Some("`in` is for checking the entity hierarchy; use `has` to test if a record has a key".into()),
583                                        _ => {}
584                                    }
585                                };
586                                e
587                            })?;
588                        match self.entities.entity(uid1) {
589                            Dereference::Residual(r) => Ok(PartialValue::Residual(
590                                Expr::binary_app(BinaryOp::In, r, arg2.into()),
591                            )),
592                            Dereference::NoSuchEntity => self.eval_in(uid1, None, arg2),
593                            Dereference::Data(entity1) => self.eval_in(uid1, Some(entity1), arg2),
594                        }
595                    }
596                    // contains, which works on Sets
597                    BinaryOp::Contains => {
598                        if let Ok(s) = arg1.get_as_set() {
599                            Ok(s.contains(&arg2).into())
600                        } else {
601                            Err(EvaluationError::type_error_single(Type::Set, &arg1))
602                        }
603                    }
604                    // ContainsAll, which works on Sets
605                    BinaryOp::ContainsAll => {
606                        let arg1_set = arg1.get_as_set()?;
607                        let arg2_set = arg2.get_as_set()?;
608
609                        Ok((arg2_set.is_subset(arg1_set)).into())
610                    }
611                    // ContainsAny, which works on Sets
612                    BinaryOp::ContainsAny => {
613                        let arg1_set = arg1.get_as_set()?;
614                        let arg2_set = arg2.get_as_set()?;
615                        Ok((!arg1_set.is_disjoint(arg2_set)).into())
616                    }
617                    // GetTag and HasTag, which require an Entity on the left and a String on the right
618                    BinaryOp::GetTag | BinaryOp::HasTag => {
619                        let uid = arg1.get_as_entity()?;
620                        let tag = arg2.get_as_string()?;
621                        match op {
622                            BinaryOp::GetTag => {
623                                match self.entities.entity(uid) {
624                                    Dereference::NoSuchEntity => {
625                                        // intentionally using the location of the euid (the LHS) and not the entire GetTag expression
626                                        Err(EvaluationError::entity_does_not_exist(
627                                            Arc::new(uid.clone()),
628                                            arg1.source_loc().cloned(),
629                                        ))
630                                    }
631                                    Dereference::Residual(r) => Ok(PartialValue::Residual(
632                                        Expr::get_tag(r, Expr::val(tag.clone())),
633                                    )),
634                                    Dereference::Data(entity) => entity
635                                        .get_tag(tag)
636                                        .ok_or_else(|| {
637                                            EvaluationError::entity_tag_does_not_exist(
638                                                Arc::new(uid.clone()),
639                                                tag.clone(),
640                                                entity.tag_keys(),
641                                                entity.get(tag).is_some(),
642                                                entity.tags_len(),
643                                                loc.cloned(), // intentionally using the location of the entire `GetTag` expression
644                                            )
645                                        })
646                                        .cloned(),
647                                }
648                            }
649                            BinaryOp::HasTag => match self.entities.entity(uid) {
650                                Dereference::NoSuchEntity => Ok(false.into()),
651                                Dereference::Residual(r) => Ok(PartialValue::Residual(
652                                    Expr::has_tag(r, Expr::val(tag.clone())),
653                                )),
654                                Dereference::Data(entity) => {
655                                    Ok(entity.get_tag(tag).is_some().into())
656                                }
657                            },
658                            // PANIC SAFETY `op` is checked to be one of these two above
659                            #[allow(clippy::unreachable)]
660                            _ => {
661                                unreachable!("Should have already checked that op was one of these")
662                            }
663                        }
664                    }
665                }
666            }
667            ExprKind::ExtensionFunctionApp { fn_name, args } => {
668                let args = args
669                    .iter()
670                    .map(|arg| self.partial_interpret(arg, slots))
671                    .collect::<Result<Vec<_>>>()?;
672                match split(args) {
673                    Either::Left(vals) => {
674                        let vals: Vec<_> = vals.collect();
675                        let efunc = self.extensions.func(fn_name)?;
676                        efunc.call(&vals)
677                    }
678                    Either::Right(residuals) => Ok(PartialValue::Residual(
679                        Expr::call_extension_fn(fn_name.clone(), residuals.collect()),
680                    )),
681                }
682            }
683            ExprKind::GetAttr { expr, attr } => self.get_attr(expr.as_ref(), attr, slots, loc),
684            ExprKind::HasAttr { expr, attr } => match self.partial_interpret(expr, slots)? {
685                PartialValue::Value(Value {
686                    value: ValueKind::Record(record),
687                    ..
688                }) => Ok(record.get(attr).is_some().into()),
689                PartialValue::Value(Value {
690                    value: ValueKind::Lit(Literal::EntityUID(uid)),
691                    ..
692                }) => match self.entities.entity(&uid) {
693                    Dereference::NoSuchEntity => Ok(false.into()),
694                    Dereference::Residual(r) => {
695                        Ok(PartialValue::Residual(Expr::has_attr(r, attr.clone())))
696                    }
697                    Dereference::Data(e) => Ok(e.get(attr).is_some().into()),
698                },
699                PartialValue::Value(val) => Err(err::EvaluationError::type_error(
700                    nonempty![
701                        Type::Record,
702                        Type::entity_type(names::ANY_ENTITY_TYPE.clone())
703                    ],
704                    &val,
705                )),
706                PartialValue::Residual(r) => Ok(Expr::has_attr(r, attr.clone()).into()),
707            },
708            ExprKind::Like { expr, pattern } => {
709                let v = self.partial_interpret(expr, slots)?;
710                match v {
711                    PartialValue::Value(v) => {
712                        Ok((pattern.wildcard_match(v.get_as_string()?)).into())
713                    }
714                    PartialValue::Residual(r) => Ok(Expr::like(r, pattern.clone()).into()),
715                }
716            }
717            ExprKind::Is { expr, entity_type } => {
718                let v = self.partial_interpret(expr, slots)?;
719                match v {
720                    PartialValue::Value(v) => {
721                        Ok((v.get_as_entity()?.entity_type() == entity_type).into())
722                    }
723                    PartialValue::Residual(r) => {
724                        if let ExprKind::Unknown(Unknown {
725                            type_annotation:
726                                Some(Type::Entity {
727                                    ty: type_of_unknown,
728                                }),
729                            ..
730                        }) = r.expr_kind()
731                        {
732                            return Ok((type_of_unknown == entity_type).into());
733                        }
734                        Ok(Expr::is_entity_type(r, entity_type.clone()).into())
735                    }
736                }
737            }
738            ExprKind::Set(items) => {
739                let vals = items
740                    .iter()
741                    .map(|item| self.partial_interpret(item, slots))
742                    .collect::<Result<Vec<_>>>()?;
743                match split(vals) {
744                    Either::Left(vals) => Ok(Value::set(vals, loc.cloned()).into()),
745                    Either::Right(r) => Ok(Expr::set(r).into()),
746                }
747            }
748            ExprKind::Record(map) => {
749                let map = map
750                    .iter()
751                    .map(|(k, v)| Ok((k.clone(), self.partial_interpret(v, slots)?)))
752                    .collect::<Result<Vec<_>>>()?;
753                let (names, evalled): (Vec<SmolStr>, Vec<PartialValue>) = map.into_iter().unzip();
754                match split(evalled) {
755                    Either::Left(vals) => {
756                        Ok(Value::record(names.into_iter().zip(vals), loc.cloned()).into())
757                    }
758                    Either::Right(rs) => {
759                        // PANIC SAFETY: can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`
760                        #[allow(clippy::expect_used)]
761                        Ok(
762                            Expr::record(names.into_iter().zip(rs))
763                                .expect("can't have a duplicate key here because `names` is the set of keys of the input `BTreeMap`")
764                                .into()
765                        )
766                    }
767                }
768            }
769            #[cfg(feature = "tolerant-ast")]
770            ExprKind::Error { .. } => Err(ASTErrorExpr(ASTErrorExprError {
771                source_loc: loc.cloned(),
772            })),
773        }
774    }
775
776    // Never map unknowns when feature flag is not set
777    #[cfg(not(feature = "partial-eval"))]
778    #[inline(always)]
779    fn unknown_to_partialvalue(&self, u: &Unknown) -> Result<PartialValue> {
780        Ok(PartialValue::Residual(Expr::unknown(u.clone())))
781    }
782
783    // Try resolving a named Unknown into a Value
784    #[cfg(feature = "partial-eval")]
785    fn unknown_to_partialvalue(&self, u: &Unknown) -> Result<PartialValue> {
786        match (self.unknowns_mapper.as_ref()(&u.name), &u.type_annotation) {
787            // The mapper might not recognize the unknown
788            (None, _) => Ok(PartialValue::Residual(Expr::unknown(u.clone()))),
789            // Replace the unknown value with the concrete one found
790            (Some(v), None) => Ok(PartialValue::Value(v)),
791            (Some(v), Some(t)) => {
792                if v.type_of() == *t {
793                    Ok(PartialValue::Value(v))
794                } else {
795                    Err(EvaluationError::type_error_single(t.clone(), &v))
796                }
797            }
798        }
799    }
800
801    fn eval_in(
802        &self,
803        uid1: &EntityUID,
804        entity1: Option<&Entity>,
805        arg2: Value,
806    ) -> Result<PartialValue> {
807        // `rhs` is a list of all the UIDs for which we need to
808        // check if `uid1` is a descendant of
809        let rhs = match arg2.value {
810            ValueKind::Lit(Literal::EntityUID(uid)) => vec![Arc::unwrap_or_clone(uid)],
811            // we assume that iterating the `authoritative` BTreeSet is
812            // approximately the same cost as iterating the `fast` HashSet
813            ValueKind::Set(Set { authoritative, .. }) => authoritative
814                .iter()
815                .map(|val| Ok(val.get_as_entity()?.clone()))
816                .collect::<Result<Vec<EntityUID>>>()?,
817            _ => {
818                return Err(EvaluationError::type_error(
819                    nonempty![Type::Set, Type::entity_type(names::ANY_ENTITY_TYPE.clone())],
820                    &arg2,
821                ))
822            }
823        };
824        for uid2 in rhs {
825            if uid1 == &uid2
826                || entity1
827                    .map(|e1| e1.is_descendant_of(&uid2))
828                    .unwrap_or(false)
829            {
830                return Ok(true.into());
831            }
832        }
833        // if we get here, `uid1` is not a descendant of (or equal to)
834        // any UID in `rhs`
835        Ok(false.into())
836    }
837
838    /// Evaluation of conditionals
839    /// Must be sure to respect short-circuiting semantics
840    fn eval_if(
841        &self,
842        guard: &Expr,
843        consequent: &Arc<Expr>,
844        alternative: &Arc<Expr>,
845        slots: &SlotEnv,
846    ) -> Result<PartialValue> {
847        match self.partial_interpret(guard, slots)? {
848            PartialValue::Value(v) => {
849                if v.get_as_bool()? {
850                    self.partial_interpret(consequent, slots)
851                } else {
852                    self.partial_interpret(alternative, slots)
853                }
854            }
855            PartialValue::Residual(guard) => {
856                Ok(Expr::ite_arc(Arc::new(guard), consequent.clone(), alternative.clone()).into())
857            }
858        }
859    }
860
861    /// We don't use the `source_loc()` on `expr` because that's only the loc
862    /// for the LHS of the GetAttr. `source_loc` argument should be the loc for
863    /// the entire GetAttr expression
864    fn get_attr(
865        &self,
866        expr: &Expr,
867        attr: &SmolStr,
868        slots: &SlotEnv,
869        source_loc: Option<&Loc>,
870    ) -> Result<PartialValue> {
871        match self.partial_interpret(expr, slots)? {
872            // PE Cases
873            PartialValue::Residual(res) => {
874                match res.expr_kind() {
875                    ExprKind::Record(map) => {
876                        // If we have a residual record, we evaluate as follows:
877                        // 1) If it's safe to project, we can project. We can evaluate to see if this attribute can become a value
878                        // 2) If it's not safe to project, we can check to see if the requested key exists in the record
879                        //    if it doesn't, we can fail early
880                        if res.is_projectable() {
881                            map.as_ref()
882                                .iter()
883                                .filter_map(|(k, v)| if k == attr { Some(v) } else { None })
884                                .next()
885                                .ok_or_else(|| {
886                                    EvaluationError::record_attr_does_not_exist(
887                                        attr.clone(),
888                                        map.keys(),
889                                        map.len(),
890                                        source_loc.cloned(),
891                                    )
892                                })
893                                .and_then(|e| self.partial_interpret(e, slots))
894                        } else if map.keys().any(|k| k == attr) {
895                            Ok(PartialValue::Residual(Expr::get_attr(
896                                Expr::record_arc(Arc::clone(map)),
897                                attr.clone(),
898                            )))
899                        } else {
900                            Err(EvaluationError::record_attr_does_not_exist(
901                                attr.clone(),
902                                map.keys(),
903                                map.len(),
904                                source_loc.cloned(),
905                            ))
906                        }
907                    }
908                    // We got a residual, that is not a record at the top level
909                    _ => Ok(PartialValue::Residual(Expr::get_attr(res, attr.clone()))),
910                }
911            }
912            PartialValue::Value(Value {
913                value: ValueKind::Record(record),
914                ..
915            }) => record
916                .as_ref()
917                .get(attr)
918                .ok_or_else(|| {
919                    EvaluationError::record_attr_does_not_exist(
920                        attr.clone(),
921                        record.keys(),
922                        record.len(),
923                        source_loc.cloned(),
924                    )
925                })
926                .map(|v| PartialValue::Value(v.clone())),
927            PartialValue::Value(Value {
928                value: ValueKind::Lit(Literal::EntityUID(uid)),
929                loc,
930            }) => match self.entities.entity(uid.as_ref()) {
931                Dereference::NoSuchEntity => {
932                    // intentionally using the location of the euid (the LHS) and not the entire GetAttr expression
933                    Err(EvaluationError::entity_does_not_exist(uid.clone(), loc))
934                }
935                Dereference::Residual(r) => {
936                    Ok(PartialValue::Residual(Expr::get_attr(r, attr.clone())))
937                }
938                Dereference::Data(entity) => entity
939                    .get(attr)
940                    .map(|pv| match pv {
941                        PartialValue::Value(_) => Ok(pv.clone()),
942                        PartialValue::Residual(e) => match e.expr_kind() {
943                            ExprKind::Unknown(u) => self.unknown_to_partialvalue(u),
944                            _ => Ok(pv.clone()),
945                        },
946                    })
947                    .ok_or_else(|| {
948                        EvaluationError::entity_attr_does_not_exist(
949                            uid,
950                            attr.clone(),
951                            entity.keys(),
952                            entity.get_tag(attr).is_some(),
953                            entity.attrs_len(),
954                            source_loc.cloned(),
955                        )
956                    })?,
957            },
958            PartialValue::Value(v) => {
959                // PANIC SAFETY Entity type name is fully static and a valid unqualified `Name`
960                #[allow(clippy::unwrap_used)]
961                Err(EvaluationError::type_error(
962                    nonempty![
963                        Type::Record,
964                        Type::entity_type(names::ANY_ENTITY_TYPE.clone()),
965                    ],
966                    &v,
967                ))
968            }
969        }
970    }
971
972    /// Evaluate a binary operation between a residual expression (left) and a value (right). If despite the unknown contained in the residual, concrete result
973    /// can be obtained (using the type annotation on the residual), it is returned.
974    fn short_circuit_residual_and_value(
975        &self,
976        e1: &Expr,
977        v2: &Value,
978        op: BinaryOp,
979    ) -> Option<PartialValue> {
980        match op {
981            // Since these operators are commutative, we can use just one order, and have one implementation of the actual logic
982            BinaryOp::Add | BinaryOp::Eq | BinaryOp::Mul | BinaryOp::ContainsAny => {
983                self.short_circuit_value_and_residual(v2, e1, op)
984            }
985            _ => None,
986        }
987    }
988
989    /// Evaluate a binary operation between a value (left) and a residual expression (right). If despite the unknown contained in the residual, concrete result
990    /// can be obtained (using the type annotation on the residual), it is returned.
991    fn short_circuit_value_and_residual(
992        &self,
993        v1: &Value,
994        e2: &Expr,
995        op: BinaryOp,
996    ) -> Option<PartialValue> {
997        match (op, v1.value_kind(), e2.expr_kind()) {
998            // We detect comparing a typed unknown entity id to a literal entity id, and short-circuit to false if the literal is not the same type
999            (
1000                BinaryOp::Eq,
1001                ValueKind::Lit(Literal::EntityUID(uid1)),
1002                ExprKind::Unknown(Unknown {
1003                    type_annotation:
1004                        Some(Type::Entity {
1005                            ty: type_of_unknown,
1006                        }),
1007                    ..
1008                }),
1009            ) => {
1010                if uid1.entity_type() != type_of_unknown {
1011                    Some(false.into())
1012                } else {
1013                    None
1014                }
1015            }
1016            _ => None,
1017        }
1018    }
1019
1020    fn short_circuit_two_typed_residuals(
1021        &self,
1022        e1: &Expr,
1023        e2: &Expr,
1024        op: BinaryOp,
1025    ) -> Option<PartialValue> {
1026        match (op, e1.expr_kind(), e2.expr_kind()) {
1027            // We detect comparing two typed unknown entities, and return false if they don't have the same type.
1028            (
1029                BinaryOp::Eq,
1030                ExprKind::Unknown(Unknown {
1031                    type_annotation: Some(Type::Entity { ty: t1 }),
1032                    ..
1033                }),
1034                ExprKind::Unknown(Unknown {
1035                    type_annotation: Some(Type::Entity { ty: t2 }),
1036                    ..
1037                }),
1038            ) => {
1039                if t1 != t2 {
1040                    Some(false.into())
1041                } else {
1042                    None
1043                }
1044            }
1045            _ => None,
1046        }
1047    }
1048}
1049
1050#[cfg(test)]
1051impl Evaluator<'_> {
1052    /// Interpret an `Expr` in an empty `SlotEnv`. Also checks that the source
1053    /// location is propagated to the result.
1054    pub fn interpret_inline_policy(&self, e: &Expr) -> Result<Value> {
1055        use std::collections::HashMap;
1056        match self.partial_interpret(e, &HashMap::new())? {
1057            PartialValue::Value(v) => {
1058                debug_assert!(e.source_loc().is_some() == v.source_loc().is_some());
1059                Ok(v)
1060            }
1061            PartialValue::Residual(r) => {
1062                debug_assert!(e.source_loc().is_some() == r.source_loc().is_some());
1063                Err(err::EvaluationError::non_value(r))
1064            }
1065        }
1066    }
1067
1068    /// Evaluate an expression, potentially leaving a residual
1069    pub fn partial_eval_expr(&self, p: &Expr) -> Result<Either<Value, Expr>> {
1070        let env = SlotEnv::new();
1071        match self.partial_interpret(p, &env)? {
1072            PartialValue::Value(v) => Ok(Either::Left(v)),
1073            PartialValue::Residual(r) => Ok(Either::Right(r)),
1074        }
1075    }
1076}
1077
1078impl std::fmt::Debug for Evaluator<'_> {
1079    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1080        write!(
1081            f,
1082            "<Evaluator with principal = {:?}, action = {:?}, resource = {:?}",
1083            &self.principal, &self.action, &self.resource
1084        )
1085    }
1086}
1087
1088impl Value {
1089    /// Convert the `Value` to a boolean, or throw a type error if it's not a
1090    /// boolean.
1091    pub(crate) fn get_as_bool(&self) -> Result<bool> {
1092        match &self.value {
1093            ValueKind::Lit(Literal::Bool(b)) => Ok(*b),
1094            _ => Err(EvaluationError::type_error_single(Type::Bool, self)),
1095        }
1096    }
1097
1098    /// Convert the `Value` to a Long, or throw a type error if it's not a
1099    /// Long.
1100    pub(crate) fn get_as_long(&self) -> Result<Integer> {
1101        match &self.value {
1102            ValueKind::Lit(Literal::Long(i)) => Ok(*i),
1103            _ => Err(EvaluationError::type_error_single(Type::Long, self)),
1104        }
1105    }
1106
1107    /// Convert the `Value` to a String, or throw a type error if it's not a
1108    /// String.
1109    pub(crate) fn get_as_string(&self) -> Result<&SmolStr> {
1110        match &self.value {
1111            ValueKind::Lit(Literal::String(s)) => Ok(s),
1112            _ => Err(EvaluationError::type_error_single(Type::String, self)),
1113        }
1114    }
1115
1116    /// Convert the `Value` to a Set, or throw a type error if it's not a Set.
1117    pub(crate) fn get_as_set(&self) -> Result<&Set> {
1118        match &self.value {
1119            ValueKind::Set(set) => Ok(set),
1120            _ => Err(EvaluationError::type_error_single(Type::Set, self)),
1121        }
1122    }
1123
1124    /// Convert the `Value` to a Record, or throw a type error if it's not a Record.
1125    #[cfg(feature = "partial-eval")]
1126    pub(crate) fn get_as_record(&self) -> Result<&Arc<BTreeMap<SmolStr, Value>>> {
1127        match &self.value {
1128            ValueKind::Record(rec) => Ok(rec),
1129            _ => Err(EvaluationError::type_error_single(Type::Record, self)),
1130        }
1131    }
1132
1133    /// Convert the `Value` to an Entity, or throw a type error if it's not a
1134    /// Entity.
1135    pub(crate) fn get_as_entity(&self) -> Result<&EntityUID> {
1136        match &self.value {
1137            ValueKind::Lit(Literal::EntityUID(uid)) => Ok(uid.as_ref()),
1138            _ => Err(EvaluationError::type_error_single(
1139                Type::entity_type(names::ANY_ENTITY_TYPE.clone()),
1140                self,
1141            )),
1142        }
1143    }
1144}
1145
1146#[inline(always)]
1147fn stack_size_check() -> Result<()> {
1148    // We assume there's enough space if we cannot determine it with `remaining_stack`
1149    if stacker::remaining_stack().unwrap_or(REQUIRED_STACK_SPACE) < REQUIRED_STACK_SPACE {
1150        return Err(EvaluationError::recursion_limit(None));
1151    }
1152    Ok(())
1153}
1154
1155// PANIC SAFETY: Unit Test Code
1156#[allow(clippy::panic)]
1157#[allow(clippy::cognitive_complexity)]
1158#[cfg(test)]
1159pub(crate) mod test {
1160    use std::collections::{HashMap, HashSet};
1161    use std::str::FromStr;
1162
1163    use super::*;
1164
1165    use crate::{
1166        entities::{EntityJsonParser, NoEntitiesSchema, TCComputation},
1167        parser::{self, parse_expr, parse_policy_or_template, parse_policyset},
1168        test_utils::{expect_err, ExpectedErrorMessageBuilder},
1169    };
1170
1171    use cool_asserts::assert_matches;
1172
1173    /// Many of these tests use this Request
1174    pub fn basic_request() -> Request {
1175        Request::new(
1176            (EntityUID::with_eid("test_principal"), None),
1177            (EntityUID::with_eid("test_action"), None),
1178            (EntityUID::with_eid("test_resource"), None),
1179            Context::from_pairs(
1180                [
1181                    ("cur_time".into(), RestrictedExpr::val("03:22:11")),
1182                    (
1183                        "device_properties".into(),
1184                        RestrictedExpr::record(vec![
1185                            ("os_name".into(), RestrictedExpr::val("Windows")),
1186                            ("manufacturer".into(), RestrictedExpr::val("ACME Corp")),
1187                        ])
1188                        .unwrap(),
1189                    ),
1190                    ("violations".into(), RestrictedExpr::set([])),
1191                ],
1192                Extensions::none(),
1193            )
1194            .unwrap(),
1195            Some(&RequestSchemaAllPass),
1196            Extensions::none(),
1197        )
1198        .unwrap()
1199    }
1200
1201    /// Many of these tests use this basic `Entities`
1202    pub fn basic_entities() -> Entities {
1203        Entities::from_entities(
1204            vec![
1205                Entity::with_uid(EntityUID::with_eid("foo")),
1206                Entity::with_uid(EntityUID::with_eid("test_principal")),
1207                Entity::with_uid(EntityUID::with_eid("test_action")),
1208                Entity::with_uid(EntityUID::with_eid("test_resource")),
1209            ],
1210            None::<&NoEntitiesSchema>,
1211            TCComputation::ComputeNow,
1212            Extensions::none(),
1213        )
1214        .expect("failed to create basic entities")
1215    }
1216
1217    /// This `Entities` has richer Entities
1218    pub fn rich_entities() -> Entities {
1219        let entity_no_attrs_no_parents =
1220            Entity::with_uid(EntityUID::with_eid("entity_no_attrs_no_parents"));
1221
1222        let attrs = HashMap::from([
1223            ("spoon".into(), RestrictedExpr::val(787)),
1224            ("fork".into(), RestrictedExpr::val("spoon")),
1225            (
1226                "tags".into(),
1227                RestrictedExpr::set(vec![
1228                    RestrictedExpr::val("fun"),
1229                    RestrictedExpr::val("good"),
1230                    RestrictedExpr::val("useful"),
1231                ]),
1232            ),
1233            (
1234                "address".into(),
1235                RestrictedExpr::record(vec![
1236                    ("street".into(), RestrictedExpr::val("234 magnolia")),
1237                    ("town".into(), RestrictedExpr::val("barmstadt")),
1238                    ("country".into(), RestrictedExpr::val("amazonia")),
1239                ])
1240                .unwrap(),
1241            ),
1242        ]);
1243        let entity_with_attrs = Entity::new(
1244            EntityUID::with_eid("entity_with_attrs"),
1245            attrs.clone(),
1246            HashSet::new(),
1247            HashSet::new(),
1248            HashMap::new(),
1249            Extensions::none(),
1250        )
1251        .unwrap();
1252
1253        let tags = HashMap::from([("spoon".into(), RestrictedExpr::val(-121))]);
1254        let entity_with_tags = Entity::new(
1255            EntityUID::with_eid("entity_with_tags"),
1256            HashMap::new(),
1257            HashSet::new(),
1258            HashSet::new(),
1259            tags.clone(),
1260            Extensions::none(),
1261        )
1262        .unwrap();
1263
1264        let entity_with_tags_and_attrs = Entity::new(
1265            EntityUID::with_eid("entity_with_tags_and_attrs"),
1266            attrs,
1267            HashSet::new(),
1268            HashSet::new(),
1269            tags,
1270            Extensions::none(),
1271        )
1272        .unwrap();
1273
1274        let mut child = Entity::with_uid(EntityUID::with_eid("child"));
1275        let mut parent = Entity::with_uid(EntityUID::with_eid("parent"));
1276        let grandparent = Entity::with_uid(EntityUID::with_eid("grandparent"));
1277        let mut sibling = Entity::with_uid(EntityUID::with_eid("sibling"));
1278        let unrelated = Entity::with_uid(EntityUID::with_eid("unrelated"));
1279        child.add_parent(parent.uid().clone());
1280        sibling.add_parent(parent.uid().clone());
1281        parent.add_parent(grandparent.uid().clone());
1282        let mut child_diff_type = Entity::with_uid(
1283            EntityUID::with_eid_and_type("other_type", "other_child")
1284                .expect("should be a valid identifier"),
1285        );
1286        child_diff_type.add_parent(parent.uid().clone());
1287        child_diff_type.add_indirect_ancestor(grandparent.uid().clone());
1288
1289        Entities::from_entities(
1290            vec![
1291                entity_no_attrs_no_parents,
1292                entity_with_attrs,
1293                entity_with_tags,
1294                entity_with_tags_and_attrs,
1295                child,
1296                child_diff_type,
1297                parent,
1298                grandparent,
1299                sibling,
1300                unrelated,
1301            ],
1302            None::<&NoEntitiesSchema>,
1303            TCComputation::ComputeNow,
1304            Extensions::all_available(),
1305        )
1306        .expect("Failed to create rich entities")
1307    }
1308
1309    #[cfg(feature = "partial-eval")]
1310    #[test]
1311    fn partial_entity_stores_in_set() {
1312        let q = basic_request();
1313        let entities = rich_entities().partial();
1314        let child = EntityUID::with_eid("child");
1315        let second = EntityUID::with_eid("joseph");
1316        let missing = EntityUID::with_eid("non-present");
1317        let parent = EntityUID::with_eid("parent");
1318        let eval = Evaluator::new(q, &entities, Extensions::none());
1319
1320        let e = Expr::binary_app(
1321            BinaryOp::In,
1322            Expr::val(child),
1323            Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1324        );
1325        let r = eval.partial_eval_expr(&e).unwrap();
1326        assert_eq!(r, Either::Left(true.into()));
1327
1328        let e = Expr::binary_app(
1329            BinaryOp::In,
1330            Expr::val(missing.clone()),
1331            Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1332        );
1333        let r = eval.partial_eval_expr(&e).unwrap();
1334        let expected_residual = Expr::binary_app(
1335            BinaryOp::In,
1336            Expr::unknown(Unknown::new_with_type(
1337                format!("{missing}"),
1338                Type::Entity {
1339                    ty: EntityUID::test_entity_type(),
1340                },
1341            )),
1342            Expr::set([Expr::val(parent.clone()), Expr::val(second.clone())]),
1343        );
1344        let expected_residual2 = Expr::binary_app(
1345            BinaryOp::In,
1346            Expr::unknown(Unknown::new_with_type(
1347                format!("{missing}"),
1348                Type::Entity {
1349                    ty: EntityUID::test_entity_type(),
1350                },
1351            )),
1352            Expr::set([Expr::val(second), Expr::val(parent)]),
1353        );
1354
1355        // Either ordering is valid
1356        assert!(r == Either::Right(expected_residual) || r == Either::Right(expected_residual2));
1357    }
1358
1359    #[cfg(feature = "partial-eval")]
1360    #[test]
1361    fn partial_entity_stores_in() {
1362        let q = basic_request();
1363        let entities = rich_entities().partial();
1364        let child = EntityUID::with_eid("child");
1365        let missing = EntityUID::with_eid("non-present");
1366        let parent = EntityUID::with_eid("parent");
1367        let eval = Evaluator::new(q, &entities, Extensions::none());
1368
1369        let e = Expr::binary_app(BinaryOp::In, Expr::val(child), Expr::val(parent.clone()));
1370        let r = eval.partial_eval_expr(&e).unwrap();
1371        assert_eq!(r, Either::Left(true.into()));
1372
1373        let e = Expr::binary_app(
1374            BinaryOp::In,
1375            Expr::val(missing.clone()),
1376            Expr::val(parent.clone()),
1377        );
1378        let r = eval.partial_eval_expr(&e).unwrap();
1379        let expected_residual = Expr::binary_app(
1380            BinaryOp::In,
1381            Expr::unknown(Unknown::new_with_type(
1382                format!("{missing}"),
1383                Type::Entity {
1384                    ty: EntityUID::test_entity_type(),
1385                },
1386            )),
1387            Expr::val(parent),
1388        );
1389        assert_eq!(r, Either::Right(expected_residual));
1390    }
1391
1392    #[cfg(feature = "partial-eval")]
1393    #[test]
1394    fn partial_entity_stores_hasattr() {
1395        let q = basic_request();
1396        let entities = rich_entities().partial();
1397        let has_attr = EntityUID::with_eid("entity_with_attrs");
1398        let missing = EntityUID::with_eid("missing");
1399        let eval = Evaluator::new(q, &entities, Extensions::none());
1400
1401        let e = Expr::has_attr(Expr::val(has_attr), "spoon".into());
1402        let r = eval.partial_eval_expr(&e).unwrap();
1403        assert_eq!(r, Either::Left(true.into()));
1404
1405        let e = Expr::has_attr(Expr::val(missing.clone()), "spoon".into());
1406        let r = eval.partial_eval_expr(&e).unwrap();
1407        let expected_residual = Expr::has_attr(
1408            Expr::unknown(Unknown::new_with_type(
1409                format!("{missing}"),
1410                Type::Entity {
1411                    ty: EntityUID::test_entity_type(),
1412                },
1413            )),
1414            "spoon".into(),
1415        );
1416        assert_eq!(r, Either::Right(expected_residual));
1417    }
1418
1419    #[cfg(feature = "partial-eval")]
1420    #[test]
1421    fn partial_entity_stores_getattr() {
1422        let q = basic_request();
1423        let entities = rich_entities().partial();
1424        let has_attr = EntityUID::with_eid("entity_with_attrs");
1425        let missing = EntityUID::with_eid("missing");
1426        let eval = Evaluator::new(q, &entities, Extensions::none());
1427
1428        let e = Expr::get_attr(Expr::val(has_attr), "spoon".into());
1429        let r = eval.partial_eval_expr(&e).unwrap();
1430        assert_eq!(r, Either::Left(787.into()));
1431
1432        let e = Expr::get_attr(Expr::val(missing.clone()), "spoon".into());
1433        let r = eval.partial_eval_expr(&e).unwrap();
1434        let expected_residual = Expr::get_attr(
1435            Expr::unknown(Unknown::new_with_type(
1436                format!("{missing}"),
1437                Type::Entity {
1438                    ty: EntityUID::test_entity_type(),
1439                },
1440            )),
1441            "spoon".into(),
1442        );
1443        assert_eq!(r, Either::Right(expected_residual));
1444    }
1445
1446    #[test]
1447    fn interpret_primitives() {
1448        let request = basic_request();
1449        let entities = basic_entities();
1450        let eval = Evaluator::new(request, &entities, Extensions::none());
1451        // The below `assert_eq`s don't actually check the value's source location,
1452        // because `PartialEq` and `Eq` for `Value` don't compare source locations,
1453        // but checking the value's source location would not be an interesting
1454        // test, because these tests don't invoke the parser and there's no way
1455        // they could produce any source location other than `None`
1456        assert_eq!(
1457            eval.interpret_inline_policy(&Expr::val(false)),
1458            Ok(Value {
1459                value: ValueKind::Lit(Literal::Bool(false)),
1460                loc: None,
1461            }),
1462        );
1463        assert_eq!(
1464            eval.interpret_inline_policy(&Expr::val(true)),
1465            Ok(Value {
1466                value: ValueKind::Lit(Literal::Bool(true)),
1467                loc: None,
1468            }),
1469        );
1470        assert_eq!(
1471            eval.interpret_inline_policy(&Expr::val(57)),
1472            Ok(Value {
1473                value: ValueKind::Lit(Literal::Long(57)),
1474                loc: None,
1475            }),
1476        );
1477        assert_eq!(
1478            eval.interpret_inline_policy(&Expr::val(-3)),
1479            Ok(Value {
1480                value: ValueKind::Lit(Literal::Long(-3)),
1481                loc: None,
1482            }),
1483        );
1484        assert_eq!(
1485            eval.interpret_inline_policy(&Expr::val("")),
1486            Ok(Value {
1487                value: ValueKind::Lit(Literal::String("".into())),
1488                loc: None,
1489            }),
1490        );
1491        assert_eq!(
1492            eval.interpret_inline_policy(&Expr::val("Hello")),
1493            Ok(Value {
1494                value: ValueKind::Lit(Literal::String("Hello".into())),
1495                loc: None,
1496            }),
1497        );
1498    }
1499
1500    #[test]
1501    fn interpret_entities() {
1502        let request = basic_request();
1503        let entities = basic_entities();
1504        let eval = Evaluator::new(request, &entities, Extensions::none());
1505        // The below `assert_eq`s don't actually check the value's source location,
1506        // because `PartialEq` and `Eq` for `Value` don't compare source locations,
1507        // but checking the value's source location would not be an interesting
1508        // test, because these tests don't invoke the parser and there's no way
1509        // they could produce any source location other than `None`
1510        assert_eq!(
1511            eval.interpret_inline_policy(&Expr::val(EntityUID::with_eid("foo"))),
1512            Ok(Value {
1513                value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid("foo")))),
1514                loc: None,
1515            }),
1516        );
1517        // should be no error here even for entities that do not exist.
1518        // (for instance, A == B is allowed even when A and/or B do not exist.)
1519        assert_eq!(
1520            eval.interpret_inline_policy(&Expr::val(EntityUID::with_eid("doesnotexist"))),
1521            Ok(Value {
1522                value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid(
1523                    "doesnotexist"
1524                )))),
1525                loc: None,
1526            }),
1527        );
1528    }
1529
1530    #[test]
1531    fn interpret_builtin_vars() {
1532        let request = basic_request();
1533        let entities = basic_entities();
1534        let eval = Evaluator::new(request, &entities, Extensions::none());
1535        assert_eq!(
1536            eval.interpret_inline_policy(&Expr::var(Var::Principal)),
1537            Ok(Value::from(EntityUID::with_eid("test_principal")))
1538        );
1539        assert_eq!(
1540            eval.interpret_inline_policy(&Expr::var(Var::Action)),
1541            Ok(Value::from(EntityUID::with_eid("test_action")))
1542        );
1543        assert_eq!(
1544            eval.interpret_inline_policy(&Expr::var(Var::Resource)),
1545            Ok(Value::from(EntityUID::with_eid("test_resource")))
1546        );
1547    }
1548
1549    #[test]
1550    fn interpret_entity_attrs() {
1551        let request = basic_request();
1552        let entities = rich_entities();
1553        let eval = Evaluator::new(request, &entities, Extensions::none());
1554        // has_attr on an entity with no attrs
1555        assert_eq!(
1556            eval.interpret_inline_policy(&Expr::has_attr(
1557                Expr::val(EntityUID::with_eid("entity_no_attrs_no_parents")),
1558                "doesnotexist".into()
1559            )),
1560            Ok(Value::from(false))
1561        );
1562        // has_attr on an entity that has attrs, but not that one
1563        assert_eq!(
1564            eval.interpret_inline_policy(&Expr::has_attr(
1565                Expr::val(EntityUID::with_eid("entity_with_attrs")),
1566                "doesnotexist".into()
1567            )),
1568            Ok(Value::from(false))
1569        );
1570        // has_attr where the response is true
1571        assert_eq!(
1572            eval.interpret_inline_policy(&Expr::has_attr(
1573                Expr::val(EntityUID::with_eid("entity_with_attrs")),
1574                "tags".into()
1575            )),
1576            Ok(Value::from(true))
1577        );
1578        // get_attr on an attr which doesn't exist (and no tags exist)
1579        assert_matches!(
1580            eval.interpret_inline_policy(&Expr::get_attr(
1581                Expr::val(EntityUID::with_eid("entity_with_attrs")),
1582                "doesnotexist".into()
1583            )),
1584            Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1585                let report = miette::Report::new(e.clone());
1586                assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_attrs"));
1587                assert_eq!(&e.attr_or_tag, "doesnotexist");
1588                let available_attrs = e.available_attrs_or_tags;
1589                assert_eq!(available_attrs.len(), 4);
1590                assert!(available_attrs.contains(&"spoon".into()));
1591                assert!(available_attrs.contains(&"address".into()));
1592                assert!(available_attrs.contains(&"tags".into()));
1593                expect_err(
1594                    "",
1595                    &report,
1596                    &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_attrs"` does not have the attribute `doesnotexist`"#)
1597                        .help("available attributes: [address,fork,spoon,tags]")
1598                        .build()
1599                );
1600            }
1601        );
1602        // get_attr on an attr which doesn't exist (but the corresponding tag does)
1603        assert_matches!(
1604            eval.interpret_inline_policy(&Expr::get_attr(
1605                Expr::val(EntityUID::with_eid("entity_with_tags")),
1606                "spoon".into()
1607            )),
1608            Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1609                let report = miette::Report::new(e.clone());
1610                assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_tags"));
1611                assert_eq!(&e.attr_or_tag, "spoon");
1612                let available_attrs = e.available_attrs_or_tags;
1613                assert_eq!(available_attrs.len(), 0);
1614                let expected_error_message =
1615                    ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags"` does not have the attribute `spoon`"#)
1616                        .help(r#"`test_entity_type::"entity_with_tags"` does not have any attributes; note that a tag (not an attribute) named `spoon` does exist"#)
1617                        .build();
1618                expect_err("", &report, &expected_error_message);
1619            }
1620        );
1621        // get_attr on an attr which does exist (and has integer type)
1622        assert_eq!(
1623            eval.interpret_inline_policy(&Expr::get_attr(
1624                Expr::val(EntityUID::with_eid("entity_with_attrs")),
1625                "spoon".into()
1626            )),
1627            Ok(Value::from(787))
1628        );
1629        // get_attr on an attr which does exist (and has Set type)
1630        assert_eq!(
1631            eval.interpret_inline_policy(&Expr::contains(
1632                Expr::get_attr(
1633                    Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1634                    "tags".into()
1635                ),
1636                Expr::val("useful")
1637            )),
1638            Ok(Value::from(true))
1639        );
1640        // has_attr on an entity which doesn't exist
1641        assert_eq!(
1642            eval.interpret_inline_policy(&Expr::has_attr(
1643                Expr::val(EntityUID::with_eid("doesnotexist")),
1644                "foo".into()
1645            )),
1646            Ok(Value::from(false))
1647        );
1648        // get_attr on an entity which doesn't exist
1649        assert_eq!(
1650            eval.interpret_inline_policy(&Expr::get_attr(
1651                Expr::val(EntityUID::with_eid("doesnotexist")),
1652                "foo".into()
1653            )),
1654            Err(EvaluationError::entity_does_not_exist(
1655                Arc::new(EntityUID::with_eid("doesnotexist")),
1656                None
1657            ))
1658        );
1659    }
1660
1661    #[test]
1662    fn interpret_entity_tags() {
1663        let request = basic_request();
1664        let entities = rich_entities();
1665        let eval = Evaluator::new(request, &entities, Extensions::none());
1666        // hasTag on an entity with no tags
1667        assert_eq!(
1668            eval.interpret_inline_policy(&Expr::has_tag(
1669                Expr::val(EntityUID::with_eid("entity_no_attrs_no_parents")),
1670                Expr::val("doesnotexist"),
1671            )),
1672            Ok(Value::from(false))
1673        );
1674        // hasTag on an entity that has tags, but not that one (and no attrs exist)
1675        assert_eq!(
1676            eval.interpret_inline_policy(&Expr::has_tag(
1677                Expr::val(EntityUID::with_eid("entity_with_tags")),
1678                Expr::val("doesnotexist"),
1679            )),
1680            Ok(Value::from(false))
1681        );
1682        // hasTag on an entity that has tags, but not that one (but does have an attr of that name)
1683        assert_eq!(
1684            eval.interpret_inline_policy(&Expr::has_tag(
1685                Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1686                Expr::val("address"),
1687            )),
1688            Ok(Value::from(false))
1689        );
1690        // hasTag where the response is true
1691        assert_eq!(
1692            eval.interpret_inline_policy(&Expr::has_tag(
1693                Expr::val(EntityUID::with_eid("entity_with_tags")),
1694                Expr::val("spoon"),
1695            )),
1696            Ok(Value::from(true))
1697        );
1698        // hasTag, with a computed key, where the response is true
1699        assert_eq!(
1700            eval.interpret_inline_policy(&Expr::has_tag(
1701                Expr::val(EntityUID::with_eid("entity_with_tags")),
1702                Expr::get_attr(
1703                    Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1704                    "fork".into()
1705                ),
1706            )),
1707            Ok(Value::from(true))
1708        );
1709        // getTag on a tag which doesn't exist (and no attrs exist)
1710        assert_matches!(
1711            eval.interpret_inline_policy(&Expr::get_tag(
1712                Expr::val(EntityUID::with_eid("entity_with_tags")),
1713                Expr::val("doesnotexist"),
1714            )),
1715            Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1716                let report = miette::Report::new(e.clone());
1717                assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_tags"));
1718                assert_eq!(&e.attr_or_tag, "doesnotexist");
1719                let available_attrs = e.available_attrs_or_tags;
1720                assert_eq!(available_attrs.len(), 1);
1721                assert!(available_attrs.contains(&"spoon".into()));
1722                expect_err(
1723                    "",
1724                    &report,
1725                    &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags"` does not have the tag `doesnotexist`"#)
1726                        .help("available tags: [spoon]")
1727                        .build()
1728                );
1729            }
1730        );
1731        // getTag on a tag which doesn't exist (but the corresponding attr does)
1732        assert_matches!(
1733            eval.interpret_inline_policy(&Expr::get_tag(
1734                Expr::val(EntityUID::with_eid("entity_with_tags_and_attrs")),
1735                Expr::val("address"),
1736            )),
1737            Err(EvaluationError::EntityAttrDoesNotExist(e)) => {
1738                let report = miette::Report::new(e.clone());
1739                assert_eq!(e.entity.as_ref(), &EntityUID::with_eid("entity_with_tags_and_attrs"));
1740                assert_eq!(&e.attr_or_tag, "address");
1741                let available_attrs = e.available_attrs_or_tags;
1742                assert_eq!(available_attrs.len(), 1);
1743                assert!(available_attrs.contains(&"spoon".into()));
1744                expect_err(
1745                    "",
1746                    &report,
1747                    &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags_and_attrs"` does not have the tag `address`"#)
1748                        .help("available tags: [spoon]; note that an attribute (not a tag) named `address` does exist")
1749                        .build()
1750                );
1751            }
1752        );
1753        // getTag on a tag which does exist (and has integer type)
1754        assert_eq!(
1755            eval.interpret_inline_policy(&Expr::get_tag(
1756                Expr::val(EntityUID::with_eid("entity_with_tags")),
1757                Expr::val("spoon"),
1758            )),
1759            Ok(Value::from(-121))
1760        );
1761        // getTag with a computed key on a tag which does exist
1762        assert_eq!(
1763            eval.interpret_inline_policy(&Expr::get_tag(
1764                Expr::val(EntityUID::with_eid("entity_with_tags")),
1765                Expr::get_attr(
1766                    Expr::val(EntityUID::with_eid("entity_with_attrs")),
1767                    "fork".into()
1768                ),
1769            )),
1770            Ok(Value::from(-121))
1771        );
1772        // getTag with a computed key on a tag which doesn't exist
1773        assert_matches!(
1774            eval.interpret_inline_policy(&Expr::get_tag(
1775                Expr::val(EntityUID::with_eid("entity_with_tags")),
1776                Expr::get_attr(
1777                    Expr::get_attr(
1778                        Expr::val(EntityUID::with_eid("entity_with_attrs")),
1779                        "address".into()
1780                    ),
1781                    "country".into()
1782                ),
1783            )),
1784            Err(e) => {
1785                expect_err(
1786                    "",
1787                    &miette::Report::new(e),
1788                    &ExpectedErrorMessageBuilder::error(r#"`test_entity_type::"entity_with_tags"` does not have the tag `amazonia`"#)
1789                        .help("available tags: [spoon]")
1790                        .build(),
1791                )
1792            }
1793        );
1794        // hasTag on an entity which doesn't exist
1795        assert_eq!(
1796            eval.interpret_inline_policy(&Expr::has_tag(
1797                Expr::val(EntityUID::with_eid("doesnotexist")),
1798                Expr::val("foo"),
1799            )),
1800            Ok(Value::from(false))
1801        );
1802        // getTag on an entity which doesn't exist
1803        assert_eq!(
1804            eval.interpret_inline_policy(&Expr::get_tag(
1805                Expr::val(EntityUID::with_eid("doesnotexist")),
1806                Expr::val("foo"),
1807            )),
1808            Err(EvaluationError::entity_does_not_exist(
1809                Arc::new(EntityUID::with_eid("doesnotexist")),
1810                None
1811            ))
1812        );
1813        // getTag on something that's not an entity
1814        assert_matches!(
1815            eval.interpret_inline_policy(&Expr::get_tag(
1816                Expr::record([
1817                    ("spoon".into(), Expr::val(78)),
1818                ]).unwrap(),
1819                Expr::val("spoon"),
1820            )),
1821            Err(e) => {
1822                expect_err(
1823                    "",
1824                    &miette::Report::new(e),
1825                    &ExpectedErrorMessageBuilder::error("type error: expected (entity of type `any_entity_type`), got record")
1826                        .build()
1827                );
1828            }
1829        );
1830        // hasTag on something that's not an entity
1831        assert_matches!(
1832            eval.interpret_inline_policy(&Expr::has_tag(
1833                Expr::record([
1834                    ("spoon".into(), Expr::val(78)),
1835                ]).unwrap(),
1836                Expr::val("spoon"),
1837            )),
1838            Err(e) => {
1839                expect_err(
1840                    "",
1841                    &miette::Report::new(e),
1842                    &ExpectedErrorMessageBuilder::error("type error: expected (entity of type `any_entity_type`), got record")
1843                        .build()
1844                );
1845            }
1846        );
1847        // getTag with a computed key that doesn't evaluate to a String
1848        assert_matches!(
1849            eval.interpret_inline_policy(&Expr::get_tag(
1850                Expr::val(EntityUID::with_eid("entity_with_tags")),
1851                Expr::get_attr(Expr::val(EntityUID::with_eid("entity_with_attrs")), "spoon".into()),
1852            )),
1853            Err(e) => {
1854                expect_err(
1855                    "",
1856                    &miette::Report::new(e),
1857                    &ExpectedErrorMessageBuilder::error("type error: expected string, got long")
1858                        .build()
1859                );
1860            }
1861        );
1862        // hasTag with a computed key that doesn't evaluate to a String
1863        assert_matches!(
1864            eval.interpret_inline_policy(&Expr::has_tag(
1865                Expr::val(EntityUID::with_eid("entity_with_tags")),
1866                Expr::get_attr(Expr::val(EntityUID::with_eid("entity_with_attrs")), "spoon".into()),
1867            )),
1868            Err(e) => {
1869                expect_err(
1870                    "",
1871                    &miette::Report::new(e),
1872                    &ExpectedErrorMessageBuilder::error("type error: expected string, got long")
1873                        .build()
1874                );
1875            }
1876        );
1877    }
1878
1879    #[test]
1880    fn interpret_ternaries() {
1881        let request = basic_request();
1882        let entities = basic_entities();
1883        let eval = Evaluator::new(request, &entities, Extensions::none());
1884        // if true then 3 else 8
1885        assert_eq!(
1886            eval.interpret_inline_policy(&Expr::ite(Expr::val(true), Expr::val(3), Expr::val(8))),
1887            Ok(Value::from(3))
1888        );
1889        // if false then 3 else 8
1890        assert_eq!(
1891            eval.interpret_inline_policy(&Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8))),
1892            Ok(Value::from(8))
1893        );
1894        // if false then false else true
1895        assert_eq!(
1896            eval.interpret_inline_policy(&Expr::ite(
1897                Expr::val(false),
1898                Expr::val(false),
1899                Expr::val(true)
1900            )),
1901            Ok(Value::from(true))
1902        );
1903        // if false then principal else resource
1904        assert_eq!(
1905            eval.interpret_inline_policy(&Expr::ite(
1906                Expr::val(false),
1907                Expr::var(Var::Principal),
1908                Expr::var(Var::Resource)
1909            )),
1910            Ok(Value::from(EntityUID::with_eid("test_resource")))
1911        );
1912        // if "hello" then 3 else 8
1913        assert_matches!(
1914            eval.interpret_inline_policy(&Expr::ite(
1915                Expr::val("hello"),
1916                Expr::val(3),
1917                Expr::val(8)
1918            )),
1919            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1920                assert_eq!(expected, nonempty![Type::Bool]);
1921                assert_eq!(actual, Type::String);
1922                assert_eq!(advice, None);
1923            }
1924        );
1925        // if principal then 3 else 8
1926        assert_matches!(
1927            eval.interpret_inline_policy(&Expr::ite(
1928                Expr::var(Var::Principal),
1929                Expr::val(3),
1930                Expr::val(8)
1931            )),
1932            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
1933                assert_eq!(expected, nonempty![Type::Bool]);
1934                assert_eq!(actual, Type::Entity {
1935                    ty: EntityUID::test_entity_type(),
1936                });
1937                assert_eq!(advice, None);
1938            }
1939        );
1940        // if true then "hello" else 2
1941        assert_eq!(
1942            eval.interpret_inline_policy(&Expr::ite(
1943                Expr::val(true),
1944                Expr::val("hello"),
1945                Expr::val(2)
1946            )),
1947            Ok(Value::from("hello"))
1948        );
1949        // if false then "hello" else 2
1950        assert_eq!(
1951            eval.interpret_inline_policy(&Expr::ite(
1952                Expr::val(false),
1953                Expr::val("hello"),
1954                Expr::val(2)
1955            )),
1956            Ok(Value::from(2))
1957        );
1958        // if true then (if true then 3 else 8) else -10
1959        assert_eq!(
1960            eval.interpret_inline_policy(&Expr::ite(
1961                Expr::val(true),
1962                Expr::ite(Expr::val(true), Expr::val(3), Expr::val(8)),
1963                Expr::val(-10)
1964            )),
1965            Ok(Value::from(3))
1966        );
1967        // if true then (if false then 3 else 8) else -10
1968        assert_eq!(
1969            eval.interpret_inline_policy(&Expr::ite(
1970                Expr::val(true),
1971                Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8)),
1972                Expr::val(-10)
1973            )),
1974            Ok(Value::from(8))
1975        );
1976        // if false then (if false then 3 else 8) else -10
1977        assert_eq!(
1978            eval.interpret_inline_policy(&Expr::ite(
1979                Expr::val(false),
1980                Expr::ite(Expr::val(false), Expr::val(3), Expr::val(8)),
1981                Expr::val(-10)
1982            )),
1983            Ok(Value::from(-10))
1984        );
1985        // if false then (if "hello" then 3 else 8) else -10
1986        assert_eq!(
1987            eval.interpret_inline_policy(&Expr::ite(
1988                Expr::val(false),
1989                Expr::ite(Expr::val("hello"), Expr::val(3), Expr::val(8)),
1990                Expr::val(-10)
1991            )),
1992            Ok(Value::from(-10))
1993        );
1994        // if true then 3 else (if true then 8 else -10)
1995        assert_eq!(
1996            eval.interpret_inline_policy(&Expr::ite(
1997                Expr::val(true),
1998                Expr::val(3),
1999                Expr::ite(Expr::val(true), Expr::val(8), Expr::val(-10))
2000            )),
2001            Ok(Value::from(3))
2002        );
2003        // if (if true then false else true) then 3 else 8
2004        assert_eq!(
2005            eval.interpret_inline_policy(&Expr::ite(
2006                Expr::ite(Expr::val(true), Expr::val(false), Expr::val(true)),
2007                Expr::val(3),
2008                Expr::val(8)
2009            )),
2010            Ok(Value::from(8))
2011        );
2012        // if true then 3 else <err>
2013        assert_eq!(
2014            eval.interpret_inline_policy(&Expr::ite(
2015                Expr::val(true),
2016                Expr::val(3),
2017                Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2018            )),
2019            Ok(Value::from(3))
2020        );
2021        // if false then 3 else <err>
2022        assert_eq!(
2023            eval.interpret_inline_policy(&Expr::ite(
2024                Expr::val(false),
2025                Expr::val(3),
2026                Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2027            )),
2028            Err(EvaluationError::record_attr_does_not_exist(
2029                "foo".into(),
2030                std::iter::empty(),
2031                0,
2032                None,
2033            ))
2034        );
2035        // if true then <err> else 3
2036        assert_eq!(
2037            eval.interpret_inline_policy(&Expr::ite(
2038                Expr::val(true),
2039                Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2040                Expr::val(3),
2041            )),
2042            Err(EvaluationError::record_attr_does_not_exist(
2043                "foo".into(),
2044                std::iter::empty(),
2045                0,
2046                None,
2047            ))
2048        );
2049        // if false then <err> else 3
2050        assert_eq!(
2051            eval.interpret_inline_policy(&Expr::ite(
2052                Expr::val(false),
2053                Expr::get_attr(Expr::record(vec![]).unwrap(), "foo".into()),
2054                Expr::val(3),
2055            )),
2056            Ok(Value::from(3))
2057        );
2058    }
2059
2060    #[test]
2061    fn interpret_sets() {
2062        let request = basic_request();
2063        let entities = basic_entities();
2064        let eval = Evaluator::new(request, &entities, Extensions::none());
2065        // The below `assert_eq`s don't actually check the value's source location,
2066        // because `PartialEq` and `Eq` for `Value` don't compare source locations,
2067        // but checking the value's source location would not be an interesting
2068        // test, because these tests don't invoke the parser and there's no way
2069        // they could produce any source location other than `None`
2070
2071        // set(8)
2072        assert_eq!(
2073            eval.interpret_inline_policy(&Expr::set(vec![Expr::val(8)])),
2074            Ok(Value::set(
2075                vec![Value {
2076                    value: ValueKind::Lit(Literal::Long(8)),
2077                    loc: None,
2078                }],
2079                None,
2080            )),
2081        );
2082        // set(8, 2, 101)
2083        assert_eq!(
2084            eval.interpret_inline_policy(&Expr::set(vec![
2085                Expr::val(8),
2086                Expr::val(2),
2087                Expr::val(101),
2088            ])),
2089            Ok(Value::set(
2090                vec![
2091                    Value {
2092                        value: ValueKind::Lit(Literal::Long(8)),
2093                        loc: None,
2094                    },
2095                    Value {
2096                        value: ValueKind::Lit(Literal::Long(2)),
2097                        loc: None,
2098                    },
2099                    Value {
2100                        value: ValueKind::Lit(Literal::Long(101)),
2101                        loc: None,
2102                    },
2103                ],
2104                None,
2105            )),
2106        );
2107        // empty set
2108        assert_eq!(
2109            eval.interpret_inline_policy(&Expr::set(vec![])),
2110            Ok(Value::empty_set(None)),
2111        );
2112        assert_eq!(
2113            eval.interpret_inline_policy(&Expr::set(vec![])),
2114            Ok(Value::empty_set(None)),
2115        );
2116        // set(8)["hello"]
2117        assert_matches!(
2118            eval.interpret_inline_policy(&Expr::get_attr(
2119                Expr::set(vec![Expr::val(8)]),
2120                "hello".into()
2121            )),
2122            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2123                    assert_eq!(expected, nonempty![
2124                        Type::Record,
2125                        Type::entity_type(
2126                            Name::parse_unqualified_name("any_entity_type")
2127                                .expect("should be a valid identifier")
2128                        ),
2129                    ]);
2130                    assert_eq!(actual, Type::Set);
2131                    assert_eq!(advice, None);
2132                }
2133        );
2134        // indexing into empty set
2135        assert_matches!(
2136            eval.interpret_inline_policy(&Expr::get_attr(Expr::set(vec![]), "hello".into())),
2137            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2138                assert_eq!(expected, nonempty![
2139                    Type::Record,
2140                    Type::entity_type(
2141                        Name::parse_unqualified_name("any_entity_type")
2142                            .expect("should be a valid identifier")
2143                    ),
2144                ]);
2145                assert_eq!(actual, Type::Set);
2146                assert_eq!(advice, None);
2147            }
2148        );
2149        // set("hello", 2, true, <entity foo>)
2150        let mixed_set = Expr::set(vec![
2151            Expr::val("hello"),
2152            Expr::val(2),
2153            Expr::val(true),
2154            Expr::val(EntityUID::with_eid("foo")),
2155        ]);
2156        assert_eq!(
2157            eval.interpret_inline_policy(&mixed_set),
2158            Ok(Value::set(
2159                vec![
2160                    Value {
2161                        value: ValueKind::Lit(Literal::String("hello".into())),
2162                        loc: None,
2163                    },
2164                    Value {
2165                        value: ValueKind::Lit(Literal::Long(2)),
2166                        loc: None,
2167                    },
2168                    Value {
2169                        value: ValueKind::Lit(Literal::Bool(true)),
2170                        loc: None,
2171                    },
2172                    Value {
2173                        value: ValueKind::Lit(Literal::EntityUID(Arc::new(EntityUID::with_eid(
2174                            "foo"
2175                        )))),
2176                        loc: None,
2177                    },
2178                ],
2179                None,
2180            )),
2181        );
2182        // set("hello", 2, true, <entity foo>)["hello"]
2183        assert_matches!(
2184            eval.interpret_inline_policy(&Expr::get_attr(mixed_set, "hello".into())),
2185            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2186                assert_eq!(expected, nonempty![
2187                    Type::Record,
2188                    Type::entity_type(
2189                        Name::parse_unqualified_name("any_entity_type")
2190                            .expect("should be a valid identifier")
2191                    ),
2192                ]);
2193                assert_eq!(actual, Type::Set);
2194                assert_eq!(advice, None);
2195            }
2196        );
2197        // set(set(8, 2), set(13, 702), set(3))
2198        let set_of_sets = Expr::set(vec![
2199            Expr::set(vec![Expr::val(8), Expr::val(2)]),
2200            Expr::set(vec![Expr::val(13), Expr::val(702)]),
2201            Expr::set(vec![Expr::val(3)]),
2202        ]);
2203        assert_eq!(
2204            eval.interpret_inline_policy(&set_of_sets),
2205            Ok(Value::set(
2206                vec![
2207                    Value::set(
2208                        vec![
2209                            Value {
2210                                value: ValueKind::Lit(Literal::Long(8)),
2211                                loc: None,
2212                            },
2213                            Value {
2214                                value: ValueKind::Lit(Literal::Long(2)),
2215                                loc: None,
2216                            },
2217                        ],
2218                        None,
2219                    ),
2220                    Value::set(
2221                        vec![
2222                            Value {
2223                                value: ValueKind::Lit(Literal::Long(13)),
2224                                loc: None,
2225                            },
2226                            Value {
2227                                value: ValueKind::Lit(Literal::Long(702)),
2228                                loc: None,
2229                            },
2230                        ],
2231                        None,
2232                    ),
2233                    Value::set(
2234                        vec![Value {
2235                            value: ValueKind::Lit(Literal::Long(3)),
2236                            loc: None,
2237                        }],
2238                        None,
2239                    ),
2240                ],
2241                None,
2242            )),
2243        );
2244        // set(set(8, 2), set(13, 702), set(3))["hello"]
2245        assert_matches!(
2246            eval.interpret_inline_policy(&Expr::get_attr(set_of_sets.clone(), "hello".into())),
2247            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2248                assert_eq!(expected, nonempty![
2249                    Type::Record,
2250                    Type::entity_type(
2251                        Name::parse_unqualified_name("any_entity_type")
2252                            .expect("should be a valid identifier")
2253                    ),
2254                ]);
2255                assert_eq!(actual, Type::Set);
2256                assert_eq!(advice, None);
2257            }
2258        );
2259        // set(set(8, 2), set(13, 702), set(3))["ham"]["eggs"]
2260        assert_matches!(
2261            eval.interpret_inline_policy(&Expr::get_attr(
2262                Expr::get_attr(set_of_sets, "ham".into()),
2263                "eggs".into()
2264            )),
2265            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2266                assert_eq!(expected, nonempty![
2267                    Type::Record,
2268                    Type::entity_type(
2269                        Name::parse_unqualified_name("any_entity_type")
2270                            .expect("should be a valid identifier")
2271                    ),
2272                ]);
2273                assert_eq!(actual, Type::Set);
2274                assert_eq!(advice, None);
2275            }
2276        );
2277    }
2278
2279    #[test]
2280    fn interpret_records() {
2281        let request = basic_request();
2282        let entities = rich_entities();
2283        let eval = Evaluator::new(request, &entities, Extensions::none());
2284        // {"key": 3}["key"] or {"key": 3}.key
2285        let string_key = Expr::record(vec![("key".into(), Expr::val(3))]).unwrap();
2286        assert_eq!(
2287            eval.interpret_inline_policy(&Expr::get_attr(string_key, "key".into())),
2288            Ok(Value::from(3))
2289        );
2290        // {"ham": 3, "eggs": 7}["ham"] or {"ham": 3, "eggs": 7}.ham
2291        let ham_and_eggs = Expr::record(vec![
2292            ("ham".into(), Expr::val(3)),
2293            ("eggs".into(), Expr::val(7)),
2294        ])
2295        .unwrap();
2296        assert_eq!(
2297            eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs.clone(), "ham".into())),
2298            Ok(Value::from(3))
2299        );
2300        // {"ham": 3, "eggs": 7}["eggs"]
2301        assert_eq!(
2302            eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs.clone(), "eggs".into())),
2303            Ok(Value::from(7))
2304        );
2305        // {"ham": 3, "eggs": 7}["what"]
2306        assert_eq!(
2307            eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs, "what".into())),
2308            Err(EvaluationError::record_attr_does_not_exist(
2309                "what".into(),
2310                [&"eggs".into(), &"ham".into()],
2311                2,
2312                None,
2313            ))
2314        );
2315
2316        // {"ham": 3, "eggs": "why"}["ham"]
2317        let ham_and_eggs_2 = Expr::record(vec![
2318            ("ham".into(), Expr::val(3)),
2319            ("eggs".into(), Expr::val("why")),
2320        ])
2321        .unwrap();
2322        assert_eq!(
2323            eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_2.clone(), "ham".into())),
2324            Ok(Value::from(3))
2325        );
2326        // {"ham": 3, "eggs": "why"}["eggs"]
2327        assert_eq!(
2328            eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_2, "eggs".into())),
2329            Ok(Value::from("why"))
2330        );
2331        // {"ham": 3, "eggs": "why", "else": <entity foo>}["else"]
2332        let ham_and_eggs_3 = Expr::record(vec![
2333            ("ham".into(), Expr::val(3)),
2334            ("eggs".into(), Expr::val("why")),
2335            ("else".into(), Expr::val(EntityUID::with_eid("foo"))),
2336        ])
2337        .unwrap();
2338        assert_eq!(
2339            eval.interpret_inline_policy(&Expr::get_attr(ham_and_eggs_3, "else".into())),
2340            Ok(Value::from(EntityUID::with_eid("foo")))
2341        );
2342        // {"hams": {"some": 1, "more": 2}, "eggs": "why"}["hams"]["more"]
2343        let hams_and_eggs = Expr::record(vec![
2344            (
2345                "hams".into(),
2346                Expr::record(vec![
2347                    ("some".into(), Expr::val(1)),
2348                    ("more".into(), Expr::val(2)),
2349                ])
2350                .unwrap(),
2351            ),
2352            ("eggs".into(), Expr::val("why")),
2353        ])
2354        .unwrap();
2355        assert_eq!(
2356            eval.interpret_inline_policy(&Expr::get_attr(
2357                Expr::get_attr(hams_and_eggs, "hams".into()),
2358                "more".into()
2359            )),
2360            Ok(Value::from(2))
2361        );
2362        // {"this is a valid map key+.-_%() ": 7}["this is a valid map key+.-_%() "]
2363        let weird_key = Expr::record(vec![(
2364            "this is a valid map key+.-_%() ".into(),
2365            Expr::val(7),
2366        )])
2367        .unwrap();
2368        assert_eq!(
2369            eval.interpret_inline_policy(&Expr::get_attr(
2370                weird_key,
2371                "this is a valid map key+.-_%() ".into()
2372            )),
2373            Ok(Value::from(7))
2374        );
2375        // { foo: 2, bar: [3, 33, 333] }.bar
2376        assert_eq!(
2377            eval.interpret_inline_policy(&Expr::get_attr(
2378                Expr::record(vec![
2379                    ("foo".into(), Expr::val(2)),
2380                    (
2381                        "bar".into(),
2382                        Expr::set(vec![Expr::val(3), Expr::val(33), Expr::val(333)])
2383                    )
2384                ])
2385                .unwrap(),
2386                "bar".into()
2387            )),
2388            Ok(Value::set(
2389                vec![Value::from(3), Value::from(33), Value::from(333)],
2390                None
2391            ))
2392        );
2393        // { foo: 2, bar: {"a+b": 5, "jkl;": 10} }.bar["a+b"]
2394        assert_eq!(
2395            eval.interpret_inline_policy(&Expr::get_attr(
2396                Expr::get_attr(
2397                    Expr::record(vec![
2398                        ("foo".into(), Expr::val(2)),
2399                        (
2400                            "bar".into(),
2401                            Expr::record(vec![
2402                                ("a+b".into(), Expr::val(5)),
2403                                ("jkl;".into(), Expr::val(10)),
2404                            ])
2405                            .unwrap()
2406                        ),
2407                    ])
2408                    .unwrap(),
2409                    "bar".into()
2410                ),
2411                "a+b".into()
2412            )),
2413            Ok(Value::from(5))
2414        );
2415        // { foo: 2, bar: { foo: 4, cake: 77 } }.bar.foo
2416        assert_eq!(
2417            eval.interpret_inline_policy(&Expr::get_attr(
2418                Expr::get_attr(
2419                    Expr::record(vec![
2420                        ("foo".into(), Expr::val(2)),
2421                        (
2422                            "bar".into(),
2423                            Expr::record(vec![
2424                                ("foo".into(), Expr::val(4)),
2425                                ("cake".into(), Expr::val(77)),
2426                            ])
2427                            .unwrap()
2428                        ),
2429                    ])
2430                    .unwrap(),
2431                    "bar".into(),
2432                ),
2433                "foo".into(),
2434            )),
2435            Ok(Value::from(4))
2436        );
2437        // duplicate record key
2438        // { foo: 2, bar: 4, foo: "hi" }.bar
2439        assert_eq!(
2440            Expr::record(vec![
2441                ("foo".into(), Expr::val(2)),
2442                ("bar".into(), Expr::val(4)),
2443                ("foo".into(), Expr::val("hi")),
2444            ]),
2445            Err(expression_construction_errors::DuplicateKeyError {
2446                key: "foo".into(),
2447                context: "in record literal",
2448            }
2449            .into())
2450        );
2451        // entity_with_attrs.address.street
2452        assert_eq!(
2453            eval.interpret_inline_policy(&Expr::get_attr(
2454                Expr::get_attr(
2455                    Expr::val(EntityUID::with_eid("entity_with_attrs")),
2456                    "address".into()
2457                ),
2458                "street".into()
2459            )),
2460            Ok(Value::from("234 magnolia"))
2461        );
2462        // context.cur_time
2463        assert_eq!(
2464            eval.interpret_inline_policy(&Expr::get_attr(
2465                Expr::var(Var::Context),
2466                "cur_time".into()
2467            )),
2468            Ok(Value::from("03:22:11"))
2469        );
2470        // context.device_properties.os_name
2471        assert_eq!(
2472            eval.interpret_inline_policy(&Expr::get_attr(
2473                Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2474                "os_name".into()
2475            )),
2476            Ok(Value::from("Windows"))
2477        );
2478        // using has() to test for existence of a record field (which does exist)
2479        // has({"foo": 77, "bar" : "pancakes"}.foo)
2480        assert_eq!(
2481            eval.interpret_inline_policy(&Expr::has_attr(
2482                Expr::record(vec![
2483                    ("foo".into(), Expr::val(77)),
2484                    ("bar".into(), Expr::val("pancakes")),
2485                ])
2486                .unwrap(),
2487                "foo".into()
2488            )),
2489            Ok(Value::from(true))
2490        );
2491        // using has() to test for existence of a record field (which doesn't exist)
2492        // {"foo": 77, "bar" : "pancakes"} has pancakes
2493        assert_eq!(
2494            eval.interpret_inline_policy(&Expr::has_attr(
2495                Expr::record(vec![
2496                    ("foo".into(), Expr::val(77)),
2497                    ("bar".into(), Expr::val("pancakes")),
2498                ])
2499                .unwrap(),
2500                "pancakes".into()
2501            )),
2502            Ok(Value::from(false))
2503        );
2504        // {"2": "ham"} has "2"
2505        assert_eq!(
2506            eval.interpret_inline_policy(&Expr::has_attr(
2507                Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
2508                "2".into()
2509            )),
2510            Ok(Value::from(true))
2511        );
2512        // {"ham": 17, "eggs": if foo has spaghetti then 3 else 7} has ham
2513        assert_eq!(
2514            eval.interpret_inline_policy(&Expr::has_attr(
2515                Expr::record(vec![
2516                    ("ham".into(), Expr::val(17)),
2517                    (
2518                        "eggs".into(),
2519                        Expr::ite(
2520                            Expr::has_attr(
2521                                Expr::val(EntityUID::with_eid("foo")),
2522                                "spaghetti".into()
2523                            ),
2524                            Expr::val(3),
2525                            Expr::val(7)
2526                        )
2527                    ),
2528                ])
2529                .unwrap(),
2530                "ham".into()
2531            )),
2532            Ok(Value::from(true))
2533        );
2534        // indexing into something that's not a record, 1010122["hello"]
2535        assert_matches!(
2536            eval.interpret_inline_policy(&Expr::get_attr(Expr::val(1010122), "hello".into())),
2537            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2538                assert_eq!(expected, nonempty![
2539                    Type::Record,
2540                    Type::entity_type(
2541                        Name::parse_unqualified_name("any_entity_type")
2542                            .expect("should be a valid identifier")
2543                    ),
2544                ]);
2545                assert_eq!(actual, Type::Long);
2546                assert_eq!(advice, None);
2547            }
2548        );
2549        // indexing into something that's not a record, "hello"["eggs"]
2550        assert_matches!(
2551            eval.interpret_inline_policy(&Expr::get_attr(Expr::val("hello"), "eggs".into())),
2552            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2553                assert_eq!(expected, nonempty![
2554                    Type::Record,
2555                    Type::entity_type(
2556                        Name::parse_unqualified_name("any_entity_type")
2557                            .expect("should be a valid identifier")
2558                    ),
2559                ]);
2560                assert_eq!(actual, Type::String);
2561                assert_eq!(advice, None);
2562            }
2563        );
2564        // has_attr on something that's not a record, 1010122 has hello
2565        assert_matches!(
2566            eval.interpret_inline_policy(&Expr::has_attr(Expr::val(1010122), "hello".into())),
2567            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2568                assert_eq!(expected, nonempty![
2569                    Type::Record,
2570                    Type::entity_type(
2571                        Name::parse_unqualified_name("any_entity_type")
2572                            .expect("should be a valid identifier")
2573                    ),
2574                ]);
2575                assert_eq!(actual, Type::Long);
2576                assert_eq!(advice, None);
2577            }
2578        );
2579        // has_attr on something that's not a record, "hello" has eggs
2580        assert_matches!(
2581            eval.interpret_inline_policy(&Expr::has_attr(Expr::val("hello"), "eggs".into())),
2582            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2583                assert_eq!(expected, nonempty![
2584                    Type::Record,
2585                    Type::entity_type(
2586                        Name::parse_unqualified_name("any_entity_type")
2587                            .expect("should be a valid identifier")
2588                    ),
2589                ]);
2590                assert_eq!(actual, Type::String);
2591                assert_eq!(advice, None);
2592            }
2593        );
2594    }
2595
2596    #[test]
2597    fn large_entity_err() {
2598        let expr = Expr::get_attr(
2599            Expr::val(EntityUID::from_str(r#"Foo::"bar""#).unwrap()),
2600            "foo".into(),
2601        );
2602        let attrs = (1..=7)
2603            .map(|id| (format!("{id}").into(), RestrictedExpr::val(true)))
2604            .collect::<HashMap<SmolStr, _>>();
2605        let entity = Entity::new(
2606            r#"Foo::"bar""#.parse().unwrap(),
2607            attrs,
2608            HashSet::new(),
2609            HashSet::new(),
2610            [],
2611            Extensions::none(),
2612        )
2613        .unwrap();
2614        let request = basic_request();
2615        let entities = Entities::from_entities(
2616            [entity],
2617            None::<&NoEntitiesSchema>,
2618            TCComputation::ComputeNow,
2619            Extensions::none(),
2620        )
2621        .unwrap();
2622        let eval = Evaluator::new(request, &entities, Extensions::none());
2623        let result = eval.interpret_inline_policy(&expr).unwrap_err();
2624        // These are arbitrarily determined by BTreeMap ordering, but are deterministic
2625        let expected_keys = ["1", "2", "3", "4", "5"]
2626            .into_iter()
2627            .map(|x| x.into())
2628            .collect::<Vec<SmolStr>>();
2629        let expected = EvaluationError::entity_attr_does_not_exist(
2630            Arc::new(r#"Foo::"bar""#.parse().unwrap()),
2631            "foo".into(),
2632            expected_keys.iter(),
2633            false,
2634            7,
2635            None,
2636        );
2637        assert_eq!(result, expected);
2638    }
2639
2640    #[test]
2641    fn large_record_err() {
2642        let expr = Expr::get_attr(
2643            Expr::record((1..=7).map(|id| (format!("{id}").into(), Expr::val(true)))).unwrap(),
2644            "foo".into(),
2645        );
2646        let request = basic_request();
2647        let entities = rich_entities();
2648        let eval = Evaluator::new(request, &entities, Extensions::none());
2649        let result = eval.interpret_inline_policy(&expr).unwrap_err();
2650        let first_five = (1..=5)
2651            .map(|id| format!("{id}").into())
2652            .collect::<Vec<SmolStr>>();
2653        let expected =
2654            EvaluationError::record_attr_does_not_exist("foo".into(), first_five.iter(), 7, None);
2655        assert_eq!(result, expected);
2656    }
2657
2658    #[test]
2659    fn interpret_nots() {
2660        let request = basic_request();
2661        let entities = basic_entities();
2662        let eval = Evaluator::new(request, &entities, Extensions::none());
2663        // not(true)
2664        assert_eq!(
2665            eval.interpret_inline_policy(&Expr::not(Expr::val(true))),
2666            Ok(Value::from(false))
2667        );
2668        // not(false)
2669        assert_eq!(
2670            eval.interpret_inline_policy(&Expr::not(Expr::val(false))),
2671            Ok(Value::from(true))
2672        );
2673        // not(8)
2674        assert_matches!(
2675            eval.interpret_inline_policy(&Expr::not(Expr::val(8))),
2676            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2677                assert_eq!(expected, nonempty![Type::Bool]);
2678                assert_eq!(actual, Type::Long);
2679                assert_eq!(advice, None);
2680            }
2681        );
2682        // not(action)
2683        assert_matches!(
2684            eval.interpret_inline_policy(&Expr::not(Expr::var(Var::Action))),
2685            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2686                assert_eq!(expected, nonempty![Type::Bool]);
2687                assert_eq!(actual, Type::Entity {
2688                    ty: EntityUID::test_entity_type(),
2689                });
2690                assert_eq!(advice, None);
2691            }
2692        );
2693        // not(not(true))
2694        assert_eq!(
2695            eval.interpret_inline_policy(&Expr::not(Expr::not(Expr::val(true)))),
2696            Ok(Value::from(true))
2697        );
2698        // not(if true then false else true)
2699        assert_eq!(
2700            eval.interpret_inline_policy(&Expr::not(Expr::ite(
2701                Expr::val(true),
2702                Expr::val(false),
2703                Expr::val(true)
2704            ))),
2705            Ok(Value::from(true))
2706        );
2707        // if not(true) then "hello" else "goodbye"
2708        assert_eq!(
2709            eval.interpret_inline_policy(&Expr::ite(
2710                Expr::not(Expr::val(true)),
2711                Expr::val("hello"),
2712                Expr::val("goodbye")
2713            )),
2714            Ok(Value::from("goodbye"))
2715        );
2716    }
2717
2718    #[test]
2719    fn interpret_negs() {
2720        let request = basic_request();
2721        let entities = basic_entities();
2722        let eval = Evaluator::new(request, &entities, Extensions::none());
2723        // neg(101)
2724        assert_eq!(
2725            eval.interpret_inline_policy(&Expr::neg(Expr::val(101))),
2726            Ok(Value::from(-101))
2727        );
2728        // neg(-101)
2729        assert_eq!(
2730            eval.interpret_inline_policy(&Expr::neg(Expr::val(-101))),
2731            Ok(Value::from(101))
2732        );
2733        // neg(0)
2734        assert_eq!(
2735            eval.interpret_inline_policy(&Expr::neg(Expr::val(0))),
2736            Ok(Value::from(0))
2737        );
2738        // neg(neg(7))
2739        assert_eq!(
2740            eval.interpret_inline_policy(&Expr::neg(Expr::neg(Expr::val(7)))),
2741            Ok(Value::from(7))
2742        );
2743        // if true then neg(8) else neg(1)
2744        assert_eq!(
2745            eval.interpret_inline_policy(&Expr::ite(
2746                Expr::val(true),
2747                Expr::neg(Expr::val(8)),
2748                Expr::neg(Expr::val(1))
2749            )),
2750            Ok(Value::from(-8))
2751        );
2752        // overflow
2753        assert_eq!(
2754            eval.interpret_inline_policy(&Expr::neg(Expr::val(Integer::MIN))),
2755            Err(IntegerOverflowError::UnaryOp(UnaryOpOverflowError {
2756                op: UnaryOp::Neg,
2757                arg: Value::from(Integer::MIN),
2758                source_loc: None,
2759            })
2760            .into()),
2761        );
2762        // neg(false)
2763        assert_matches!(
2764            eval.interpret_inline_policy(&Expr::neg(Expr::val(false))),
2765            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2766                assert_eq!(expected, nonempty![Type::Long]);
2767                assert_eq!(actual, Type::Bool);
2768                assert_eq!(advice, None);
2769            }
2770        );
2771        // neg([1, 2, 3])
2772        assert_matches!(
2773            eval.interpret_inline_policy(&Expr::neg(Expr::set([
2774                Expr::val(1),
2775                Expr::val(2),
2776                Expr::val(3)
2777            ]))),
2778            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
2779                assert_eq!(expected, nonempty![Type::Long]);
2780                assert_eq!(actual, Type::Set);
2781                assert_eq!(advice, None);
2782            }
2783        );
2784    }
2785
2786    #[test]
2787    fn interpret_eqs() {
2788        let request = basic_request();
2789        let entities = basic_entities();
2790        let eval = Evaluator::new(request, &entities, Extensions::none());
2791        // eq(33, 33)
2792        assert_eq!(
2793            eval.interpret_inline_policy(&Expr::is_eq(Expr::val(33), Expr::val(33))),
2794            Ok(Value::from(true))
2795        );
2796        // eq(33, -12)
2797        assert_eq!(
2798            eval.interpret_inline_policy(&Expr::is_eq(Expr::val(33), Expr::val(-12))),
2799            Ok(Value::from(false))
2800        );
2801        // if eq("foo", "foo") then 12 else 97
2802        assert_eq!(
2803            eval.interpret_inline_policy(&Expr::ite(
2804                Expr::is_eq(Expr::val("foo"), Expr::val("foo")),
2805                Expr::val(12),
2806                Expr::val(97),
2807            )),
2808            Ok(Value::from(12))
2809        );
2810        // if eq([1, -33, 707], [1, -33]) then 12 else 97
2811        assert_eq!(
2812            eval.interpret_inline_policy(&Expr::ite(
2813                Expr::is_eq(
2814                    Expr::set(vec![Expr::val(1), Expr::val(-33), Expr::val(707)]),
2815                    Expr::set(vec![Expr::val(1), Expr::val(-33)])
2816                ),
2817                Expr::val(12),
2818                Expr::val(97),
2819            )),
2820            Ok(Value::from(97))
2821        );
2822        // eq(2>0, 0>(-2))
2823        assert_eq!(
2824            eval.interpret_inline_policy(&Expr::is_eq(
2825                Expr::greater(Expr::val(2), Expr::val(0)),
2826                Expr::greater(Expr::val(0), Expr::val(-2))
2827            )),
2828            Ok(Value::from(true))
2829        );
2830        // eq(12+33, 50-5)
2831        assert_eq!(
2832            eval.interpret_inline_policy(&Expr::is_eq(
2833                Expr::add(Expr::val(12), Expr::val(33)),
2834                Expr::sub(Expr::val(50), Expr::val(5)),
2835            )),
2836            Ok(Value::from(true))
2837        );
2838        // eq([1, 2, 40], [1, 2, 40])
2839        assert_eq!(
2840            eval.interpret_inline_policy(&Expr::is_eq(
2841                Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)]),
2842                Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)])
2843            )),
2844            Ok(Value::from(true))
2845        );
2846        // eq([1, 2, 40], [1, 40, 2])
2847        assert_eq!(
2848            eval.interpret_inline_policy(&Expr::is_eq(
2849                Expr::set(vec![Expr::val(1), Expr::val(2), Expr::val(40)]),
2850                Expr::set(vec![Expr::val(1), Expr::val(40), Expr::val(2)])
2851            )),
2852            Ok(Value::from(true))
2853        );
2854        // eq([1, -2, 40], [1, 40])
2855        assert_eq!(
2856            eval.interpret_inline_policy(&Expr::is_eq(
2857                Expr::set(vec![Expr::val(1), Expr::val(-2), Expr::val(40)]),
2858                Expr::set(vec![Expr::val(1), Expr::val(40)])
2859            )),
2860            Ok(Value::from(false))
2861        );
2862        // eq([1, 1, 1, 2, 40], [40, 1, 2])
2863        assert_eq!(
2864            eval.interpret_inline_policy(&Expr::is_eq(
2865                Expr::set(vec![
2866                    Expr::val(1),
2867                    Expr::val(1),
2868                    Expr::val(1),
2869                    Expr::val(2),
2870                    Expr::val(40)
2871                ]),
2872                Expr::set(vec![Expr::val(40), Expr::val(1), Expr::val(2)])
2873            )),
2874            Ok(Value::from(true))
2875        );
2876        // eq([1, 1, 2, 1, 40, 2, 1, 2, 40, 1], [1, 40, 1, 2])
2877        assert_eq!(
2878            eval.interpret_inline_policy(&Expr::is_eq(
2879                Expr::set(vec![
2880                    Expr::val(1),
2881                    Expr::val(1),
2882                    Expr::val(2),
2883                    Expr::val(1),
2884                    Expr::val(40),
2885                    Expr::val(2),
2886                    Expr::val(1),
2887                    Expr::val(2),
2888                    Expr::val(40),
2889                    Expr::val(1)
2890                ]),
2891                Expr::set(vec![
2892                    Expr::val(1),
2893                    Expr::val(40),
2894                    Expr::val(1),
2895                    Expr::val(2)
2896                ])
2897            )),
2898            Ok(Value::from(true))
2899        );
2900        // eq(context.device_properties, { appropriate record literal })
2901        assert_eq!(
2902            eval.interpret_inline_policy(&Expr::is_eq(
2903                Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2904                Expr::record(vec![
2905                    ("os_name".into(), Expr::val("Windows")),
2906                    ("manufacturer".into(), Expr::val("ACME Corp")),
2907                ])
2908                .unwrap()
2909            )),
2910            Ok(Value::from(true))
2911        );
2912        // eq(context.device_properties, { record literal missing one field })
2913        assert_eq!(
2914            eval.interpret_inline_policy(&Expr::is_eq(
2915                Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2916                Expr::record(vec![("os_name".into(), Expr::val("Windows"))]).unwrap()
2917            )),
2918            Ok(Value::from(false))
2919        );
2920        // eq(context.device_properties, { record literal with an extra field })
2921        assert_eq!(
2922            eval.interpret_inline_policy(&Expr::is_eq(
2923                Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2924                Expr::record(vec![
2925                    ("os_name".into(), Expr::val("Windows")),
2926                    ("manufacturer".into(), Expr::val("ACME Corp")),
2927                    ("extrafield".into(), Expr::val(true)),
2928                ])
2929                .unwrap()
2930            )),
2931            Ok(Value::from(false))
2932        );
2933        // eq(context.device_properties, { record literal with the same keys/values })
2934        assert_eq!(
2935            eval.interpret_inline_policy(&Expr::is_eq(
2936                Expr::get_attr(Expr::var(Var::Context), "device_properties".into()),
2937                Expr::record(vec![
2938                    ("os_name".into(), Expr::val("Windows")),
2939                    ("manufacturer".into(), Expr::val("ACME Corp")),
2940                ])
2941                .unwrap()
2942            )),
2943            Ok(Value::from(true))
2944        );
2945        // eq(A, A) where A is an Entity
2946        assert_eq!(
2947            eval.interpret_inline_policy(&Expr::is_eq(
2948                Expr::val(EntityUID::with_eid("foo")),
2949                Expr::val(EntityUID::with_eid("foo")),
2950            )),
2951            Ok(Value::from(true))
2952        );
2953        // eq(A, A) where A is an Entity that doesn't exist
2954        assert_eq!(
2955            eval.interpret_inline_policy(&Expr::is_eq(
2956                Expr::val(EntityUID::with_eid("doesnotexist")),
2957                Expr::val(EntityUID::with_eid("doesnotexist")),
2958            )),
2959            Ok(Value::from(true))
2960        );
2961        // eq(A, B) where A and B are entities of the same type
2962        assert_eq!(
2963            eval.interpret_inline_policy(&Expr::is_eq(
2964                Expr::val(EntityUID::with_eid("foo")),
2965                Expr::val(EntityUID::with_eid("bar")),
2966            )),
2967            Ok(Value::from(false))
2968        );
2969        // eq(A, B) where A and B are entities of different types
2970        assert_eq!(
2971            eval.interpret_inline_policy(&Expr::is_eq(
2972                Expr::val(
2973                    EntityUID::with_eid_and_type("type1", "foo")
2974                        .expect("should be a valid identifier")
2975                ),
2976                Expr::val(
2977                    EntityUID::with_eid_and_type("type2", "bar")
2978                        .expect("should be a valid identifier")
2979                ),
2980            )),
2981            Ok(Value::from(false))
2982        );
2983        // eq(A, B) where A and B are entities of different types but happen to
2984        // have the same name
2985        assert_eq!(
2986            eval.interpret_inline_policy(&Expr::is_eq(
2987                Expr::val(
2988                    EntityUID::with_eid_and_type("type1", "foo")
2989                        .expect("should be a valid identifier")
2990                ),
2991                Expr::val(
2992                    EntityUID::with_eid_and_type("type2", "foo")
2993                        .expect("should be a valid identifier")
2994                ),
2995            )),
2996            Ok(Value::from(false))
2997        );
2998        // eq(A, B) where A exists but B does not
2999        assert_eq!(
3000            eval.interpret_inline_policy(&Expr::is_eq(
3001                Expr::val(EntityUID::with_eid("foo")),
3002                Expr::val(EntityUID::with_eid("doesnotexist")),
3003            )),
3004            Ok(Value::from(false))
3005        );
3006        // eq("foo", <entity foo>)
3007        assert_eq!(
3008            eval.interpret_inline_policy(&Expr::is_eq(
3009                Expr::val("foo"),
3010                Expr::val(EntityUID::with_eid("foo"))
3011            )),
3012            Ok(Value::from(false))
3013        );
3014    }
3015
3016    #[test]
3017    fn interpret_compares() {
3018        let request = basic_request();
3019        let entities = basic_entities();
3020        let extensions = Extensions::all_available();
3021        let eval = Evaluator::new(request, &entities, extensions);
3022        let expected_types = valid_comparison_op_types(extensions);
3023        let assert_type_error = |expr, actual_type| {
3024            assert_matches!(
3025                eval.interpret_inline_policy(&expr),
3026                Err(EvaluationError::TypeError(TypeError { expected, actual, .. })) => {
3027                    assert_eq!(expected, expected_types.clone());
3028                    assert_eq!(actual, actual_type);
3029                }
3030            );
3031        };
3032        // 3 < 303
3033        assert_eq!(
3034            eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(303))),
3035            Ok(Value::from(true))
3036        );
3037        // 3 < -303
3038        assert_eq!(
3039            eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(-303))),
3040            Ok(Value::from(false))
3041        );
3042        // -303 < -1
3043        assert_eq!(
3044            eval.interpret_inline_policy(&Expr::less(Expr::val(-303), Expr::val(-1))),
3045            Ok(Value::from(true))
3046        );
3047        // 3 < 3
3048        assert_eq!(
3049            eval.interpret_inline_policy(&Expr::less(Expr::val(3), Expr::val(3))),
3050            Ok(Value::from(false))
3051        );
3052        // -33 <= 0
3053        assert_eq!(
3054            eval.interpret_inline_policy(&Expr::lesseq(Expr::val(-33), Expr::val(0))),
3055            Ok(Value::from(true))
3056        );
3057        // 3 <= 3
3058        assert_eq!(
3059            eval.interpret_inline_policy(&Expr::lesseq(Expr::val(3), Expr::val(3))),
3060            Ok(Value::from(true))
3061        );
3062        // 7 > 3
3063        assert_eq!(
3064            eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(3))),
3065            Ok(Value::from(true))
3066        );
3067        // 7 > -3
3068        assert_eq!(
3069            eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(-3))),
3070            Ok(Value::from(true))
3071        );
3072        // 7 > 7
3073        assert_eq!(
3074            eval.interpret_inline_policy(&Expr::greater(Expr::val(7), Expr::val(7))),
3075            Ok(Value::from(false))
3076        );
3077        // 0 >= -7
3078        assert_eq!(
3079            eval.interpret_inline_policy(&Expr::greatereq(Expr::val(0), Expr::val(-7))),
3080            Ok(Value::from(true))
3081        );
3082        // -1 >= 7
3083        assert_eq!(
3084            eval.interpret_inline_policy(&Expr::greatereq(Expr::val(-1), Expr::val(7))),
3085            Ok(Value::from(false))
3086        );
3087        // 7 >= 7
3088        assert_eq!(
3089            eval.interpret_inline_policy(&Expr::greatereq(Expr::val(7), Expr::val(7))),
3090            Ok(Value::from(true))
3091        );
3092        // false < true
3093        assert_type_error(Expr::less(Expr::val(false), Expr::val(true)), Type::Bool);
3094
3095        // false < false
3096        assert_type_error(Expr::less(Expr::val(false), Expr::val(false)), Type::Bool);
3097
3098        // true <= false
3099        assert_type_error(Expr::lesseq(Expr::val(true), Expr::val(false)), Type::Bool);
3100
3101        // false <= false
3102        assert_type_error(Expr::lesseq(Expr::val(false), Expr::val(false)), Type::Bool);
3103
3104        // false > true
3105        assert_type_error(Expr::greater(Expr::val(false), Expr::val(true)), Type::Bool);
3106
3107        // true > true
3108        assert_type_error(Expr::greater(Expr::val(true), Expr::val(true)), Type::Bool);
3109
3110        // true >= false
3111        assert_type_error(
3112            Expr::greatereq(Expr::val(true), Expr::val(false)),
3113            Type::Bool,
3114        );
3115
3116        // true >= true
3117        assert_type_error(
3118            Expr::greatereq(Expr::val(true), Expr::val(true)),
3119            Type::Bool,
3120        );
3121
3122        // bc < zzz
3123        assert_type_error(Expr::less(Expr::val("bc"), Expr::val("zzz")), Type::String);
3124        // banana < zzz
3125        assert_type_error(
3126            Expr::less(Expr::val("banana"), Expr::val("zzz")),
3127            Type::String,
3128        );
3129        // "" < zzz
3130        assert_type_error(Expr::less(Expr::val(""), Expr::val("zzz")), Type::String);
3131        // a < 1
3132        assert_type_error(Expr::less(Expr::val("a"), Expr::val("1")), Type::String);
3133        // a < A
3134        assert_type_error(Expr::less(Expr::val("a"), Expr::val("A")), Type::String);
3135        // A < A
3136        assert_type_error(Expr::less(Expr::val("A"), Expr::val("A")), Type::String);
3137        // zebra < zebras
3138        assert_type_error(
3139            Expr::less(Expr::val("zebra"), Expr::val("zebras")),
3140            Type::String,
3141        );
3142        // zebra <= zebras
3143        assert_type_error(
3144            Expr::lesseq(Expr::val("zebra"), Expr::val("zebras")),
3145            Type::String,
3146        );
3147        // zebras <= zebras
3148        assert_type_error(
3149            Expr::lesseq(Expr::val("zebras"), Expr::val("zebras")),
3150            Type::String,
3151        );
3152        // zebras <= Zebras
3153        assert_type_error(
3154            Expr::lesseq(Expr::val("zebras"), Expr::val("Zebras")),
3155            Type::String,
3156        );
3157        // 123 > 78
3158        assert_type_error(
3159            Expr::greater(Expr::val("123"), Expr::val("78")),
3160            Type::String,
3161        );
3162        // <space>zebras >= zebras
3163        assert_type_error(
3164            Expr::greatereq(Expr::val(" zebras"), Expr::val("zebras")),
3165            Type::String,
3166        );
3167        // "" >= ""
3168        assert_type_error(Expr::greatereq(Expr::val(""), Expr::val("")), Type::String);
3169        // "" >= _hi
3170        assert_type_error(
3171            Expr::greatereq(Expr::val(""), Expr::val("_hi")),
3172            Type::String,
3173        );
3174        // 🦀 >= _hi
3175        assert_type_error(
3176            Expr::greatereq(Expr::val("🦀"), Expr::val("_hi")),
3177            Type::String,
3178        );
3179        // 2 < "4"
3180        assert_matches!(
3181            eval.interpret_inline_policy(&Expr::less(Expr::val(2), Expr::val("4"))),
3182            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3183                assert_eq!(expected, nonempty![Type::Long]);
3184                assert_eq!(actual, Type::String);
3185                assert_eq!(advice, None);
3186            }
3187        );
3188        // "4" < 2
3189        assert_matches!(
3190            eval.interpret_inline_policy(&Expr::less(Expr::val("4"), Expr::val(2))),
3191            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3192                assert_eq!(expected, nonempty![Type::Long]);
3193                assert_eq!(actual, Type::String);
3194                assert_eq!(advice, None);
3195            }
3196        );
3197        // false < 1
3198        assert_matches!(
3199            eval.interpret_inline_policy(&Expr::less(Expr::val(false), Expr::val(1))),
3200            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3201                assert_eq!(expected, nonempty![Type::Long]);
3202                assert_eq!(actual, Type::Bool);
3203                assert_eq!(advice, None);
3204            }
3205        );
3206        // 1 < false
3207        assert_matches!(
3208            eval.interpret_inline_policy(&Expr::less(Expr::val(1), Expr::val(false))),
3209            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3210                assert_eq!(expected, nonempty![Type::Long]);
3211                assert_eq!(actual, Type::Bool);
3212                assert_eq!(advice, None);
3213            }
3214        );
3215        // [1, 2] < [47, 0]
3216        assert_type_error(
3217            Expr::less(
3218                Expr::set(vec![Expr::val(1), Expr::val(2)]),
3219                Expr::set(vec![Expr::val(47), Expr::val(0)]),
3220            ),
3221            Type::Set,
3222        );
3223    }
3224
3225    #[test]
3226    fn interpret_datetime_extension_compares() {
3227        let request = basic_request();
3228        let entities = basic_entities();
3229        let extensions = Extensions::all_available();
3230        let eval = Evaluator::new(request, &entities, extensions);
3231        let datetime_constructor: Name = "datetime".parse().unwrap();
3232        let duration_constructor: Name = "duration".parse().unwrap();
3233        assert_matches!(eval.interpret_inline_policy(
3234            &Expr::less(
3235                Expr::call_extension_fn(
3236                    datetime_constructor.clone(),
3237                    vec![Value::from("2024-01-01").into()]),
3238                Expr::call_extension_fn(
3239                    datetime_constructor.clone(),
3240                    vec![Value::from("2024-01-23").into()]))),
3241            Ok(v) if v == Value::from(true));
3242        assert_matches!(eval.interpret_inline_policy(
3243            &Expr::lesseq(
3244                Expr::call_extension_fn(
3245                    datetime_constructor.clone(),
3246                    vec![Value::from("2024-01-01").into()]),
3247                Expr::call_extension_fn(
3248                    datetime_constructor.clone(),
3249                    vec![Value::from("2024-01-23").into()]))),
3250            Ok(v) if v == Value::from(true));
3251        assert_matches!(eval.interpret_inline_policy(
3252            &Expr::less(
3253                Expr::call_extension_fn(
3254                    datetime_constructor.clone(),
3255                    vec![Value::from("2024-01-01T01:02:03Z").into()]),
3256                Expr::call_extension_fn(
3257                    datetime_constructor.clone(),
3258                    vec![Value::from("2023-01-23").into()]))),
3259            Ok(v) if v == Value::from(false));
3260        assert_matches!(eval.interpret_inline_policy(
3261            &Expr::lesseq(
3262                Expr::call_extension_fn(
3263                    datetime_constructor.clone(),
3264                    vec![Value::from("2024-01-01T01:02:03Z").into()]),
3265                Expr::call_extension_fn(
3266                    datetime_constructor.clone(),
3267                    vec![Value::from("2023-01-23").into()]))),
3268            Ok(v) if v == Value::from(false));
3269        assert_matches!(eval.interpret_inline_policy(
3270            &Expr::less(
3271                Expr::call_extension_fn(
3272                    duration_constructor.clone(),
3273                    vec![Value::from("5s").into()]),
3274                Expr::call_extension_fn(
3275                    duration_constructor.clone(),
3276                    vec![Value::from("2m").into()]))),
3277            Ok(v) if v == Value::from(true));
3278        assert_matches!(eval.interpret_inline_policy(
3279            &Expr::lesseq(
3280                Expr::call_extension_fn(
3281                    duration_constructor.clone(),
3282                    vec![Value::from("1h").into()]),
3283                Expr::call_extension_fn(
3284                    duration_constructor.clone(),
3285                    vec![Value::from("2h").into()]))),
3286            Ok(v) if v == Value::from(true));
3287        assert_matches!(eval.interpret_inline_policy(
3288            &Expr::less(
3289                Expr::call_extension_fn(
3290                    duration_constructor.clone(),
3291                    vec![Value::from("3h2m").into()]),
3292                Expr::call_extension_fn(
3293                    duration_constructor.clone(),
3294                    vec![Value::from("2h").into()]))),
3295            Ok(v) if v == Value::from(false));
3296        assert_matches!(eval.interpret_inline_policy(
3297            &Expr::lesseq(
3298                Expr::call_extension_fn(
3299                    duration_constructor.clone(),
3300                    vec![Value::from("3h2m").into()]),
3301                Expr::call_extension_fn(
3302                    duration_constructor.clone(),
3303                    vec![Value::from("2h").into()]))),
3304            Ok(v) if v == Value::from(false));
3305
3306        // datetimes that are different times on the same day
3307        assert_matches!(eval.interpret_inline_policy(
3308            &Expr::noteq(
3309                Expr::call_extension_fn(
3310                    datetime_constructor.clone(),
3311                    vec![Value::from("2024-11-07").into()]),
3312                Expr::call_extension_fn(
3313                    datetime_constructor.clone(),
3314                    vec![Value::from("2024-11-07T14:00:00Z").into()]))),
3315            Ok(v) if v == Value::from(true));
3316        assert_matches!(eval.interpret_inline_policy(
3317            &Expr::noteq(
3318                Expr::call_extension_fn(
3319                    datetime_constructor.clone(),
3320                    vec![Value::from("2024-11-07T14:00:00.123Z").into()]),
3321                Expr::call_extension_fn(
3322                    datetime_constructor.clone(),
3323                    vec![Value::from("2024-11-07T14:00:00Z").into()]))),
3324            Ok(v) if v == Value::from(true));
3325        assert_matches!(eval.interpret_inline_policy(
3326            &Expr::noteq(
3327                Expr::call_extension_fn(
3328                    datetime_constructor.clone(),
3329                    vec![Value::from("2024-11-07T14:00:00Z").into()]),
3330                Expr::call_extension_fn(
3331                    datetime_constructor.clone(),
3332                    vec![Value::from("2024-11-07T17:00:00Z").into()]))),
3333            Ok(v) if v == Value::from(true));
3334
3335        // datetimes that use the UTC offset
3336        // both datetimes are UTC 2024-11-07T12:00:00Z
3337        assert_matches!(eval.interpret_inline_policy(
3338            &Expr::noteq(
3339                Expr::call_extension_fn(
3340                    datetime_constructor.clone(),
3341                    vec![Value::from("2024-11-07T14:00:00+0200").into()]),
3342                Expr::call_extension_fn(
3343                    datetime_constructor.clone(),
3344                    vec![Value::from("2024-11-07T11:00:00-0100").into()]))),
3345            Ok(v) if v == Value::from(false));
3346        // both datetimes are UTC 2024-11-08
3347        assert_matches!(eval.interpret_inline_policy(
3348            &Expr::noteq(
3349                Expr::call_extension_fn(
3350                    datetime_constructor.clone(),
3351                    vec![Value::from("2024-11-08T02:00:00+0200").into()]),
3352                Expr::call_extension_fn(
3353                    datetime_constructor.clone(),
3354                    vec![Value::from("2024-11-07T23:00:00-0100").into()]))),
3355            Ok(v) if v == Value::from(false));
3356
3357        // feb 28 < feb 29 < mar 1 for a leap year
3358        assert_matches!(eval.interpret_inline_policy(
3359            &Expr::less(
3360                Expr::call_extension_fn(
3361                    datetime_constructor.clone(),
3362                    vec![Value::from("2024-02-28").into()]),
3363                Expr::call_extension_fn(
3364                    datetime_constructor.clone(),
3365                    vec![Value::from("2024-02-29").into()]))),
3366            Ok(v) if v == Value::from(true));
3367        assert_matches!(eval.interpret_inline_policy(
3368            &Expr::less(
3369                Expr::call_extension_fn(
3370                    datetime_constructor.clone(),
3371                    vec![Value::from("2024-02-29").into()]),
3372                Expr::call_extension_fn(
3373                    datetime_constructor.clone(),
3374                    vec![Value::from("2024-03-01").into()]))),
3375            Ok(v) if v == Value::from(true));
3376
3377        // type error favors long and then extension types with operator overloading
3378        assert_matches!(eval.interpret_inline_policy(
3379        &Expr::lesseq(
3380            Value::from(1).into(),
3381            Expr::call_extension_fn(
3382                duration_constructor.clone(),
3383                vec![Value::from("2h").into()]))),
3384        Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3385                assert_eq!(expected, nonempty![Type::Long]);
3386                assert_eq!(actual, Type::Extension { name: duration_constructor.clone() });
3387                assert_eq!(advice, None);
3388        });
3389
3390        assert_matches!(eval.interpret_inline_policy(
3391            &Expr::lesseq(
3392                Expr::call_extension_fn(
3393                    duration_constructor.clone(),
3394                    vec![Value::from("2h").into()]),
3395                Value::from(1).into())),
3396            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3397                assert_eq!(expected, nonempty![Type::Long]);
3398                assert_eq!(actual, Type::Extension { name: duration_constructor.clone() });
3399                assert_eq!(advice, None);
3400        });
3401
3402        assert_matches!(eval.interpret_inline_policy(
3403        &Expr::lesseq(
3404            Expr::call_extension_fn(
3405                duration_constructor.clone(),
3406                vec![Value::from("2h").into()]),
3407            Expr::call_extension_fn(
3408                "decimal".parse().unwrap(),
3409                vec![Value::from("2.0").into()]))),
3410        Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3411                assert_eq!(expected, nonempty![Type::Extension { name: duration_constructor.clone() }]);
3412                assert_eq!(actual, Type::Extension { name: "decimal".parse().unwrap() });
3413                assert_eq!(advice, None);
3414        });
3415
3416        assert_matches!(eval.interpret_inline_policy(
3417            &Expr::lesseq(
3418                Expr::call_extension_fn(
3419                    "decimal".parse().unwrap(),
3420                    vec![Value::from("2.0").into()]),
3421                Expr::call_extension_fn(
3422                    duration_constructor.clone(),
3423                    vec![Value::from("2h").into()]))),
3424            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3425                assert_eq!(expected, nonempty![Type::Extension { name: duration_constructor.clone() }]);
3426                assert_eq!(actual, Type::Extension { name: "decimal".parse().unwrap() });
3427                assert_eq!(advice, None);
3428        });
3429
3430        // if both sides support overloading, favor lhs
3431        assert_matches!(eval.interpret_inline_policy(
3432            &Expr::lesseq(
3433                Expr::call_extension_fn(
3434                    datetime_constructor.clone(),
3435                    vec![Value::from("2023-01-23").into()]),
3436                Expr::call_extension_fn(
3437                    duration_constructor.clone(),
3438                    vec![Value::from("2h").into()]))),
3439            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3440                assert_eq!(expected, nonempty![Type::Extension { name: datetime_constructor }]);
3441                assert_eq!(actual, Type::Extension { name: duration_constructor });
3442                assert_eq!(advice, None);
3443        });
3444
3445        // if both sides are of the same extension type without any operator overloading, remind users those that have
3446        assert_matches!(eval.interpret_inline_policy(
3447            &Expr::lesseq(
3448                Expr::call_extension_fn(
3449                    "decimal".parse().unwrap(),
3450                    vec![Value::from("2.0").into()]),
3451                Expr::call_extension_fn(
3452                    "decimal".parse().unwrap(),
3453                    vec![Value::from("3.0").into()]))),
3454            Err(EvaluationError::TypeError(TypeError { expected, actual, .. })) => {
3455                assert_eq!(expected, valid_comparison_op_types(extensions));
3456                assert_eq!(actual, Type::Extension { name: "decimal".parse().unwrap() });
3457        });
3458    }
3459
3460    #[test]
3461    fn interpret_comparison_err_order() {
3462        // Expressions are evaluated left to right, so the unexpected-string
3463        // type error should be reported for all of the following. This tests a
3464        // fix for incorrect evaluation order in `>` and `>=`.
3465        let request = basic_request();
3466        let entities = basic_entities();
3467        let eval = Evaluator::new(request, &entities, Extensions::none());
3468
3469        assert_matches!(
3470            eval.interpret_inline_policy(&Expr::greatereq(
3471                Expr::add(Expr::val("a"), Expr::val("b")),
3472                Expr::add(Expr::val(false), Expr::val(true))
3473            )),
3474            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3475                assert_eq!(expected, nonempty![Type::Long]);
3476                assert_eq!(actual, Type::String);
3477                assert_eq!(advice, None);
3478            }
3479        );
3480
3481        assert_matches!(
3482            eval.interpret_inline_policy(&Expr::greater(
3483                Expr::add(Expr::val("a"), Expr::val("b")),
3484                Expr::add(Expr::val(false), Expr::val(true))
3485            )),
3486            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3487                assert_eq!(expected, nonempty![Type::Long]);
3488                assert_eq!(actual, Type::String);
3489                assert_eq!(advice, None);
3490            }
3491        );
3492
3493        assert_matches!(
3494            eval.interpret_inline_policy(&Expr::lesseq(
3495                Expr::add(Expr::val("a"), Expr::val("b")),
3496                Expr::add(Expr::val(false), Expr::val(true))
3497            )),
3498            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3499                assert_eq!(expected, nonempty![Type::Long]);
3500                assert_eq!(actual, Type::String);
3501                assert_eq!(advice, None);
3502            }
3503        );
3504
3505        assert_matches!(
3506            eval.interpret_inline_policy(&Expr::less(
3507                Expr::add(Expr::val("a"), Expr::val("b")),
3508                Expr::add(Expr::val(false), Expr::val(true))
3509            )),
3510            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3511                assert_eq!(expected, nonempty![Type::Long]);
3512                assert_eq!(actual, Type::String);
3513                assert_eq!(advice, None);
3514            }
3515        );
3516    }
3517
3518    #[test]
3519    fn interpret_arithmetic() {
3520        let request = basic_request();
3521        let entities = basic_entities();
3522        let eval = Evaluator::new(request, &entities, Extensions::none());
3523        // 11 + 22
3524        assert_eq!(
3525            eval.interpret_inline_policy(&Expr::add(Expr::val(11), Expr::val(22))),
3526            Ok(Value::from(33))
3527        );
3528        // 11 + 0
3529        assert_eq!(
3530            eval.interpret_inline_policy(&Expr::add(Expr::val(11), Expr::val(0))),
3531            Ok(Value::from(11))
3532        );
3533        // -1 + 1
3534        assert_eq!(
3535            eval.interpret_inline_policy(&Expr::add(Expr::val(-1), Expr::val(1))),
3536            Ok(Value::from(0))
3537        );
3538        // overflow
3539        assert_eq!(
3540            eval.interpret_inline_policy(&Expr::add(Expr::val(Integer::MAX), Expr::val(1))),
3541            Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
3542                op: BinaryOp::Add,
3543                arg1: Value::from(Integer::MAX),
3544                arg2: Value::from(1),
3545                source_loc: None,
3546            })
3547            .into())
3548        );
3549        // 7 + "3"
3550        assert_matches!(
3551            eval.interpret_inline_policy(&Expr::add(Expr::val(7), Expr::val("3"))),
3552            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3553                assert_eq!(expected, nonempty![Type::Long]);
3554                assert_eq!(actual, Type::String);
3555                assert_eq!(advice, None);
3556            }
3557        );
3558        // 44 - 31
3559        assert_eq!(
3560            eval.interpret_inline_policy(&Expr::sub(Expr::val(44), Expr::val(31))),
3561            Ok(Value::from(13))
3562        );
3563        // 5 - (-3)
3564        assert_eq!(
3565            eval.interpret_inline_policy(&Expr::sub(Expr::val(5), Expr::val(-3))),
3566            Ok(Value::from(8))
3567        );
3568        // overflow
3569        assert_eq!(
3570            eval.interpret_inline_policy(&Expr::sub(Expr::val(Integer::MIN + 2), Expr::val(3))),
3571            Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
3572                op: BinaryOp::Sub,
3573                arg1: Value::from(Integer::MIN + 2),
3574                arg2: Value::from(3),
3575                source_loc: None,
3576            })
3577            .into())
3578        );
3579        // "ham" - "ha"
3580        assert_matches!(
3581            eval.interpret_inline_policy(&Expr::sub(Expr::val("ham"), Expr::val("ha"))),
3582            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3583                assert_eq!(expected, nonempty![Type::Long]);
3584                assert_eq!(actual, Type::String);
3585                assert_eq!(advice, None);
3586            }
3587        );
3588        // 5 * (-3)
3589        assert_eq!(
3590            eval.interpret_inline_policy(&Expr::mul(Expr::val(5), Expr::val(-3))),
3591            Ok(Value::from(-15))
3592        );
3593        // 5 * 0
3594        assert_eq!(
3595            eval.interpret_inline_policy(&Expr::mul(Expr::val(5), Expr::val(0))),
3596            Ok(Value::from(0))
3597        );
3598        // "5" * 0
3599        assert_matches!(
3600            eval.interpret_inline_policy(&Expr::mul(Expr::val("5"), Expr::val(0))),
3601            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3602                assert_eq!(expected, nonempty![Type::Long]);
3603                assert_eq!(actual, Type::String);
3604                assert_eq!(advice, None);
3605            }
3606        );
3607        // overflow
3608        assert_eq!(
3609            eval.interpret_inline_policy(&Expr::mul(Expr::val(Integer::MAX - 1), Expr::val(3))),
3610            Err(IntegerOverflowError::BinaryOp(BinaryOpOverflowError {
3611                op: BinaryOp::Mul,
3612                arg1: Value::from(Integer::MAX - 1),
3613                arg2: Value::from(3),
3614                source_loc: None,
3615            })
3616            .into())
3617        );
3618    }
3619
3620    #[test]
3621    fn interpret_set_and_map_membership() {
3622        let request = basic_request();
3623        let entities = rich_entities();
3624        let eval = Evaluator::new(request, &entities, Extensions::none());
3625
3626        // [2, 3, 4] contains 2
3627        assert_eq!(
3628            eval.interpret_inline_policy(&Expr::contains(
3629                Expr::set(vec![Expr::val(2), Expr::val(3), Expr::val(4)]),
3630                Expr::val(2)
3631            )),
3632            Ok(Value::from(true))
3633        );
3634        // [34, 2, -7] contains 2
3635        assert_eq!(
3636            eval.interpret_inline_policy(&Expr::contains(
3637                Expr::set(vec![Expr::val(34), Expr::val(2), Expr::val(-7)]),
3638                Expr::val(2)
3639            )),
3640            Ok(Value::from(true))
3641        );
3642        // [34, 2, -7] contains 3
3643        assert_eq!(
3644            eval.interpret_inline_policy(&Expr::contains(
3645                Expr::set(vec![Expr::val(34), Expr::val(2), Expr::val(-7)]),
3646                Expr::val(3)
3647            )),
3648            Ok(Value::from(false))
3649        );
3650        // [] contains 7
3651        assert_eq!(
3652            eval.interpret_inline_policy(&Expr::contains(Expr::set(vec![]), Expr::val(7))),
3653            Ok(Value::from(false))
3654        );
3655        // ["some", "useful", "tags"] contains "foo"
3656        assert_eq!(
3657            eval.interpret_inline_policy(&Expr::contains(
3658                Expr::set(vec![
3659                    Expr::val("some"),
3660                    Expr::val("useful"),
3661                    Expr::val("tags")
3662                ]),
3663                Expr::val("foo")
3664            )),
3665            Ok(Value::from(false))
3666        );
3667        // ["some", "useful", "tags"] contains "useful"
3668        assert_eq!(
3669            eval.interpret_inline_policy(&Expr::contains(
3670                Expr::set(vec![
3671                    Expr::val("some"),
3672                    Expr::val("useful"),
3673                    Expr::val("tags")
3674                ]),
3675                Expr::val("useful")
3676            )),
3677            Ok(Value::from(true))
3678        );
3679        // [<entity child>, <entity sibling>] contains <entity child>
3680        assert_eq!(
3681            eval.interpret_inline_policy(&Expr::contains(
3682                Expr::set(vec![
3683                    Expr::val(EntityUID::with_eid("child")),
3684                    Expr::val(EntityUID::with_eid("sibling"))
3685                ]),
3686                Expr::val(EntityUID::with_eid("child"))
3687            )),
3688            Ok(Value::from(true))
3689        );
3690        // [<entity parent>, <entity sibling>] contains <entity child>
3691        assert_eq!(
3692            eval.interpret_inline_policy(&Expr::contains(
3693                Expr::set(vec![
3694                    Expr::val(EntityUID::with_eid("parent")),
3695                    Expr::val(EntityUID::with_eid("sibling"))
3696                ]),
3697                Expr::val(EntityUID::with_eid("child"))
3698            )),
3699            Ok(Value::from(false))
3700        );
3701        // ["foo", "bar"] contains 3
3702        assert_eq!(
3703            eval.interpret_inline_policy(&Expr::contains(
3704                Expr::set(vec![Expr::val("foo"), Expr::val("bar")]),
3705                Expr::val(3)
3706            )),
3707            Ok(Value::from(false))
3708        );
3709        // ["foo", "bar"] contains [3]
3710        assert_eq!(
3711            eval.interpret_inline_policy(&Expr::contains(
3712                Expr::set(vec![Expr::val("foo"), Expr::val("bar")]),
3713                Expr::set(vec![Expr::val(3)])
3714            )),
3715            Ok(Value::from(false))
3716        );
3717        // [[7], "eggs", [3]] contains [3]
3718        assert_eq!(
3719            eval.interpret_inline_policy(&Expr::contains(
3720                Expr::set(vec![
3721                    Expr::set(vec![Expr::val(7)]),
3722                    Expr::val("eggs"),
3723                    Expr::set(vec![Expr::val(3)])
3724                ]),
3725                Expr::set(vec![Expr::val(3)])
3726            )),
3727            Ok(Value::from(true))
3728        );
3729
3730        // ["2", 20, true, <entity foo>] contains 2
3731        assert_eq!(
3732            eval.interpret_inline_policy(&Expr::contains(
3733                Expr::set(vec![
3734                    Expr::val("2"),
3735                    Expr::val(20),
3736                    Expr::val(true),
3737                    Expr::val(EntityUID::with_eid("foo")),
3738                ]),
3739                Expr::val(2)
3740            )),
3741            Ok(Value::from(false))
3742        );
3743        // ["ham", entity_with_attrs.address.town, -1] contains "barmstadt"
3744        assert_eq!(
3745            eval.interpret_inline_policy(&Expr::contains(
3746                Expr::set(vec![
3747                    Expr::val("ham"),
3748                    Expr::get_attr(
3749                        Expr::get_attr(
3750                            Expr::val(EntityUID::with_eid("entity_with_attrs")),
3751                            "address".into()
3752                        ),
3753                        "town".into()
3754                    ),
3755                    Expr::val(-1),
3756                ]),
3757                Expr::val("barmstadt")
3758            )),
3759            Ok(Value::from(true))
3760        );
3761        // 3 contains 7
3762        assert_matches!(
3763            eval.interpret_inline_policy(&Expr::contains(Expr::val(3), Expr::val(7))),
3764            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3765                assert_eq!(expected, nonempty![Type::Set]);
3766                assert_eq!(actual, Type::Long);
3767                assert_eq!(advice, None);
3768            }
3769        );
3770        // { ham: "eggs" } contains "ham"
3771        assert_matches!(
3772            eval.interpret_inline_policy(&Expr::contains(
3773                Expr::record(vec![("ham".into(), Expr::val("eggs"))]).unwrap(),
3774                Expr::val("ham")
3775            )),
3776            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3777                assert_eq!(expected, nonempty![Type::Set]);
3778                assert_eq!(actual, Type::Record);
3779                assert_eq!(advice, None);
3780            }
3781        );
3782        // wrong argument order
3783        assert_matches!(
3784            eval.interpret_inline_policy(&Expr::contains(
3785                Expr::val(3),
3786                Expr::set(vec![Expr::val(1), Expr::val(3), Expr::val(7)])
3787            )),
3788            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3789                assert_eq!(expected, nonempty![Type::Set]);
3790                assert_eq!(actual, Type::Long);
3791                assert_eq!(advice, None);
3792            }
3793        );
3794    }
3795
3796    #[test]
3797    fn interpret_hierarchy_membership() {
3798        let request = basic_request();
3799        let entities = rich_entities();
3800        let eval = Evaluator::new(request, &entities, Extensions::none());
3801        // A in B, where A and B are unrelated (but same type)
3802        assert_eq!(
3803            eval.interpret_inline_policy(&Expr::is_in(
3804                Expr::val(EntityUID::with_eid("child")),
3805                Expr::val(EntityUID::with_eid("unrelated"))
3806            )),
3807            Ok(Value::from(false))
3808        );
3809        // A in B, where A and B are the same type and it's true
3810        assert_eq!(
3811            eval.interpret_inline_policy(&Expr::is_in(
3812                Expr::val(EntityUID::with_eid("child")),
3813                Expr::val(EntityUID::with_eid("parent"))
3814            )),
3815            Ok(Value::from(true))
3816        );
3817        // A in B, where A and B are different types and it's true
3818        assert_eq!(
3819            eval.interpret_inline_policy(&Expr::is_in(
3820                Expr::val(
3821                    EntityUID::with_eid_and_type("other_type", "other_child")
3822                        .expect("should be a valid identifier")
3823                ),
3824                Expr::val(EntityUID::with_eid("parent"))
3825            )),
3826            Ok(Value::from(true))
3827        );
3828        // A in B, where A and B are unrelated _and_ different types
3829        assert_eq!(
3830            eval.interpret_inline_policy(&Expr::is_in(
3831                Expr::val(
3832                    EntityUID::with_eid_and_type("other_type", "other_child")
3833                        .expect("should be a valid identifier")
3834                ),
3835                Expr::val(EntityUID::with_eid("unrelated"))
3836            )),
3837            Ok(Value::from(false))
3838        );
3839        // A in B, where A and B are siblings
3840        assert_eq!(
3841            eval.interpret_inline_policy(&Expr::is_in(
3842                Expr::val(EntityUID::with_eid("child")),
3843                Expr::val(EntityUID::with_eid("sibling"))
3844            )),
3845            Ok(Value::from(false))
3846        );
3847        // A in A, where A exists
3848        assert_eq!(
3849            eval.interpret_inline_policy(&Expr::is_in(
3850                Expr::val(EntityUID::with_eid("parent")),
3851                Expr::val(EntityUID::with_eid("parent"))
3852            )),
3853            Ok(Value::from(true))
3854        );
3855        // A in A, where A does not exist
3856        assert_eq!(
3857            eval.interpret_inline_policy(&Expr::is_in(
3858                Expr::val(EntityUID::with_eid("doesnotexist")),
3859                Expr::val(EntityUID::with_eid("doesnotexist")),
3860            )),
3861            Ok(Value::from(true))
3862        );
3863        // A in B, where actually B in A
3864        assert_eq!(
3865            eval.interpret_inline_policy(&Expr::is_in(
3866                Expr::val(EntityUID::with_eid("parent")),
3867                Expr::val(EntityUID::with_eid("child"))
3868            )),
3869            Ok(Value::from(false))
3870        );
3871        // A in B, where actually A is a grandchild of B
3872        assert_eq!(
3873            eval.interpret_inline_policy(&Expr::is_in(
3874                Expr::val(EntityUID::with_eid("child")),
3875                Expr::val(EntityUID::with_eid("grandparent"))
3876            )),
3877            Ok(Value::from(true))
3878        );
3879        // A in B, where A doesn't exist but B does
3880        assert_eq!(
3881            eval.interpret_inline_policy(&Expr::is_in(
3882                Expr::val(EntityUID::with_eid("doesnotexist")),
3883                Expr::val(EntityUID::with_eid("parent"))
3884            )),
3885            Ok(Value::from(false))
3886        );
3887        // A in B, where B doesn't exist but A does
3888        assert_eq!(
3889            eval.interpret_inline_policy(&Expr::is_in(
3890                Expr::val(EntityUID::with_eid("parent")),
3891                Expr::val(EntityUID::with_eid("doesnotexist"))
3892            )),
3893            Ok(Value::from(false))
3894        );
3895        // A in [B, C] where A in B but not A in C
3896        assert_eq!(
3897            eval.interpret_inline_policy(&Expr::is_in(
3898                Expr::val(EntityUID::with_eid("child")),
3899                Expr::set(vec![
3900                    Expr::val(EntityUID::with_eid("grandparent")),
3901                    Expr::val(EntityUID::with_eid("sibling")),
3902                ])
3903            )),
3904            Ok(Value::from(true))
3905        );
3906        // A in [B, C] where A in C but not A in B
3907        assert_eq!(
3908            eval.interpret_inline_policy(&Expr::is_in(
3909                Expr::val(EntityUID::with_eid("child")),
3910                Expr::set(vec![
3911                    Expr::val(EntityUID::with_eid("sibling")),
3912                    Expr::val(EntityUID::with_eid("grandparent")),
3913                ])
3914            )),
3915            Ok(Value::from(true))
3916        );
3917        // A in [B, C] where A is in neither B nor C
3918        assert_eq!(
3919            eval.interpret_inline_policy(&Expr::is_in(
3920                Expr::val(EntityUID::with_eid("child")),
3921                Expr::set(vec![
3922                    Expr::val(EntityUID::with_eid("sibling")),
3923                    Expr::val(EntityUID::with_eid("unrelated")),
3924                ])
3925            )),
3926            Ok(Value::from(false))
3927        );
3928        // A in [A, B] where B is unrelated
3929        assert_eq!(
3930            eval.interpret_inline_policy(&Expr::is_in(
3931                Expr::val(EntityUID::with_eid("child")),
3932                Expr::set(vec![
3933                    Expr::val(EntityUID::with_eid("unrelated")),
3934                    Expr::val(EntityUID::with_eid("child")),
3935                ])
3936            )),
3937            Ok(Value::from(true))
3938        );
3939        // A in [B, A] where B is unrelated
3940        assert_eq!(
3941            eval.interpret_inline_policy(&Expr::is_in(
3942                Expr::val(EntityUID::with_eid("child")),
3943                Expr::set(vec![
3944                    Expr::val(EntityUID::with_eid("child")),
3945                    Expr::val(EntityUID::with_eid("unrelated")),
3946                ])
3947            )),
3948            Ok(Value::from(true))
3949        );
3950        // A in [A, true]
3951        assert_matches!(
3952            eval.interpret_inline_policy(&Expr::is_in(
3953                Expr::val(EntityUID::with_eid("child")),
3954                Expr::set(vec![
3955                    Expr::val(EntityUID::with_eid("child")),
3956                    Expr::val(true),
3957                ])
3958            )),
3959            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
3960                assert_eq!(expected, nonempty![Type::entity_type(
3961                    Name::parse_unqualified_name("any_entity_type")
3962                        .expect("should be a valid identifier")
3963                )]);
3964                assert_eq!(actual, Type::Bool);
3965                assert_eq!(advice, None);
3966            }
3967        );
3968        // A in [A, B] where A and B do not exist
3969        assert_eq!(
3970            eval.interpret_inline_policy(&Expr::is_in(
3971                Expr::val(EntityUID::with_eid("doesnotexistA")),
3972                Expr::set(vec![
3973                    Expr::val(EntityUID::with_eid("doesnotexistA")),
3974                    Expr::val(EntityUID::with_eid("doesnotexistB")),
3975                ])
3976            )),
3977            Ok(Value::from(true))
3978        );
3979        // A in [B, C] where none of A, B, or C exist
3980        assert_eq!(
3981            eval.interpret_inline_policy(&Expr::is_in(
3982                Expr::val(EntityUID::with_eid("doesnotexistA")),
3983                Expr::set(vec![
3984                    Expr::val(EntityUID::with_eid("doesnotexistB")),
3985                    Expr::val(EntityUID::with_eid("doesnotexistC")),
3986                ])
3987            )),
3988            Ok(Value::from(false))
3989        );
3990        // A in [B, C] where B and C do not exist but A does
3991        assert_eq!(
3992            eval.interpret_inline_policy(&Expr::is_in(
3993                Expr::val(EntityUID::with_eid("child")),
3994                Expr::set(vec![
3995                    Expr::val(EntityUID::with_eid("doesnotexistB")),
3996                    Expr::val(EntityUID::with_eid("doesnotexistC")),
3997                ])
3998            )),
3999            Ok(Value::from(false))
4000        );
4001        // A in [B, C] where B and C exist but A does not
4002        assert_eq!(
4003            eval.interpret_inline_policy(&Expr::is_in(
4004                Expr::val(EntityUID::with_eid("doesnotexistA")),
4005                Expr::set(vec![
4006                    Expr::val(EntityUID::with_eid("child")),
4007                    Expr::val(EntityUID::with_eid("grandparent")),
4008                ])
4009            )),
4010            Ok(Value::from(false))
4011        );
4012        // "foo" in "foobar"
4013        assert_matches!(
4014            eval.interpret_inline_policy(&Expr::is_in(Expr::val("foo"), Expr::val("foobar"))),
4015            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4016                assert_eq!(expected, nonempty![Type::entity_type(
4017                    Name::parse_unqualified_name("any_entity_type")
4018                        .expect("should be a valid identifier")
4019                )]);
4020                assert_eq!(actual, Type::String);
4021                assert_eq!(advice, None);
4022            }
4023        );
4024        // "spoon" in A (where has(A.spoon))
4025        assert_matches!(
4026            eval.interpret_inline_policy(&Expr::is_in(
4027                Expr::val("spoon"),
4028                Expr::val(EntityUID::with_eid("entity_with_attrs"))
4029            )),
4030            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4031                assert_eq!(expected, nonempty![Type::entity_type(
4032                    Name::parse_unqualified_name("any_entity_type")
4033                        .expect("should be a valid identifier")
4034                )]);
4035                assert_eq!(actual, Type::String);
4036                assert_eq!(advice, None);
4037            }
4038        );
4039        // 3 in [34, -2, 7]
4040        assert_matches!(
4041            eval.interpret_inline_policy(&Expr::is_in(
4042                Expr::val(3),
4043                Expr::set(vec![Expr::val(34), Expr::val(-2), Expr::val(7)])
4044            )),
4045            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4046                assert_eq!(expected, nonempty![Type::entity_type(
4047                    Name::parse_unqualified_name("any_entity_type")
4048                        .expect("should be a valid identifier")
4049                )]);
4050                assert_eq!(actual, Type::Long);
4051                assert_eq!(advice, Some("`in` is for checking the entity hierarchy; use `.contains()` to test set membership".into()));
4052            }
4053        );
4054        // "foo" in { "foo": 2, "bar": true }
4055        assert_matches!(
4056            eval.interpret_inline_policy(&Expr::is_in(
4057                Expr::val("foo"),
4058                Expr::record(vec![
4059                    ("foo".into(), Expr::val(2)),
4060                    ("bar".into(), Expr::val(true)),
4061                ]).unwrap()
4062            )),
4063            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4064                assert_eq!(expected, nonempty![Type::entity_type(
4065                    Name::parse_unqualified_name("any_entity_type")
4066                        .expect("should be a valid identifier")
4067                )]);
4068                assert_eq!(actual, Type::String);
4069                assert_eq!(advice, Some("`in` is for checking the entity hierarchy; use `has` to test if a record has a key".into()));
4070            }
4071        );
4072        // A in { "foo": 2, "bar": true }
4073        assert_matches!(
4074            eval.interpret_inline_policy(&Expr::is_in(
4075                Expr::val(EntityUID::with_eid("child")),
4076                Expr::record(vec![
4077                    ("foo".into(), Expr::val(2)),
4078                    ("bar".into(), Expr::val(true)),
4079                ])
4080                .unwrap()
4081            )),
4082            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4083                assert_eq!(expected, nonempty![
4084                    Type::Set,
4085                    Type::entity_type(
4086                        Name::parse_unqualified_name("any_entity_type")
4087                            .expect("should be a valid identifier")
4088                    )
4089                ]);
4090                assert_eq!(actual, Type::Record);
4091                assert_eq!(advice, None);
4092            }
4093        );
4094    }
4095
4096    #[test]
4097    fn interpret_hierarchy_membership_slice() {
4098        // User::"Alice" in Group::"Friends".
4099        // Slice.attributes = {Alice},
4100        // Slice.hierarchy = {Alice, Group::Friends}
4101        // Should be allow under new semantics for "in"
4102
4103        let request = Request::new(
4104            (EntityUID::with_eid("Alice"), None),
4105            (EntityUID::with_eid("test_action"), None),
4106            (EntityUID::with_eid("test_resource"), None),
4107            Context::empty(),
4108            Some(&RequestSchemaAllPass),
4109            Extensions::none(),
4110        )
4111        .unwrap();
4112        //Alice has parent "Friends" but we don't add "Friends" to the slice
4113        let mut alice = Entity::with_uid(EntityUID::with_eid("Alice"));
4114        let parent = Entity::with_uid(EntityUID::with_eid("Friends"));
4115        alice.add_parent(parent.uid().clone());
4116        let entities = Entities::from_entities(
4117            vec![alice],
4118            None::<&NoEntitiesSchema>,
4119            TCComputation::AssumeAlreadyComputed,
4120            Extensions::all_available(),
4121        )
4122        .expect("failed to create basic entities");
4123        let eval = Evaluator::new(request, &entities, Extensions::none());
4124        assert_eq!(
4125            eval.interpret_inline_policy(&Expr::is_in(
4126                Expr::val(EntityUID::with_eid("Alice")),
4127                Expr::val(EntityUID::with_eid("Friends"))
4128            )),
4129            Ok(Value::from(true))
4130        );
4131        assert_eq!(
4132            eval.interpret_inline_policy(&Expr::is_in(
4133                Expr::val(EntityUID::with_eid("Bob")),
4134                Expr::val(EntityUID::with_eid("Friends"))
4135            )),
4136            Ok(Value::from(false))
4137        );
4138        assert_eq!(
4139            eval.interpret_inline_policy(&Expr::is_in(
4140                Expr::val(EntityUID::with_eid("Alice")),
4141                Expr::set(vec![
4142                    Expr::val(EntityUID::with_eid("Friends")),
4143                    Expr::val(EntityUID::with_eid("Bob"))
4144                ])
4145            )),
4146            Ok(Value::from(true))
4147        );
4148        assert_eq!(
4149            eval.interpret_inline_policy(&Expr::is_in(
4150                Expr::val(EntityUID::with_eid("Bob")),
4151                Expr::set(vec![
4152                    Expr::val(EntityUID::with_eid("Friends")),
4153                    Expr::val(EntityUID::with_eid("Alice"))
4154                ])
4155            )),
4156            Ok(Value::from(false))
4157        );
4158    }
4159
4160    #[test]
4161    fn interpret_string_like() {
4162        let request = basic_request();
4163        let entities = basic_entities();
4164        let eval = Evaluator::new(request, &entities, Extensions::none());
4165        // "eggs" vs "ham"
4166        assert_eq!(
4167            eval.interpret_inline_policy(
4168                &parse_expr(r#""eggs" like "ham*""#).expect("parsing error")
4169            ),
4170            Ok(Value::from(false))
4171        );
4172        assert_eq!(
4173            eval.interpret_inline_policy(
4174                &parse_expr(r#""eggs" like "*ham""#).expect("parsing error")
4175            ),
4176            Ok(Value::from(false))
4177        );
4178        assert_eq!(
4179            eval.interpret_inline_policy(
4180                &parse_expr(r#""eggs" like "*ham*""#).expect("parsing error")
4181            ),
4182            Ok(Value::from(false))
4183        );
4184        // "ham and eggs" vs "ham"
4185        assert_eq!(
4186            eval.interpret_inline_policy(
4187                &parse_expr(r#""ham and eggs" like "ham*""#).expect("parsing error")
4188            ),
4189            Ok(Value::from(true))
4190        );
4191        assert_eq!(
4192            eval.interpret_inline_policy(
4193                &parse_expr(r#""ham and eggs" like "*ham""#).expect("parsing error")
4194            ),
4195            Ok(Value::from(false))
4196        );
4197        assert_eq!(
4198            eval.interpret_inline_policy(
4199                &parse_expr(r#""ham and eggs" like "*ham*""#).expect("parsing error")
4200            ),
4201            Ok(Value::from(true))
4202        );
4203        assert_eq!(
4204            eval.interpret_inline_policy(
4205                &parse_expr(r#""ham and eggs" like "*h*a*m*""#).expect("parsing error")
4206            ),
4207            Ok(Value::from(true))
4208        );
4209        // "eggs and ham" vs "ham"
4210        assert_eq!(
4211            eval.interpret_inline_policy(
4212                &parse_expr(r#""eggs and ham" like "ham*""#).expect("parsing error")
4213            ),
4214            Ok(Value::from(false))
4215        );
4216        assert_eq!(
4217            eval.interpret_inline_policy(
4218                &parse_expr(r#""eggs and ham" like "*ham""#).expect("parsing error")
4219            ),
4220            Ok(Value::from(true))
4221        );
4222        // "eggs, ham, and spinach" vs "ham"
4223        assert_eq!(
4224            eval.interpret_inline_policy(
4225                &parse_expr(r#""eggs, ham, and spinach" like "ham*""#).expect("parsing error")
4226            ),
4227            Ok(Value::from(false))
4228        );
4229        assert_eq!(
4230            eval.interpret_inline_policy(
4231                &parse_expr(r#""eggs, ham, and spinach" like "*ham""#).expect("parsing error")
4232            ),
4233            Ok(Value::from(false))
4234        );
4235        assert_eq!(
4236            eval.interpret_inline_policy(
4237                &parse_expr(r#""eggs, ham, and spinach" like "*ham*""#).expect("parsing error")
4238            ),
4239            Ok(Value::from(true))
4240        );
4241        // "Gotham" vs "ham"
4242        assert_eq!(
4243            eval.interpret_inline_policy(
4244                &parse_expr(r#""Gotham" like "ham*""#).expect("parsing error")
4245            ),
4246            Ok(Value::from(false))
4247        );
4248        assert_eq!(
4249            eval.interpret_inline_policy(
4250                &parse_expr(r#""Gotham" like "*ham""#).expect("parsing error")
4251            ),
4252            Ok(Value::from(true))
4253        );
4254        // "ham" vs "ham"
4255        assert_eq!(
4256            eval.interpret_inline_policy(
4257                &parse_expr(r#""ham" like "ham""#).expect("parsing error")
4258            ),
4259            Ok(Value::from(true))
4260        );
4261        assert_eq!(
4262            eval.interpret_inline_policy(
4263                &parse_expr(r#""ham" like "ham*""#).expect("parsing error")
4264            ),
4265            Ok(Value::from(true))
4266        );
4267        assert_eq!(
4268            eval.interpret_inline_policy(
4269                &parse_expr(r#""ham" like "*ham""#).expect("parsing error")
4270            ),
4271            Ok(Value::from(true))
4272        );
4273        assert_eq!(
4274            eval.interpret_inline_policy(
4275                &parse_expr(r#""ham" like "*h*a*m*""#).expect("parsing error")
4276            ),
4277            Ok(Value::from(true))
4278        );
4279        // "ham and ham" vs "ham"
4280        assert_eq!(
4281            eval.interpret_inline_policy(
4282                &parse_expr(r#""ham and ham" like "ham*""#).expect("parsing error")
4283            ),
4284            Ok(Value::from(true))
4285        );
4286        assert_eq!(
4287            eval.interpret_inline_policy(
4288                &parse_expr(r#""ham and ham" like "*ham""#).expect("parsing error")
4289            ),
4290            Ok(Value::from(true))
4291        );
4292        // "ham" vs "ham and eggs"
4293        assert_eq!(
4294            eval.interpret_inline_policy(
4295                &parse_expr(r#""ham" like "*ham and eggs*""#).expect("parsing error")
4296            ),
4297            Ok(Value::from(false))
4298        );
4299        // type error
4300        assert_matches!(
4301            eval.interpret_inline_policy(&Expr::like(Expr::val(354), Pattern::from(vec![]))),
4302            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4303                assert_eq!(expected, nonempty![Type::String]);
4304                assert_eq!(actual, Type::Long);
4305                assert_eq!(advice, None);
4306            }
4307        );
4308        // 'contains' is not allowed on strings
4309        assert_matches!(
4310            eval.interpret_inline_policy(&Expr::contains(
4311                Expr::val("ham and ham"),
4312                Expr::val("ham")
4313            )),
4314            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4315                assert_eq!(expected, nonempty![Type::Set]);
4316                assert_eq!(actual, Type::String);
4317                assert_eq!(advice, None);
4318            }
4319        );
4320        // '\0' should not match '*'
4321        assert_eq!(
4322            eval.interpret_inline_policy(&Expr::like(
4323                Expr::val("*"),
4324                Pattern::from(vec![PatternElem::Char('\u{0000}')])
4325            )),
4326            Ok(Value::from(false))
4327        );
4328
4329        assert_eq!(
4330            eval.interpret_inline_policy(
4331                &parse_expr(r#"   "\\afterslash" like "\\*"   "#).expect("parsing error")
4332            ),
4333            Ok(Value::from(true))
4334        );
4335    }
4336
4337    #[test]
4338    fn interpret_string_like_escaped_chars() {
4339        let request = basic_request();
4340        let entities = basic_entities();
4341        let eval = Evaluator::new(request, &entities, Extensions::none());
4342        // testing like wth escaped characters -- similar tests are also in parser/convert.rs
4343        assert_eq!(
4344            eval.interpret_inline_policy(
4345                &parse_expr(r#""string\\with\\backslashes" like "string\\with\\backslashes""#)
4346                    .expect("parsing error")
4347            ),
4348            Ok(Value::from(true))
4349        );
4350        assert_eq!(
4351            eval.interpret_inline_policy(
4352                &parse_expr(
4353                    r#""string\\with\\backslashes" like "string\u{0000}with\u{0000}backslashe""#
4354                )
4355                .expect("parsing error")
4356            ),
4357            Ok(Value::from(false))
4358        );
4359        assert_eq!(
4360            eval.interpret_inline_policy(
4361                &parse_expr(r#""string\\with\\backslashes" like "string*with*backslashes""#)
4362                    .expect("parsing error")
4363            ),
4364            Ok(Value::from(true))
4365        );
4366        assert_eq!(
4367            eval.interpret_inline_policy(
4368                &parse_expr(r#""string*with*stars" like "string\*with\*stars""#)
4369                    .expect("parsing error")
4370            ),
4371            Ok(Value::from(true))
4372        );
4373        assert_eq!(eval.interpret_inline_policy(&parse_expr(r#""string\\*with\\*backslashes\\*and\\*stars" like "string\\*with\\*backslashes\\*and\\*stars""#).expect("parsing error")), Ok(Value::from(true)));
4374    }
4375
4376    #[test]
4377    fn interpret_is() {
4378        let request = basic_request();
4379        let entities = basic_entities();
4380        let eval = Evaluator::new(request, &entities, Extensions::none());
4381        assert_eq!(
4382            eval.interpret_inline_policy(
4383                &parse_expr(&format!(
4384                    r#"principal is {}"#,
4385                    EntityUID::test_entity_type()
4386                ))
4387                .expect("parsing error")
4388            ),
4389            Ok(Value::from(true))
4390        );
4391        assert_eq!(
4392            eval.interpret_inline_policy(
4393                &parse_expr(&format!(
4394                    r#"principal is N::S::{}"#,
4395                    EntityUID::test_entity_type()
4396                ))
4397                .expect("parsing error")
4398            ),
4399            Ok(Value::from(false))
4400        );
4401        assert_eq!(
4402            eval.interpret_inline_policy(
4403                &parse_expr(r#"User::"alice" is User"#).expect("parsing error")
4404            ),
4405            Ok(Value::from(true))
4406        );
4407        assert_eq!(
4408            eval.interpret_inline_policy(
4409                &parse_expr(r#"User::"alice" is Group"#).expect("parsing error")
4410            ),
4411            Ok(Value::from(false))
4412        );
4413        assert_eq!(
4414            eval.interpret_inline_policy(
4415                &parse_expr(r#"N::S::User::"alice" is N::S::User"#).expect("parsing error")
4416            ),
4417            Ok(Value::from(true))
4418        );
4419        assert_eq!(
4420            eval.interpret_inline_policy(
4421                &parse_expr(r#"N::S::User::"alice" is User"#).expect("parsing error")
4422            ),
4423            Ok(Value::from(false))
4424        );
4425        assert_matches!(
4426            eval.interpret_inline_policy(&parse_expr(r#"1 is Group"#).expect("parsing error")),
4427            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4428                assert_eq!(expected, nonempty![Type::entity_type(names::ANY_ENTITY_TYPE.clone())]);
4429                assert_eq!(actual, Type::Long);
4430                assert_eq!(advice, None);
4431            }
4432        );
4433    }
4434
4435    #[test]
4436    fn interpret_is_empty() {
4437        let request = basic_request();
4438        let entities = basic_entities();
4439        let eval = Evaluator::new(request, &entities, Extensions::none());
4440        // [].isEmpty()
4441        assert_eq!(
4442            eval.interpret_inline_policy(&Expr::is_empty(Expr::set([]),)),
4443            Ok(Value::from(true))
4444        );
4445        // [1].isEmpty()
4446        assert_eq!(
4447            eval.interpret_inline_policy(&Expr::is_empty(Expr::set(vec![Expr::val(1)]),)),
4448            Ok(Value::from(false))
4449        );
4450        // [false].isEmpty()
4451        assert_eq!(
4452            eval.interpret_inline_policy(&Expr::is_empty(Expr::set(vec![Expr::val(false)]),)),
4453            Ok(Value::from(false))
4454        );
4455        // [1,2,3,4,5,User::"alice"].isEmpty()
4456        assert_eq!(
4457            eval.interpret_inline_policy(&Expr::is_empty(Expr::set(vec![
4458                Expr::val(1),
4459                Expr::val(2),
4460                Expr::val(3),
4461                Expr::val(4),
4462                Expr::val(5),
4463                Expr::val(EntityUID::with_eid("jane"))
4464            ]))),
4465            Ok(Value::from(false))
4466        );
4467        // 0.isEmpty()
4468        assert_matches!(
4469            eval.interpret_inline_policy(&Expr::is_empty(
4470                Expr::val(0)
4471            )),
4472            Err(e) => {
4473                expect_err(
4474                    "",
4475                    &miette::Report::new(e),
4476                    &ExpectedErrorMessageBuilder::error("type error: expected set, got long").build(),
4477                );
4478            }
4479        );
4480        // { foo: [] }.isEmpty()
4481        assert_matches!(
4482            eval.interpret_inline_policy(&Expr::is_empty(
4483                Expr::record([
4484                    ("foo".into(), Expr::set([]))
4485                ]).unwrap()
4486            )),
4487            Err(e) => {
4488                expect_err(
4489                    "",
4490                    &miette::Report::new(e),
4491                    &ExpectedErrorMessageBuilder::error("type error: expected set, got record").build(),
4492                );
4493            }
4494        );
4495    }
4496
4497    #[test]
4498    fn interpret_contains_all_and_contains_any() {
4499        let request = basic_request();
4500        let entities = basic_entities();
4501        let eval = Evaluator::new(request, &entities, Extensions::none());
4502        //  [1, -22, 34] containsall of [1, -22]?
4503        assert_eq!(
4504            eval.interpret_inline_policy(&Expr::contains_all(
4505                Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4506                Expr::set(vec![Expr::val(1), Expr::val(-22)])
4507            )),
4508            Ok(Value::from(true))
4509        );
4510        // [1, -22, 34] containsall [-22, 1]?
4511        assert_eq!(
4512            eval.interpret_inline_policy(&Expr::contains_all(
4513                Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4514                Expr::set(vec![Expr::val(-22), Expr::val(1)])
4515            )),
4516            Ok(Value::from(true))
4517        );
4518        // [1, -22, 34] containsall [-22]?
4519        assert_eq!(
4520            eval.interpret_inline_policy(&Expr::contains_all(
4521                Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4522                Expr::set(vec![Expr::val(-22)])
4523            )),
4524            Ok(Value::from(true))
4525        );
4526        // [43, 34] containsall [34, 43]?
4527        assert_eq!(
4528            eval.interpret_inline_policy(&Expr::contains_all(
4529                Expr::set(vec![Expr::val(43), Expr::val(34)]),
4530                Expr::set(vec![Expr::val(34), Expr::val(43)])
4531            )),
4532            Ok(Value::from(true))
4533        );
4534        // [1, -2, 34] containsall [1, -22]?
4535        assert_eq!(
4536            eval.interpret_inline_policy(&Expr::contains_all(
4537                Expr::set(vec![Expr::val(1), Expr::val(-2), Expr::val(34)]),
4538                Expr::set(vec![Expr::val(1), Expr::val(-22)])
4539            )),
4540            Ok(Value::from(false))
4541        );
4542        // [1, 34] containsall [1, 101, 34]?
4543        assert_eq!(
4544            eval.interpret_inline_policy(&Expr::contains_all(
4545                Expr::set(vec![Expr::val(1), Expr::val(34)]),
4546                Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
4547            )),
4548            Ok(Value::from(false))
4549        );
4550        // [1, 34, 102] containsall [1, 101, 34]?
4551        assert_eq!(
4552            eval.interpret_inline_policy(&Expr::contains_all(
4553                Expr::set(vec![Expr::val(1), Expr::val(34), Expr::val(102)]),
4554                Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
4555            )),
4556            Ok(Value::from(false))
4557        );
4558        // [2, -7, 387] containsall [1, 101, 34]?
4559        assert_eq!(
4560            eval.interpret_inline_policy(&Expr::contains_all(
4561                Expr::set(vec![Expr::val(2), Expr::val(-7), Expr::val(387)]),
4562                Expr::set(vec![Expr::val(1), Expr::val(101), Expr::val(34)])
4563            )),
4564            Ok(Value::from(false))
4565        );
4566        // [2, 43] containsall []?
4567        assert_eq!(
4568            eval.interpret_inline_policy(&Expr::contains_all(
4569                Expr::set(vec![Expr::val(2), Expr::val(43)]),
4570                Expr::set(vec![])
4571            )),
4572            Ok(Value::from(true))
4573        );
4574        // [] containsall [2, 43]?
4575        assert_eq!(
4576            eval.interpret_inline_policy(&Expr::contains_all(
4577                Expr::set(vec![]),
4578                Expr::set(vec![Expr::val(2), Expr::val(43)])
4579            )),
4580            Ok(Value::from(false))
4581        );
4582        // [<entity bar>, <entity foo>] containsall [<entity foo>]?
4583        assert_eq!(
4584            eval.interpret_inline_policy(&Expr::contains_all(
4585                Expr::set(vec![
4586                    Expr::val(EntityUID::with_eid("bar")),
4587                    Expr::val(EntityUID::with_eid("foo"))
4588                ]),
4589                Expr::set(vec![Expr::val(EntityUID::with_eid("foo"))])
4590            )),
4591            Ok(Value::from(true))
4592        );
4593        // [false, 3, [47, 0], {"2": "ham"}] containsall [3, {"2": "ham"}]?
4594        assert_eq!(
4595            eval.interpret_inline_policy(&Expr::contains_all(
4596                Expr::set(vec![
4597                    Expr::val(false),
4598                    Expr::val(3),
4599                    Expr::set(vec![Expr::val(47), Expr::val(0)]),
4600                    Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap()
4601                ]),
4602                Expr::set(vec![
4603                    Expr::val(3),
4604                    Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap()
4605                ])
4606            )),
4607            Ok(Value::from(true))
4608        );
4609        //  "ham and eggs" containsall "ham"?
4610        assert_matches!(
4611            eval.interpret_inline_policy(&Expr::contains_all(
4612                Expr::val("ham"),
4613                Expr::val("ham and eggs")
4614            )),
4615            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4616                assert_eq!(expected, nonempty![Type::Set]);
4617                assert_eq!(actual, Type::String);
4618                assert_eq!(advice, None);
4619            }
4620        );
4621        // {"2": "ham", "3": "eggs"} containsall {"2": "ham"} ?
4622        assert_matches!(
4623            eval.interpret_inline_policy(&Expr::contains_all(
4624                Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
4625                Expr::record(vec![
4626                    ("2".into(), Expr::val("ham")),
4627                    ("3".into(), Expr::val("eggs"))
4628                ])
4629                .unwrap()
4630            )),
4631            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4632                assert_eq!(expected, nonempty![Type::Set]);
4633                assert_eq!(actual, Type::Record);
4634                assert_eq!(advice, None);
4635            }
4636        );
4637        // test for [1, -22] contains_any of [1, -22, 34]
4638        assert_eq!(
4639            eval.interpret_inline_policy(&Expr::contains_any(
4640                Expr::set(vec![Expr::val(1), Expr::val(-22)]),
4641                Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
4642            )),
4643            Ok(Value::from(true))
4644        );
4645        // test for [1, -22, 34] contains_any of [1, -22]
4646        assert_eq!(
4647            eval.interpret_inline_policy(&Expr::contains_any(
4648                Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)]),
4649                Expr::set(vec![Expr::val(1), Expr::val(-22)])
4650            )),
4651            Ok(Value::from(true))
4652        );
4653        // test for [-22] contains_any of [1, -22, 34]
4654        assert_eq!(
4655            eval.interpret_inline_policy(&Expr::contains_any(
4656                Expr::set(vec![Expr::val(-22)]),
4657                Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
4658            )),
4659            Ok(Value::from(true))
4660        );
4661        // test for [1, 101] contains_any of [1, -22, 34]
4662        assert_eq!(
4663            eval.interpret_inline_policy(&Expr::contains_any(
4664                Expr::set(vec![Expr::val(1), Expr::val(101)]),
4665                Expr::set(vec![Expr::val(1), Expr::val(-22), Expr::val(34)])
4666            )),
4667            Ok(Value::from(true))
4668        );
4669        // test for [1, 101] contains_any of [-22, 34]
4670        assert_eq!(
4671            eval.interpret_inline_policy(&Expr::contains_any(
4672                Expr::set(vec![Expr::val(1), Expr::val(101)]),
4673                Expr::set(vec![Expr::val(-22), Expr::val(34)])
4674            )),
4675            Ok(Value::from(false))
4676        );
4677        // test for [] contains_any of [-22, 34]
4678        assert_eq!(
4679            eval.interpret_inline_policy(&Expr::contains_any(
4680                Expr::set(vec![]),
4681                Expr::set(vec![Expr::val(-22), Expr::val(34)])
4682            )),
4683            Ok(Value::from(false))
4684        );
4685        // test for [-22, 34] contains_any of []
4686        assert_eq!(
4687            eval.interpret_inline_policy(&Expr::contains_any(
4688                Expr::set(vec![Expr::val(-22), Expr::val(34)]),
4689                Expr::set(vec![])
4690            )),
4691            Ok(Value::from(false))
4692        );
4693        // test for [<entity foo>, <entity bar>] contains_any of [<entity ham>, <entity eggs>]
4694        assert_eq!(
4695            eval.interpret_inline_policy(&Expr::contains_any(
4696                Expr::set(vec![
4697                    Expr::val(EntityUID::with_eid("foo")),
4698                    Expr::val(EntityUID::with_eid("bar"))
4699                ]),
4700                Expr::set(vec![
4701                    Expr::val(EntityUID::with_eid("ham")),
4702                    Expr::val(EntityUID::with_eid("eggs"))
4703                ])
4704            )),
4705            Ok(Value::from(false))
4706        );
4707        // test for [3, {"2": "ham", "1": "eggs"}] contains_any of [7, false, [-22, true], {"1": "eggs", "2": "ham"}]
4708        assert_eq!(
4709            eval.interpret_inline_policy(&Expr::contains_any(
4710                Expr::set(vec![
4711                    Expr::val(3),
4712                    Expr::record(vec![
4713                        ("2".into(), Expr::val("ham")),
4714                        ("1".into(), Expr::val("eggs"))
4715                    ])
4716                    .unwrap()
4717                ]),
4718                Expr::set(vec![
4719                    Expr::val(7),
4720                    Expr::val(false),
4721                    Expr::set(vec![Expr::val(-22), Expr::val(true)]),
4722                    Expr::record(vec![
4723                        ("1".into(), Expr::val("eggs")),
4724                        ("2".into(), Expr::val("ham"))
4725                    ])
4726                    .unwrap()
4727                ])
4728            )),
4729            Ok(Value::from(true))
4730        );
4731        // test for "ham" contains_any of "ham and eggs"
4732        assert_matches!(
4733            eval.interpret_inline_policy(&Expr::contains_any(
4734                Expr::val("ham"),
4735                Expr::val("ham and eggs")
4736            )),
4737            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4738                assert_eq!(expected, nonempty![Type::Set]);
4739                assert_eq!(actual, Type::String);
4740                assert_eq!(advice, None);
4741            }
4742        );
4743        // test for {"2": "ham"} contains_any of {"2": "ham", "3": "eggs"}
4744        assert_matches!(
4745            eval.interpret_inline_policy(&Expr::contains_any(
4746                Expr::record(vec![("2".into(), Expr::val("ham"))]).unwrap(),
4747                Expr::record(vec![
4748                    ("2".into(), Expr::val("ham")),
4749                    ("3".into(), Expr::val("eggs"))
4750                ])
4751                .unwrap()
4752            )),
4753            Err(EvaluationError::TypeError(TypeError { expected, actual, advice, .. })) => {
4754                assert_eq!(expected, nonempty![Type::Set]);
4755                assert_eq!(actual, Type::Record);
4756                assert_eq!(advice, None);
4757            }
4758        );
4759    }
4760
4761    #[test]
4762    fn eval_and_or() -> Result<()> {
4763        use crate::parser;
4764        let request = basic_request();
4765        let eparser: EntityJsonParser<'_, '_> =
4766            EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4767        let entities = eparser.from_json_str("[]").expect("empty slice");
4768        let evaluator = Evaluator::new(request, &entities, Extensions::none());
4769
4770        // short-circuit allows these to pass without error
4771        let raw_expr = "(false && 3)";
4772        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4773        assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4774
4775        let raw_expr = "(true || 3)";
4776        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4777        assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4778
4779        // short-circuit plus total equality allows these to pass without error
4780        let raw_expr = "(false && 3) == 3";
4781        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4782        assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4783
4784        let raw_expr = "(true || 3) == 3";
4785        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4786        assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4787
4788        let raw_expr = "(false && 3 && true) == 3";
4789        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4790        assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4791
4792        let raw_expr = "(true || 3 || true) == 3";
4793        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4794        assert_matches!(evaluator.interpret_inline_policy(&expr), Ok(_));
4795
4796        // These must error
4797        let raw_expr = "(true && 3)";
4798        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4799        let t = evaluator.interpret_inline_policy(&expr);
4800        println!("EXPR={t:?}");
4801        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4802
4803        let raw_expr = "(3 && true)";
4804        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4805        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4806
4807        let raw_expr = "(3 && false)";
4808        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4809        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4810
4811        let raw_expr = "(3 || true)";
4812        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4813        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4814
4815        let raw_expr = "(3 || false)";
4816        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4817        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4818
4819        let raw_expr = "(false || 3)";
4820        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4821        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4822
4823        let raw_expr = "(true && 3) == 3";
4824        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4825        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4826
4827        let raw_expr = "(3 && true) == 3";
4828        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4829        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4830
4831        let raw_expr = "(3 && false) == 3";
4832        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4833        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4834
4835        let raw_expr = "(3 || true) == 3";
4836        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4837        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4838
4839        let raw_expr = "(3 || false) == 3";
4840        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4841        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4842
4843        let raw_expr = "(false || 3) == 3";
4844        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4845        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4846
4847        let raw_expr = "(true && 3 && true) == 3";
4848        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4849        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4850
4851        let raw_expr = "(3 && true && true) == 3";
4852        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4853        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4854
4855        let raw_expr = "(3 && false && true) == 3";
4856        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4857        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4858
4859        let raw_expr = "(3 || true || true) == 3";
4860        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4861        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4862
4863        let raw_expr = "(3 || false || true) == 3";
4864        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4865        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4866
4867        let raw_expr = "(false || 3 || true) == 3";
4868        let expr = parser::parse_expr(raw_expr).expect("parse fail");
4869        assert_matches!(evaluator.interpret_inline_policy(&expr), Err(_));
4870
4871        Ok(())
4872    }
4873
4874    #[test]
4875    fn template_env_tests() {
4876        let request = basic_request();
4877        let eparser: EntityJsonParser<'_, '_> =
4878            EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4879        let entities = eparser.from_json_str("[]").expect("empty slice");
4880        let evaluator = Evaluator::new(request, &entities, Extensions::none());
4881        let e = Expr::slot(SlotId::principal());
4882
4883        let slots = HashMap::new();
4884        let r = evaluator.partial_interpret(&e, &slots);
4885        assert_matches!(r, Err(EvaluationError::UnlinkedSlot(UnlinkedSlotError { slot, .. })) => {
4886            assert_eq!(slot, SlotId::principal());
4887        });
4888
4889        let mut slots = HashMap::new();
4890        slots.insert(SlotId::principal(), EntityUID::with_eid("eid"));
4891        let r = evaluator.partial_interpret(&e, &slots);
4892        assert_matches!(r, Ok(e) => {
4893            assert_eq!(
4894                e,
4895                PartialValue::Value(Value::from(
4896                    EntityUID::with_eid("eid")
4897                ))
4898            );
4899        });
4900    }
4901
4902    #[test]
4903    fn template_interp() {
4904        let t = parse_policy_or_template(
4905            Some(PolicyID::from_string("template")),
4906            r#"permit(principal == ?principal, action, resource);"#,
4907        )
4908        .expect("Parse Error");
4909        let mut pset = PolicySet::new();
4910        pset.add_template(t)
4911            .expect("Template already present in PolicySet");
4912        let mut values = HashMap::new();
4913        values.insert(SlotId::principal(), EntityUID::with_eid("p"));
4914        pset.link(
4915            PolicyID::from_string("template"),
4916            PolicyID::from_string("instance"),
4917            values,
4918        )
4919        .expect("Linking failed!");
4920        let q = Request::new(
4921            (EntityUID::with_eid("p"), None),
4922            (EntityUID::with_eid("a"), None),
4923            (EntityUID::with_eid("r"), None),
4924            Context::empty(),
4925            Some(&RequestSchemaAllPass),
4926            Extensions::none(),
4927        )
4928        .unwrap();
4929        let eparser: EntityJsonParser<'_, '_> =
4930            EntityJsonParser::new(None, Extensions::none(), TCComputation::ComputeNow);
4931        let entities = eparser.from_json_str("[]").expect("empty slice");
4932        let eval = Evaluator::new(q, &entities, Extensions::none());
4933
4934        let ir = pset.policies().next().expect("No linked policies");
4935        assert_matches!(eval.partial_evaluate(ir), Ok(Either::Left(b)) => {
4936            assert!(b, "Should be enforced");
4937        });
4938    }
4939
4940    #[track_caller] // report the caller's location as the location of the panic, not the location in this function
4941    fn assert_restricted_expression_error(e: &Expr) {
4942        assert_matches!(
4943            BorrowedRestrictedExpr::new(e),
4944            Err(RestrictedExpressionError::InvalidRestrictedExpression { .. })
4945        );
4946    }
4947
4948    #[test]
4949    fn restricted_expressions() {
4950        let evaluator = RestrictedEvaluator::new(Extensions::all_available());
4951
4952        // simple expressions
4953        assert_eq!(
4954            evaluator.partial_interpret(BorrowedRestrictedExpr::new(&Expr::val(true)).unwrap()),
4955            Ok(Value::from(true).into())
4956        );
4957        assert_eq!(
4958            evaluator.partial_interpret(BorrowedRestrictedExpr::new(&Expr::val(-2)).unwrap()),
4959            Ok(Value::from(-2).into())
4960        );
4961        assert_eq!(
4962            evaluator
4963                .partial_interpret(BorrowedRestrictedExpr::new(&Expr::val("hello world")).unwrap()),
4964            Ok(Value::from("hello world").into())
4965        );
4966        assert_eq!(
4967            evaluator.partial_interpret(
4968                BorrowedRestrictedExpr::new(&Expr::val(EntityUID::with_eid("alice"))).unwrap()
4969            ),
4970            Ok(Value::from(EntityUID::with_eid("alice")).into())
4971        );
4972        assert_restricted_expression_error(&Expr::var(Var::Principal));
4973        assert_restricted_expression_error(&Expr::var(Var::Action));
4974        assert_restricted_expression_error(&Expr::var(Var::Resource));
4975        assert_restricted_expression_error(&Expr::var(Var::Context));
4976        assert_restricted_expression_error(&Expr::ite(
4977            Expr::val(true),
4978            Expr::val(7),
4979            Expr::val(12),
4980        ));
4981        assert_restricted_expression_error(&Expr::and(Expr::val("bogus"), Expr::val(true)));
4982        assert_restricted_expression_error(&Expr::or(Expr::val("bogus"), Expr::val(true)));
4983        assert_restricted_expression_error(&Expr::not(Expr::val(true)));
4984        assert_restricted_expression_error(&Expr::is_in(
4985            Expr::val(EntityUID::with_eid("alice")),
4986            Expr::val(EntityUID::with_eid("some_group")),
4987        ));
4988        assert_restricted_expression_error(&Expr::is_eq(
4989            Expr::val(EntityUID::with_eid("alice")),
4990            Expr::val(EntityUID::with_eid("some_group")),
4991        ));
4992        #[cfg(feature = "ipaddr")]
4993        assert_matches!(
4994            evaluator.partial_interpret(
4995                BorrowedRestrictedExpr::new(&Expr::call_extension_fn(
4996                    "ip".parse().expect("should be a valid Name"),
4997                    vec![Expr::val("222.222.222.222")]
4998                ))
4999                .unwrap()
5000            ),
5001            Ok(PartialValue::Value(Value {
5002                value: ValueKind::ExtensionValue(_),
5003                ..
5004            }))
5005        );
5006        assert_restricted_expression_error(&Expr::get_attr(
5007            Expr::val(EntityUID::with_eid("alice")),
5008            "pancakes".into(),
5009        ));
5010        assert_restricted_expression_error(&Expr::has_attr(
5011            Expr::val(EntityUID::with_eid("alice")),
5012            "pancakes".into(),
5013        ));
5014        assert_restricted_expression_error(&Expr::like(
5015            Expr::val("abcdefg12"),
5016            Pattern::from(vec![
5017                PatternElem::Char('a'),
5018                PatternElem::Char('b'),
5019                PatternElem::Char('c'),
5020                PatternElem::Wildcard,
5021            ]),
5022        ));
5023        assert_matches!(
5024            evaluator.partial_interpret(
5025                BorrowedRestrictedExpr::new(&Expr::set([Expr::val("hi"), Expr::val("there")]))
5026                    .unwrap()
5027            ),
5028            Ok(PartialValue::Value(Value {
5029                value: ValueKind::Set(_),
5030                ..
5031            }))
5032        );
5033        assert_matches!(
5034            evaluator.partial_interpret(
5035                BorrowedRestrictedExpr::new(
5036                    &Expr::record([
5037                        ("hi".into(), Expr::val(1001)),
5038                        ("foo".into(), Expr::val("bar"))
5039                    ])
5040                    .unwrap()
5041                )
5042                .unwrap()
5043            ),
5044            Ok(PartialValue::Value(Value {
5045                value: ValueKind::Record(_),
5046                ..
5047            }))
5048        );
5049
5050        // complex expressions -- for instance, violation not at top level
5051        assert_restricted_expression_error(&Expr::set([
5052            Expr::val("hi"),
5053            Expr::and(Expr::val("bogus"), Expr::val(false)),
5054        ]));
5055        assert_restricted_expression_error(&Expr::call_extension_fn(
5056            "ip".parse().expect("should be a valid Name"),
5057            vec![Expr::var(Var::Principal)],
5058        ));
5059
5060        assert_restricted_expression_error(&Expr::is_entity_type(
5061            Expr::val(EntityUID::with_eid("alice")),
5062            "User".parse().unwrap(),
5063        ));
5064    }
5065
5066    #[test]
5067    fn simple_partial() {
5068        let pset = parse_policyset(
5069            r#"
5070            permit(principal == Principal::"alice", action, resource);
5071            "#,
5072        )
5073        .expect("Failed to parse");
5074        let euid =
5075            Arc::new(EntityUID::from_str(r#"Principal::"alice""#).expect("EUID failed to parse"));
5076        let p = pset
5077            .get(&PolicyID::from_string("policy0"))
5078            .expect("No such policy");
5079        let q = Request::new_with_unknowns(
5080            EntityUIDEntry::unknown(),
5081            EntityUIDEntry::unknown(),
5082            EntityUIDEntry::unknown(),
5083            Some(Context::empty()),
5084            Some(&RequestSchemaAllPass),
5085            Extensions::none(),
5086        )
5087        .unwrap();
5088        let es = Entities::new();
5089        let e = Evaluator::new(q, &es, Extensions::none());
5090        match e.partial_evaluate(p).expect("eval error") {
5091            Either::Left(_) => panic!("Evalled to a value"),
5092            Either::Right(expr) => {
5093                println!("{expr}");
5094                assert!(expr.contains_unknown());
5095                let m: HashMap<_, _> = HashMap::from([("principal".into(), Value::from(euid))]);
5096                let new_expr = expr.substitute_typed(&m).unwrap();
5097                assert_eq!(
5098                    e.partial_interpret(&new_expr, &HashMap::new())
5099                        .expect("Failed to eval"),
5100                    PartialValue::Value(true.into())
5101                );
5102            }
5103        }
5104    }
5105
5106    fn partial_context_test(context_expr: Expr, e: &Expr) -> Either<Value, Expr> {
5107        let euid: EntityUID = r#"Test::"test""#.parse().unwrap();
5108        let rexpr = RestrictedExpr::new(context_expr)
5109            .expect("Context Expression was not a restricted expression");
5110        let context = Context::from_expr(rexpr.as_borrowed(), Extensions::none()).unwrap();
5111        let q = Request::new(
5112            (euid.clone(), None),
5113            (euid.clone(), None),
5114            (euid, None),
5115            context,
5116            Some(&RequestSchemaAllPass),
5117            Extensions::none(),
5118        )
5119        .unwrap();
5120        let es = Entities::new();
5121        let eval = Evaluator::new(q, &es, Extensions::none());
5122        eval.partial_eval_expr(e).unwrap()
5123    }
5124
5125    #[test]
5126    fn partial_contexts1() {
5127        // { "cell" : <unknown> }
5128        let c_expr =
5129            Expr::record([("cell".into(), Expr::unknown(Unknown::new_untyped("cell")))]).unwrap();
5130        let expr = Expr::binary_app(
5131            BinaryOp::Eq,
5132            Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5133            Expr::val(2),
5134        );
5135        let expected = Expr::binary_app(
5136            BinaryOp::Eq,
5137            Expr::unknown(Unknown::new_untyped("cell")),
5138            Expr::val(2),
5139        );
5140
5141        let r = partial_context_test(c_expr, &expr);
5142
5143        assert_eq!(r, Either::Right(expected));
5144    }
5145
5146    #[test]
5147    fn partial_contexts2() {
5148        // { "loc" : "test", "cell" : <unknown> }
5149        let c_expr = Expr::record([
5150            ("loc".into(), Expr::val("test")),
5151            ("cell".into(), Expr::unknown(Unknown::new_untyped("cell"))),
5152        ])
5153        .unwrap();
5154        // context["cell"] == 2
5155        let expr = Expr::binary_app(
5156            BinaryOp::Eq,
5157            Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5158            Expr::val(2),
5159        );
5160        let r = partial_context_test(c_expr.clone(), &expr);
5161        let expected = Expr::binary_app(
5162            BinaryOp::Eq,
5163            Expr::unknown(Unknown::new_untyped("cell")),
5164            Expr::val(2),
5165        );
5166        assert_eq!(r, Either::Right(expected));
5167
5168        // context["loc"] == 2
5169        let expr = Expr::binary_app(
5170            BinaryOp::Eq,
5171            Expr::get_attr(Expr::var(Var::Context), "loc".into()),
5172            Expr::val(2),
5173        );
5174        let r = partial_context_test(c_expr, &expr);
5175        assert_eq!(r, Either::Left(false.into()));
5176    }
5177
5178    #[test]
5179    fn partial_contexts3() {
5180        // { "loc" : "test", "cell" : { "row" : <unknown> } }
5181        let row =
5182            Expr::record([("row".into(), Expr::unknown(Unknown::new_untyped("row")))]).unwrap();
5183        //assert!(row.is_partially_projectable());
5184        let c_expr =
5185            Expr::record([("loc".into(), Expr::val("test")), ("cell".into(), row)]).unwrap();
5186        //assert!(c_expr.is_partially_projectable());
5187        // context["cell"]["row"] == 2
5188        let expr = Expr::binary_app(
5189            BinaryOp::Eq,
5190            Expr::get_attr(
5191                Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5192                "row".into(),
5193            ),
5194            Expr::val(2),
5195        );
5196        let r = partial_context_test(c_expr, &expr);
5197        let expected = Expr::binary_app(
5198            BinaryOp::Eq,
5199            Expr::unknown(Unknown::new_untyped("row")),
5200            Expr::val(2),
5201        );
5202        assert_eq!(r, Either::Right(expected));
5203    }
5204
5205    #[test]
5206    fn partial_contexts4() {
5207        // { "loc" : "test", "cell" : { "row" : <unknown>, "col" : <unknown> } }
5208        let row = Expr::record([
5209            ("row".into(), Expr::unknown(Unknown::new_untyped("row"))),
5210            ("col".into(), Expr::unknown(Unknown::new_untyped("col"))),
5211        ])
5212        .unwrap();
5213        //assert!(row.is_partially_projectable());
5214        let c_expr =
5215            Expr::record([("loc".into(), Expr::val("test")), ("cell".into(), row)]).unwrap();
5216        //assert!(c_expr.is_partially_projectable());
5217        // context["cell"]["row"] == 2
5218        let expr = Expr::binary_app(
5219            BinaryOp::Eq,
5220            Expr::get_attr(
5221                Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5222                "row".into(),
5223            ),
5224            Expr::val(2),
5225        );
5226        let r = partial_context_test(c_expr.clone(), &expr);
5227        let expected = Expr::binary_app(
5228            BinaryOp::Eq,
5229            Expr::unknown(Unknown::new_untyped("row")),
5230            Expr::val(2),
5231        );
5232        assert_eq!(r, Either::Right(expected));
5233        // context["cell"]["col"] == 2
5234        let expr = Expr::binary_app(
5235            BinaryOp::Eq,
5236            Expr::get_attr(
5237                Expr::get_attr(Expr::var(Var::Context), "cell".into()),
5238                "col".into(),
5239            ),
5240            Expr::val(2),
5241        );
5242        let r = partial_context_test(c_expr, &expr);
5243        let expected = Expr::binary_app(
5244            BinaryOp::Eq,
5245            Expr::unknown(Unknown::new_untyped("col")),
5246            Expr::val(2),
5247        );
5248        assert_eq!(r, Either::Right(expected));
5249    }
5250
5251    #[test]
5252    fn partial_context_fail() {
5253        let context = Context::from_expr(
5254            RestrictedExpr::new_unchecked(
5255                Expr::record([
5256                    ("a".into(), Expr::val(3)),
5257                    ("b".into(), Expr::unknown(Unknown::new_untyped("b"))),
5258                ])
5259                .unwrap(),
5260            )
5261            .as_borrowed(),
5262            Extensions::none(),
5263        )
5264        .unwrap();
5265        let euid: EntityUID = r#"Test::"test""#.parse().unwrap();
5266        let q = Request::new(
5267            (euid.clone(), None),
5268            (euid.clone(), None),
5269            (euid, None),
5270            context,
5271            Some(&RequestSchemaAllPass),
5272            Extensions::none(),
5273        )
5274        .unwrap();
5275        let es = Entities::new();
5276        let eval = Evaluator::new(q, &es, Extensions::none());
5277        let e = Expr::get_attr(Expr::var(Var::Context), "foo".into());
5278        assert_matches!(eval.partial_eval_expr(&e), Err(_))
5279    }
5280
5281    #[test]
5282    fn mikes_test() {
5283        let policyset = parse_policyset(
5284            r#"
5285            permit(
5286                principal == Principal::"p",
5287                action == Action::"a",
5288                resource == Table::"t"
5289            ) when {
5290                context.cell.row > 5 && context.cell.col < 2
5291            };
5292        "#,
5293        )
5294        .expect("Failed to parse");
5295        let policy = policyset
5296            .get(&PolicyID::from_string("policy0"))
5297            .expect("No such policy");
5298
5299        let es = Entities::new();
5300
5301        let p: EntityUID = r#"Principal::"p""#.parse().expect("Failed to parse");
5302        let a: EntityUID = r#"Action::"a""#.parse().expect("Failed to parse");
5303        let r: EntityUID = r#"Table::"t""#.parse().expect("Failed to parse");
5304
5305        let c_expr = RestrictedExpr::new(
5306            Expr::record([("cell".into(), Expr::unknown(Unknown::new_untyped("cell")))]).unwrap(),
5307        )
5308        .expect("should qualify as restricted");
5309        let context = Context::from_expr(c_expr.as_borrowed(), Extensions::none()).unwrap();
5310
5311        let q = Request::new(
5312            (p, None),
5313            (a, None),
5314            (r, None),
5315            context,
5316            Some(&RequestSchemaAllPass),
5317            Extensions::none(),
5318        )
5319        .unwrap();
5320        let eval = Evaluator::new(q, &es, Extensions::none());
5321
5322        let result = eval.partial_evaluate(policy).expect("Eval error");
5323        match result {
5324            Either::Left(_) => panic!("Got a value"),
5325            Either::Right(r) => {
5326                println!("{r}");
5327            }
5328        }
5329    }
5330
5331    fn empty_request() -> Request {
5332        let p: EntityUID = r#"p::"Principal""#.parse().unwrap();
5333        let a: EntityUID = r#"a::"Action""#.parse().unwrap();
5334        let r: EntityUID = r#"r::"Resource""#.parse().unwrap();
5335        let c = Context::empty();
5336        Request::new(
5337            (p, None),
5338            (a, None),
5339            (r, None),
5340            c,
5341            Some(&RequestSchemaAllPass),
5342            Extensions::none(),
5343        )
5344        .unwrap()
5345    }
5346
5347    #[test]
5348    fn if_semantics_residual_guard() {
5349        let a = Expr::unknown(Unknown::new_untyped("guard"));
5350        let b = Expr::and(Expr::val(1), Expr::val(2));
5351        let c = Expr::val(true);
5352
5353        let e = Expr::ite(a, b.clone(), c);
5354
5355        let es = Entities::new();
5356
5357        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5358
5359        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5360
5361        assert_eq!(
5362            r,
5363            PartialValue::Residual(Expr::ite(
5364                Expr::unknown(Unknown::new_untyped("guard")),
5365                b,
5366                Expr::val(true)
5367            ))
5368        )
5369    }
5370
5371    #[test]
5372    fn if_semantics_residual_reduce() {
5373        let a = Expr::binary_app(
5374            BinaryOp::Eq,
5375            Expr::get_attr(Expr::var(Var::Context), "condition".into()),
5376            Expr::val("value"),
5377        );
5378        let b = Expr::val("true branch");
5379        let c = Expr::val("false branch");
5380
5381        let e = Expr::ite(a, b.clone(), c.clone());
5382
5383        let es = Entities::new();
5384
5385        let q = Request::new(
5386            (EntityUID::with_eid("p"), None),
5387            (EntityUID::with_eid("a"), None),
5388            (EntityUID::with_eid("r"), None),
5389            Context::from_expr(
5390                RestrictedExpr::new_unchecked(
5391                    Expr::record([(
5392                        "condition".into(),
5393                        Expr::unknown(Unknown::new_untyped("unknown_condition")),
5394                    )])
5395                    .unwrap(),
5396                )
5397                .as_borrowed(),
5398                Extensions::none(),
5399            )
5400            .unwrap(),
5401            Some(&RequestSchemaAllPass),
5402            Extensions::none(),
5403        )
5404        .unwrap();
5405        let eval = Evaluator::new(q, &es, Extensions::none());
5406
5407        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5408
5409        assert_eq!(
5410            r,
5411            PartialValue::Residual(Expr::ite(
5412                Expr::binary_app(
5413                    BinaryOp::Eq,
5414                    Expr::unknown(Unknown::new_untyped("unknown_condition")),
5415                    Expr::val("value"),
5416                ),
5417                b,
5418                c
5419            ))
5420        );
5421    }
5422
5423    #[test]
5424    fn if_semantics_both_err() {
5425        let a = Expr::unknown(Unknown::new_untyped("guard"));
5426        let b = Expr::and(Expr::val(1), Expr::val(2));
5427        let c = Expr::or(Expr::val(1), Expr::val(3));
5428
5429        let e = Expr::ite(a, b.clone(), c.clone());
5430
5431        let es = Entities::new();
5432
5433        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5434
5435        assert_eq!(
5436            eval.partial_interpret(&e, &HashMap::new()).unwrap(),
5437            PartialValue::Residual(Expr::ite(
5438                Expr::unknown(Unknown::new_untyped("guard")),
5439                b,
5440                c
5441            ))
5442        );
5443    }
5444
5445    #[test]
5446    fn and_semantics1() {
5447        // Left-hand-side evaluates to `false`, should short-circuit to value
5448        let e = Expr::and(
5449            Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(2)),
5450            Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5451        );
5452
5453        let es = Entities::new();
5454        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5455
5456        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5457
5458        assert_eq!(r, PartialValue::Value(Value::from(false)));
5459    }
5460
5461    #[test]
5462    fn and_semantics2() {
5463        // Left hand sides evaluates to `true`, can't drop it due to dynamic types
5464        let e = Expr::and(
5465            Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2)),
5466            Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5467        );
5468
5469        let es = Entities::new();
5470        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5471
5472        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5473
5474        assert_eq!(
5475            r,
5476            PartialValue::Residual(Expr::and(
5477                Expr::val(true),
5478                Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false))
5479            ))
5480        );
5481    }
5482
5483    #[test]
5484    fn and_semantics3() {
5485        // Errors on left hand side should propagate
5486        let e = Expr::and(
5487            Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5488            Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5489        );
5490
5491        let es = Entities::new();
5492        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5493
5494        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5495    }
5496
5497    #[test]
5498    fn and_semantics4() {
5499        // Left hand is residual, errors on right hand side should _not_ propagate
5500        let e = Expr::and(
5501            Expr::binary_app(
5502                BinaryOp::Eq,
5503                Expr::unknown(Unknown::new_untyped("a")),
5504                Expr::val(2),
5505            ),
5506            Expr::and(Expr::val("hello"), Expr::val("bye")),
5507        );
5508
5509        let es = Entities::new();
5510        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5511
5512        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Ok(_));
5513    }
5514
5515    #[test]
5516    fn or_semantics1() {
5517        // Left-hand-side evaluates to `true`, should short-circuit to value
5518
5519        let e = Expr::or(
5520            Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2)),
5521            Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5522        );
5523
5524        let es = Entities::new();
5525        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5526
5527        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5528
5529        assert_eq!(r, PartialValue::Value(Value::from(true)));
5530    }
5531
5532    #[test]
5533    fn or_semantics2() {
5534        // Left hand sides evaluates to `false`, can't drop it due to dynamic types
5535        let e = Expr::or(
5536            Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(2)),
5537            Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5538        );
5539
5540        let es = Entities::new();
5541        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5542
5543        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5544
5545        assert_eq!(
5546            r,
5547            PartialValue::Residual(Expr::or(
5548                Expr::val(false),
5549                Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false))
5550            ))
5551        );
5552    }
5553
5554    #[test]
5555    fn or_semantics3() {
5556        // Errors on left hand side should propagate
5557        let e = Expr::or(
5558            Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5559            Expr::and(Expr::unknown(Unknown::new_untyped("a")), Expr::val(false)),
5560        );
5561
5562        let es = Entities::new();
5563        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5564
5565        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5566    }
5567
5568    #[test]
5569    fn or_semantics4() {
5570        // Left hand is residual, errors on right hand side should _not_ propagate
5571        let e = Expr::or(
5572            Expr::binary_app(
5573                BinaryOp::Eq,
5574                Expr::unknown(Unknown::new_untyped("a")),
5575                Expr::val(2),
5576            ),
5577            Expr::and(Expr::val("hello"), Expr::val("bye")),
5578        );
5579
5580        let es = Entities::new();
5581        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5582
5583        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Ok(_));
5584    }
5585
5586    #[test]
5587    fn record_semantics_err() {
5588        let a = Expr::get_attr(
5589            Expr::record([("value".into(), Expr::unknown(Unknown::new_untyped("test")))]).unwrap(),
5590            "notpresent".into(),
5591        );
5592
5593        let es = Entities::new();
5594        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5595
5596        assert_matches!(eval.partial_interpret(&a, &HashMap::new()), Err(_));
5597    }
5598
5599    #[test]
5600    fn record_semantics_key_present() {
5601        let a = Expr::get_attr(
5602            Expr::record([("value".into(), Expr::unknown(Unknown::new_untyped("test")))]).unwrap(),
5603            "value".into(),
5604        );
5605
5606        let es = Entities::new();
5607        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5608
5609        let r = eval.partial_interpret(&a, &HashMap::new()).unwrap();
5610
5611        let expected = PartialValue::unknown(Unknown::new_untyped("test"));
5612
5613        assert_eq!(r, expected);
5614    }
5615
5616    #[test]
5617    fn record_semantics_missing_attr() {
5618        let a = Expr::get_attr(
5619            Expr::record([
5620                ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
5621                ("b".into(), Expr::unknown(Unknown::new_untyped("c"))),
5622            ])
5623            .unwrap(),
5624            "c".into(),
5625        );
5626
5627        let es = Entities::new();
5628        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5629
5630        assert_matches!(eval.partial_interpret(&a, &HashMap::new()), Err(_));
5631    }
5632
5633    #[test]
5634    fn record_semantics_mult_unknowns() {
5635        let a = Expr::get_attr(
5636            Expr::record([
5637                ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
5638                ("b".into(), Expr::unknown(Unknown::new_untyped("b"))),
5639            ])
5640            .unwrap(),
5641            "b".into(),
5642        );
5643
5644        let es = Entities::new();
5645        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5646
5647        let r = eval.partial_interpret(&a, &HashMap::new()).unwrap();
5648
5649        let expected = PartialValue::unknown(Unknown::new_untyped("b"));
5650
5651        assert_eq!(r, expected);
5652    }
5653
5654    #[test]
5655    fn partial_if_noerrors() {
5656        let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5657        let cons = Expr::val(1);
5658        let alt = Expr::val(2);
5659        let e = Expr::ite(guard.clone(), cons, alt);
5660
5661        let es = Entities::new();
5662        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5663
5664        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5665
5666        let expected = Expr::ite(guard, Expr::val(1), Expr::val(2));
5667
5668        assert_eq!(r, PartialValue::Residual(expected));
5669    }
5670
5671    #[test]
5672    fn parital_if_cons_error() {
5673        let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5674        let cons = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
5675        let alt = Expr::val(2);
5676        let e = Expr::ite(guard.clone(), cons.clone(), alt);
5677
5678        let es = Entities::new();
5679        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5680
5681        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5682
5683        let expected = Expr::ite(guard, cons, Expr::val(2));
5684
5685        assert_eq!(r, PartialValue::Residual(expected));
5686    }
5687
5688    #[test]
5689    fn parital_if_alt_error() {
5690        let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5691        let cons = Expr::val(2);
5692        let alt = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
5693        let e = Expr::ite(guard.clone(), cons, alt.clone());
5694
5695        let es = Entities::new();
5696        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5697
5698        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5699
5700        let expected = Expr::ite(guard, Expr::val(2), alt);
5701        assert_eq!(r, PartialValue::Residual(expected));
5702    }
5703
5704    #[test]
5705    fn parital_if_both_error() {
5706        let guard = Expr::get_attr(Expr::unknown(Unknown::new_untyped("a")), "field".into());
5707        let cons = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(true));
5708        let alt = Expr::less(Expr::val("hello"), Expr::val("bye"));
5709        let e = Expr::ite(guard.clone(), cons.clone(), alt.clone());
5710
5711        let es = Entities::new();
5712        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5713
5714        assert_eq!(
5715            eval.partial_interpret(&e, &HashMap::new()).unwrap(),
5716            PartialValue::Residual(Expr::ite(guard, cons, alt))
5717        );
5718    }
5719
5720    // err && res -> err
5721    #[test]
5722    fn partial_and_err_res() {
5723        let lhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("test"));
5724        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5725        let e = Expr::and(lhs, rhs);
5726        let es = Entities::new();
5727        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5728
5729        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5730    }
5731
5732    // err || res -> err
5733    #[test]
5734    fn partial_or_err_res() {
5735        let lhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("test"));
5736        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5737        let e = Expr::or(lhs, rhs);
5738        let es = Entities::new();
5739        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5740
5741        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5742    }
5743
5744    // true && res -> true && res
5745    #[test]
5746    fn partial_and_true_res() {
5747        let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(1));
5748        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5749        let e = Expr::and(lhs, rhs);
5750        let es = Entities::new();
5751        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5752
5753        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5754
5755        let expected = Expr::and(
5756            Expr::val(true),
5757            Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5758        );
5759        assert_eq!(r, PartialValue::Residual(expected));
5760    }
5761
5762    // false && res -> false
5763    #[test]
5764    fn partial_and_false_res() {
5765        let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5766        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5767        let e = Expr::and(lhs, rhs);
5768        let es = Entities::new();
5769        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5770
5771        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5772        assert_eq!(r, PartialValue::Value(Value::from(false)));
5773    }
5774
5775    // res && true -> res && true
5776    #[test]
5777    fn partial_and_res_true() {
5778        let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5779        let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2));
5780        let e = Expr::and(lhs.clone(), rhs.clone());
5781        let es = Entities::new();
5782        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5783
5784        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5785        let expected = Expr::and(lhs, rhs);
5786        assert_eq!(r, PartialValue::Residual(expected));
5787    }
5788
5789    #[test]
5790    fn partial_and_res_false() {
5791        let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5792        let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5793        let e = Expr::and(lhs.clone(), rhs.clone());
5794        let es = Entities::new();
5795        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5796
5797        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5798        let expected = Expr::and(lhs, rhs);
5799        assert_eq!(r, PartialValue::Residual(expected));
5800    }
5801
5802    // res && res -> res && res
5803    #[test]
5804    fn partial_and_res_res() {
5805        let lhs = Expr::unknown(Unknown::new_untyped("b"));
5806        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5807        let e = Expr::and(lhs, rhs);
5808        let es = Entities::new();
5809        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5810
5811        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5812
5813        let expected = Expr::and(
5814            Expr::unknown(Unknown::new_untyped("b")),
5815            Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5816        );
5817        assert_eq!(r, PartialValue::Residual(expected));
5818    }
5819
5820    // res && err -> res && err
5821    #[test]
5822    fn partial_and_res_err() {
5823        let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5824        let rhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("oops"));
5825        let e = Expr::and(lhs, rhs.clone());
5826        let es = Entities::new();
5827        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5828
5829        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5830
5831        let expected = Expr::and(
5832            Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5833            rhs,
5834        );
5835        assert_eq!(r, PartialValue::Residual(expected));
5836    }
5837
5838    // true || res -> true
5839    #[test]
5840    fn partial_or_true_res() {
5841        let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(1), Expr::val(1));
5842        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5843        let e = Expr::or(lhs, rhs);
5844        let es = Entities::new();
5845        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5846
5847        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5848        assert_eq!(r, PartialValue::Value(Value::from(true)));
5849    }
5850
5851    // false || res -> false || res
5852    #[test]
5853    fn partial_or_false_res() {
5854        let lhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5855        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5856        let e = Expr::or(lhs, rhs);
5857        let es = Entities::new();
5858        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5859
5860        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5861        let expected = Expr::or(
5862            Expr::val(false),
5863            Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5864        );
5865        assert_eq!(r, PartialValue::Residual(expected));
5866    }
5867
5868    // res || true -> res || true
5869    #[test]
5870    fn partial_or_res_true() {
5871        let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5872        let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(2));
5873        let e = Expr::or(lhs.clone(), rhs.clone());
5874        let es = Entities::new();
5875        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5876
5877        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5878        let expected = Expr::or(lhs, rhs);
5879        assert_eq!(r, PartialValue::Residual(expected));
5880    }
5881
5882    #[test]
5883    fn partial_or_res_false() {
5884        let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5885        let rhs = Expr::binary_app(BinaryOp::Eq, Expr::val(2), Expr::val(1));
5886        let e = Expr::or(lhs.clone(), rhs.clone());
5887        let es = Entities::new();
5888        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5889
5890        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5891        let expected = Expr::or(lhs, rhs);
5892        assert_eq!(r, PartialValue::Residual(expected));
5893    }
5894
5895    // res || res -> res || res
5896    #[test]
5897    fn partial_or_res_res() {
5898        let lhs = Expr::unknown(Unknown::new_untyped("b"));
5899        let rhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5900        let e = Expr::or(lhs, rhs);
5901        let es = Entities::new();
5902        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5903
5904        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5905
5906        let expected = Expr::or(
5907            Expr::unknown(Unknown::new_untyped("b")),
5908            Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5909        );
5910        assert_eq!(r, PartialValue::Residual(expected));
5911    }
5912
5913    // res || err -> res || err
5914    #[test]
5915    fn partial_or_res_err() {
5916        let lhs = Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into());
5917        let rhs = Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("oops"));
5918        let e = Expr::or(lhs, rhs.clone());
5919        let es = Entities::new();
5920        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5921
5922        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5923
5924        let expected = Expr::or(
5925            Expr::get_attr(Expr::unknown(Unknown::new_untyped("test")), "field".into()),
5926            rhs,
5927        );
5928        assert_eq!(r, PartialValue::Residual(expected));
5929    }
5930
5931    #[test]
5932    fn partial_unop() {
5933        let es = Entities::new();
5934        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5935
5936        let e = Expr::unary_app(UnaryOp::Neg, Expr::unknown(Unknown::new_untyped("a")));
5937        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5938        assert_eq!(r, PartialValue::Residual(e));
5939
5940        let e = Expr::unary_app(UnaryOp::Not, Expr::unknown(Unknown::new_untyped("a")));
5941        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5942        assert_eq!(r, PartialValue::Residual(e));
5943
5944        let e = Expr::unary_app(UnaryOp::IsEmpty, Expr::unknown(Unknown::new_untyped("a")));
5945        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5946        assert_eq!(r, PartialValue::Residual(e));
5947    }
5948
5949    #[test]
5950    fn partial_binop() {
5951        let es = Entities::new();
5952        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
5953
5954        let binops = [
5955            BinaryOp::Add,
5956            BinaryOp::Contains,
5957            BinaryOp::ContainsAll,
5958            BinaryOp::ContainsAny,
5959            BinaryOp::Eq,
5960            BinaryOp::In,
5961            BinaryOp::Less,
5962            BinaryOp::LessEq,
5963            BinaryOp::Sub,
5964        ];
5965
5966        for binop in binops {
5967            // ensure PE evaluates left side
5968            let e = Expr::binary_app(
5969                binop,
5970                Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5971                Expr::unknown(Unknown::new_untyped("a")),
5972            );
5973            let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5974            let expected = Expr::binary_app(
5975                binop,
5976                Expr::val(3),
5977                Expr::unknown(Unknown::new_untyped("a")),
5978            );
5979            assert_eq!(r, PartialValue::Residual(expected));
5980            // ensure PE propagates left side errors
5981            let e = Expr::binary_app(
5982                binop,
5983                Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
5984                Expr::unknown(Unknown::new_untyped("a")),
5985            );
5986            assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
5987            // ensure PE evaluates right side
5988            let e = Expr::binary_app(
5989                binop,
5990                Expr::unknown(Unknown::new_untyped("a")),
5991                Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
5992            );
5993            let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
5994            let expected = Expr::binary_app(
5995                binop,
5996                Expr::unknown(Unknown::new_untyped("a")),
5997                Expr::val(3),
5998            );
5999            assert_eq!(r, PartialValue::Residual(expected));
6000            // ensure PE propagates right side errors
6001            let e = Expr::binary_app(
6002                binop,
6003                Expr::unknown(Unknown::new_untyped("a")),
6004                Expr::binary_app(BinaryOp::Add, Expr::val("hello"), Expr::val(2)),
6005            );
6006            assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6007            // Both left and right residuals
6008            let e = Expr::binary_app(
6009                binop,
6010                Expr::unknown(Unknown::new_untyped("a")),
6011                Expr::unknown(Unknown::new_untyped("b")),
6012            );
6013            let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6014            let expected = Expr::binary_app(
6015                binop,
6016                Expr::unknown(Unknown::new_untyped("a")),
6017                Expr::unknown(Unknown::new_untyped("b")),
6018            );
6019            assert_eq!(r, PartialValue::Residual(expected));
6020        }
6021    }
6022
6023    #[test]
6024    fn partial_mul() {
6025        let es = Entities::new();
6026        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6027
6028        let e = Expr::mul(Expr::unknown(Unknown::new_untyped("a")), Expr::val(32));
6029        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6030        assert_eq!(r, PartialValue::Residual(e));
6031    }
6032
6033    #[test]
6034    fn partial_ext_constructors() {
6035        let es = Entities::new();
6036        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6037
6038        let e = Expr::call_extension_fn(
6039            "ip".parse().unwrap(),
6040            vec![Expr::unknown(Unknown::new_untyped("a"))],
6041        );
6042
6043        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6044
6045        assert_eq!(r, PartialValue::Residual(e));
6046    }
6047
6048    #[cfg(feature = "ipaddr")]
6049    #[test]
6050    fn partial_ext_unfold() {
6051        let es = Entities::new();
6052        let eval = Evaluator::new(empty_request(), &es, Extensions::all_available());
6053
6054        let a = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("127.0.0.1/32")]);
6055        let b = Expr::unknown(Unknown::new_untyped("a"));
6056        let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
6057
6058        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6059
6060        assert_eq!(r, PartialValue::Residual(e));
6061
6062        let b = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("127.0.0.1/32")]);
6063        let a = Expr::unknown(Unknown::new_untyped("a"));
6064        let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
6065
6066        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6067
6068        assert_eq!(r, PartialValue::Residual(e));
6069
6070        let b = Expr::call_extension_fn("ip".parse().unwrap(), vec![Expr::val("invalid")]);
6071        let a = Expr::unknown(Unknown::new_untyped("a"));
6072        let e = Expr::call_extension_fn("isInRange".parse().unwrap(), vec![a, b]);
6073
6074        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6075    }
6076
6077    #[test]
6078    fn partial_like() {
6079        let es = Entities::new();
6080        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6081
6082        let e = Expr::like(
6083            Expr::unknown(Unknown::new_untyped("a")),
6084            Pattern::from(vec![]),
6085        );
6086
6087        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6088
6089        assert_eq!(r, PartialValue::Residual(e));
6090    }
6091
6092    #[test]
6093    fn partial_is() {
6094        let es = Entities::new();
6095        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6096
6097        let e = Expr::is_entity_type(
6098            Expr::unknown(Unknown::new_untyped("a")),
6099            "User".parse().unwrap(),
6100        );
6101
6102        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6103
6104        assert_eq!(r, PartialValue::Residual(e));
6105    }
6106
6107    #[test]
6108    fn partial_hasattr() {
6109        let es = Entities::new();
6110        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6111
6112        let e = Expr::has_attr(Expr::unknown(Unknown::new_untyped("a")), "test".into());
6113
6114        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6115
6116        assert_eq!(r, PartialValue::Residual(e));
6117    }
6118
6119    #[test]
6120    fn partial_set() {
6121        let es = Entities::new();
6122        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6123
6124        let e = Expr::set([
6125            Expr::val(1),
6126            Expr::unknown(Unknown::new_untyped("a")),
6127            Expr::val(2),
6128        ]);
6129        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6130        assert_eq!(r, PartialValue::Residual(e));
6131
6132        let e = Expr::set([
6133            Expr::val(1),
6134            Expr::unknown(Unknown::new_untyped("a")),
6135            Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
6136        ]);
6137        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6138        assert_eq!(
6139            r,
6140            PartialValue::Residual(Expr::set([
6141                Expr::val(1),
6142                Expr::unknown(Unknown::new_untyped("a")),
6143                Expr::val(3)
6144            ]))
6145        );
6146
6147        let e = Expr::set([
6148            Expr::val(1),
6149            Expr::unknown(Unknown::new_untyped("a")),
6150            Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("a")),
6151        ]);
6152        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6153    }
6154
6155    #[test]
6156    fn partial_record() {
6157        let es = Entities::new();
6158        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6159
6160        let e = Expr::record([
6161            ("a".into(), Expr::val(1)),
6162            ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6163            ("c".into(), Expr::val(2)),
6164        ])
6165        .unwrap();
6166        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6167        assert_eq!(r, PartialValue::Residual(e));
6168
6169        let e = Expr::record([
6170            ("a".into(), Expr::val(1)),
6171            ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
6172        ]);
6173        assert_eq!(
6174            e,
6175            Err(expression_construction_errors::DuplicateKeyError {
6176                key: "a".into(),
6177                context: "in record literal",
6178            }
6179            .into())
6180        );
6181
6182        let e = Expr::record([
6183            ("a".into(), Expr::unknown(Unknown::new_untyped("a"))),
6184            ("a".into(), Expr::val(1)),
6185        ]);
6186        assert_eq!(
6187            e,
6188            Err(expression_construction_errors::DuplicateKeyError {
6189                key: "a".into(),
6190                context: "in record literal",
6191            }
6192            .into())
6193        );
6194
6195        let e = Expr::record([
6196            ("a".into(), Expr::val(1)),
6197            ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6198            (
6199                "c".into(),
6200                Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val(2)),
6201            ),
6202        ])
6203        .unwrap();
6204        let r = eval.partial_interpret(&e, &HashMap::new()).unwrap();
6205        assert_eq!(
6206            r,
6207            PartialValue::Residual(
6208                Expr::record([
6209                    ("a".into(), Expr::val(1)),
6210                    ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6211                    ("c".into(), Expr::val(3))
6212                ])
6213                .unwrap()
6214            )
6215        );
6216
6217        let e = Expr::record([
6218            ("a".into(), Expr::val(1)),
6219            ("b".into(), Expr::unknown(Unknown::new_untyped("a"))),
6220            (
6221                "c".into(),
6222                Expr::binary_app(BinaryOp::Add, Expr::val(1), Expr::val("hello")),
6223            ),
6224        ])
6225        .unwrap();
6226        assert_matches!(eval.partial_interpret(&e, &HashMap::new()), Err(_));
6227    }
6228
6229    #[test]
6230    fn small() {
6231        let e = parser::parse_expr("[[1]]").unwrap();
6232        let re = RestrictedExpr::new(e).unwrap();
6233        let eval = RestrictedEvaluator::new(Extensions::none());
6234        let r = eval.partial_interpret(re.as_borrowed()).unwrap();
6235        assert_matches!(r, PartialValue::Value(Value { value: ValueKind::Set(set), .. }) => {
6236            assert_eq!(set.len(), 1);
6237        });
6238    }
6239
6240    #[test]
6241    fn unprojectable_residual() {
6242        let q = basic_request();
6243        let entities = basic_entities();
6244        let eval = Evaluator::new(q, &entities, Extensions::none());
6245
6246        let e = Expr::get_attr(
6247            Expr::record([
6248                (
6249                    "a".into(),
6250                    Expr::binary_app(
6251                        BinaryOp::Add,
6252                        Expr::unknown(Unknown::new_untyped("a")),
6253                        Expr::val(3),
6254                    ),
6255                ),
6256                ("b".into(), Expr::val(83)),
6257            ])
6258            .unwrap(),
6259            "b".into(),
6260        );
6261        let r = eval.partial_eval_expr(&e).unwrap();
6262        assert_eq!(r, Either::Right(e));
6263
6264        let e = Expr::get_attr(
6265            Expr::record([(
6266                "a".into(),
6267                Expr::binary_app(
6268                    BinaryOp::Add,
6269                    Expr::unknown(Unknown::new_untyped("a")),
6270                    Expr::val(3),
6271                ),
6272            )])
6273            .unwrap(),
6274            "b".into(),
6275        );
6276        assert_matches!(eval.partial_eval_expr(&e), Err(_));
6277    }
6278
6279    #[test]
6280    fn interpret_extended_has() {
6281        let es = Entities::new();
6282        let eval = Evaluator::new(empty_request(), &es, Extensions::none());
6283        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6284    {a: {b: {c: 1}}} has a.b.c
6285        "#).unwrap()), Ok(v) => {
6286            assert_eq!(v, Value::from(true));
6287        });
6288        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6289    {a: {b: {c: 1}}} has a.b
6290        "#).unwrap()), Ok(v) => {
6291            assert_eq!(v, Value::from(true));
6292        });
6293        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6294    {a: {b: {c: 1}}} has a
6295        "#).unwrap()), Ok(v) => {
6296            assert_eq!(v, Value::from(true));
6297        });
6298        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6299    {a: {b: {c: 1}}} has b.c
6300        "#).unwrap()), Ok(v) => {
6301            assert_eq!(v, Value::from(false));
6302        });
6303        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6304    {a: {b: {c: 1}}} has c
6305        "#).unwrap()), Ok(v) => {
6306            assert_eq!(v, Value::from(false));
6307        });
6308        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6309    {a: {b: {c: 1}}} has d
6310        "#).unwrap()), Ok(v) => {
6311            assert_eq!(v, Value::from(false));
6312        });
6313        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6314    {a: {b: {c: 1}}} has "🚫"
6315        "#).unwrap()), Ok(v) => {
6316            assert_eq!(v, Value::from(false));
6317        });
6318
6319        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6320    {a: {b: {c: 1}}} has a.b.c && {a: {b: {c: 1}}}.a.b.c == 1
6321        "#).unwrap()), Ok(v) => {
6322            assert_eq!(v, Value::from(true));
6323        });
6324        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6325    {a: {b: {c: 1}}} has a.b && {a: {b: {c: 1}}}.a.b == {c: 1}
6326        "#).unwrap()), Ok(v) => {
6327            assert_eq!(v, Value::from(true));
6328        });
6329        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6330    {a: {b: {c: 1}}} has a && {a: {b: {c: 1}}}.a == {b: {c: 1}}
6331        "#).unwrap()), Ok(v) => {
6332            assert_eq!(v, Value::from(true));
6333        });
6334        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6335    {a: {b: {d: 1}}} has a.b.c && {a: {b: {d: 1}}}.a.b.c == 1
6336        "#).unwrap()), Ok(v) => {
6337            assert_eq!(v, Value::from(false));
6338        });
6339
6340        assert_matches!(eval.interpret_inline_policy(&parse_expr(r#"
6341        {a: {b: {c: 1}}} has a.b && {a: {b: {c: 1}}}.a.b.d == 1
6342            "#).unwrap()), Err(EvaluationError::RecordAttrDoesNotExist(err)) => {
6343            assert_eq!(err.attr, "d");
6344        });
6345    }
6346
6347    #[test]
6348    fn typed_unknown_entity_id() {
6349        let mut q = basic_request();
6350        let entities = basic_entities();
6351        q.principal = EntityUIDEntry::unknown_with_type(
6352            EntityType::from_str("different_test_type").expect("must parse"),
6353            None,
6354        );
6355        q.resource = EntityUIDEntry::unknown_with_type(
6356            EntityType::from_str("other_different_test_type").expect("must parse"),
6357            None,
6358        );
6359        let eval = Evaluator::new(q, &entities, Extensions::none());
6360
6361        let e = Expr::is_entity_type(Expr::var(Var::Principal), EntityUID::test_entity_type());
6362        let r = eval.partial_eval_expr(&e).unwrap();
6363        assert_eq!(r, Either::Left(Value::from(false)));
6364
6365        let e = Expr::is_eq(
6366            Expr::var(Var::Principal),
6367            Expr::val(EntityUID::with_eid("something")),
6368        );
6369        let r = eval.partial_eval_expr(&e).unwrap();
6370        assert_eq!(r, Either::Left(Value::from(false)));
6371
6372        let e = Expr::noteq(
6373            Expr::val(EntityUID::with_eid("something")),
6374            Expr::var(Var::Principal),
6375        );
6376        let r = eval.partial_eval_expr(&e).unwrap();
6377        assert_eq!(r, Either::Left(Value::from(true)));
6378
6379        // Two differently typed unknowns should not be equal
6380        let e = Expr::is_eq(Expr::var(Var::Principal), Expr::var(Var::Resource));
6381        let r = eval.partial_eval_expr(&e).unwrap();
6382        assert_eq!(r, Either::Left(Value::from(false)));
6383    }
6384}