@@ -291,6 +291,50 @@ static void test_get(
291291 }
292292}
293293
294+ static void test_delete (GglList key_path , GglError expected_result ) {
295+ GGL_LOGD (
296+ "test_delete %s, expecting %s" ,
297+ print_key_path (& key_path ),
298+ ggl_strerror (expected_result )
299+ );
300+ GglBuffer server = GGL_STR ("gg_config" );
301+
302+ GglMap params = GGL_MAP ({ GGL_STR ("key_path" ), GGL_OBJ_LIST (key_path ) }, );
303+
304+ GglError remote_error = GGL_ERR_OK ;
305+ GglError error = ggl_call (
306+ server , GGL_STR ("delete" ), params , & remote_error , NULL , NULL
307+ );
308+ if (expected_result != GGL_ERR_OK && error != GGL_ERR_REMOTE ) {
309+ GGL_LOGE (
310+ "delete key %s expected result %s but there was not a remote error" ,
311+ print_key_path (& key_path ),
312+ ggl_strerror (expected_result )
313+ );
314+ assert (0 );
315+ }
316+ if (expected_result == GGL_ERR_OK && error != GGL_ERR_OK ) {
317+ GGL_LOGE (
318+ "delete key %s did not expect error but got error %s and remote "
319+ "error %s" ,
320+ print_key_path (& key_path ),
321+ ggl_strerror (error ),
322+ ggl_strerror (remote_error )
323+ );
324+ assert (0 );
325+ }
326+ if (remote_error != expected_result ) {
327+ GGL_LOGE (
328+ "delete key %s expected result %s but got %s" ,
329+ print_key_path (& key_path ),
330+ ggl_strerror (expected_result ),
331+ ggl_strerror (remote_error )
332+ );
333+ assert (0 );
334+ return ;
335+ }
336+ }
337+
294338static GglError subscription_callback (
295339 void * ctx , unsigned int handle , GglObject data
296340) {
@@ -899,6 +943,68 @@ int main(int argc, char **argv) {
899943 GGL_ERR_OK
900944 );
901945
946+ // Test to ensure a key can be deleted, not affecting its parent
947+ test_insert (
948+ GGL_LIST (
949+ GGL_OBJ_BUF (GGL_STR ("component13" )), GGL_OBJ_BUF (GGL_STR ("key" ))
950+ ),
951+ GGL_OBJ_BUF (GGL_STR ("value" )),
952+ -1 ,
953+ GGL_ERR_OK
954+ );
955+ test_delete (
956+ GGL_LIST (
957+ GGL_OBJ_BUF (GGL_STR ("component13" )), GGL_OBJ_BUF (GGL_STR ("key" ))
958+ ),
959+ GGL_ERR_OK
960+ );
961+ test_get (
962+ GGL_LIST (
963+ GGL_OBJ_BUF (GGL_STR ("component13" )), GGL_OBJ_BUF (GGL_STR ("key" ))
964+ ),
965+ GGL_OBJ_NULL (),
966+ GGL_ERR_NOENTRY
967+ );
968+ test_get (
969+ GGL_LIST (GGL_OBJ_BUF (GGL_STR ("component13" ))),
970+ GGL_OBJ_MAP (GGL_MAP ()),
971+ GGL_ERR_OK
972+ );
973+
974+ // Test to ensure deletes are recursive
975+ test_insert (
976+ GGL_LIST (
977+ GGL_OBJ_BUF (GGL_STR ("component14" )),
978+ GGL_OBJ_BUF (GGL_STR ("foo" )),
979+ GGL_OBJ_BUF (GGL_STR ("bar" ))
980+ ),
981+ GGL_OBJ_BUF (GGL_STR ("value" )),
982+ -1 ,
983+ GGL_ERR_OK
984+ );
985+ test_delete (GGL_LIST (GGL_OBJ_BUF (GGL_STR ("component14" ))), GGL_ERR_OK );
986+ test_get (
987+ GGL_LIST (
988+ GGL_OBJ_BUF (GGL_STR ("component14" )),
989+ GGL_OBJ_BUF (GGL_STR ("foo" )),
990+ GGL_OBJ_BUF (GGL_STR ("bar" ))
991+ ),
992+ GGL_OBJ_NULL (),
993+ GGL_ERR_NOENTRY
994+ );
995+ test_get (
996+ GGL_LIST (
997+ GGL_OBJ_BUF (GGL_STR ("component14" )), GGL_OBJ_BUF (GGL_STR ("foo" ))
998+ ),
999+ GGL_OBJ_NULL (),
1000+ GGL_ERR_NOENTRY
1001+ );
1002+ test_get (
1003+ GGL_LIST (GGL_OBJ_BUF (GGL_STR ("component14" ))),
1004+ GGL_OBJ_NULL (),
1005+ GGL_ERR_NOENTRY
1006+ );
1007+
9021008 // Test to ensure an empty map can be written and read
9031009 test_insert (
9041010 GGL_LIST (GGL_OBJ_BUF (GGL_STR ("component15" ))),
@@ -1004,6 +1110,55 @@ int main(int argc, char **argv) {
10041110 GGL_ERR_FAILURE
10051111 );
10061112
1113+ // Test to check subscriber behavior on deleted keys
1114+ test_insert (
1115+ GGL_LIST (
1116+ GGL_OBJ_BUF (GGL_STR ("component20" )),
1117+ GGL_OBJ_BUF (GGL_STR ("foo" )),
1118+ GGL_OBJ_BUF (GGL_STR ("key" ))
1119+ ),
1120+ GGL_OBJ_BUF (GGL_STR ("value1" )),
1121+ -1 ,
1122+ GGL_ERR_OK
1123+ );
1124+ test_subscribe (
1125+ GGL_LIST (
1126+ GGL_OBJ_BUF (GGL_STR ("component20" )),
1127+ GGL_OBJ_BUF (GGL_STR ("foo" )),
1128+ GGL_OBJ_BUF (GGL_STR ("key" ))
1129+ ),
1130+ GGL_ERR_OK
1131+ );
1132+ test_subscribe (
1133+ GGL_LIST (
1134+ GGL_OBJ_BUF (GGL_STR ("component20" )), GGL_OBJ_BUF (GGL_STR ("foo" ))
1135+ ),
1136+ GGL_ERR_OK
1137+ );
1138+ test_delete (
1139+ GGL_LIST (
1140+ GGL_OBJ_BUF (GGL_STR ("component20" )),
1141+ GGL_OBJ_BUF (GGL_STR ("foo" )),
1142+ GGL_OBJ_BUF (GGL_STR ("key" ))
1143+ ),
1144+ GGL_ERR_OK
1145+ );
1146+ test_insert (
1147+ GGL_LIST (
1148+ GGL_OBJ_BUF (GGL_STR ("component20" )),
1149+ GGL_OBJ_BUF (GGL_STR ("foo" )),
1150+ GGL_OBJ_BUF (GGL_STR ("key" ))
1151+ ),
1152+ GGL_OBJ_BUF (GGL_STR ("value2" )),
1153+ -1 ,
1154+ GGL_ERR_OK
1155+ ); // Should see one `read component20/foo/key` on the callback handle
1156+ // created for component20/foo
1157+ // Currently, the other subscription callback for component20/foo/key is not
1158+ // notified. In the future, it would be good to have that behavior too. See
1159+ // the docs/design/ggconfigd.md section "Subscription behavior for keys
1160+ // which become deleted" for more info.
1161+
10071162 // test_insert(
10081163 // GGL_LIST(GGL_OBJ_BUF(GGL_STR("component")),
10091164 // GGL_OBJ_BUF(GGL_STR("bar"))), GGL_OBJ_MAP(GGL_MAP({ GGL_STR("foo"),
0 commit comments