cedar_policy_core/
evaluator.rs

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