// Copyright 2015-2024 Swim Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::collections::HashMap; use std::fmt::Write; use swimos::agent::agent_model::ItemFlags; use swimos::agent::lanes::{CommandLane, MapLane, ValueLane}; use swimos::agent::model::MapMessage; use swimos::agent::model::Text; use swimos::agent::reexport::bytes::BytesMut; use swimos::agent::reexport::uuid::Uuid; use swimos::agent::AgentLaneModel; use swimos_agent::agent_model::{ItemDescriptor, ItemSpec}; use swimos_agent::lanes::http::Recon; use swimos_agent::lanes::{ DemandLane, DemandMapLane, HttpLane, JoinMapLane, JoinValueLane, SimpleHttpLane, SupplyLane, }; use swimos_agent::reexport::bytes::Bytes; use swimos_agent::stores::{MapStore, ValueStore}; use swimos_api::{ agent::{HttpLaneRequest, StoreKind, WarpLaneKind}, http::{HttpRequest, Uri}, }; const SYNC_ID: Uuid = Uuid::from_u128(85883); fn persistent_lane(id: u64, name: &'static str, kind: WarpLaneKind) -> (&'static str, ItemSpec) { ( name, ItemSpec::new( id, name, ItemDescriptor::WarpLane { kind, flags: ItemFlags::empty(), }, ), ) } fn persistent_lane_renamed( id: u64, name: &'static str, lifecycle_name: &'static str, kind: WarpLaneKind, ) -> (&'static str, ItemSpec) { ( name, ItemSpec::new( id, lifecycle_name, ItemDescriptor::WarpLane { kind, flags: ItemFlags::empty(), }, ), ) } fn transient_lane(id: u64, name: &'static str, kind: WarpLaneKind) -> (&'static str, ItemSpec) { ( name, ItemSpec::new( id, name, ItemDescriptor::WarpLane { kind, flags: ItemFlags::TRANSIENT, }, ), ) } fn persistent_store(id: u64, name: &'static str, kind: StoreKind) -> (&'static str, ItemSpec) { ( name, ItemSpec::new( id, name, ItemDescriptor::Store { kind, flags: ItemFlags::empty(), }, ), ) } fn transient_store(id: u64, name: &'static str, kind: StoreKind) -> (&'static str, ItemSpec) { ( name, ItemSpec::new( id, name, ItemDescriptor::Store { kind, flags: ItemFlags::TRANSIENT, }, ), ) } fn http_lane(id: u64, name: &'static str) -> (&'static str, ItemSpec) { (name, ItemSpec::new(id, name, ItemDescriptor::Http)) } fn check_agent(specs: Vec<(&'static str, ItemSpec)>) where A: AgentLaneModel + Default, { let agent = A::default(); let expected = specs.into_iter().collect::>(); assert_eq!(A::item_specs(), expected); for (name, ItemSpec { descriptor, .. }) in expected { match descriptor { ItemDescriptor::WarpLane { kind, .. } => { if kind.map_like() { assert!(agent.on_map_command(name, MapMessage::Clear).is_some()); assert!(agent.on_sync(name, SYNC_ID).is_some()); } else { assert!(agent.on_value_command(name, get_i32_buffer(4)).is_some()); assert!(agent.on_sync(name, SYNC_ID).is_some()); } } ItemDescriptor::Store { .. } => { assert!(agent.on_map_command(name, MapMessage::Clear).is_none()); assert!(agent.on_sync(name, SYNC_ID).is_none()); assert!(agent.on_value_command(name, get_i32_buffer(4)).is_none()); assert!(agent.on_sync(name, SYNC_ID).is_none()); } ItemDescriptor::Http => { let uri = format!("http://example/node?lane={}", name) .parse::() .unwrap(); let request_inner = HttpRequest::get(uri).map(|_| Bytes::new()); let (request, _response_rx) = HttpLaneRequest::new(request_inner); assert!(agent.on_http_request(name, request).is_ok()); } } } } fn get_i32_buffer(n: i32) -> BytesMut { let mut buf = BytesMut::new(); write!(&mut buf, "{}", n).expect("Write to buffer failed."); buf } #[test] fn single_value_lane() { #[derive(AgentLaneModel)] struct SingleValueLane { lane: ValueLane, } check_agent::(vec![persistent_lane(0, "lane", WarpLaneKind::Value)]); } #[test] fn single_value_store() { #[derive(AgentLaneModel)] struct SingleValueStore { store: ValueStore, } check_agent::(vec![persistent_store(0, "store", StoreKind::Value)]); } #[test] fn single_map_lane() { #[derive(AgentLaneModel)] struct SingleMapLane { lane: MapLane, } check_agent::(vec![persistent_lane(0, "lane", WarpLaneKind::Map)]); } #[test] fn single_map_store() { #[derive(AgentLaneModel)] struct SingleMapStore { store: MapStore, } check_agent::(vec![persistent_store(0, "store", StoreKind::Map)]); } #[test] fn single_command_lane() { #[derive(AgentLaneModel)] struct SingleCommandLane { lane: CommandLane, } check_agent::(vec![transient_lane(0, "lane", WarpLaneKind::Command)]); } #[test] fn single_demand_lane() { #[derive(AgentLaneModel)] struct SingleDemandLane { lane: DemandLane, } check_agent::(vec![transient_lane(0, "lane", WarpLaneKind::Demand)]); } #[test] fn single_demand_map_lane() { #[derive(AgentLaneModel)] struct SingleDemandMapLane { lane: DemandMapLane, } check_agent::(vec![transient_lane(0, "lane", WarpLaneKind::DemandMap)]); } #[test] fn single_supply_lane() { #[derive(AgentLaneModel)] struct SingleSupplyLane { lane: SupplyLane, } check_agent::(vec![transient_lane(0, "lane", WarpLaneKind::Supply)]); } #[test] fn two_value_lanes() { #[derive(AgentLaneModel)] struct TwoValueLanes { first: ValueLane, second: ValueLane, } check_agent::(vec![ persistent_lane(0, "first", WarpLaneKind::Value), persistent_lane(1, "second", WarpLaneKind::Value), ]); } #[test] fn two_value_stores() { #[derive(AgentLaneModel)] struct TwoValueStores { first: ValueStore, second: ValueStore, } check_agent::(vec![ persistent_store(0, "first", StoreKind::Value), persistent_store(1, "second", StoreKind::Value), ]); } #[test] fn two_map_lanes() { #[derive(AgentLaneModel)] struct TwoMapLanes { first: MapLane, second: MapLane, } check_agent::(vec![ persistent_lane(0, "first", WarpLaneKind::Map), persistent_lane(1, "second", WarpLaneKind::Map), ]); } #[test] fn two_map_stores() { #[derive(AgentLaneModel)] struct TwoMapStores { first: MapStore, second: MapStore, } check_agent::(vec![ persistent_store(0, "first", StoreKind::Map), persistent_store(1, "second", StoreKind::Map), ]); } #[test] fn two_command_lanes() { #[derive(AgentLaneModel)] struct TwoCommandLanes { first: CommandLane, second: CommandLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Command), transient_lane(1, "second", WarpLaneKind::Command), ]); } #[test] fn two_demand_lanes() { #[derive(AgentLaneModel)] struct TwoDemandLanes { first: DemandLane, second: DemandLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Demand), transient_lane(1, "second", WarpLaneKind::Demand), ]); } #[test] fn two_supply_lanes() { #[derive(AgentLaneModel)] struct TwoSupplyLanes { first: SupplyLane, second: SupplyLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Supply), transient_lane(1, "second", WarpLaneKind::Supply), ]); } #[test] fn two_demand_map_lanes() { #[derive(AgentLaneModel)] struct TwoDemandMapLanes { first: DemandMapLane, second: DemandMapLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::DemandMap), transient_lane(1, "second", WarpLaneKind::DemandMap), ]); } #[test] fn mixed_lanes() { #[derive(AgentLaneModel)] struct MixedLanes { first: ValueLane, second: MapLane, } check_agent::(vec![ persistent_lane(0, "first", WarpLaneKind::Value), persistent_lane(1, "second", WarpLaneKind::Map), ]); } #[test] fn mixed_stores() { #[derive(AgentLaneModel)] struct MixedStores { first: ValueStore, second: MapStore, } check_agent::(vec![ persistent_store(0, "first", StoreKind::Value), persistent_store(1, "second", StoreKind::Map), ]); } #[test] fn multiple_lanes() { #[derive(AgentLaneModel)] struct MultipleLanes { first: ValueLane, second: MapLane, third: ValueLane, fourth: MapLane, fifth: CommandLane, sixth: JoinValueLane, seventh: DemandLane, eighth: DemandMapLane, ninth: SimpleHttpLane, tenth: JoinMapLane, eleventh: SupplyLane, } check_agent::(vec![ persistent_lane(0, "first", WarpLaneKind::Value), persistent_lane(1, "second", WarpLaneKind::Map), persistent_lane(2, "third", WarpLaneKind::Value), persistent_lane(3, "fourth", WarpLaneKind::Map), transient_lane(4, "fifth", WarpLaneKind::Command), transient_lane(5, "sixth", WarpLaneKind::JoinValue), transient_lane(6, "seventh", WarpLaneKind::Demand), transient_lane(7, "eighth", WarpLaneKind::DemandMap), http_lane(8, "ninth"), transient_lane(9, "tenth", WarpLaneKind::JoinMap), transient_lane(10, "eleventh", WarpLaneKind::Supply), ]); } #[test] fn stores_and_lanes() { #[derive(AgentLaneModel)] struct StoresAndLanes { first: ValueStore, second: ValueLane, third: MapStore, fourth: MapLane, } check_agent::(vec![ persistent_store(0, "first", StoreKind::Value), persistent_lane(1, "second", WarpLaneKind::Value), persistent_store(2, "third", StoreKind::Map), persistent_lane(3, "fourth", WarpLaneKind::Map), ]); } #[test] fn value_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoValueLanes { #[item(transient)] first: ValueLane, second: ValueLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Value), persistent_lane(1, "second", WarpLaneKind::Value), ]); } #[test] fn value_store_tagged_transient() { #[derive(AgentLaneModel)] struct TwoValueStores { #[item(transient)] first: ValueStore, second: ValueStore, } check_agent::(vec![ transient_store(0, "first", StoreKind::Value), persistent_store(1, "second", StoreKind::Value), ]); } #[test] fn map_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoMapLanes { first: MapLane, #[item(transient)] second: MapLane, } check_agent::(vec![ persistent_lane(0, "first", WarpLaneKind::Map), transient_lane(1, "second", WarpLaneKind::Map), ]); } #[test] fn map_store_tagged_transient() { #[derive(AgentLaneModel)] struct TwoMapStores { first: MapStore, #[item(transient)] second: MapStore, } check_agent::(vec![ persistent_store(0, "first", StoreKind::Map), transient_store(1, "second", StoreKind::Map), ]); } #[test] fn command_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoCommandLanes { #[item(transient)] first: CommandLane, second: CommandLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Command), transient_lane(1, "second", WarpLaneKind::Command), ]); } #[test] fn demand_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoDemandLanes { #[item(transient)] first: DemandLane, second: DemandLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Demand), transient_lane(1, "second", WarpLaneKind::Demand), ]); } #[test] fn supply_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoSupplyLanes { #[item(transient)] first: SupplyLane, second: SupplyLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Supply), transient_lane(1, "second", WarpLaneKind::Supply), ]); } #[test] fn demand_map_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoDemandMapLanes { #[item(transient)] first: DemandMapLane, second: DemandMapLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::DemandMap), transient_lane(1, "second", WarpLaneKind::DemandMap), ]); } #[test] fn single_join_value_lane() { #[derive(AgentLaneModel)] struct SingleJoinValueLane { lane: JoinValueLane, } check_agent::(vec![transient_lane(0, "lane", WarpLaneKind::JoinValue)]); } #[test] fn two_join_value_lanes() { #[derive(AgentLaneModel)] struct TwoJoinValueLanes { first: JoinValueLane, second: JoinValueLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::JoinValue), transient_lane(1, "second", WarpLaneKind::JoinValue), ]); } #[test] fn join_value_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoJoinValueLanes { first: JoinValueLane, #[item(transient)] second: JoinValueLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::JoinValue), transient_lane(1, "second", WarpLaneKind::JoinValue), ]); } #[test] fn single_join_map_lane() { #[derive(AgentLaneModel)] struct SingleJoinMapLane { lane: JoinMapLane, } check_agent::(vec![transient_lane(0, "lane", WarpLaneKind::JoinMap)]); } #[test] fn two_join_map_lanes() { #[derive(AgentLaneModel)] struct TwoJoinMapLanes { first: JoinMapLane, second: JoinMapLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::JoinMap), transient_lane(1, "second", WarpLaneKind::JoinMap), ]); } #[test] fn join_map_lane_tagged_transient() { #[derive(AgentLaneModel)] struct TwoJoinMapLanes { first: JoinMapLane, #[item(transient)] second: JoinMapLane, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::JoinMap), transient_lane(1, "second", WarpLaneKind::JoinMap), ]); } #[test] fn single_simple_http_lane() { #[derive(AgentLaneModel)] struct SingleSimpleHttpLane { lane: SimpleHttpLane, } check_agent::(vec![http_lane(0, "lane")]); } #[test] fn single_simple_http_lane_explicit_codec() { #[derive(AgentLaneModel)] struct SingleSimpleHttpLane { lane: SimpleHttpLane, } check_agent::(vec![http_lane(0, "lane")]); } #[test] fn two_simple_http_lanes() { #[derive(AgentLaneModel)] struct TwoSimpleHttpLanes { first: SimpleHttpLane, second: SimpleHttpLane, } check_agent::(vec![http_lane(0, "first"), http_lane(1, "second")]); } #[test] fn get_and_post_http_lane() { #[derive(AgentLaneModel)] struct GetAndPostHttpLane { lane: HttpLane, } check_agent::(vec![http_lane(0, "lane")]); } #[test] fn get_post_and_put_http_lane() { #[derive(AgentLaneModel)] struct GetPostAndPutHttpLane { lane: HttpLane, } check_agent::(vec![http_lane(0, "lane")]); } #[test] fn general_http_lane_explicit_codec() { #[derive(AgentLaneModel)] struct GeneralHttpLane { lane: HttpLane, } check_agent::(vec![http_lane(0, "lane")]); } #[test] fn two_general_http_lanes() { #[derive(AgentLaneModel)] struct TwoGeneralHttpLanes { first: HttpLane, second: HttpLane, } check_agent::(vec![http_lane(0, "first"), http_lane(1, "second")]); } mod isolated { use swimos_api::agent::{StoreKind, WarpLaneKind}; use crate::check_agent; use super::{http_lane, persistent_lane, persistent_store, transient_lane}; #[test] fn multiple_items_qualified() { #[derive(swimos::agent::AgentLaneModel)] struct MultipleLanes { first: swimos::agent::lanes::ValueLane, second: swimos::agent::lanes::MapLane, third: swimos::agent::lanes::ValueLane, fourth: swimos::agent::lanes::MapLane, fifth: swimos::agent::lanes::CommandLane, sixth: swimos::agent::stores::ValueStore, seventh: swimos::agent::stores::MapStore, eighth: swimos::agent::lanes::JoinValueLane, ninth: swimos::agent::lanes::DemandLane, tenth: swimos::agent::lanes::DemandMapLane, eleventh: swimos::agent::lanes::SimpleHttpLane, twelfth: swimos::agent::lanes::HttpLane, thirteenth: swimos::agent::lanes::JoinMapLane, } check_agent::(vec![ persistent_lane(0, "first", WarpLaneKind::Value), persistent_lane(2, "third", WarpLaneKind::Value), transient_lane(4, "fifth", WarpLaneKind::Command), persistent_store(5, "sixth", StoreKind::Value), persistent_lane(1, "second", WarpLaneKind::Map), persistent_lane(3, "fourth", WarpLaneKind::Map), persistent_store(6, "seventh", StoreKind::Map), transient_lane(7, "eighth", WarpLaneKind::JoinValue), transient_lane(8, "ninth", WarpLaneKind::Demand), transient_lane(9, "tenth", WarpLaneKind::DemandMap), http_lane(10, "eleventh"), http_lane(11, "twelfth"), transient_lane(12, "thirteenth", WarpLaneKind::JoinMap), ]); } } #[test] fn two_types_single_scope() { #[derive(AgentLaneModel)] struct First { lane: ValueLane, } #[derive(AgentLaneModel)] struct Second { lane: ValueLane, } check_agent::(vec![persistent_lane(0, "lane", WarpLaneKind::Value)]); check_agent::(vec![persistent_lane(0, "lane", WarpLaneKind::Value)]); } #[test] fn rename_lane() { #[derive(AgentLaneModel)] struct RenameExplicit { #[item(name = "renamed")] first: ValueLane, second: ValueLane, } check_agent::(vec![ persistent_lane_renamed(0, "renamed", "first", WarpLaneKind::Value), persistent_lane(1, "second", WarpLaneKind::Value), ]); } #[test] fn rename_lane_with_convention() { #[derive(AgentLaneModel)] struct RenameConvention { #[item(convention = "camel")] first_lane: ValueLane, second_lane: ValueLane, } check_agent::(vec![ persistent_lane_renamed(0, "firstLane", "first_lane", WarpLaneKind::Value), persistent_lane(1, "second_lane", WarpLaneKind::Value), ]); } #[test] fn rename_all_lanes_with_convention() { #[derive(AgentLaneModel)] #[agent(convention = "camel")] struct RenameAll { first_lane: ValueLane, second_lane: ValueLane, third_lane: ValueLane, } check_agent::(vec![ persistent_lane_renamed(0, "firstLane", "first_lane", WarpLaneKind::Value), persistent_lane_renamed(1, "secondLane", "second_lane", WarpLaneKind::Value), persistent_lane_renamed(2, "thirdLane", "third_lane", WarpLaneKind::Value), ]); } #[test] fn override_top_level_convention() { #[derive(AgentLaneModel)] #[agent(convention = "camel")] struct OverrideRename { first_lane: ValueLane, #[item(name = "renamed")] second_lane: ValueLane, third_lane: ValueLane, } check_agent::(vec![ persistent_lane_renamed(0, "firstLane", "first_lane", WarpLaneKind::Value), persistent_lane_renamed(1, "renamed", "second_lane", WarpLaneKind::Value), persistent_lane_renamed(2, "thirdLane", "third_lane", WarpLaneKind::Value), ]); } #[test] fn agent_level_transient_flag() { #[derive(AgentLaneModel)] #[agent(transient)] struct EverythingTransient { first: ValueLane, second: MapLane, third: CommandLane, fourth: ValueStore, } check_agent::(vec![ transient_lane(0, "first", WarpLaneKind::Value), transient_lane(1, "second", WarpLaneKind::Map), transient_lane(2, "third", WarpLaneKind::Command), transient_store(3, "fourth", StoreKind::Value), ]); }