/*
* 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_interpreter_data::ExecutionTrace;
use air_test_framework::AirScriptExecutor;
use air_test_utils::key_utils::at;
use air_test_utils::prelude::*;
use pretty_assertions::assert_eq;
#[tokio::test]
// test for github.com/fluencelabs/aquavm/issues/221
async fn issue_221() {
let peer_1_name = "peer_1_id";
let peer_2_name = "peer_2_id";
let join_1_name = "join_1_id";
let join_2_name = "join_2_id";
let set_variable_name = "set_variable_id";
let peer_1_value = "peer_1_value";
let peer_2_value = "peer_2_value";
let script = format!(
r#"
(seq
(seq
(seq
;; let's peers be an array of two values [peer_1_id, peer_2_id]
(call "{set_variable_name}" ("" "") [] peers) ; ok = [@"{peer_1_name}", @"{peer_2_name}"]
(fold peers peer
(par
(seq
(call peer ("" "") [peer] value) ; map = {{@"{peer_1_name}": "{peer_1_value}", @"{peer_2_name}": "{peer_2_value}"}}
;; it's crucial to reproduce this bug to add value to stream
;; with help of ap instruction
(ap value $stream)
)
(next peer)
)
)
)
;; join streams on join_1/join_2 peers in such a way that will have different state:
;; join_1 $stream: [peer_1_value, peer_2_value]
;; join_2 $stream: [peer_2_value, peer_1_value]
(fold $stream iterator
;; here it'll encounter a bug in trace handler, because fold won't shuffle lores in
;; appropriate way and state for (1) is returned
(par
(par
(call "{join_1_name}" ("" "") [iterator]) ; behaviour = echo
(call "{join_2_name}" ("" "") [iterator]) ; behaviour = echo
)
(next iterator)
)
)
)
(call "some_peer_name" ("" "") []) ;; (1)
)
"#
);
let executor = ::new(
TestRunParameters::from_init_peer_id("set_variable_id"),
vec![],
vec![peer_1_name, peer_2_name].into_iter().map(Into::into),
&script,
)
.await
.expect("Invalid annotated AIR script");
let peer_1_id = at(peer_1_name);
let peer_2_id = at(peer_2_name);
let join_1_id = at(join_1_name);
let _result = executor.execute_one(set_variable_name).await.unwrap();
let _peer_1_result = executor.execute_one(peer_1_name).await.unwrap();
let _peer_2_result = executor.execute_one(peer_2_name).await.unwrap();
let _join_1_result = executor.execute_one(join_1_name).await.unwrap();
let join_1_result = executor.execute_one(join_1_name).await.unwrap(); // before 0.20.9 it fails here
let actual_trace = trace_from_result(&join_1_result);
let expected_trace = ExecutionTrace::from(vec![
scalar!(
json!([peer_1_id, peer_2_id]),
peer_name = set_variable_name,
service = "..0"
),
executed_state::par(2, 3),
scalar!(
peer_1_value,
peer_name = peer_1_name,
service = "..1",
args = vec![peer_1_id.as_str()]
),
executed_state::ap(0),
executed_state::par(2, 0),
scalar!(
peer_2_value,
peer_name = peer_2_name,
service = "..1",
args = vec![peer_2_id.as_str()]
),
executed_state::ap(1),
executed_state::fold(vec![
executed_state::subtrace_lore(3, SubTraceDesc::new(8.into(), 4), SubTraceDesc::new(12.into(), 0)),
executed_state::subtrace_lore(6, SubTraceDesc::new(12.into(), 4), SubTraceDesc::new(16.into(), 0)),
]),
executed_state::par(3, 0),
executed_state::par(1, 1),
unused!(
peer_1_value,
peer_name = join_1_name,
service = "..2",
args = vec![peer_1_value]
),
executed_state::request_sent_by(peer_1_id),
executed_state::par(3, 0),
executed_state::par(1, 1),
unused!(
peer_2_value,
peer_name = join_1_name,
service = "..2",
args = vec![peer_2_value]
),
executed_state::request_sent_by(peer_2_id),
executed_state::request_sent_by(join_1_id),
]);
assert_eq!(actual_trace, expected_trace);
}