diff --git a/lib/pacemaker/tests/pcmk_ticket/Makefile.am b/lib/pacemaker/tests/pcmk_ticket/Makefile.am index 17969710d0..d9660a08ad 100644 --- a/lib/pacemaker/tests/pcmk_ticket/Makefile.am +++ b/lib/pacemaker/tests/pcmk_ticket/Makefile.am @@ -1,26 +1,27 @@ # # Copyright 2024 the Pacemaker project contributors # # The version control history for this file may have further details. # # This source code is licensed under the GNU General Public License version 2 # or later (GPLv2+) WITHOUT ANY WARRANTY. # include $(top_srcdir)/mk/tap.mk include $(top_srcdir)/mk/unittest.mk LDADD += $(top_builddir)/lib/pacemaker/libpacemaker.la LDADD += $(top_builddir)/lib/cib/libcib.la # Add "_test" to the end of all test program names to simplify .gitignore. check_PROGRAMS = pcmk__get_ticket_state_test \ pcmk_ticket_constraints_test \ pcmk_ticket_delete_test \ pcmk_ticket_get_attr_test \ pcmk_ticket_info_test \ pcmk_ticket_remove_attr_test \ + pcmk_ticket_set_attr_test \ pcmk_ticket_state_test TESTS = $(check_PROGRAMS) diff --git a/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_set_attr_test.c b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_set_attr_test.c new file mode 100644 index 0000000000..cb10ca0da4 --- /dev/null +++ b/lib/pacemaker/tests/pcmk_ticket/pcmk_ticket_set_attr_test.c @@ -0,0 +1,225 @@ +/* + * Copyright 2024 the Pacemaker project contributors + * + * The version control history for this file may have further details. + * + * This source code is licensed under the GNU General Public License version 2 + * or later (GPLv2+) WITHOUT ANY WARRANTY. + */ + +#include + +#include +#include +#include +#include + +static char *cib_path = NULL; + +static void +cib_not_connected(void **state) +{ + xmlNode *xml = NULL; + + /* Without any special setup, cib_new() in pcmk_ticket_set_attr will use the + * native CIB which means IPC calls. But there's nothing listening for those + * calls, so signon() will return ENOTCONN. Check that we handle that. + */ + assert_int_equal(pcmk_ticket_set_attr(&xml, NULL, NULL), ENOTCONN); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static int +setup_test(void **state) +{ + cib_path = pcmk__cib_test_copy_cib("tickets.xml"); + + if (cib_path == NULL) { + return -1; + } + + return 0; +} + +static int +teardown_test(void **state) +{ + pcmk__cib_test_cleanup(cib_path); + cib_path = NULL; + return 0; +} + +static void +bad_arguments(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk_ticket_set_attr(NULL, "ticketA", NULL), EINVAL); + + assert_int_equal(pcmk_ticket_set_attr(&xml, NULL, NULL), EINVAL); + pcmk__assert_validates(xml); + free_xml(xml); +} + +static void +unknown_ticket_no_attrs(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib = cib_new(); + + cib->cmds->signon(cib, crm_system_name, cib_command); + + /* Setting no attributes on a ticket that doesn't exist is a no-op */ + assert_int_equal(pcmk_ticket_set_attr(&xml, "XYZ", NULL), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + xml = NULL; + + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + /* Another way of specifying no attributes */ + assert_int_equal(pcmk_ticket_set_attr(&xml, "XYZ", attrs), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + assert_null(xml_search); + + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +unknown_ticket_with_attrs(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, "attrA", "123"); + pcmk__insert_dup(attrs, "attrB", "456"); + + /* Setting attributes on a ticket that doesn't exist causes the ticket to + * be created with the given attributes + */ + assert_int_equal(pcmk_ticket_set_attr(&xml, "XYZ", attrs), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"XYZ\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("123", crm_element_value(xml_search, "attrA")); + assert_string_equal("456", crm_element_value(xml_search, "attrB")); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +overwrite_existing_attr(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, "owner", "2"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketA", attrs), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("2", crm_element_value(xml_search, "owner")); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +not_granted_to_granted(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, PCMK__XA_GRANTED, "true"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketA", attrs), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketA\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("true", crm_element_value(xml_search, PCMK__XA_GRANTED)); + assert_non_null(crm_element_value(xml_search, PCMK_XA_LAST_GRANTED)); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +static void +granted_to_not_granted(void **state) +{ + GHashTable *attrs = pcmk__strkey_table(free, free); + xmlNode *xml = NULL; + xmlNode *xml_search = NULL; + cib_t *cib; + + pcmk__insert_dup(attrs, PCMK__XA_GRANTED, "false"); + + assert_int_equal(pcmk_ticket_set_attr(&xml, "ticketB", attrs), pcmk_rc_ok); + pcmk__assert_validates(xml); + free_xml(xml); + + cib = cib_new(); + cib->cmds->signon(cib, crm_system_name, cib_command); + cib->cmds->query(cib, "//" PCMK__XE_TICKET_STATE "[@" PCMK_XA_ID "=\"ticketB\"]", + &xml_search, cib_xpath | cib_scope_local); + + assert_string_equal("false", crm_element_value(xml_search, PCMK__XA_GRANTED)); + assert_null(crm_element_value(xml_search, PCMK_XA_LAST_GRANTED)); + + free_xml(xml_search); + g_hash_table_destroy(attrs); + cib__clean_up_connection(&cib); +} + +/* There are two kinds of tests in this file: + * + * (1) Those that test what happens if the CIB is not set up correctly, and + * (2) Those that test what happens when run against a CIB. + * + * Therefore, we need two kinds of setup/teardown functions. We only do + * minimal overall setup for the entire group, and then setup the CIB for + * those tests that need it. + */ +PCMK__UNIT_TEST(pcmk__cib_test_setup_group, NULL, + cmocka_unit_test(cib_not_connected), + cmocka_unit_test_setup_teardown(bad_arguments, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket_no_attrs, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(unknown_ticket_with_attrs, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(overwrite_existing_attr, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(not_granted_to_granted, setup_test, teardown_test), + cmocka_unit_test_setup_teardown(granted_to_not_granted, setup_test, teardown_test))