diff --git a/lib/wp/session-item.c b/lib/wp/session-item.c
index a0e015be97d320340678881acb86c89d4254e1f6..6ef53ab0b129ed3c6ade52cf3b8317a360a341d2 100644
--- a/lib/wp/session-item.c
+++ b/lib/wp/session-item.c
@@ -178,9 +178,7 @@ wp_session_item_default_execute_step (WpSessionItem * self,
 {
   switch (step) {
   case WP_TRANSITION_STEP_NONE:
-    break;
   case WP_TRANSITION_STEP_ERROR:
-    wp_session_item_reset (self);
     break;
   default:
     g_return_if_reached ();
@@ -578,7 +576,7 @@ wp_session_item_configure (WpSessionItem * self, GVariant * args)
   g_return_val_if_fail (WP_IS_SESSION_ITEM (self), FALSE);
   g_return_val_if_fail (WP_SESSION_ITEM_GET_CLASS (self)->configure,
       FALSE);
-  g_return_val_if_fail (g_variant_is_of_type (args, G_VARIANT_TYPE ("a{sv}")),
+  g_return_val_if_fail (g_variant_is_of_type (args, G_VARIANT_TYPE_VARDICT),
       FALSE);
 
   return WP_SESSION_ITEM_GET_CLASS (self)->configure (self, args);
diff --git a/modules/module-si-adapter.c b/modules/module-si-adapter.c
index 1b8ebde1db28cca7090c6deb9f7b49041b14fff8..3c4b6c87c9f225d27e4e85db58eb3bd23104f9c9 100644
--- a/modules/module-si-adapter.c
+++ b/modules/module-si-adapter.c
@@ -354,7 +354,7 @@ si_adapter_execute_step (WpSessionItem * item, WpTransition * transition,
       break;
     }
     default:
-      WP_SESSION_ITEM_GET_CLASS (si_adapter_parent_class)->execute_step (item,
+      WP_SESSION_ITEM_CLASS (si_adapter_parent_class)->execute_step (item,
           transition, step);
       break;
   }
diff --git a/modules/module-si-standard-link.c b/modules/module-si-standard-link.c
index 973cee37466f69a259b92700fec1bde6fc9a4f1f..f03a45339f358f0bc855ac3d3bebaa924985533c 100644
--- a/modules/module-si-standard-link.c
+++ b/modules/module-si-standard-link.c
@@ -374,7 +374,7 @@ si_standard_link_execute_step (WpSessionItem * item, WpTransition * transition,
     break;
   }
   default:
-    WP_SESSION_ITEM_GET_CLASS (si_standard_link_parent_class)->execute_step (
+    WP_SESSION_ITEM_CLASS (si_standard_link_parent_class)->execute_step (
           item, transition, step);
       break;
   }
diff --git a/tests/wp/meson.build b/tests/wp/meson.build
index 2dff1c91738d886ca0e416e8fe4f42d23bc4e9d8..c0f9f4eea9a39d59afb1c67b9c4cd3db4fb0a848 100644
--- a/tests/wp/meson.build
+++ b/tests/wp/meson.build
@@ -35,6 +35,13 @@ test(
   env: common_env,
 )
 
+test(
+  'test-session-item',
+  executable('test-session-item', 'session-item.c',
+      dependencies: common_deps, c_args: common_args),
+  env: common_env,
+)
+
 test(
   'test-spa-pod',
   executable('test-spa-pod', 'spa-pod.c',
diff --git a/tests/wp/session-item.c b/tests/wp/session-item.c
new file mode 100644
index 0000000000000000000000000000000000000000..6cca612329023b731a781a188aaf42fa9d3f3c3d
--- /dev/null
+++ b/tests/wp/session-item.c
@@ -0,0 +1,338 @@
+/* WirePlumber
+ *
+ * Copyright © 2020 Collabora Ltd.
+ *    @author George Kiagiadakis <george.kiagiadakis@collabora.com>
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#include <wp/wp.h>
+
+G_DEFINE_QUARK (test-domain, test_domain)
+
+enum {
+  STEP_1 = WP_TRANSITION_STEP_CUSTOM_START,
+  STEP_2,
+};
+
+struct _TestSiDummy
+{
+  WpSessionItem parent;
+  gboolean fail;
+  gboolean step_1_done;
+  gboolean step_2_done;
+  gboolean cleaned_up;
+};
+
+G_DECLARE_FINAL_TYPE (TestSiDummy, si_dummy, TEST, SI_DUMMY, WpSessionItem)
+G_DEFINE_TYPE (TestSiDummy, si_dummy, WP_TYPE_SESSION_ITEM)
+
+static void
+si_dummy_init (TestSiDummy * self)
+{
+}
+
+static GVariant *
+si_dummy_get_configuration (WpSessionItem * item)
+{
+  TestSiDummy *self = TEST_SI_DUMMY (item);
+  GVariantBuilder b;
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}",
+      "fail", g_variant_new_boolean (self->fail));
+  return g_variant_builder_end (&b);
+}
+
+static gboolean
+si_dummy_configure (WpSessionItem * item, GVariant * args)
+{
+  TestSiDummy *self = TEST_SI_DUMMY (item);
+
+  if (wp_session_item_get_flags (item) & (WP_SI_FLAG_ACTIVATING | WP_SI_FLAG_ACTIVE))
+    return FALSE;
+
+  g_variant_lookup (args, "fail", "b", &self->fail);
+  wp_session_item_set_flag (item, WP_SI_FLAG_CONFIGURED);
+
+  return TRUE;
+}
+
+static guint
+si_dummy_get_next_step (WpSessionItem * item,
+     WpTransition * transition, guint step)
+{
+  switch (step) {
+    case WP_TRANSITION_STEP_NONE:
+      return STEP_1;
+    case STEP_1:
+      return STEP_2;
+    case STEP_2:
+      return WP_TRANSITION_STEP_NONE;
+    default:
+      return WP_TRANSITION_STEP_ERROR;
+  }
+}
+
+static gboolean
+si_dummy_step_1 (gpointer data)
+{
+  WpTransition *transition = data;
+  g_assert_true (WP_IS_TRANSITION (transition));
+
+  TestSiDummy *self = wp_transition_get_source_object (transition);
+  g_assert_true (TEST_IS_SI_DUMMY (self));
+
+  self->step_1_done = TRUE;
+
+  if (self->fail)
+    wp_transition_return_error (transition,
+        g_error_new (test_domain_quark (), 0, "error"));
+  else
+    wp_transition_advance (transition);
+
+  return G_SOURCE_REMOVE;
+}
+
+static void
+si_dummy_execute_step (WpSessionItem * item, WpTransition * transition,
+    guint step)
+{
+  TestSiDummy *self = TEST_SI_DUMMY (item);
+
+  switch (step) {
+    case STEP_1:
+      /* execute async */
+      g_idle_add (si_dummy_step_1, transition);
+      break;
+
+    case STEP_2:
+      /* execute sync */
+      self->step_2_done = TRUE;
+      wp_transition_advance (transition);
+      break;
+
+    case WP_TRANSITION_STEP_ERROR:
+      self->cleaned_up = TRUE;
+      self->step_1_done = FALSE;
+      self->step_2_done = FALSE;
+      WP_SESSION_ITEM_CLASS (si_dummy_parent_class)->execute_step (item,
+          transition, step);
+      break;
+
+    default:
+      g_assert_not_reached ();
+  }
+}
+
+static void
+si_dummy_deactivate (WpSessionItem * item)
+{
+  TestSiDummy *self = TEST_SI_DUMMY (item);
+
+  self->cleaned_up = FALSE;
+  self->step_1_done = FALSE;
+  self->step_2_done = FALSE;
+
+  WP_SESSION_ITEM_CLASS (si_dummy_parent_class)->deactivate (item);
+}
+
+static void
+si_dummy_class_init (TestSiDummyClass * klass)
+{
+  WpSessionItemClass *si_class = (WpSessionItemClass *) klass;
+
+  si_class->configure = si_dummy_configure;
+  si_class->get_configuration = si_dummy_get_configuration;
+  si_class->get_next_step = si_dummy_get_next_step;
+  si_class->execute_step = si_dummy_execute_step;
+  si_class->deactivate = si_dummy_deactivate;
+}
+
+static void
+expect_flags (WpSessionItem * item, WpSiFlags flags, WpSiFlags *signalled_flags)
+{
+  *signalled_flags = flags;
+}
+
+static void
+test_flags (void)
+{
+  g_autoptr (WpSessionItem) item = NULL;
+  WpSiFlags signalled_flags = 0;
+
+  item = g_object_new (si_dummy_get_type (), NULL);
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, 0);
+
+  g_signal_connect (item, "flags-changed", G_CALLBACK (expect_flags),
+      &signalled_flags);
+  wp_session_item_set_flag (item, WP_SI_FLAG_CUSTOM_START);
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CUSTOM_START);
+  g_assert_cmpint (signalled_flags, ==, WP_SI_FLAG_CUSTOM_START);
+
+  /* internal flag, cannot be set */
+  signalled_flags = 0;
+  wp_session_item_set_flag (item, WP_SI_FLAG_ACTIVATING);
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CUSTOM_START);
+  g_assert_cmpint (signalled_flags, ==, 0);
+
+  signalled_flags = WP_SI_FLAG_CUSTOM_START;
+  wp_session_item_clear_flag (item, WP_SI_FLAG_CUSTOM_START);
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, 0);
+  g_assert_cmpint (signalled_flags, ==, 0);
+}
+
+static void
+test_configuration (void)
+{
+  g_autoptr (WpSessionItem) item = NULL;
+  g_autoptr (GVariant) v = NULL;
+  WpSiFlags signalled_flags = 0;
+  gboolean fail = FALSE;
+  GVariantBuilder b;
+
+  item = g_object_new (si_dummy_get_type (), NULL);
+  g_signal_connect (item, "flags-changed", G_CALLBACK (expect_flags),
+      &signalled_flags);
+
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}", "fail", g_variant_new_boolean (TRUE));
+  g_assert_true (wp_session_item_configure (item, g_variant_builder_end (&b)));
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CONFIGURED);
+  g_assert_cmpint (signalled_flags, ==, WP_SI_FLAG_CONFIGURED);
+
+  v = wp_session_item_get_configuration (item);
+  g_assert_nonnull (v);
+  g_assert_true (g_variant_is_of_type (v, G_VARIANT_TYPE_VARDICT));
+  g_assert_true (g_variant_lookup (v, "fail", "b", &fail));
+  g_assert_true (fail);
+}
+
+static void
+expect_activate_success (WpSessionItem * item, GAsyncResult * res, gpointer data)
+{
+  GMainLoop *loop = data;
+  g_autoptr (GError) error = NULL;
+
+  g_assert_true (TEST_IS_SI_DUMMY (item));
+  g_assert_true (g_async_result_is_tagged (res, wp_session_item_activate));
+  g_assert_true (wp_session_item_activate_finish (item, res, &error));
+  g_assert_no_error (error);
+
+  g_main_loop_quit (loop);
+}
+
+static void
+test_activation (void)
+{
+  g_autoptr (WpSessionItem) item = NULL;
+  g_autoptr (GMainLoop) loop = NULL;
+  WpSiFlags signalled_flags = 0;
+  TestSiDummy *dummy;
+
+  loop = g_main_loop_new (NULL, FALSE);
+  item = g_object_new (si_dummy_get_type (), NULL);
+  dummy = TEST_SI_DUMMY (item);
+  g_signal_connect (item, "flags-changed", G_CALLBACK (expect_flags),
+      &signalled_flags);
+
+  wp_session_item_activate (item,
+      (GAsyncReadyCallback) expect_activate_success, loop);
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_ACTIVATING);
+  g_assert_cmpint (signalled_flags, ==, WP_SI_FLAG_ACTIVATING);
+
+  g_main_loop_run (loop);
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_ACTIVE);
+  g_assert_cmpint (signalled_flags, ==, WP_SI_FLAG_ACTIVE);
+  g_assert_true (dummy->step_1_done);
+  g_assert_true (dummy->step_2_done);
+  g_assert_false (dummy->cleaned_up);
+
+  wp_session_item_deactivate (item);
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, 0);
+  g_assert_cmpint (signalled_flags, ==, 0);
+  g_assert_false (dummy->step_1_done);
+  g_assert_false (dummy->step_2_done);
+  g_assert_false (dummy->cleaned_up);
+}
+
+
+static void
+expect_activate_failure (WpSessionItem * item, GAsyncResult * res, gpointer data)
+{
+  GMainLoop *loop = data;
+  g_autoptr (GError) error = NULL;
+
+  g_assert_true (TEST_IS_SI_DUMMY (item));
+  g_assert_true (g_async_result_is_tagged (res, wp_session_item_activate));
+  g_assert_false (wp_session_item_activate_finish (item, res, &error));
+  g_assert_error (error, test_domain_quark (), 0);
+
+  g_main_loop_quit (loop);
+}
+
+static void
+test_activation_error (void)
+{
+  g_autoptr (WpSessionItem) item = NULL;
+  g_autoptr (GMainLoop) loop = NULL;
+  WpSiFlags signalled_flags = 0;
+  TestSiDummy *dummy;
+  GVariantBuilder b;
+
+  loop = g_main_loop_new (NULL, FALSE);
+  item = g_object_new (si_dummy_get_type (), NULL);
+  dummy = TEST_SI_DUMMY (item);
+  g_signal_connect (item, "flags-changed", G_CALLBACK (expect_flags),
+      &signalled_flags);
+
+  g_variant_builder_init (&b, G_VARIANT_TYPE_VARDICT);
+  g_variant_builder_add (&b, "{sv}", "fail", g_variant_new_boolean (TRUE));
+  g_assert_true (wp_session_item_configure (item, g_variant_builder_end (&b)));
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CONFIGURED);
+  g_assert_cmpint (signalled_flags, ==, WP_SI_FLAG_CONFIGURED);
+
+  wp_session_item_activate (item,
+      (GAsyncReadyCallback) expect_activate_failure, loop);
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==,
+      WP_SI_FLAG_CONFIGURED | WP_SI_FLAG_ACTIVATING);
+  g_assert_cmpint (signalled_flags, ==,
+      WP_SI_FLAG_CONFIGURED | WP_SI_FLAG_ACTIVATING);
+
+  g_main_loop_run (loop);
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==,
+      WP_SI_FLAG_CONFIGURED | WP_SI_FLAG_IN_ERROR);
+  g_assert_cmpint (signalled_flags, ==,
+      WP_SI_FLAG_CONFIGURED | WP_SI_FLAG_IN_ERROR);
+  g_assert_false (dummy->step_1_done);
+  g_assert_false (dummy->step_2_done);
+  g_assert_true (dummy->cleaned_up);
+
+  wp_session_item_deactivate (item);
+
+  g_assert_cmpint (wp_session_item_get_flags (item), ==, WP_SI_FLAG_CONFIGURED);
+  g_assert_cmpint (signalled_flags, ==, WP_SI_FLAG_CONFIGURED);
+  g_assert_false (dummy->step_1_done);
+  g_assert_false (dummy->step_2_done);
+  g_assert_false (dummy->cleaned_up);
+}
+
+gint
+main (gint argc, gchar *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_log_set_writer_func (wp_log_writer_default, NULL, NULL);
+
+  g_test_add_func ("/wp/session-item/flags", test_flags);
+  g_test_add_func ("/wp/session-item/configuration", test_configuration);
+  g_test_add_func ("/wp/session-item/activation", test_activation);
+  g_test_add_func ("/wp/session-item/activation-error", test_activation_error);
+
+  return g_test_run ();
+}