diff --git a/sdk/src/azure/iot/az_iot_adu_client.c b/sdk/src/azure/iot/az_iot_adu_client.c index 6539e1ed12..162c0b7083 100644 --- a/sdk/src/azure/iot/az_iot_adu_client.c +++ b/sdk/src/azure/iot/az_iot_adu_client.c @@ -387,6 +387,7 @@ AZ_NODISCARD az_result az_iot_adu_client_parse_service_properties( (void)client; + az_json_token property_name_token_holder; int32_t required_size; int32_t out_length; @@ -538,48 +539,53 @@ AZ_NODISCARD az_result az_iot_adu_client_parse_service_properties( { RETURN_IF_JSON_TOKEN_NOT_TYPE(ref_json_reader, AZ_JSON_TOKEN_PROPERTY_NAME); - required_size = ref_json_reader->token.size + NULL_TERM_CHAR_SIZE; + // Hold the url id temporarily and advance. + // If the value is null, we are going to skip over it. + property_name_token_holder = ref_json_reader->token; - _az_RETURN_IF_NOT_ENOUGH_SIZE(buffer, required_size); + _az_RETURN_IF_FAILED(az_json_reader_next_token(ref_json_reader)); + if (ref_json_reader->token.kind != AZ_JSON_TOKEN_NULL) + { + required_size = property_name_token_holder.size + NULL_TERM_CHAR_SIZE; - update_request->file_urls[update_request->file_urls_count].id - = split_az_span(buffer, required_size, &buffer); + _az_RETURN_IF_NOT_ENOUGH_SIZE(buffer, required_size); - _az_RETURN_IF_FAILED(az_json_token_get_string( - &ref_json_reader->token, - (char*)az_span_ptr(update_request->file_urls[update_request->file_urls_count].id), - az_span_size(update_request->file_urls[update_request->file_urls_count].id), - &out_length)); + update_request->file_urls[update_request->file_urls_count].id + = split_az_span(buffer, required_size, &buffer); - // TODO: find a way to get rid of az_json_token_get_string (which adds a \0 at the - // end!!!!!!) - // Preferably have a function that does not copy anything. - update_request->file_urls[update_request->file_urls_count].id = az_span_slice( - update_request->file_urls[update_request->file_urls_count].id, 0, out_length); + _az_RETURN_IF_FAILED(az_json_token_get_string( + &property_name_token_holder, + (char*)az_span_ptr(update_request->file_urls[update_request->file_urls_count].id), + az_span_size(update_request->file_urls[update_request->file_urls_count].id), + &out_length)); - _az_RETURN_IF_FAILED(az_json_reader_next_token(ref_json_reader)); - RETURN_IF_JSON_TOKEN_NOT_TYPE(ref_json_reader, AZ_JSON_TOKEN_STRING); + // TODO: find a way to get rid of az_json_token_get_string (which adds a \0 at the + // end!!!!!!) + // Preferably have a function that does not copy anything. + update_request->file_urls[update_request->file_urls_count].id = az_span_slice( + update_request->file_urls[update_request->file_urls_count].id, 0, out_length); - required_size = ref_json_reader->token.size + NULL_TERM_CHAR_SIZE; + required_size = ref_json_reader->token.size + NULL_TERM_CHAR_SIZE; - _az_RETURN_IF_NOT_ENOUGH_SIZE(buffer, required_size); + _az_RETURN_IF_NOT_ENOUGH_SIZE(buffer, required_size); - update_request->file_urls[update_request->file_urls_count].url - = split_az_span(buffer, required_size, &buffer); + update_request->file_urls[update_request->file_urls_count].url + = split_az_span(buffer, required_size, &buffer); - _az_RETURN_IF_FAILED(az_json_token_get_string( - &ref_json_reader->token, - (char*)az_span_ptr(update_request->file_urls[update_request->file_urls_count].url), - az_span_size(update_request->file_urls[update_request->file_urls_count].url), - &out_length)); + _az_RETURN_IF_FAILED(az_json_token_get_string( + &ref_json_reader->token, + (char*)az_span_ptr(update_request->file_urls[update_request->file_urls_count].url), + az_span_size(update_request->file_urls[update_request->file_urls_count].url), + &out_length)); - // TODO: find a way to get rid of az_json_token_get_string (which adds a \0 at the - // end!!!!!!) - // Preferably have a function that does not copy anything. - update_request->file_urls[update_request->file_urls_count].url = az_span_slice( - update_request->file_urls[update_request->file_urls_count].url, 0, out_length); + // TODO: find a way to get rid of az_json_token_get_string (which adds a \0 at the + // end!!!!!!) + // Preferably have a function that does not copy anything. + update_request->file_urls[update_request->file_urls_count].url = az_span_slice( + update_request->file_urls[update_request->file_urls_count].url, 0, out_length); - update_request->file_urls_count++; + update_request->file_urls_count++; + } _az_RETURN_IF_FAILED(az_json_reader_next_token(ref_json_reader)); } diff --git a/sdk/tests/iot/adu/test_az_iot_adu.c b/sdk/tests/iot/adu/test_az_iot_adu.c index d897855e7b..6747304fd5 100644 --- a/sdk/tests/iot/adu/test_az_iot_adu.c +++ b/sdk/tests/iot/adu/test_az_iot_adu.c @@ -104,6 +104,95 @@ static uint8_t adu_request_payload[] "\"f2f4a804ca17afbae\":\"http://contoso-adu-instance--contoso-adu.b.nlu.dl.adu.microsoft.com/" "westus2/contoso-adu-instance--contoso-adu/67c8d2ef5148403391bed74f51a28597/" "iot-middleware-sample-adu-v1.1\"}}}"; +static uint8_t adu_request_payload_with_null_file_url_value[] + = "{\"service\":{\"workflow\":{\"action\":3,\"id\":\"51552a54-765e-419f-892a-c822549b6f38\"}," + "\"updateManifest\":\"{\\\"manifestVersion\\\":\\\"4\\\",\\\"updateId\\\":{\\\"provider\\\":" + "\\\"Contoso\\\",\\\"name\\\":\\\"Foobar\\\",\\\"version\\\":\\\"1.1\\\"}," + "\\\"compatibility\\\":[{\\\"deviceManufacturer\\\":\\\"Contoso\\\",\\\"deviceModel\\\":" + "\\\"Foobar\\\"}],\\\"instructions\\\":{\\\"steps\\\":[{\\\"handler\\\":\\\"microsoft/" + "swupdate:1\\\",\\\"files\\\":[\\\"f2f4a804ca17afbae\\\"],\\\"handlerProperties\\\":{" + "\\\"installedCriteria\\\":\\\"1.0\\\"}}]},\\\"files\\\":{\\\"f2f4a804ca17afbae\\\":{" + "\\\"fileName\\\":\\\"iot-middleware-sample-adu-v1.1\\\",\\\"sizeInBytes\\\":844976," + "\\\"hashes\\\":{\\\"sha256\\\":\\\"xsoCnYAMkZZ7m9RL9Vyg9jKfFehCNxyuPFaJVM/" + "WBi0=\\\"}}},\\\"createdDateTime\\\":\\\"2022-07-07T03:02:48.8449038Z\\\"}\"," + "\"updateManifestSignature\":" + "\"eyJhbGciOiJSUzI1NiIsInNqd2siOiJleUpoYkdjaU9pSlNVekkxTmlJc0ltdHBaQ0k2SWtGRVZTNHlNREEzTURJdV" + "VpSjkuZXlKcmRIa2lPaUpTVTBFaUxDSnVJam9pYkV4bWMwdHZPRmwwWW1Oak1sRXpUalV3VlhSTVNXWlhVVXhXVTBGRl" + "ltTm9LMFl2WTJVM1V6Rlpja3BvV0U5VGNucFRaa051VEhCVmFYRlFWSGMwZWxndmRHbEJja0ZGZFhrM1JFRmxWVzVGU0" + "VWamVEZE9hM2QzZVRVdk9IcExaV3AyWTBWWWNFRktMMlV6UWt0SE5FVTBiMjVtU0ZGRmNFOXplSGRQUzBWbFJ6Qkhkam" + "wzVjB3emVsUmpUblprUzFoUFJGaEdNMVZRWlVveGIwZGlVRkZ0Y3pKNmJVTktlRUppZEZOSldVbDBiWFpwWTNneVpXdG" + "tWbnBYUm5jdmRrdFVUblZMYXpob2NVczNTRkptYWs5VlMzVkxXSGxqSzNsSVVVa3dZVVpDY2pKNmEyc3plR2d4ZEVWUF" + "N6azRWMHBtZUdKamFsQnpSRTgyWjNwWmVtdFlla05OZW1Fd1R6QkhhV0pDWjB4QlZGUTVUV1k0V1ZCd1dVY3lhblpQWV" + "VSVmIwTlJiakpWWTFWU1RtUnNPR2hLWW5scWJscHZNa3B5SzFVNE5IbDFjVTlyTjBZMFdubFRiMEoyTkdKWVNrZ3lXbE" + "pTV2tab0wzVlRiSE5XT1hkU2JWbG9XWEoyT1RGRVdtbHhhemhJVWpaRVUyeHVabTVsZFRJNFJsUm9SVzF0YjNOVlRUTn" + "JNbGxNYzBKak5FSnZkWEIwTTNsaFNEaFpia3BVTnpSMU16TjFlakU1TDAxNlZIVnFTMmMzVkdGcE1USXJXR0owYmxwRU" + "9XcFVSMkY1U25Sc2FFWmxWeXRJUXpVM1FYUkJSbHBvY1ZsM2VVZHJXQ3M0TTBGaFVGaGFOR0V4VHpoMU1qTk9WVWQxTW" + "tGd04yOU5NVTR3ZVVKS0swbHNUM29pTENKbElqb2lRVkZCUWlJc0ltRnNaeUk2SWxKVE1qVTJJaXdpYTJsa0lqb2lRVV" + "JWTGpJeE1EWXdPUzVTTGxNaWZRLlJLS2VBZE02dGFjdWZpSVU3eTV2S3dsNFpQLURMNnEteHlrTndEdkljZFpIaTBIa2" + "RIZ1V2WnoyZzZCTmpLS21WTU92dXp6TjhEczhybXo1dnMwT1RJN2tYUG1YeDZFLUYyUXVoUXNxT3J5LS1aN2J3TW5LYT" + "NkZk1sbkthWU9PdURtV252RWMyR0hWdVVTSzREbmw0TE9vTTQxOVlMNThWTDAtSEthU18xYmNOUDhXYjVZR08xZXh1Rm" + "piVGtIZkNIU0duVThJeUFjczlGTjhUT3JETHZpVEtwcWtvM3RiSUwxZE1TN3NhLWJkZExUVWp6TnVLTmFpNnpIWTdSan" + "ZGbjhjUDN6R2xjQnN1aVQ0XzVVaDZ0M05rZW1UdV9tZjdtZUFLLTBTMTAzMFpSNnNTR281azgtTE1sX0ZaUmh4djNFZF" + "NtR2RBUTNlMDVMRzNnVVAyNzhTQWVzWHhNQUlHWmcxUFE3aEpoZGZHdmVGanJNdkdTSVFEM09wRnEtZHREcEFXbUo2Zm" + "5sZFA1UWxYek5tQkJTMlZRQUtXZU9BYjh0Yjl5aVhsemhtT1dLRjF4SzlseHpYUG9GNmllOFRUWlJ4T0hxTjNiSkVISk" + "VoQmVLclh6YkViV2tFNm4zTEoxbkd5M1htUlVFcER0Umdpa0tBUzZybFhFT0VneXNjIn0." + "eyJzaGEyNTYiOiJiUlkrcis0MzdsYTV5d2hIeDdqVHhlVVRkeDdJdXQyQkNlcVpoQys5bmFNPSJ9." + "eYoBoq9EOiCebTJAMhRh9DARC69F3C4Qsia86no9YbMJzwKt-rH88Va4dL59uNTlPNBQid4u0RlXSUTuma_v-" + "Sf4hyw70tCskwru5Fp41k9Ve3YSkulUKzctEhaNUJ9tUSA11Tz9HwJHOAEA1-S_dXWR_yuxabk9G_" + "BiucsuKhoI0Bas4e1ydQE2jXZNdVVibrFSqxvuVZrxHKVhwm-" + "G9RYHjZcoSgmQ58vWyaC2l8K8ZqnlQWmuLur0CZFQlanUVxDocJUtu1MnB2ER6emMRD_" + "4Azup2K4apq9E1EfYBbXxOZ0N5jaSr-2xg8NVSow5NqNSaYYY43wy_NIUefRlbSYu5zOrSWtuIwRdsO-" + "43Eo8b9vuJj1Qty9ee6xz1gdUNHnUdnM6dHEplZK0GZznsxRviFXt7yv8bVLd32Z7QDtFh3s17xlKulBZxWP-" + "q96r92RoUTov2M3ynPZSDmc6Mz7-r8ioO5VHO5pAPCH-tF5zsqzipPJKmBMaf5gYk8wR\",\"fileUrls\":{" + "\"f2f4a804ca17afbae\":\"http://contoso-adu-instance--contoso-adu.b.nlu.dl.adu.microsoft.com/" + "westus2/contoso-adu-instance--contoso-adu/67c8d2ef5148403391bed74f51a28597/" + "iot-middleware-sample-adu-v1.1\",\"f06bfc80808396ed5\":null}}}"; +static uint8_t adu_request_payload_multiple_file_url_value[] + = "{\"service\":{\"workflow\":{\"action\":3,\"id\":\"51552a54-765e-419f-892a-c822549b6f38\"}," + "\"updateManifest\":\"{\\\"manifestVersion\\\":\\\"4\\\",\\\"updateId\\\":{\\\"provider\\\":" + "\\\"Contoso\\\",\\\"name\\\":\\\"Foobar\\\",\\\"version\\\":\\\"1.1\\\"}," + "\\\"compatibility\\\":[{\\\"deviceManufacturer\\\":\\\"Contoso\\\",\\\"deviceModel\\\":" + "\\\"Foobar\\\"}],\\\"instructions\\\":{\\\"steps\\\":[{\\\"handler\\\":\\\"microsoft/" + "swupdate:1\\\",\\\"files\\\":[\\\"f2f4a804ca17afbae\\\"],\\\"handlerProperties\\\":{" + "\\\"installedCriteria\\\":\\\"1.0\\\"}}]},\\\"files\\\":{\\\"f2f4a804ca17afbae\\\":{" + "\\\"fileName\\\":\\\"iot-middleware-sample-adu-v1.1\\\",\\\"sizeInBytes\\\":844976," + "\\\"hashes\\\":{\\\"sha256\\\":\\\"xsoCnYAMkZZ7m9RL9Vyg9jKfFehCNxyuPFaJVM/" + "WBi0=\\\"}}},\\\"createdDateTime\\\":\\\"2022-07-07T03:02:48.8449038Z\\\"}\"," + "\"updateManifestSignature\":" + "\"eyJhbGciOiJSUzI1NiIsInNqd2siOiJleUpoYkdjaU9pSlNVekkxTmlJc0ltdHBaQ0k2SWtGRVZTNHlNREEzTURJdV" + "VpSjkuZXlKcmRIa2lPaUpTVTBFaUxDSnVJam9pYkV4bWMwdHZPRmwwWW1Oak1sRXpUalV3VlhSTVNXWlhVVXhXVTBGRl" + "ltTm9LMFl2WTJVM1V6Rlpja3BvV0U5VGNucFRaa051VEhCVmFYRlFWSGMwZWxndmRHbEJja0ZGZFhrM1JFRmxWVzVGU0" + "VWamVEZE9hM2QzZVRVdk9IcExaV3AyWTBWWWNFRktMMlV6UWt0SE5FVTBiMjVtU0ZGRmNFOXplSGRQUzBWbFJ6Qkhkam" + "wzVjB3emVsUmpUblprUzFoUFJGaEdNMVZRWlVveGIwZGlVRkZ0Y3pKNmJVTktlRUppZEZOSldVbDBiWFpwWTNneVpXdG" + "tWbnBYUm5jdmRrdFVUblZMYXpob2NVczNTRkptYWs5VlMzVkxXSGxqSzNsSVVVa3dZVVpDY2pKNmEyc3plR2d4ZEVWUF" + "N6azRWMHBtZUdKamFsQnpSRTgyWjNwWmVtdFlla05OZW1Fd1R6QkhhV0pDWjB4QlZGUTVUV1k0V1ZCd1dVY3lhblpQWV" + "VSVmIwTlJiakpWWTFWU1RtUnNPR2hLWW5scWJscHZNa3B5SzFVNE5IbDFjVTlyTjBZMFdubFRiMEoyTkdKWVNrZ3lXbE" + "pTV2tab0wzVlRiSE5XT1hkU2JWbG9XWEoyT1RGRVdtbHhhemhJVWpaRVUyeHVabTVsZFRJNFJsUm9SVzF0YjNOVlRUTn" + "JNbGxNYzBKak5FSnZkWEIwTTNsaFNEaFpia3BVTnpSMU16TjFlakU1TDAxNlZIVnFTMmMzVkdGcE1USXJXR0owYmxwRU" + "9XcFVSMkY1U25Sc2FFWmxWeXRJUXpVM1FYUkJSbHBvY1ZsM2VVZHJXQ3M0TTBGaFVGaGFOR0V4VHpoMU1qTk9WVWQxTW" + "tGd04yOU5NVTR3ZVVKS0swbHNUM29pTENKbElqb2lRVkZCUWlJc0ltRnNaeUk2SWxKVE1qVTJJaXdpYTJsa0lqb2lRVV" + "JWTGpJeE1EWXdPUzVTTGxNaWZRLlJLS2VBZE02dGFjdWZpSVU3eTV2S3dsNFpQLURMNnEteHlrTndEdkljZFpIaTBIa2" + "RIZ1V2WnoyZzZCTmpLS21WTU92dXp6TjhEczhybXo1dnMwT1RJN2tYUG1YeDZFLUYyUXVoUXNxT3J5LS1aN2J3TW5LYT" + "NkZk1sbkthWU9PdURtV252RWMyR0hWdVVTSzREbmw0TE9vTTQxOVlMNThWTDAtSEthU18xYmNOUDhXYjVZR08xZXh1Rm" + "piVGtIZkNIU0duVThJeUFjczlGTjhUT3JETHZpVEtwcWtvM3RiSUwxZE1TN3NhLWJkZExUVWp6TnVLTmFpNnpIWTdSan" + "ZGbjhjUDN6R2xjQnN1aVQ0XzVVaDZ0M05rZW1UdV9tZjdtZUFLLTBTMTAzMFpSNnNTR281azgtTE1sX0ZaUmh4djNFZF" + "NtR2RBUTNlMDVMRzNnVVAyNzhTQWVzWHhNQUlHWmcxUFE3aEpoZGZHdmVGanJNdkdTSVFEM09wRnEtZHREcEFXbUo2Zm" + "5sZFA1UWxYek5tQkJTMlZRQUtXZU9BYjh0Yjl5aVhsemhtT1dLRjF4SzlseHpYUG9GNmllOFRUWlJ4T0hxTjNiSkVISk" + "VoQmVLclh6YkViV2tFNm4zTEoxbkd5M1htUlVFcER0Umdpa0tBUzZybFhFT0VneXNjIn0." + "eyJzaGEyNTYiOiJiUlkrcis0MzdsYTV5d2hIeDdqVHhlVVRkeDdJdXQyQkNlcVpoQys5bmFNPSJ9." + "eYoBoq9EOiCebTJAMhRh9DARC69F3C4Qsia86no9YbMJzwKt-rH88Va4dL59uNTlPNBQid4u0RlXSUTuma_v-" + "Sf4hyw70tCskwru5Fp41k9Ve3YSkulUKzctEhaNUJ9tUSA11Tz9HwJHOAEA1-S_dXWR_yuxabk9G_" + "BiucsuKhoI0Bas4e1ydQE2jXZNdVVibrFSqxvuVZrxHKVhwm-" + "G9RYHjZcoSgmQ58vWyaC2l8K8ZqnlQWmuLur0CZFQlanUVxDocJUtu1MnB2ER6emMRD_" + "4Azup2K4apq9E1EfYBbXxOZ0N5jaSr-2xg8NVSow5NqNSaYYY43wy_NIUefRlbSYu5zOrSWtuIwRdsO-" + "43Eo8b9vuJj1Qty9ee6xz1gdUNHnUdnM6dHEplZK0GZznsxRviFXt7yv8bVLd32Z7QDtFh3s17xlKulBZxWP-" + "q96r92RoUTov2M3ynPZSDmc6Mz7-r8ioO5VHO5pAPCH-tF5zsqzipPJKmBMaf5gYk8wR\",\"fileUrls\":{" + "\"f2f4a804ca17afbae\":\"http://contoso-adu-instance--contoso-adu.b.nlu.dl.adu.microsoft.com/" + "westus2/contoso-adu-instance--contoso-adu/67c8d2ef5148403391bed74f51a28597/" + "iot-middleware-sample-adu-v1.1\",\"f06bfc80808396ed5\":null," + "\"f9fec76f10aede60e\":\"http://contoso-adu-instance--contoso-adu.b.nlu.dl.adu.microsoft.com/" + "westus2/contoso-adu-instance--contoso-adu/9f9bdc01a5cd49c09e79e35505a913c5/" + "contoso-v1.1.bin\"}}}"; static uint8_t adu_request_payload_reverse_order[] = "{\"service\":{\"updateManifest\":\"{\\\"manifestVersion\\\":\\\"4\\\",\\\"updateId\\\":{" "\\\"provider\\\":\\\"Contoso\\\",\\\"name\\\":\\\"Foobar\\\",\\\"version\\\":\\\"1.1\\\"}," @@ -150,6 +239,9 @@ static uint8_t adu_request_payload_reverse_order[] static uint8_t adu_request_manifest_no_deployment_null_manifest[] = "{\"service\":{\"workflow\":{\"action\":255,\"id\":\"nodeployment\"},\"updateManifest\":null," "\"updateManifestSignature\":null,\"fileUrls\":null},\"__t\":\"c\"}"; +static uint8_t adu_request_manifest_no_deployment_null_file_url_value[] + = "{\"service\":{\"workflow\":{\"action\":255,\"id\":\"nodeployment\"},\"updateManifest\":null," + "\"updateManifestSignature\":null,\"fileUrls\":{\"f2f4a804ca17afbae\":null}},\"__t\":\"c\"}"; static uint8_t adu_request_manifest[] = "{\"manifestVersion\":\"4\",\"updateId\":{\"provider\":\"Contoso\",\"name\":\"Foobar\"," "\"version\":\"1.1\"},\"compatibility\":[{\"deviceManufacturer\":\"Contoso\",\"deviceModel\":" @@ -180,6 +272,7 @@ static uint8_t instructions_steps_file[] = "f2f4a804ca17afbae"; static uint8_t instructions_steps_handler_properties_install_criteria[] = "1.0"; static uint8_t files_id[] = "f2f4a804ca17afbae"; static uint8_t files_filename[] = "iot-middleware-sample-adu-v1.1"; +static uint8_t files_id_contoso[] = "f9fec76f10aede60e"; static uint32_t files_size_in_bytes = 844976; static uint8_t files_hash_id[] = "sha256"; static uint8_t files_hashes_sha[] = "xsoCnYAMkZZ7m9RL9Vyg9jKfFehCNxyuPFaJVM/WBi0="; @@ -216,6 +309,10 @@ static uint8_t valid_signature[] static uint8_t file_url[] = "http://contoso-adu-instance--contoso-adu.b.nlu.dl.adu.microsoft.com/" "westus2/contoso-adu-instance--contoso-adu/" "67c8d2ef5148403391bed74f51a28597/iot-middleware-sample-adu-v1.1"; +static uint8_t file_url_contoso[] + = "http://contoso-adu-instance--contoso-adu.b.nlu.dl.adu.microsoft.com/" + "westus2/contoso-adu-instance--contoso-adu/9f9bdc01a5cd49c09e79e35505a913c5/" + "contoso-v1.1.bin"; #ifndef AZ_NO_PRECONDITION_CHECKING ENABLE_PRECONDITION_CHECK_TESTS() @@ -563,6 +660,125 @@ static void test_az_iot_adu_client_parse_service_properties_succeed(void** state assert_int_equal(az_span_size(request.file_urls[0].id), sizeof(files_id) - 1); assert_memory_equal(az_span_ptr(request.file_urls[0].url), file_url, sizeof(file_url) - 1); assert_int_equal(az_span_size(request.file_urls[0].url), sizeof(file_url) - 1); + assert_int_equal(request.file_urls_count, 1); +} + +static void test_az_iot_adu_client_parse_service_properties_with_null_file_url_value_succeed( + void** state) +{ + (void)state; + + az_iot_adu_client adu_client; + az_json_reader reader; + az_iot_adu_client_update_request request; + az_span remainder; + + assert_int_equal(az_iot_adu_client_init(&adu_client, NULL), AZ_OK); + + assert_int_equal( + az_json_reader_init( + &reader, + az_span_create( + adu_request_payload_with_null_file_url_value, + sizeof(adu_request_payload_with_null_file_url_value) - 1), + NULL), + AZ_OK); + + // parse_service_properties requires that the reader be placed on the "service" prop name + assert_int_equal(az_json_reader_next_token(&reader), AZ_OK); + assert_int_equal(az_json_reader_next_token(&reader), AZ_OK); + + assert_int_equal( + az_iot_adu_client_parse_service_properties( + &adu_client, + &reader, + az_span_create(scratch_buffer, sizeof(scratch_buffer)), + &request, + &remainder), + AZ_OK); + + // Workflow + assert_int_equal(request.workflow.action, workflow_action); + assert_memory_equal(az_span_ptr(request.workflow.id), workflow_id, sizeof(workflow_id) - 1); + + // Update Manifest + assert_memory_equal( + az_span_ptr(request.update_manifest), adu_request_manifest, sizeof(adu_request_manifest) - 1); + assert_int_equal(az_span_size(request.update_manifest), sizeof(adu_request_manifest) - 1); + + // Signature + assert_memory_equal( + az_span_ptr(request.update_manifest_signature), valid_signature, sizeof(valid_signature) - 1); + assert_int_equal(az_span_size(request.update_manifest_signature), sizeof(valid_signature) - 1); + + // File URLs + assert_memory_equal(az_span_ptr(request.file_urls[0].id), files_id, sizeof(files_id) - 1); + assert_int_equal(az_span_size(request.file_urls[0].id), sizeof(files_id) - 1); + assert_memory_equal(az_span_ptr(request.file_urls[0].url), file_url, sizeof(file_url) - 1); + assert_int_equal(az_span_size(request.file_urls[0].url), sizeof(file_url) - 1); + assert_int_equal(request.file_urls_count, 1); +} + +static void test_az_iot_adu_client_parse_service_properties_multiple_file_url_values_succeed( + void** state) +{ + (void)state; + + az_iot_adu_client adu_client; + az_json_reader reader; + az_iot_adu_client_update_request request; + az_span remainder; + + assert_int_equal(az_iot_adu_client_init(&adu_client, NULL), AZ_OK); + + assert_int_equal( + az_json_reader_init( + &reader, + az_span_create( + adu_request_payload_multiple_file_url_value, + sizeof(adu_request_payload_multiple_file_url_value) - 1), + NULL), + AZ_OK); + + // parse_service_properties requires that the reader be placed on the "service" prop name + assert_int_equal(az_json_reader_next_token(&reader), AZ_OK); + assert_int_equal(az_json_reader_next_token(&reader), AZ_OK); + + assert_int_equal( + az_iot_adu_client_parse_service_properties( + &adu_client, + &reader, + az_span_create(scratch_buffer, sizeof(scratch_buffer)), + &request, + &remainder), + AZ_OK); + + // Workflow + assert_int_equal(request.workflow.action, workflow_action); + assert_memory_equal(az_span_ptr(request.workflow.id), workflow_id, sizeof(workflow_id) - 1); + + // Update Manifest + assert_memory_equal( + az_span_ptr(request.update_manifest), adu_request_manifest, sizeof(adu_request_manifest) - 1); + assert_int_equal(az_span_size(request.update_manifest), sizeof(adu_request_manifest) - 1); + + // Signature + assert_memory_equal( + az_span_ptr(request.update_manifest_signature), valid_signature, sizeof(valid_signature) - 1); + assert_int_equal(az_span_size(request.update_manifest_signature), sizeof(valid_signature) - 1); + + // File URLs + assert_memory_equal(az_span_ptr(request.file_urls[0].id), files_id, sizeof(files_id) - 1); + assert_int_equal(az_span_size(request.file_urls[0].id), sizeof(files_id) - 1); + assert_memory_equal(az_span_ptr(request.file_urls[0].url), file_url, sizeof(file_url) - 1); + assert_int_equal(az_span_size(request.file_urls[0].url), sizeof(file_url) - 1); + assert_memory_equal( + az_span_ptr(request.file_urls[1].id), files_id_contoso, sizeof(files_id_contoso) - 1); + assert_int_equal(az_span_size(request.file_urls[1].id), sizeof(files_id_contoso) - 1); + assert_memory_equal( + az_span_ptr(request.file_urls[1].url), file_url_contoso, sizeof(file_url_contoso) - 1); + assert_int_equal(az_span_size(request.file_urls[1].url), sizeof(file_url_contoso) - 1); + assert_int_equal(request.file_urls_count, 2); } static void test_az_iot_adu_client_parse_service_properties_payload_reverse_order_succeed( @@ -617,6 +833,7 @@ static void test_az_iot_adu_client_parse_service_properties_payload_reverse_orde assert_int_equal(az_span_size(request.file_urls[0].id), sizeof(files_id) - 1); assert_memory_equal(az_span_ptr(request.file_urls[0].url), file_url, sizeof(file_url) - 1); assert_int_equal(az_span_size(request.file_urls[0].url), sizeof(file_url) - 1); + assert_int_equal(request.file_urls_count, 1); } static void test_az_iot_adu_client_parse_service_properties_payload_no_deployment_null_manifest( @@ -672,6 +889,60 @@ static void test_az_iot_adu_client_parse_service_properties_payload_no_deploymen assert_int_equal(request.file_urls_count, 0); } +static void +test_az_iot_adu_client_parse_service_properties_payload_no_deployment_null_file_url_value( + void** state) +{ + (void)state; + + az_iot_adu_client adu_client; + az_json_reader reader; + az_iot_adu_client_update_request request; + az_span remainder; + + assert_int_equal(az_iot_adu_client_init(&adu_client, NULL), AZ_OK); + + assert_int_equal( + az_json_reader_init( + &reader, + az_span_create( + adu_request_manifest_no_deployment_null_file_url_value, + sizeof(adu_request_manifest_no_deployment_null_file_url_value) - 1), + NULL), + AZ_OK); + + // parse_service_properties requires that the reader be placed on the "service" prop name + assert_int_equal(az_json_reader_next_token(&reader), AZ_OK); + assert_int_equal(az_json_reader_next_token(&reader), AZ_OK); + + assert_int_equal( + az_iot_adu_client_parse_service_properties( + &adu_client, + &reader, + az_span_create(scratch_buffer, sizeof(scratch_buffer)), + &request, + &remainder), + AZ_OK); + + // Workflow + assert_int_equal(request.workflow.action, workflow_action_failed); + assert_memory_equal( + az_span_ptr(request.workflow.id), + workflow_id_nodeployment, + sizeof(workflow_id_nodeployment) - 1); + + // Update Manifest + assert_null(az_span_ptr(request.update_manifest)); + assert_int_equal(az_span_size(request.update_manifest), 0); + + // Signature + assert_null(az_span_ptr(request.update_manifest_signature)); + assert_int_equal(az_span_size(request.update_manifest_signature), 0); + + // File URLs + assert_int_equal(request.file_urls_count, 0); +} + static void test_az_iot_adu_client_parse_update_manifest_succeed(void** state) { (void)state; @@ -875,9 +1146,15 @@ int test_az_iot_adu() cmocka_unit_test(test_az_iot_adu_client_get_agent_state_long_payload_succeed), cmocka_unit_test(test_az_iot_adu_client_get_service_properties_response_succeed), cmocka_unit_test(test_az_iot_adu_client_parse_service_properties_succeed), + cmocka_unit_test( + test_az_iot_adu_client_parse_service_properties_with_null_file_url_value_succeed), + cmocka_unit_test( + test_az_iot_adu_client_parse_service_properties_multiple_file_url_values_succeed), cmocka_unit_test(test_az_iot_adu_client_parse_service_properties_payload_reverse_order_succeed), cmocka_unit_test( test_az_iot_adu_client_parse_service_properties_payload_no_deployment_null_manifest), + cmocka_unit_test( + test_az_iot_adu_client_parse_service_properties_payload_no_deployment_null_file_url_value), cmocka_unit_test(test_az_iot_adu_client_parse_update_manifest_succeed), cmocka_unit_test(test_az_iot_adu_client_parse_update_manifest_payload_reverse_order_succeed), };