Skip to content

Commit

Permalink
Rework exceptions on DeviceModelPropertiesController #742 (#748)
Browse files Browse the repository at this point in the history
* Rework exceptions on DeviceModelPropertiesController #742

* Add unit tests on DeviceModelPropertiesController

* Add unit test on SetProperties Model validation

* typo fix

Co-authored-by: ben salim <salim.benahben@cgi.com>
  • Loading branch information
hocinehacherouf and Sben65 authored May 25, 2022
1 parent b6bf6d9 commit 900a05f
Show file tree
Hide file tree
Showing 2 changed files with 281 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,14 @@ namespace AzureIoTHub.Portal.Server.Tests.Unit.Controllers.V10
using AzureIoTHub.Portal.Server.Entities;
using AzureIoTHub.Portal.Server.Factories;
using AzureIoTHub.Portal.Models.v10;
using FluentAssertions;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Moq;
using NUnit.Framework;
using Server.Exceptions;

[TestFixture]
public class DeviceModelPropertiesControllerTests
Expand Down Expand Up @@ -94,6 +97,59 @@ public async Task GetPropertiesStateUnderTestExpectedBehavior()
this.mockRepository.VerifyAll();
}

[Test]
public async Task GetPropertiesShouldThrowInternalServerErrorExceptionWhenIssueOccurs()
{
// Arrange
var deviceModelPropertiesController = CreateDeviceModelPropertiesController();
var entity = SetupMockEntity();

_ = this.mockDeviceModelPropertiesTableClient.Setup(c => c.QueryAsync<DeviceModelProperty>(
It.Is<string>(x => x == $"PartitionKey eq '{entity.RowKey}'"),
It.IsAny<int?>(),
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.Throws(new RequestFailedException("test"));

_ = this.mockTableClientFactory.Setup(c => c.GetDeviceTemplateProperties())
.Returns(this.mockDeviceModelPropertiesTableClient.Object);

// Act
var act = () => deviceModelPropertiesController.GetProperties(entity.RowKey);

// Assert
_ = await act.Should().ThrowAsync<InternalServerErrorException>();
this.mockRepository.VerifyAll();
}

[Test]
public async Task GetPropertiesShouldThrowInternalServerErrorExceptionWhenIssueOccursWhenCheckingIfDeviceModelExists()
{
// Arrange
var deviceModelPropertiesController = CreateDeviceModelPropertiesController();
var modelId = Guid.NewGuid().ToString();
var entity = new TableEntity("0", modelId);

_ = this.mockDeviceTemplatesTableClient.Setup(c => c.GetEntityAsync<TableEntity>(
It.Is<string>(p => p == "0"),
It.Is<string>(k => k == modelId),
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.ThrowsAsync(new RequestFailedException("test"));

_ = this.mockTableClientFactory.Setup(c => c.GetDeviceTemplates())
.Returns(this.mockDeviceTemplatesTableClient.Object);

_ = this.mockLogger.Setup(x => x.Log(It.IsAny<LogLevel>(), It.IsAny<EventId>(), It.IsAny<It.IsAnyType>(), It.IsAny<Exception>(), It.IsAny<Func<It.IsAnyType, Exception, string>>()));

// Act
var act = () => deviceModelPropertiesController.GetProperties(entity.RowKey);

// Assert
_ = await act.Should().ThrowAsync<InternalServerErrorException>();
this.mockRepository.VerifyAll();
}

[Test]
public async Task WhenDeviceModelNotExistsGetPropertiesShouldReturnHttp404()
{
Expand Down Expand Up @@ -176,6 +232,182 @@ public async Task SetPropertiesStateUnderTestExpectedBehavior()
this.mockRepository.VerifyAll();
}

[Test]
public async Task SetPropertiesShouldThrowInternalServerErrorExceptionWhenIssueOccursWhenGettingExistingProperties()
{
// Arrange
var deviceModelPropertiesController = CreateDeviceModelPropertiesController();
var entity = SetupMockEntity();

var properties = new[]
{
new DeviceProperty
{
DisplayName =Guid.NewGuid().ToString(),
Name = Guid.NewGuid().ToString()
}
};

_ = this.mockDeviceModelPropertiesTableClient.Setup(c => c.QueryAsync<DeviceModelProperty>(
It.Is<string>(x => x == $"PartitionKey eq '{entity.RowKey}'"),
It.IsAny<int?>(),
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.Throws(new RequestFailedException("test"));

_ = this.mockTableClientFactory.Setup(c => c.GetDeviceTemplateProperties())
.Returns(this.mockDeviceModelPropertiesTableClient.Object);

// Act
var act = () => deviceModelPropertiesController.SetProperties(entity.RowKey, properties);

// Assert
_ = await act.Should().ThrowAsync<InternalServerErrorException>();
this.mockRepository.VerifyAll();
}

[Test]
public async Task SetPropertiesShouldThrowProblemDetailsExceptionWhenModelIsNotValid()
{
// Arrange
var deviceModelPropertiesController = CreateDeviceModelPropertiesController();
var entity = SetupMockEntity();

var properties = new[]
{
new DeviceProperty()
};

deviceModelPropertiesController.ModelState.AddModelError("Key", "Device model is invalid");

// Act
var act = () => deviceModelPropertiesController.SetProperties(entity.RowKey, properties);

// Assert
_ = await act.Should().ThrowAsync<ProblemDetailsException>();
this.mockRepository.VerifyAll();
}

[Test]
public async Task SetPropertiesShouldThrowInternalServerErrorExceptionWhenIssueOccursWhenDeletingProperty()
{
// Arrange
var deviceModelPropertiesController = CreateDeviceModelPropertiesController();
var entity = SetupMockEntity();
var mockResponse = this.mockRepository.Create<Response>();
var existingProperty = Guid.NewGuid().ToString();

var properties = new[]
{
new DeviceProperty
{
DisplayName =Guid.NewGuid().ToString(),
Name = Guid.NewGuid().ToString()
}
};

_ = this.mockDeviceModelPropertiesTableClient.Setup(c => c.QueryAsync<DeviceModelProperty>(
It.Is<string>(x => x == $"PartitionKey eq '{entity.RowKey}'"),
It.IsAny<int?>(),
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.Returns(AsyncPageable<DeviceModelProperty>.FromPages(new[]
{
Page<DeviceModelProperty>.FromValues(new DeviceModelProperty[]
{
new DeviceModelProperty
{
RowKey = existingProperty,
PartitionKey = entity.RowKey,
}
}, null, mockResponse.Object)
}));

_ = this.mockDeviceModelPropertiesTableClient.Setup(c => c.DeleteEntityAsync(
It.Is<string>(x => x == entity.RowKey),
It.Is<string>(x => x == existingProperty),
It.IsAny<ETag>(),
It.IsAny<CancellationToken>()))
.ThrowsAsync(new RequestFailedException("test"));

_ = this.mockTableClientFactory.Setup(c => c.GetDeviceTemplateProperties())
.Returns(this.mockDeviceModelPropertiesTableClient.Object);

// Act
var act = () => deviceModelPropertiesController.SetProperties(entity.RowKey, properties);

// Assert
_ = await act.Should().ThrowAsync<InternalServerErrorException>();
this.mockRepository.VerifyAll();
}

[Test]
public async Task SetPropertiesShouldThrowInternalServerErrorExceptionWhenIssueOccursWhenAddingProperty()
{
// Arrange
var deviceModelPropertiesController = CreateDeviceModelPropertiesController();
var entity = SetupMockEntity();
var mockResponse = this.mockRepository.Create<Response>();
var existingProperty = Guid.NewGuid().ToString();

var properties = new[]
{
new DeviceProperty
{
DisplayName =Guid.NewGuid().ToString(),
Name = Guid.NewGuid().ToString()
}
};

_ = this.mockDeviceModelPropertiesTableClient.Setup(c => c.QueryAsync<DeviceModelProperty>(
It.Is<string>(x => x == $"PartitionKey eq '{entity.RowKey}'"),
It.IsAny<int?>(),
It.IsAny<IEnumerable<string>>(),
It.IsAny<CancellationToken>()))
.Returns(AsyncPageable<DeviceModelProperty>.FromPages(new[]
{
Page<DeviceModelProperty>.FromValues(new DeviceModelProperty[]
{
new DeviceModelProperty
{
RowKey = existingProperty,
PartitionKey = entity.RowKey,
}
}, null, mockResponse.Object)
}));

_ = this.mockDeviceModelPropertiesTableClient.Setup(c => c.AddEntityAsync(
It.Is<DeviceModelProperty>(x => x.PartitionKey == entity.RowKey),
It.IsAny<CancellationToken>()))
.ThrowsAsync(new RequestFailedException("test"));

_ = this.mockDeviceModelPropertiesTableClient.Setup(c => c.DeleteEntityAsync(
It.Is<string>(x => x == entity.RowKey),
It.Is<string>(x => x == existingProperty),
It.IsAny<ETag>(),
It.IsAny<CancellationToken>()))
.ReturnsAsync(mockResponse.Object);

_ = this.mockMapper.Setup(c => c.Map(
It.IsAny<DeviceProperty>(),
It.IsAny<Action<IMappingOperationOptions<object, DeviceModelProperty>>>()))
.Returns((DeviceProperty x, Action<IMappingOperationOptions<object, DeviceModelProperty>> _) => new DeviceModelProperty
{
Name = x.Name,
PartitionKey = entity.RowKey
});

_ = this.mockTableClientFactory.Setup(c => c.GetDeviceTemplateProperties())
.Returns(this.mockDeviceModelPropertiesTableClient.Object);

// Act
var act = () => deviceModelPropertiesController.SetProperties(entity.RowKey, properties);

// Assert
_ = await act.Should().ThrowAsync<InternalServerErrorException>();
this.mockRepository.VerifyAll();
}

[Test]
public async Task WhenDeviceModelNotExistsSetPropertiesShouldReturnHttp404()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace AzureIoTHub.Portal.Server.Controllers.V10
using AzureIoTHub.Portal.Server.Entities;
using AzureIoTHub.Portal.Server.Factories;
using AzureIoTHub.Portal.Models.v10;
using Exceptions;
using Hellang.Middleware.ProblemDetails;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
Expand Down Expand Up @@ -56,14 +58,23 @@ protected DeviceModelPropertiesControllerBase(
/// <param name="id">The device model properties</param>
public virtual async Task<ActionResult<IEnumerable<DeviceProperty>>> GetProperties(string id)
{
if (!(await DeviceModelExists(id)))
if (!await DeviceModelExists(id))
{
return NotFound();
}

var items = this.tableClientFactory
.GetDeviceTemplateProperties()
.QueryAsync<DeviceModelProperty>($"PartitionKey eq '{id}'");
AsyncPageable<DeviceModelProperty> items;

try
{
items = this.tableClientFactory
.GetDeviceTemplateProperties()
.QueryAsync<DeviceModelProperty>($"PartitionKey eq '{id}'");
}
catch (RequestFailedException e)
{
throw new InternalServerErrorException($"Unable to get existing device model properties for device with id {id}", e);
}

var result = new List<DeviceProperty>();

Expand All @@ -89,27 +100,55 @@ public virtual async Task<ActionResult> SetProperties(string id, IEnumerable<Dev

if (!ModelState.IsValid)
{
return BadRequest(ModelState);
var validation = new ValidationProblemDetails(ModelState)
{
Status = StatusCodes.Status422UnprocessableEntity
};

throw new ProblemDetailsException(validation);
}

ArgumentNullException.ThrowIfNull(properties, nameof(properties));

var table = this.tableClientFactory
.GetDeviceTemplateProperties();

var items = table
.QueryAsync<DeviceModelProperty>($"PartitionKey eq '{id}'");
AsyncPageable<DeviceModelProperty> items;

try
{
items = table
.QueryAsync<DeviceModelProperty>($"PartitionKey eq '{id}'");
}
catch (RequestFailedException e)
{
throw new InternalServerErrorException($"Unable to get existing device model properties for device with id {id}", e);
}

await foreach (var item in items)
{
_ = await table.DeleteEntityAsync(id, item.RowKey);
try
{
_ = await table.DeleteEntityAsync(id, item.RowKey);
}
catch (RequestFailedException e)
{
throw new InternalServerErrorException($"Unable to delete the property {item.RowKey} for device model with id {id}", e);
}
}

foreach (var item in properties)
{
var entity = this.mapper.Map<DeviceModelProperty>(item, opts => opts.Items[nameof(DeviceModelProperty.PartitionKey)] = id);

_ = await table.AddEntityAsync(entity);
try
{
_ = await table.AddEntityAsync(entity);
}
catch (RequestFailedException e)
{
throw new InternalServerErrorException($"Unable to add the property {item.Name} for device model with id {id}", e);
}
}

return Ok();
Expand All @@ -134,7 +173,7 @@ private async Task<bool> DeviceModelExists(string id)

this.log.LogError(e.Message, e);

throw;
throw new InternalServerErrorException($"Unable to check if device model with id {id} exist", e);
}
}
}
Expand Down

0 comments on commit 900a05f

Please sign in to comment.