Skip to main content

txtx_core/std/functions/
assertions.rs

1use super::arg_checker;
2use kit::types::commands::AssertionResult;
3use kit::types::commands::ASSERTION_TYPE_ID;
4use txtx_addon_kit::types::functions::FunctionSpecification;
5use txtx_addon_kit::types::types::{Type, Value};
6use txtx_addon_kit::types::AuthorizationContext;
7use txtx_addon_kit::{
8    define_function, indoc,
9    types::{diagnostics::Diagnostic, functions::FunctionImplementation},
10};
11
12lazy_static! {
13    pub static ref FUNCTIONS: Vec<FunctionSpecification> = vec![
14        define_function! {
15            AssertEq => {
16                name: "assert_eq",
17                documentation: "`assert_eq` asserts that two values are equal.",
18                example: indoc!{r#"
19                    output "assertion" { 
20                        value = std::assert_eq(action.example.result, 1)
21                    }
22                "#},
23                inputs: [
24                    left: {
25                        documentation: "A value to compare.",
26                        typing: vec![Type::null(), Type::integer(), Type::float(), Type::integer(), Type::string(), Type::bool(), Type::addon(""), Type::array(Type::null()), Type::arbitrary_object()],
27                        optional: false
28                    },
29                    right: {
30                        documentation: "The value to compare against.",
31                        typing: vec![Type::null(), Type::integer(), Type::float(), Type::integer(), Type::string(), Type::bool(), Type::addon(""), Type::array(Type::null()), Type::arbitrary_object()],
32                        optional: false
33                    }
34                ],
35                output: {
36                    documentation: "The result of the assertion.",
37                    typing: Type::addon(ASSERTION_TYPE_ID)
38                },
39            }
40        },
41        define_function! {
42            AssertNe => {
43                name: "assert_ne",
44                documentation: "`assert_ne` asserts that two values are not equal.",
45                example: indoc!{r#"
46                    output "assertion" { 
47                        value = std::assert_ne(action.example.result, 1)
48                    }
49                "#},
50                inputs: [
51                    left: {
52                        documentation: "A value to compare.",
53                        typing: vec![Type::null(), Type::integer(), Type::float(), Type::integer(), Type::string(), Type::bool(), Type::addon(""), Type::array(Type::null()), Type::arbitrary_object()],
54                        optional: false
55                    },
56                    right: {
57                        documentation: "The value to compare against.",
58                        typing: vec![Type::null(), Type::integer(), Type::float(), Type::integer(), Type::string(), Type::bool(), Type::addon(""), Type::array(Type::null()), Type::arbitrary_object()],
59                        optional: false
60                    }
61                ],
62                output: {
63                    documentation: "The result of the assertion.",
64                    typing: Type::addon(ASSERTION_TYPE_ID)
65                },
66            }
67        },
68        define_function! {
69            AssertGt => {
70                name: "assert_gt",
71                documentation: "`assert_gt` asserts that the left value is greater than the right value.",
72                example: indoc!{r#"
73                    output "assertion" { 
74                        value = std::assert_gt(action.example.result, 1)
75                    }
76                "#},
77                inputs: [
78                    left: {
79                        documentation: "An integer or float to compare.",
80                        typing: vec![Type::integer(), Type::float()],
81                        optional: false
82                    },
83                    right: {
84                        documentation: "An integer or float to compare against.",
85                        typing: vec![Type::integer(), Type::float()],
86                        optional: false
87                    }
88                ],
89                output: {
90                    documentation: "The result of the assertion.",
91                    typing: Type::addon(ASSERTION_TYPE_ID)
92                },
93            }
94        },
95        define_function! {
96            AssertGte => {
97                name: "assert_gte",
98                documentation: "`assert_gte` asserts that the left value is greater than or equal to the right value.",
99                example: indoc!{r#"
100                    output "assertion" { 
101                        value = std::assert_gte(action.example.result, 1)
102                    }
103                "#},
104                inputs: [
105                    left: {
106                        documentation: "An integer or float to compare.",
107                        typing: vec![Type::integer(), Type::float()],
108                        optional: false
109                    },
110                    right: {
111                        documentation: "An integer or float to compare against.",
112                        typing: vec![Type::integer(), Type::float()],
113                        optional: false
114                    }
115                ],
116                output: {
117                    documentation: "The result of the assertion.",
118                    typing: Type::addon(ASSERTION_TYPE_ID)
119                },
120            }
121        },
122        define_function! {
123            AssertLt => {
124                name: "assert_lt",
125                documentation: "`assert_lt` asserts that the left value is less than the right value.",
126                example: indoc!{r#"
127                    output "assertion" { 
128                        value = std::assert_lt(action.example.result, 1)
129                    }
130                "#},
131                inputs: [
132                    left: {
133                        documentation: "An integer or float to compare.",
134                        typing: vec![Type::integer(), Type::float()],
135                        optional: false
136                    },
137                    right: {
138                        documentation: "An integer or float to compare against.",
139                        typing: vec![Type::integer(), Type::float()],
140                        optional: false
141                    }
142                ],
143                output: {
144                    documentation: "The result of the assertion.",
145                    typing: Type::addon(ASSERTION_TYPE_ID)
146                },
147            }
148        },
149        define_function! {
150            AssertLte => {
151                name: "assert_lte",
152                documentation: "`assert_lte` asserts that the left value is less than or equal to the right value.",
153                example: indoc!{r#"
154                    output "assertion" { 
155                        value = std::assert_lte(action.example.result, 1)
156                    }
157                "#},
158                inputs: [
159                    left: {
160                        documentation: "An integer or float to compare.",
161                        typing: vec![Type::integer(), Type::float()],
162                        optional: false
163                    },
164                    right: {
165                        documentation: "An integer or float to compare against.",
166                        typing: vec![Type::integer(), Type::float()],
167                        optional: false
168                    }
169                ],
170                output: {
171                    documentation: "The result of the assertion.",
172                    typing: Type::addon(ASSERTION_TYPE_ID)
173                },
174            }
175        }
176    ];
177}
178
179pub struct AssertEq;
180impl FunctionImplementation for AssertEq {
181    fn check_instantiability(
182        _fn_spec: &FunctionSpecification,
183        _auth_ctx: &AuthorizationContext,
184        _args: &Vec<Type>,
185    ) -> Result<Type, Diagnostic> {
186        unimplemented!()
187    }
188
189    fn run(
190        fn_spec: &FunctionSpecification,
191        _auth_ctx: &AuthorizationContext,
192        args: &Vec<Value>,
193    ) -> Result<Value, Diagnostic> {
194        arg_checker(fn_spec, args)?;
195        let left = &args[0];
196        let right = &args[1];
197        if left.eq(right) {
198            Ok(AssertionResult::Success.to_value())
199        } else {
200            Ok(AssertionResult::Failure(format!(
201                "assertion failed: expected values to be equal: left: '{}', right: '{}'",
202                left.to_string(),
203                right.to_string()
204            ))
205            .to_value())
206        }
207    }
208}
209
210pub struct AssertNe;
211impl FunctionImplementation for AssertNe {
212    fn check_instantiability(
213        _fn_spec: &FunctionSpecification,
214        _auth_ctx: &AuthorizationContext,
215        _args: &Vec<Type>,
216    ) -> Result<Type, Diagnostic> {
217        unimplemented!()
218    }
219
220    fn run(
221        fn_spec: &FunctionSpecification,
222        _auth_ctx: &AuthorizationContext,
223        args: &Vec<Value>,
224    ) -> Result<Value, Diagnostic> {
225        arg_checker(fn_spec, args)?;
226        let left = &args[0];
227        let right = &args[1];
228        if left.ne(right) {
229            Ok(AssertionResult::Success.to_value())
230        } else {
231            Ok(AssertionResult::Failure(format!(
232                "assertion failed: expected values to be not equal: left: '{}', right: '{}'",
233                left.to_string(),
234                right.to_string()
235            ))
236            .to_value())
237        }
238    }
239}
240
241pub struct AssertGt;
242impl FunctionImplementation for AssertGt {
243    fn check_instantiability(
244        _fn_spec: &FunctionSpecification,
245        _auth_ctx: &AuthorizationContext,
246        _args: &Vec<Type>,
247    ) -> Result<Type, Diagnostic> {
248        unimplemented!()
249    }
250
251    fn run(
252        fn_spec: &FunctionSpecification,
253        _auth_ctx: &AuthorizationContext,
254        args: &Vec<Value>,
255    ) -> Result<Value, Diagnostic> {
256        arg_checker(fn_spec, args)?;
257        let left = &args[0];
258        let right = &args[1];
259        match (left, right) {
260            (Value::Integer(left_int), Value::Integer(right_int)) => {
261                if left_int > right_int {
262                    Ok(AssertionResult::Success.to_value())
263                } else {
264                    Ok(AssertionResult::Failure(format!(
265                        "assertion failed: expected left value '{}' to be greater than right value '{}'",
266                        left_int, right_int
267                    ))
268                    .to_value())
269                }
270            }
271            (Value::Float(left_float), Value::Float(right_float)) => {
272                if left_float > right_float {
273                    Ok(AssertionResult::Success.to_value())
274                } else {
275                    Ok(AssertionResult::Failure(format!(
276                        "assertion failed: expected left value '{}' to be greater than right value '{}'",
277                        left_float, right_float
278                    ))
279                    .to_value())
280                }
281            }
282            (Value::Float(left_float), Value::Integer(right_int)) => {
283                if *left_float > *right_int as f64 {
284                    Ok(AssertionResult::Success.to_value())
285                } else {
286                    Ok(AssertionResult::Failure(format!(
287                        "assertion failed: expected left value '{}' to be greater than right value '{}'",
288                        left_float, right_int
289                    ))
290                    .to_value())
291                }
292            }
293            (Value::Integer(left_int), Value::Float(right_float)) => {
294                if *left_int as f64 > *right_float {
295                    Ok(AssertionResult::Success.to_value())
296                } else {
297                    Ok(AssertionResult::Failure(format!(
298                        "assertion failed: expected left value '{}' to be greater than right value '{}'",
299                        left_int, right_float
300                    ))
301                    .to_value())
302                }
303            }
304            _ => unreachable!(),
305        }
306    }
307}
308
309pub struct AssertGte;
310impl FunctionImplementation for AssertGte {
311    fn check_instantiability(
312        _fn_spec: &FunctionSpecification,
313        _auth_ctx: &AuthorizationContext,
314        _args: &Vec<Type>,
315    ) -> Result<Type, Diagnostic> {
316        unimplemented!()
317    }
318
319    fn run(
320        fn_spec: &FunctionSpecification,
321        _auth_ctx: &AuthorizationContext,
322        args: &Vec<Value>,
323    ) -> Result<Value, Diagnostic> {
324        arg_checker(fn_spec, args)?;
325        let left = &args[0];
326        let right = &args[1];
327        match (left, right) {
328            (Value::Integer(left_int), Value::Integer(right_int)) => {
329                if left_int >= right_int {
330                    Ok(AssertionResult::Success.to_value())
331                } else {
332                    Ok(AssertionResult::Failure(format!(
333                        "assertion failed: expected left value '{}' to be greater than or equal to right value '{}'",
334                        left_int, right_int
335                    ))
336                    .to_value())
337                }
338            }
339            (Value::Float(left_float), Value::Float(right_float)) => {
340                if left_float >= right_float {
341                    Ok(AssertionResult::Success.to_value())
342                } else {
343                    Ok(AssertionResult::Failure(format!(
344                        "assertion failed: expected left value '{}' to be greater than or equal to right value '{}'",
345                        left_float, right_float
346                    ))
347                    .to_value())
348                }
349            }
350            (Value::Float(left_float), Value::Integer(right_int)) => {
351                if *left_float >= *right_int as f64 {
352                    Ok(AssertionResult::Success.to_value())
353                } else {
354                    Ok(AssertionResult::Failure(format!(
355                        "assertion failed: expected left value '{}' to be greater than or equal to right value '{}'",
356                        left_float, right_int
357                    ))
358                    .to_value())
359                }
360            }
361            (Value::Integer(left_int), Value::Float(right_float)) => {
362                if *left_int as f64 >= *right_float {
363                    Ok(AssertionResult::Success.to_value())
364                } else {
365                    Ok(AssertionResult::Failure(format!(
366                        "assertion failed: expected left value '{}' to be greater than or equal to right value '{}'",
367                        left_int, right_float
368                    ))
369                    .to_value())
370                }
371            }
372            _ => unreachable!(),
373        }
374    }
375}
376
377pub struct AssertLt;
378impl FunctionImplementation for AssertLt {
379    fn check_instantiability(
380        _fn_spec: &FunctionSpecification,
381        _auth_ctx: &AuthorizationContext,
382        _args: &Vec<Type>,
383    ) -> Result<Type, Diagnostic> {
384        unimplemented!()
385    }
386
387    fn run(
388        fn_spec: &FunctionSpecification,
389        _auth_ctx: &AuthorizationContext,
390        args: &Vec<Value>,
391    ) -> Result<Value, Diagnostic> {
392        arg_checker(fn_spec, args)?;
393        let left = &args[0];
394        let right = &args[1];
395        match (left, right) {
396            (Value::Integer(left_int), Value::Integer(right_int)) => {
397                if left_int < right_int {
398                    Ok(AssertionResult::Success.to_value())
399                } else {
400                    Ok(AssertionResult::Failure(format!(
401                        "assertion failed: expected left value '{}' to be less than right value '{}'",
402                        left_int, right_int
403                    ))
404                    .to_value())
405                }
406            }
407            (Value::Float(left_float), Value::Float(right_float)) => {
408                if left_float < right_float {
409                    Ok(AssertionResult::Success.to_value())
410                } else {
411                    Ok(AssertionResult::Failure(format!(
412                        "assertion failed: expected left value '{}' to be less than right value '{}'",
413                        left_float, right_float
414                    ))
415                    .to_value())
416                }
417            }
418            (Value::Float(left_float), Value::Integer(right_int)) => {
419                if *left_float < *right_int as f64 {
420                    Ok(AssertionResult::Success.to_value())
421                } else {
422                    Ok(AssertionResult::Failure(format!(
423                        "assertion failed: expected left value '{}' to be less than right value '{}'",
424                        left_float, right_int
425                    ))
426                    .to_value())
427                }
428            }
429            (Value::Integer(left_int), Value::Float(right_float)) => {
430                if (*left_int as f64) < (*right_float) {
431                    Ok(AssertionResult::Success.to_value())
432                } else {
433                    Ok(AssertionResult::Failure(format!(
434                        "assertion failed: expected left value '{}' to be less than right value '{}'",
435                        left_int, right_float
436                    ))
437                    .to_value())
438                }
439            }
440            _ => unreachable!(),
441        }
442    }
443}
444
445pub struct AssertLte;
446impl FunctionImplementation for AssertLte {
447    fn check_instantiability(
448        _fn_spec: &FunctionSpecification,
449        _auth_ctx: &AuthorizationContext,
450        _args: &Vec<Type>,
451    ) -> Result<Type, Diagnostic> {
452        unimplemented!()
453    }
454
455    fn run(
456        fn_spec: &FunctionSpecification,
457        _auth_ctx: &AuthorizationContext,
458        args: &Vec<Value>,
459    ) -> Result<Value, Diagnostic> {
460        arg_checker(fn_spec, args)?;
461        let left = &args[0];
462        let right = &args[1];
463        match (left, right) {
464            (Value::Integer(left_int), Value::Integer(right_int)) => {
465                if left_int <= right_int {
466                    Ok(AssertionResult::Success.to_value())
467                } else {
468                    Ok(AssertionResult::Failure(format!(
469                        "assertion failed: expected left value '{}' to be less than or equal to right value '{}'",
470                        left_int, right_int
471                    ))
472                    .to_value())
473                }
474            }
475            (Value::Float(left_float), Value::Float(right_float)) => {
476                if left_float <= right_float {
477                    Ok(AssertionResult::Success.to_value())
478                } else {
479                    Ok(AssertionResult::Failure(format!(
480                        "assertion failed: expected left value '{}' to be less than or equal to right value '{}'",
481                        left_float, right_float
482                    ))
483                    .to_value())
484                }
485            }
486            (Value::Float(left_float), Value::Integer(right_int)) => {
487                if *left_float <= *right_int as f64 {
488                    Ok(AssertionResult::Success.to_value())
489                } else {
490                    Ok(AssertionResult::Failure(format!(
491                        "assertion failed: expected left value '{}' to be less than or equal to right value '{}'",
492                        left_float, right_int
493                    ))
494                    .to_value())
495                }
496            }
497            (Value::Integer(left_int), Value::Float(right_float)) => {
498                if (*left_int as f64) <= (*right_float) {
499                    Ok(AssertionResult::Success.to_value())
500                } else {
501                    Ok(AssertionResult::Failure(format!(
502                        "assertion failed: expected left value '{}' to be less than or equal to right value '{}'",
503                        left_int, right_float
504                    ))
505                    .to_value())
506                }
507            }
508            _ => unreachable!(),
509        }
510    }
511}
512
513#[cfg(test)]
514mod tests {
515    use kit::helpers::fs::FileLocation;
516    use test_case::test_case;
517
518    use super::*;
519
520    fn get_spec_by_name(name: &str) -> FunctionSpecification {
521        FUNCTIONS.iter().find(|f| f.name == name).cloned().unwrap()
522    }
523
524    fn dummy_auth_ctx() -> AuthorizationContext {
525        AuthorizationContext { workspace_location: FileLocation::working_dir() }
526    }
527
528    #[test_case("assert_eq", Value::Integer(5), Value::Integer(5), AssertionResult::Success; "assert_eq success")]
529    #[test_case("assert_eq", Value::String("foo".into()), Value::String("bar".into()), AssertionResult::Failure("assertion failed: expected values to be equal: left: 'foo', right: 'bar'".into()); "assert_eq failure")]
530    #[test_case("assert_ne", Value::Bool(true), Value::Bool(false), AssertionResult::Success; "assert_ne success")]
531    #[test_case("assert_ne", Value::Float(1.0), Value::Float(1.0), AssertionResult::Failure("assertion failed: expected values to be not equal: left: '1', right: '1'".into()); "assert_ne failure")]
532    #[test_case("assert_gt", Value::Integer(10), Value::Integer(5), AssertionResult::Success; "assert_gt success int")]
533    #[test_case("assert_gt", Value::Float(3.5), Value::Float(2.1), AssertionResult::Success; "assert_gt success float")]
534    #[test_case("assert_gt", Value::Integer(2), Value::Integer(10), AssertionResult::Failure("assertion failed: expected left value '2' to be greater than right value '10'".into()); "assert_gt failure")]
535    #[test_case("assert_gte", Value::Integer(7), Value::Integer(7), AssertionResult::Success; "assert_gte success equal")]
536    #[test_case("assert_gte", Value::Float(8.0), Value::Float(7.9), AssertionResult::Success; "assert_gte success greater")]
537    #[test_case("assert_gte", Value::Integer(1), Value::Integer(2), AssertionResult::Failure("assertion failed: expected left value '1' to be greater than or equal to right value '2'".into()); "assert_gte failure")]
538    #[test_case("assert_lt", Value::Integer(1), Value::Integer(2), AssertionResult::Success; "assert_lt success")]
539    #[test_case("assert_lt", Value::Float(3.0), Value::Float(2.1), AssertionResult::Failure("assertion failed: expected left value '3' to be less than right value '2.1'".into()); "assert_lt failure")]
540    #[test_case("assert_lte", Value::Integer(5), Value::Integer(5), AssertionResult::Success; "assert_lte success equal")]
541    #[test_case("assert_lte", Value::Float(1.1), Value::Float(2.2), AssertionResult::Success; "assert_lte success less")]
542    #[test_case("assert_lte", Value::Integer(10), Value::Integer(2), AssertionResult::Failure("assertion failed: expected left value '10' to be less than or equal to right value '2'".into()); "assert_lte failure")]
543    fn test_assertions(fn_spec_name: &str, left: Value, right: Value, expected: AssertionResult) {
544        let fn_spec = get_spec_by_name(fn_spec_name);
545        let args = vec![left, right];
546        let result = (fn_spec.runner)(&fn_spec, &dummy_auth_ctx(), &args).unwrap();
547        assert_eq!(result, expected.to_value());
548    }
549
550    #[test_case("assert_gt", Value::String("test".into()), Value::Integer(6), Diagnostic::error_from_string("function 'std::assert_gt' argument #1 (left) should be of type (integer,float), found string".into()); "assert_gt with string and integer")]
551    #[test_case("assert_gte", Value::Bool(false), Value::Float(2.5), Diagnostic::error_from_string("function 'std::assert_gte' argument #1 (left) should be of type (integer,float), found bool".into()); "assert_gte with bool and float")]
552    #[test_case("assert_lt", Value::Bool(true), Value::Float(3.5), Diagnostic::error_from_string("function 'std::assert_lt' argument #1 (left) should be of type (integer,float), found bool".into()); "assert_lt with bool and float")]
553    #[test_case("assert_lte", Value::String("test".into()), Value::Integer(6), Diagnostic::error_from_string("function 'std::assert_lte' argument #1 (left) should be of type (integer,float), found string".into()); "assert_lte with string and integer")]
554    fn test_invalid_inputs(fn_spec_name: &str, left: Value, right: Value, expected: Diagnostic) {
555        let fn_spec = get_spec_by_name(fn_spec_name);
556        let args = vec![left, right];
557        let result = (fn_spec.runner)(&fn_spec, &dummy_auth_ctx(), &args).unwrap_err();
558        assert_eq!(result, expected);
559    }
560}