Skip to main content
Glama
action.rs6.98 kB
use std::collections::HashSet; use base64::Engine; use dal::{ self, Component, ComponentId, DalContext, Func, action::{ Action, prototype::{ ActionKind, ActionPrototype, }, }, component::delete::delete_component, }; use dal_test::{ Result, helpers::{ ChangeSetTestHelpers, attribute::value, component, }, test, }; use pretty_assertions_sorted::assert_eq; use si_events::ActionState; #[test] async fn prevent_orphaned_update_actions(ctx: &mut DalContext) -> Result<()> { // Create a component with both create and update actions and apply to HEAD. let component_id = component::create(ctx, "swifty", "taylor swift").await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; ChangeSetTestHelpers::apply_change_set_to_base(ctx).await?; ChangeSetTestHelpers::wait_for_actions_to_run(ctx).await?; // Make sure we start with a clean slate. ChangeSetTestHelpers::fork_from_head_change_set(ctx).await?; Action::remove_all_for_component_id(ctx, component_id).await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; ChangeSetTestHelpers::apply_change_set_to_base(ctx).await?; ChangeSetTestHelpers::wait_for_actions_to_run(ctx).await?; // Create two change sets needed to reproduce the issue. ChangeSetTestHelpers::fork_from_head_change_set(ctx).await?; let alpha_change_set_id = ctx.change_set_id(); assert!(Action::list_topologically(ctx).await?.is_empty()); ChangeSetTestHelpers::fork_from_head_change_set(ctx).await?; let beta_change_set_id = ctx.change_set_id(); assert!(Action::list_topologically(ctx).await?.is_empty()); // In the "beta", change set, erase the component. let component = Component::get_by_id(ctx, component_id).await?; let head_components: HashSet<ComponentId> = Component::exists_on_head_by_ids(ctx, &[component_id]).await?; delete_component(ctx, &component, true, &head_components).await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; assert!(Action::list_topologically(ctx).await?.is_empty()); // In the "alpha" change set, change the component name, which auto-enqueues an update action. ctx.update_visibility_and_snapshot_to_visibility(alpha_change_set_id) .await?; assert!(Action::list_topologically(ctx).await?.is_empty()); value::set(ctx, ("taylor swift", "/si/name"), "travis kelce").await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; let mut actions = Action::list_topologically(ctx).await?; assert_eq!(1, actions.len()); let action_id = actions.pop().unwrap(); let prototype = Action::prototype(ctx, action_id).await?; assert_eq!(ActionKind::Update, prototype.kind); // Put the action "OnHold" to ensure it stays on HEAD after apply. Then, apply the change set to HEAD. Action::set_state(ctx, action_id, ActionState::OnHold).await?; ChangeSetTestHelpers::commit_and_update_snapshot_to_visibility(ctx).await?; ChangeSetTestHelpers::apply_change_set_to_base(ctx).await?; ChangeSetTestHelpers::wait_for_actions_to_run(ctx).await?; // Switch to the "beta" change set and ensure that the action does not exist. Before the change // introduced alongside this test, the action would exist in this change set, but not point to // a component. In the UI, the action would looked "orphaned" to the end user. ctx.update_visibility_and_snapshot_to_visibility(beta_change_set_id) .await?; assert!(Action::list_topologically(ctx).await?.is_empty()); Ok(()) } #[test] async fn prevent_duplicate_exclusive_actions(ctx: &mut DalContext) -> Result<()> { let test_variant_id = dal_test::helpers::schema::variant::create( ctx, "delete_will_fail", r#" function main() { return { props: [ { name: "a_prop", kind: "string", } ] }; } "#, ) .await?; let create_action_code = r#"async function main() { return { payload: { "data": "you gotta have it" }, status: "ok" }; }"#; let create_func = Func::new( ctx, "test:createAction", None::<String>, None::<String>, None::<String>, false, false, dal::FuncBackendKind::JsAction, dal::FuncBackendResponseType::Action, "main".into(), Some(base64::engine::general_purpose::STANDARD_NO_PAD.encode(create_action_code)), false, ) .await?; let _create_prototype = ActionPrototype::new( ctx, ActionKind::Create, "test:createAction".into(), None, test_variant_id, create_func.id, ) .await?; let delete_action_code = r#"async function main() { return { status: "error" }; }"#; let delete_func = Func::new( ctx, "test:deleteAction", None::<String>, None::<String>, None::<String>, false, false, dal::FuncBackendKind::JsAction, dal::FuncBackendResponseType::Action, "main".into(), Some(base64::engine::general_purpose::STANDARD_NO_PAD.encode(delete_action_code)), false, ) .await?; let _delete_proto = ActionPrototype::new( ctx, ActionKind::Destroy, "test:deleteAction".into(), None, test_variant_id, delete_func.id, ) .await?; let component_id = dal_test::helpers::component::create(ctx, test_variant_id, "du côté de chez swann").await?; ChangeSetTestHelpers::apply_change_set_to_base(ctx).await?; ChangeSetTestHelpers::wait_for_actions_to_run(ctx).await?; let cs_1 = ChangeSetTestHelpers::fork_from_head_change_set(ctx) .await? .id; ChangeSetTestHelpers::switch_to_change_set(ctx, cs_1).await?; let component = Component::get_by_id(ctx, component_id).await?; component.delete(ctx).await?; let cs_2 = ChangeSetTestHelpers::fork_from_head_change_set(ctx) .await? .id; ChangeSetTestHelpers::switch_to_change_set(ctx, cs_2).await?; let component = Component::get_by_id(ctx, component_id).await?; component.delete(ctx).await?; ChangeSetTestHelpers::apply_change_set_to_base(ctx).await?; ChangeSetTestHelpers::wait_for_actions_to_run(ctx).await?; let actions = Action::list_topologically(ctx).await?; let action_id = actions[0]; let action = Action::get_by_id(ctx, action_id).await?; assert_eq!(ActionState::Failed, action.state()); ChangeSetTestHelpers::switch_to_change_set(ctx, cs_1).await?; let actions = Action::list_topologically(ctx).await?; assert_eq!(actions.len(), 1); Ok(()) }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/systeminit/si'

If you have feedback or need assistance with the MCP directory API, please join our Discord server