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