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