/* * AquaVM Workflow Engine * * Copyright (C) 2024 Fluence DAO * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation version 3 of the * License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ use air::CatchableError; use air::ExecutionCidState; use air::ToErrorCode; use air::NO_ERROR_ERROR_CODE; use air::NO_ERROR_MESSAGE; use air_test_framework::AirScriptExecutor; use air_test_utils::prelude::*; #[tokio::test] async fn match_equal() { let set_variable_peer_id = "set_variable_peer_id"; let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id).await; let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let script = format!( r#" (seq (seq (call "{set_variable_peer_id}" ("" "") ["value_1"] value_1) (call "{set_variable_peer_id}" ("" "") ["value_1"] value_2) ) (xor (match value_1 value_2 (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_1"] result_1) ) (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_2"] result_2) ) )"# ); let result = checked_call_vm!(set_variable_vm, <_>::default(), &script, "", ""); let result = checked_call_vm!(vm, <_>::default(), script, "", result.data); let actual_trace = trace_from_result(&result); let expected_state = scalar!( "result_1", peer = local_peer_id, service = "service_id_2", function = "local_fn_name", args = ["result_1"] ); assert_eq!(actual_trace.len(), 3); assert_eq!(actual_trace[2.into()], expected_state); } #[tokio::test] async fn match_not_equal() { let set_variable_peer_id = "set_variable_peer_id"; let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id).await; let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let script = format!( r#" (seq (seq (call "{set_variable_peer_id}" ("" "") ["value_1"] value_1) (call "{set_variable_peer_id}" ("" "") ["value_2"] value_2) ) (xor (match value_1 value_2 (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_1"] result_1) ) (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_2"] result_2) ) )"# ); let result = checked_call_vm!(set_variable_vm, <_>::default(), &script, "", ""); let result = checked_call_vm!(vm, <_>::default(), script, "", result.data); let actual_trace = trace_from_result(&result); let expected_state = scalar!( "result_2", peer = local_peer_id, service = "service_id_2", function = "local_fn_name", args = ["result_2"] ); assert_eq!(actual_trace.len(), 3); assert_eq!(actual_trace[2.into()], expected_state); } #[tokio::test] async fn match_with_string() { let set_variable_peer_id = "set_variable_peer_id"; let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id).await; let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let script = format!( r#" (seq (call "{set_variable_peer_id}" ("" "") ["value_1"] value_1) (xor (match value_1 "value_1" (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_1"] result_1) ) (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_2"] result_2) ) )"# ); let result = checked_call_vm!(set_variable_vm, <_>::default(), &script, "", ""); let result = checked_call_vm!(vm, <_>::default(), script, "", result.data); let actual_trace = trace_from_result(&result); let expected_state = scalar!( "result_1", peer = local_peer_id, service = "service_id_2", function = "local_fn_name", args = ["result_1"] ); assert_eq!(actual_trace.len(), 2); assert_eq!(actual_trace[1.into()], expected_state); } #[tokio::test] async fn match_with_init_peer_id() { let set_variable_peer_id = "set_variable_peer_id"; let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id).await; let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let script = format!( r#" (seq (call "{set_variable_peer_id}" ("" "") ["{local_peer_id}"] value_1) (xor (match value_1 %init_peer_id% (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_1"] result_1) ) (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_2"] result_2) ) )"# ); let test_params = TestRunParameters::from_init_peer_id(local_peer_id); let result = checked_call_vm!(set_variable_vm, test_params.clone(), &script, "", ""); let result = checked_call_vm!(vm, test_params, script, "", result.data); let actual_trace = trace_from_result(&result); let expected_executed_call_result = scalar!( "result_1", peer = local_peer_id, service = "service_id_2", function = "local_fn_name", args = ["result_1"] ); assert_eq!(actual_trace.len(), 2); assert_eq!(actual_trace[1.into()], expected_executed_call_result); } #[tokio::test] async fn match_with_timestamp() { let set_variable_peer_id = "set_variable_peer_id"; let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id).await; let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let timestamp = 1337; let script = format!( r#" (seq (call "{set_variable_peer_id}" ("" "") [{timestamp}] value_1) (xor (match value_1 %timestamp% (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_1"] result_1) ) (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_2"] result_2) ) )"# ); let test_params = TestRunParameters::from_timestamp(timestamp); let result = checked_call_vm!(set_variable_vm, test_params.clone(), &script, "", ""); let result = checked_call_vm!(vm, test_params, script, "", result.data); let actual_trace = trace_from_result(&result); let expected_executed_call_result = scalar!( "result_1", peer = local_peer_id, service = "service_id_2", function = "local_fn_name", args = ["result_1"] ); assert_eq!(actual_trace.len(), 2); assert_eq!(actual_trace[1.into()], expected_executed_call_result); } #[tokio::test] async fn match_with_ttl() { let set_variable_peer_id = "set_variable_peer_id"; let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id).await; let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let ttl = 1337; let script = format!( r#" (seq (call "{set_variable_peer_id}" ("" "") [{ttl}] value_1) (xor (match value_1 %ttl% (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_1"] result_1) ) (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_2"] result_2) ) )"# ); let test_params = TestRunParameters::from_ttl(ttl); let result = checked_call_vm!(set_variable_vm, test_params.clone(), &script, "", ""); let result = checked_call_vm!(vm, test_params, script, "", result.data); let actual_trace = trace_from_result(&result); let expected_executed_call_result = scalar!( "result_1", peer = local_peer_id, service = "service_id_2", function = "local_fn_name", args = ["result_1"] ); assert_eq!(actual_trace.len(), 2); assert_eq!(actual_trace[1.into()], expected_executed_call_result); } #[tokio::test] async fn match_with_equal_numbers() { let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let script = " (xor (match 1 1 (null) ) (null) )"; let result = call_vm!(vm, <_>::default(), script, "", ""); assert!(is_interpreter_succeded(&result)); } #[tokio::test] async fn match_without_xor() { let set_variable_peer_id = "set_variable_peer_id"; let mut set_variable_vm = create_avm(echo_call_service(), set_variable_peer_id).await; let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let script = format!( r#" (seq (seq (call "{set_variable_peer_id}" ("" "") ["value_1"] value_1) (call "{set_variable_peer_id}" ("" "") ["value_2"] value_2) ) (match value_1 value_2 (call "{local_peer_id}" ("service_id_2" "local_fn_name") ["result_1"] result_1) ) )"# ); let result = call_vm!(set_variable_vm, <_>::default(), &script, "", ""); let result = call_vm!(vm, <_>::default(), &script, "", result.data); let expected_error = CatchableError::MatchValuesNotEqual; assert_eq!(expected_error.to_error_code(), 10001); assert!(check_error(&result, expected_error)); let result = call_vm!(vm, <_>::default(), script, "", result.data); let expected_error = CatchableError::MatchValuesNotEqual; assert!(check_error(&result, expected_error)); } #[tokio::test] async fn match_with_two_xors() { let local_peer_id = "local_peer_id"; let mut vm = create_avm(set_variable_call_service(serde_json::json!(false)), local_peer_id).await; let local_peer_id_2 = "local_peer_id_2"; let script = format!( r#" (xor (seq (seq (call "{local_peer_id}" ("getDataSrv" "condition") [] condition) (call "{local_peer_id}" ("getDataSrv" "relay") [] relay) ) (xor (match condition true (call "{local_peer_id}" ("println" "print") ["it is true"]) ) (call "{local_peer_id_2}" ("println" "print") ["it is false"]) ) ) (call "{local_peer_id}" ("errorHandlingSrv" "error") [%last_error%]) ) "# ); let result = checked_call_vm!(vm, <_>::default(), script, "", ""); let mut actual_trace = trace_from_result(&result); let expected_executed_call_result = executed_state::request_sent_by(local_peer_id); assert_eq!(actual_trace.pop().unwrap(), expected_executed_call_result); } // https://github.com/fluencelabs/aquavm/issues/165 #[tokio::test] async fn issue_165() { let result_setter_peer_id = "result_setter_peer_id"; let mut result_setter = create_avm( set_variable_call_service(serde_json::json!({"success": true})), result_setter_peer_id, ) .await; let echo_peer_id = "echo_peer_id"; let mut echo_peer = create_avm(echo_call_service(), echo_peer_id).await; let script = format!( r#" (seq (call "{result_setter_peer_id}" ("" "") ["set_result"] result) (seq (xor (match result.$.success! true (ap 1 $results) ) (ap 2 $results) ) (seq (canon "{echo_peer_id}" $results #results) (call "{echo_peer_id}" ("callbackSrv" "response") [#results.$.[0]!]) ) ) ) "# ); let setter_result = checked_call_vm!(result_setter, <_>::default(), &script, "", ""); let echo_result = checked_call_vm!(echo_peer, <_>::default(), &script, "", setter_result.data); let trace = trace_from_result(&echo_result); assert_eq!( trace.last().unwrap(), &unused!( 1, peer = echo_peer_id, service = "callbackSrv", function = "response", args = [1] ) ); } #[tokio::test] async fn match_with_undefined_last_error_errcode() { let local_peer_id = "local_peer_id"; let script = format!( r#" (xor (match 1 2 (null)) (call "local_peer_id" ("test" "error_code") [%last_error%.$.error_code] scalar) ; behaviour = echo ) "# ); let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(local_peer_id), &script) .await .expect("invalid test AIR script"); let result = executor.execute_all(local_peer_id).await.unwrap(); let actual_trace = trace_from_result(&result.last().unwrap()); let mut cid_state = ExecutionCidState::new(); let errcode_lambda_output = json!(NO_ERROR_ERROR_CODE); let expected_trace = ExecutionTrace::from(vec![scalar_tracked!( errcode_lambda_output.clone(), cid_state, peer_name = local_peer_id, service = "test..0", function = "error_code", args = vec![errcode_lambda_output] )]); assert_eq!(actual_trace, expected_trace); } #[tokio::test] async fn match_with_undefined_last_error_message() { let local_peer_id = "local_peer_id"; let script = format!( r#" (xor (match 1 2 (null)) (call "local_peer_id" ("test" "message") [%last_error%.$.message] scalar) ; behaviour = echo ) "# ); let executor = AirScriptExecutor::from_annotated(TestRunParameters::from_init_peer_id(local_peer_id), &script) .await .expect("invalid test AIR script"); let result = executor.execute_all(local_peer_id).await.unwrap(); let actual_trace = trace_from_result(&result.last().unwrap()); let mut cid_state = ExecutionCidState::new(); let message_lambda_output = json!(NO_ERROR_MESSAGE); let expected_trace = ExecutionTrace::from(vec![scalar_tracked!( message_lambda_output.clone(), cid_state, peer_name = local_peer_id, service = "test..0", function = "message", args = vec![message_lambda_output] )]); assert_eq!(actual_trace, expected_trace); } #[tokio::test] async fn match_with_error() { let local_peer_id = "local_peer_id"; let mut vm = create_avm(echo_call_service(), local_peer_id).await; let script = format!( r#" (xor (match 1 2 (null)) (call "local_peer_id" ("test" "error_code") [:error:.$.error_code] scalar) ) "# ); let result = checked_call_vm!(vm, <_>::default(), script, "", ""); let actual_trace = trace_from_result(&result); let mut cid_state = ExecutionCidState::new(); let errcode_lambda_output = json!(10001); let expected_trace = ExecutionTrace::from(vec![scalar_tracked!( errcode_lambda_output.clone(), cid_state, peer = local_peer_id, service = "test", function = "error_code", args = vec![errcode_lambda_output] )]); assert_eq!(actual_trace, expected_trace); }