]> git.ipfire.org Git - thirdparty/collectd.git/commitdiff
redfish plugin: addressing comments from PR #2926
authorAdrian Boczkowski <adrianx.boczkowski@intel.com>
Wed, 17 Oct 2018 08:33:25 +0000 (09:33 +0100)
committerAdrian Boczkowski <adrianx.boczkowski@intel.com>
Fri, 19 Oct 2018 09:08:50 +0000 (10:08 +0100)
Add unit tests.

Change-Id: Iecef07b148ca2ac067cf912bb4a6af2d2190b810
Signed-off-by: Adrian Boczkowski <adrianx.boczkowski@intel.com>
configure.ac
src/redfish.c
src/redfish_test.c

index bcfa6d3fb96b1637fd5a01ebee03263014363ea0..72b7dea2bbfbd7d3af9e0dba4fbb83621aae73ef 100644 (file)
@@ -2192,7 +2192,7 @@ AC_ARG_WITH([libredfish],
     else
       with_libredfish="yes"
       LIBREDFISH_CPPFLAGS="$LIBREDFISH_CPPFLAGS -I$withval/include"
-      LIBREDFISH_LDFLAGS="$LIBREDFISh_LDFLAGS -L$withval/lib"
+      LIBREDFISH_LDFLAGS="$LIBREDFISH_LDFLAGS -L$withval/lib"
     fi; fi
   ],
   [with_libredfish="yes"]
@@ -2201,7 +2201,7 @@ AC_ARG_WITH([libredfish],
 SAVE_CPPFLAGS="$CPPFLAGS"
 SAVE_LDFLAGS="$LDFLAGS"
 CPPFLAGS="$CPPFLAGS $LIBREDFISH_CPPFLAGS"
-LDFLAGS="$LDFLAGS $LIBHIREDIS_LDFLAGS"
+LDFLAGS="$LDFLAGS $LIBREDFISH_LDFLAGS"
 
 if test "x$with_libredfish" = "xyes"; then
   if test "x$LIBREDFISH_CPPFLAGS" != "x"; then
index e815e2b601a9eb15a025d33ecc40b7c35b86f94a..ed303eb56462cae4440bd1e5c4137df44f1a96e8 100644 (file)
@@ -24,6 +24,7 @@
  * Authors:
  *   Marcin Mozejko <marcinx.mozejko@intel.com>
  *   Martin Kennelly <martin.kennelly@intel.com>
+ *   Adrian Boczkowski <adrianx.boczkowski@intel.com>
  **/
 
 #include "collectd.h"
@@ -94,7 +95,7 @@ union redfish_value_u {
 };
 typedef union redfish_value_u redfish_value_t;
 
-redfish_ctx_t *ctx;
+static redfish_ctx_t *ctx;
 
 static int redfish_cleanup(void);
 static int redfish_validate_config(void);
@@ -131,7 +132,7 @@ static void redfish_print_config(void) {
 
   DEBUG(PLUGIN_NAME ": QUERIES: %d", c_avl_size(ctx->queries));
 
-  while (c_avl_iterator_next(i, (void **)&key, (void **)&q) == 0) {
+  while (c_avl_iterator_next(i, (void *)&key, (void *)&q) == 0) {
     DEBUG(PLUGIN_NAME ": --------------------");
     DEBUG(PLUGIN_NAME ": Query: %s", q->name);
     DEBUG(PLUGIN_NAME ":   Endpoint: %s", q->endpoint);
@@ -154,6 +155,25 @@ static void redfish_print_config(void) {
 }
 #endif
 
+static void redfish_service_destroy(redfish_service_t *service) {
+  /* This is checked internally by cleanupServiceEnumerator() also,
+   * but as long as it's a third-party library let's be a little 'defensive' */
+  if (service->redfish != NULL)
+    cleanupServiceEnumerator(service->redfish);
+
+  /* Destroy all service members, sfree() as well as strarray_free()
+   * and llist_destroy() are safe to call on NULL argument */
+  sfree(service->name);
+  sfree(service->host);
+  sfree(service->user);
+  sfree(service->passwd);
+  sfree(service->token);
+  strarray_free(service->queries, (size_t)service->queries_num);
+  llist_destroy(service->query_ptrs);
+
+  sfree(service);
+}
+
 static int redfish_init(void) {
 #if COLLECT_DEBUG
   redfish_print_config();
@@ -168,7 +188,7 @@ static int redfish_init(void) {
   for (llentry_t *le = llist_head(ctx->services); le != NULL; le = le->next) {
     redfish_service_t *service = (redfish_service_t *)le->value;
     /* Ignore redfish version */
-    service->flags |= 0x00000001;
+    service->flags |= REDFISH_FLAG_SERVICE_NO_VERSION_DOC;
 
     /* Preparing struct for authentication */
     if (service->user && service->passwd) {
@@ -187,32 +207,38 @@ static int redfish_init(void) {
     }
 
     service->query_ptrs = llist_create();
-    if (service->query_ptrs == NULL)
+    if (service->query_ptrs == NULL) {
+      ERROR(PLUGIN_NAME ": Failed to allocate memory for service query list");
       goto error;
+    }
 
     /* Preparing query pointers list for every service */
     for (size_t i = 0; i < service->queries_num; i++) {
       redfish_query_t *ptr;
-      if (c_avl_get(ctx->queries, service->queries[i], (void **)&ptr) != 0)
+      if (c_avl_get(ctx->queries, (void *)service->queries[i], (void *)&ptr) !=
+          0) {
+        ERROR(PLUGIN_NAME ": Cannot find a service query in a context");
         goto error;
+      }
 
       llentry_t *entry = llentry_create(ptr->name, ptr);
       if (entry != NULL)
         llist_append(service->query_ptrs, entry);
-      else
+      else {
+        ERROR(PLUGIN_NAME ": Failed to allocate memory for a query list entry");
         goto error;
+      }
     }
   }
 
   return 0;
 
 error:
-  ERROR(PLUGIN_NAME ": Failed to allocate memory for service queries list");
   /* Freeing libredfish resources & llists */
   for (llentry_t *le = llist_head(ctx->services); le != NULL; le = le->next) {
     redfish_service_t *service = (redfish_service_t *)le->value;
-    cleanupServiceEnumerator(service->redfish);
-    llist_destroy(service->query_ptrs);
+
+    redfish_service_destroy(service);
   }
   return -ENOMEM;
 }
@@ -229,7 +255,7 @@ static int redfish_preconfig(void) {
     goto free_ctx;
 
   /* Creating placeholder for queries */
-  ctx->queries = c_avl_create((int (*)(const void *, const void *))strcmp);
+  ctx->queries = c_avl_create((void *)strcmp);
   if (ctx->services == NULL)
     goto free_services;
 
@@ -250,20 +276,19 @@ static int redfish_config_property(redfish_resource_t *resource,
   assert(cfg_item != NULL);
 
   redfish_property_t *property = calloc(1, sizeof(*property));
-  int ret = 0;
 
   if (property == NULL) {
     ERROR(PLUGIN_NAME ": Failed to allocate memory for property");
     return -ENOMEM;
   }
 
-  ret = cf_util_get_string(cfg_item, &property->name);
+  int ret = cf_util_get_string(cfg_item, &property->name);
   if (ret != 0) {
     ERROR(PLUGIN_NAME ": Could not get property argument in resource section "
                       "named \"%s\"",
           resource->name);
     ret = -EINVAL;
-    goto free_property;
+    goto free_all;
   }
 
   for (int i = 0; i < cfg_item->children_num; i++) {
@@ -291,13 +316,12 @@ static int redfish_config_property(redfish_resource_t *resource,
   }
 
   llentry_t *entry = llentry_create(property->name, property);
-  if (entry != NULL)
-    llist_append(resource->properties, entry);
-  else {
+  if (entry == NULL) {
     ERROR(PLUGIN_NAME ": Failed to allocate memory for property");
     ret = -ENOMEM;
     goto free_all;
   }
+  llist_append(resource->properties, entry);
 
   return 0;
 
@@ -306,7 +330,6 @@ free_all:
   sfree(property->plugin_inst);
   sfree(property->type);
   sfree(property->type_inst);
-free_property:
   sfree(property);
   return ret;
 }
@@ -318,52 +341,50 @@ static int redfish_config_resource(redfish_query_t *query,
 
   redfish_resource_t *resource = calloc(1, sizeof(*resource));
 
-  if (resource == NULL)
-    goto error;
+  if (resource == NULL) {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for resource");
+    return -ENOMEM;
+  }
 
   resource->properties = llist_create();
 
   if (resource->properties == NULL)
-    goto free_resource;
+    goto free_memory;
 
   int ret = cf_util_get_string(cfg_item, &resource->name);
   if (ret != 0) {
     ERROR(PLUGIN_NAME ": Could not get resource name for query named \"%s\"",
           query->name);
-    goto free_list;
+    goto free_memory;
   }
   for (int i = 0; i < cfg_item->children_num; i++) {
     oconfig_item_t *opt = cfg_item->children + i;
-    if (strcasecmp("Property", opt->key) == 0)
-      ret = redfish_config_property(resource, opt);
-    else {
-      ERROR(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
+    if (strcasecmp("Property", opt->key) != 0) {
+      WARNING(PLUGIN_NAME ": Invalid configuration option \"%s\".", opt->key);
+      continue;
     }
 
+    ret = redfish_config_property(resource, opt);
+
     if (ret != 0) {
-      goto free_name;
+      goto free_memory;
     }
   }
 
   llentry_t *entry = llentry_create(resource->name, resource);
-  if (entry != NULL)
-    llist_append(query->resources, entry);
-  else
-    goto free_name;
+  if (entry == NULL) {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for resource list entry");
+    goto free_memory;
+  }
+  llist_append(query->resources, entry);
 
   return 0;
 
-free_name:
+free_memory:
   sfree(resource->name);
-free_list:
   llist_destroy(resource->properties);
-free_resource:
   sfree(resource);
   return -1;
-
-error:
-  ERROR(PLUGIN_NAME ": Failed to allocate memory for resource");
-  return -ENOMEM;
 }
 
 static int redfish_config_query(oconfig_item_t *cfg_item,
@@ -375,20 +396,19 @@ static int redfish_config_query(oconfig_item_t *cfg_item,
     return -ENOMEM;
   }
 
-  int ret = 0;
-
   query->resources = llist_create();
 
+  int ret;
   if (query->resources == NULL) {
     ret = -ENOMEM;
-    goto free_query;
+    goto free_all;
   }
 
   ret = cf_util_get_string(cfg_item, &query->name);
   if (ret != 0) {
     ERROR(PLUGIN_NAME ": Unable to get query name. Query ignored");
     ret = -EINVAL;
-    goto free_list;
+    goto free_all;
   }
 
   for (int i = 0; i < cfg_item->children_num; i++) {
@@ -422,51 +442,36 @@ static int redfish_config_query(oconfig_item_t *cfg_item,
 free_all:
   sfree(query->name);
   sfree(query->endpoint);
-free_list:
   llist_destroy(query->resources);
-free_query:
   sfree(query);
   return ret;
 }
 
 static int redfish_read_queries(oconfig_item_t *cfg_item, char ***queries_ptr) {
-  size_t q_num = cfg_item->values_num;
+  char **queries = NULL;
+  size_t queries_num = 0;
 
-  *queries_ptr = calloc(1, sizeof(**queries_ptr) * q_num);
-  if (*queries_ptr == NULL) {
-    ERROR(PLUGIN_NAME ": Failed to allocate memory for queries list");
-    goto error;
+  for (int i = 0; i < cfg_item->values_num; ++i) {
+    strarray_add(&queries, &queries_num, cfg_item->values[i].value.string);
   }
 
-  char **queries = *queries_ptr;
-  size_t i;
-  for (i = 0; i < q_num; i++) {
-    if (cfg_item->values[i].type != OCONFIG_TYPE_STRING) {
-      ERROR(PLUGIN_NAME ": 'Queries' requires string arguments");
-      goto free_array;
-    }
-    queries[i] = strdup(cfg_item->values[i].value.string);
-
-    if (queries[i] == NULL)
-      goto free_array;
+  if (queries_num != (size_t)cfg_item->values_num) {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for query list");
+    strarray_free(queries, queries_num);
+    return -ENOMEM;
   }
 
+  *queries_ptr = queries;
   return 0;
-
-free_array:
-  for (int j = 0; j <= i; j++)
-    sfree(queries[j]);
-  sfree(queries);
-error:
-  sfree(queries_ptr);
-  return -1;
 }
 
 static int redfish_config_service(oconfig_item_t *cfg_item) {
   redfish_service_t *service = calloc(1, sizeof(*service));
 
-  if (service == NULL)
-    goto error;
+  if (service == NULL) {
+    ERROR(PLUGIN_NAME ": Failed to allocate memory for service");
+    return -ENOMEM;
+  }
 
   int ret = cf_util_get_string(cfg_item, &service->name);
   if (ret != 0) {
@@ -496,37 +501,23 @@ static int redfish_config_service(oconfig_item_t *cfg_item) {
       ERROR(PLUGIN_NAME ": Something went wrong processing the service named \
             \"%s\"",
             service->name);
-      goto free_string_query;
+      goto free_service;
     }
   }
 
   llentry_t *entry = llentry_create(service->name, service);
-  if (entry != NULL)
-    llist_append(ctx->services, entry);
-  else {
+  if (entry == NULL) {
     ERROR(PLUGIN_NAME ": Failed to create list for service name \"%s\"",
           service->name);
-    goto free_string_query;
+    goto free_service;
   }
+  llist_append(ctx->services, entry);
 
   return 0;
 
-free_string_query:
-  sfree(service->name);
-  sfree(service->host);
-  sfree(service->user);
-  sfree(service->passwd);
-  sfree(service->token);
-  for (int j = 0; j < service->queries_num; j++)
-    sfree(service->queries[j]);
-
 free_service:
-  sfree(service);
+  redfish_service_destroy(service);
   return -1;
-
-error:
-  ERROR(PLUGIN_NAME ": Failed to allocate memory for service");
-  return -ENOMEM;
 }
 
 static int redfish_config(oconfig_item_t *cfg_item) {
@@ -590,8 +581,8 @@ static int redfish_validate_config(void) {
       bool found = false;
       char *key;
       c_avl_iterator_t *query_iter = c_avl_get_iterator(ctx->queries);
-      while (c_avl_iterator_next(query_iter, (void **)&key,
-                                 (void **)&query_query) == 0 &&
+      while (c_avl_iterator_next(query_iter, (void *)&key,
+                                 (void *)&query_query) == 0 &&
              !found) {
         if (query_query->name != NULL && service->queries[i] != NULL &&
             strcmp(query_query->name, service->queries[i]) == 0) {
@@ -615,8 +606,7 @@ static int redfish_validate_config(void) {
   redfish_query_t *query;
 
   /* Query validation */
-  while (c_avl_iterator_next(queries_iter, (void **)&key, (void **)&query) ==
-         0) {
+  while (c_avl_iterator_next(queries_iter, (void *)&key, (void *)&query) == 0) {
     if (query->name == NULL) {
       ERROR(PLUGIN_NAME ": A query does not have a name");
       goto error;
@@ -712,106 +702,113 @@ static int redfish_convert_val(redfish_value_t *value,
   return 0;
 }
 
+static void redfish_process_payload_property(const redfish_property_t *prop,
+                                             const json_t *json_array,
+                                             const redfish_resource_t *res,
+                                             const redfish_service_t *serv) {
+  /* Iterating through array of sensor(s) */
+  for (int i = 0; i < json_array_size(json_array); i++) {
+    json_t *item = json_array_get(json_array, i);
+    if (item == NULL) {
+      ERROR(PLUGIN_NAME ": Failure retrieving array member for resource \"%s\"",
+            res->name);
+      continue;
+    }
+    json_t *object = json_object_get(item, prop->name);
+    if (object == NULL) {
+      ERROR(PLUGIN_NAME
+            ": Failure retreiving property \"%s\" from resource \"%s\"",
+            prop->name, res->name);
+      continue;
+    }
+    value_list_t v1 = VALUE_LIST_INIT;
+    v1.values_len = 1;
+    if (prop->plugin_inst != NULL)
+      sstrncpy(v1.plugin_instance, prop->plugin_inst,
+               sizeof(v1.plugin_instance));
+    if (prop->type_inst != NULL)
+      sstrncpy(v1.type_instance, prop->type_inst, sizeof(v1.type_instance));
+    else {
+      /* Retrieving MemberId of sensor */
+      json_t *member_id = json_object_get(item, "MemberId");
+      if (member_id == NULL) {
+        ERROR(PLUGIN_NAME
+              ": Failed to get MemberId for property \"%s\" in resource "
+              "\"%s\"",
+              prop->name, res->name);
+        continue;
+      }
+
+      int ch_count = snprintf(v1.type_instance, sizeof(v1.type_instance), "%d",
+                              (int)json_integer_value(member_id));
+      if (ch_count == 0) {
+        ERROR(PLUGIN_NAME ": Failed to convert MemberId to a character");
+        continue;
+      }
+    }
+
+    /* Checking whether real or integer value */
+    redfish_value_t value;
+    redfish_value_type_t type = VAL_TYPE_STR;
+    if (json_is_string(object)) {
+      value.string = (char *)json_string_value(object);
+    } else if (json_is_integer(object)) {
+      type = VAL_TYPE_INT;
+      value.integer = json_integer_value(object);
+    } else if (json_is_real(object)) {
+      type = VAL_TYPE_REAL;
+      value.real = json_real_value(object);
+    }
+    const data_set_t *ds = plugin_get_ds(prop->type);
+
+    /* Check if data set found */
+    if (ds == NULL)
+      continue;
+
+    v1.values = &(value_t){0};
+    redfish_convert_val(&value, type, v1.values, ds->ds[0].type);
+
+    sstrncpy(v1.host, serv->host, sizeof(v1.host));
+    sstrncpy(v1.plugin, PLUGIN_NAME, sizeof(v1.plugin));
+    sstrncpy(v1.type, prop->type, sizeof(v1.type));
+    plugin_dispatch_values(&v1);
+    /* Clear values assigned in case of leakage */
+    v1.values = NULL;
+    v1.values_len = 0;
+  }
+}
+
 static void redfish_process_payload(bool success, unsigned short http_code,
                                     redfishPayload *payload, void *context) {
-  if (success == false) {
+  if (!success) {
     WARNING(PLUGIN_NAME ": Query has failed, HTTP code = %u\n", http_code);
     return;
   }
   redfish_payload_ctx_t *res_serv = (redfish_payload_ctx_t *)context;
   redfish_service_t *serv = res_serv->service;
-  if (payload) {
-    json_t *json_array;
-    for (llentry_t *llres = llist_head(res_serv->resources); llres != NULL;
-         llres = llres->next) {
-      redfish_resource_t *res = (redfish_resource_t *)llres->value;
-      json_array = json_object_get(payload->json, res->name);
-      for (llentry_t *llprop = llist_head(res->properties); llprop != NULL;
-           llprop = llprop->next) {
-        redfish_property_t *prop = (redfish_property_t *)llprop->value;
-        if (json_array == NULL) {
-          ERROR("Could not find resource \"%s\"", res->name);
-          continue;
-        }
+  if (!payload) {
+    WARNING(PLUGIN_NAME ": Failed to get payload for service name \"%s\"",
+            serv->name);
+    return;
+  }
 
-        /* Iterating through array of sensor(s) */
-        for (int i = 0; i < json_array_size(json_array); i++) {
-          json_t *member_id;
-          json_t *object;
-          json_t *item = json_array_get(json_array, i);
-          if (item == NULL) {
-            ERROR("Failure retrieving array member for resource \"%s\"",
-                  res->name);
-            continue;
-          }
-          object = json_object_get(item, prop->name);
-          if (object == NULL) {
-            ERROR("Failure retreiving property \"%s\" from resource \"%s\"",
-                  prop->name, res->name);
-            continue;
-          }
-          value_list_t v1 = VALUE_LIST_INIT;
-          v1.values_len = 1;
-          if (prop->plugin_inst != NULL)
-            sstrncpy(v1.plugin_instance, prop->plugin_inst,
-                     sizeof(v1.plugin_instance));
-          if (prop->type_inst != NULL)
-            sstrncpy(v1.type_instance, prop->type_inst,
-                     sizeof(v1.type_instance));
-          else {
-            /* Retrieving MemberId of sensor */
-            member_id = json_object_get(item, "MemberId");
-            if (member_id == NULL) {
-              ERROR("Failed to get MemberId for property \"%s\" in resource "
-                    "\"%s\"",
-                    prop->name, res->name);
-              continue;
-            }
-            char type_instance[sizeof(v1.type_instance)];
-            int ch_count = snprintf(type_instance, sizeof(type_instance), "%d",
-                                    (int)json_integer_value(member_id));
-            if (ch_count != 0)
-              sstrncpy(v1.type_instance, type_instance,
-                       sizeof(v1.type_instance));
-            else {
-              ERROR("Failed to convert MemberId to a character");
-              continue;
-            }
-          }
-          /* Checking whether real or integer value */
-          redfish_value_t value;
-          redfish_value_type_t type = VAL_TYPE_STR;
-          if (json_is_string(object)) {
-            value.string = (char *)json_string_value(object);
-          } else if (json_is_integer(object)) {
-            type = VAL_TYPE_INT;
-            value.integer = json_integer_value(object);
-          } else if (json_is_real(object)) {
-            type = VAL_TYPE_REAL;
-            value.real = json_real_value(object);
-          }
-          const data_set_t *ds = plugin_get_ds(prop->type);
-
-          /* Check if data set found */
-          if (ds == NULL)
-            continue;
-
-          v1.values = &(value_t){0};
-          redfish_convert_val(&value, type, v1.values, ds->ds[0].type);
-
-          sstrncpy(v1.host, serv->host, sizeof(v1.host));
-          sstrncpy(v1.plugin, PLUGIN_NAME, sizeof(v1.plugin));
-          sstrncpy(v1.type, prop->type, sizeof(v1.type));
-          plugin_dispatch_values(&v1);
-          /* Clear values assigned in case of leakage */
-          v1.values = NULL;
-          v1.values_len = 0;
-        }
-      }
-      json_decref(json_array);
+  for (llentry_t *llres = llist_head(res_serv->resources); llres != NULL;
+       llres = llres->next) {
+    redfish_resource_t *res = (redfish_resource_t *)llres->value;
+    json_t *json_array = json_object_get(payload->json, res->name);
+
+    if (json_array == NULL) {
+      ERROR(PLUGIN_NAME ": Could not find resource \"%s\"", res->name);
+      continue;
     }
-  } else {
-    WARNING("Failed to get payload for service name \"%s\"", serv->name);
+
+    for (llentry_t *llprop = llist_head(res->properties); llprop != NULL;
+         llprop = llprop->next) {
+      redfish_property_t *prop = (redfish_property_t *)llprop->value;
+
+      redfish_process_payload_property(prop, json_array, res, serv);
+    }
+    json_decref(json_array);
   }
 }
 
@@ -837,19 +834,7 @@ static int redfish_cleanup(void) {
   for (llentry_t *le = llist_head(ctx->services); le; le = le->next) {
     redfish_service_t *service = (redfish_service_t *)le->value;
 
-    cleanupServiceEnumerator(service->redfish);
-    for (size_t i = 0; i < service->queries_num; i++)
-      sfree(service->queries[i]);
-
-    llist_destroy(service->query_ptrs);
-
-    sfree(service->name);
-    sfree(service->host);
-    sfree(service->user);
-    sfree(service->passwd);
-    sfree(service->token);
-    sfree(service->queries);
-    sfree(service);
+    redfish_service_destroy(service);
   }
   llist_destroy(ctx->services);
 
@@ -858,7 +843,7 @@ static int redfish_cleanup(void) {
   char *key;
   redfish_query_t *query;
 
-  while (c_avl_iterator_next(i, (void **)&key, (void **)&query) == 0) {
+  while (c_avl_iterator_next(i, (void *)&key, (void *)&query) == 0) {
     for (llentry_t *le = llist_head(query->resources); le != NULL;
          le = le->next) {
       redfish_resource_t *resource = (redfish_resource_t *)le->value;
index 9488f746f5232701b93bf1752dc3f38ae5a26df6..d7c885c49baf42f9b14f4bf3dec66694bf223798 100644 (file)
  * Authors:
  *   Martin Kennelly <martin.kennelly@intel.com>
  *   Marcin Mozejko <marcinx.mozejko@intel.com>
+ *   Adrian Boczkowski <adrianx.boczkowski@intel.com>
  **/
 
+#define plugin_dispatch_values redfish_test_plugin_dispatch_values_mock
 #include "redfish.c"
 #include "testing.h"
 
+#define VALUE_CACHE_SIZE (1)
+
+static value_list_t last_dispatched_value_list;
+static value_t last_dispatched_values[VALUE_CACHE_SIZE];
+int redfish_test_plugin_dispatch_values_mock(value_list_t const *vl) {
+  last_dispatched_value_list = *vl;
+  size_t len = MIN(vl->values_len, VALUE_CACHE_SIZE);
+  for (size_t i = 0; i < len; ++i) {
+    last_dispatched_values[i] = vl->values[i];
+  }
+  last_dispatched_value_list.values = last_dispatched_values;
+  return 0;
+}
+
+static value_list_t *redfish_test_get_last_dispatched_value_list() {
+  return &last_dispatched_value_list;
+}
+
 DEF_TEST(read_queries) {
   oconfig_item_t *ci = calloc(1, sizeof(*ci));
 
@@ -455,6 +475,8 @@ DEF_TEST(config_service) {
     sfree(serv->passwd);
     for (int i = 0; i < serv->queries_num; i++)
       sfree(serv->queries[i]);
+    sfree(serv->queries);
+    sfree(serv);
   }
   llist_destroy(ctx->services);
   sfree(ctx);
@@ -469,6 +491,71 @@ DEF_TEST(config_service) {
   return 0;
 }
 
+DEF_TEST(process_payload_property) {
+  redfish_property_t property;
+  property.name = "Abc";
+  property.plugin_inst = "TestPluginInstance";
+  property.type = "MAGIC";
+  property.type_inst = "TestTypeInstance";
+
+  redfish_resource_t resource;
+  resource.name = "ResourceName";
+
+  redfish_service_t service;
+  service.host = "localhost";
+
+  const char *json_text = "["
+                          "  { \"Abc\": 4567 }"
+                          "]";
+  json_error_t error;
+  json_t *root = json_loads(json_text, 0, &error);
+
+  if (!root) {
+    return -1;
+  }
+
+  redfish_process_payload_property(&property, root, &resource, &service);
+
+  json_decref(root);
+
+  value_list_t *v = redfish_test_get_last_dispatched_value_list();
+  EXPECT_EQ_INT(1, v->values_len);
+  EXPECT_EQ_STR("MAGIC", v->type);
+  EXPECT_EQ_INT(4567, v->values->derive);
+  EXPECT_EQ_STR("TestPluginInstance", v->plugin_instance);
+  EXPECT_EQ_STR("TestTypeInstance", v->type_instance);
+  EXPECT_EQ_STR("localhost", v->host);
+  EXPECT_EQ_STR("redfish", v->plugin);
+  return 0;
+}
+
+DEF_TEST(service_destroy) {
+  /* Check for memory leaks when a service is destroyed */
+  redfish_service_t *service = calloc(1, sizeof(*service));
+
+  service->name = strdup("Name");
+  service->host = strdup("http://localhost:1234");
+  service->user = strdup("User");
+  service->passwd = strdup("Password");
+  service->token = strdup("Token");
+
+  service->queries = calloc(2, sizeof(*service->queries));
+  service->queries[0] = strdup("Query1");
+  service->queries[1] = strdup("Query2");
+  service->queries_num = 2;
+
+  service->query_ptrs = llist_create();
+
+  service->flags |= REDFISH_FLAG_SERVICE_NO_VERSION_DOC;
+  service->auth.authCodes.userPass.username = service->user;
+  service->auth.authCodes.userPass.password = service->passwd;
+  service->redfish = createServiceEnumerator(service->host, NULL,
+                                             &service->auth, service->flags);
+
+  redfish_service_destroy(service);
+  return 0;
+}
+
 int main(void) {
   RUN_TEST(read_queries);
   RUN_TEST(convert_val);
@@ -477,5 +564,7 @@ int main(void) {
   RUN_TEST(config_resource);
   RUN_TEST(config_query);
   RUN_TEST(config_service);
+  RUN_TEST(process_payload_property);
+  RUN_TEST(service_destroy);
   END_TEST;
 }