Skip to content

Commit

Permalink
Company Communicator v4.1.5 (#509)
Browse files Browse the repository at this point in the history
  • Loading branch information
priyank29 committed Sep 28, 2021
1 parent f6ccd25 commit 54fd0c5
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Manifest/manifest_authors.json
@@ -1,7 +1,7 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.5",
"version": "4.1.4",
"version": "4.1.5",
"id": "1c07cd26-a088-4db8-8928-ace382fa219f",
"packageName": "com.microsoft.teams.companycommunicator.authors",
"developer": {
Expand Down
2 changes: 1 addition & 1 deletion Manifest/manifest_users.json
@@ -1,7 +1,7 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.5",
"version": "4.1.4",
"version": "4.1.5",
"id": "148a66bb-e83d-425a-927d-09f4299a9274",
"packageName": "com.microsoft.teams.companycommunicator",
"developer": {
Expand Down
Expand Up @@ -59,7 +59,7 @@ public abstract class BaseRepository<T> : IRepository<T>
protected ILogger Logger { get; }

/// <inheritdoc/>
public async Task CreateOrUpdateAsync(T entity)
public virtual async Task CreateOrUpdateAsync(T entity)
{
try
{
Expand Down Expand Up @@ -113,7 +113,7 @@ public async Task DeleteAsync(T entity)
}

/// <inheritdoc/>
public async Task<T> GetAsync(string partitionKey, string rowKey)
public virtual async Task<T> GetAsync(string partitionKey, string rowKey)
{
try
{
Expand Down
Expand Up @@ -16,6 +16,13 @@ namespace Microsoft.Teams.Apps.CompanyCommunicator.Common.Repositories.Notificat
/// </summary>
public class NotificationDataRepository : BaseRepository<NotificationDataEntity>, INotificationDataRepository
{
/// <summary>
/// Maximum length of error and warning messages to save in the entity.
/// This limit ensures that we don't hit the Azure table storage limits for the max size of the data
/// in a column, and the total size of an entity.
/// </summary>
public const int MaxMessageLengthToSave = 1024;

/// <summary>
/// Initializes a new instance of the <see cref="NotificationDataRepository"/> class.
/// </summary>
Expand Down Expand Up @@ -172,8 +179,14 @@ public async Task UpdateNotificationStatusAsync(string notificationId, Notificat
notificationDataEntityId);
if (notificationDataEntity != null)
{
notificationDataEntity.ErrorMessage =
this.AppendNewLine(notificationDataEntity.ErrorMessage, errorMessage);
var newMessage = this.AppendNewLine(notificationDataEntity.ErrorMessage, errorMessage);

// Restrict the total length of stored message to avoid hitting table storage limits
if (newMessage.Length <= MaxMessageLengthToSave)
{
notificationDataEntity.ErrorMessage = newMessage;
}

notificationDataEntity.Status = NotificationStatus.Failed.ToString();

// Set the end date as current date.
Expand All @@ -195,8 +208,14 @@ public async Task UpdateNotificationStatusAsync(string notificationId, Notificat
notificationDataEntityId);
if (notificationDataEntity != null)
{
notificationDataEntity.WarningMessage =
this.AppendNewLine(notificationDataEntity.WarningMessage, warningMessage);
var newMessage = this.AppendNewLine(notificationDataEntity.WarningMessage, warningMessage);

// Restrict the total length of stored message to avoid hitting table storage limits
if (newMessage.Length <= MaxMessageLengthToSave)
{
notificationDataEntity.WarningMessage = newMessage;
}

await this.CreateOrUpdateAsync(notificationDataEntity);
}
}
Expand Down
2 changes: 1 addition & 1 deletion Source/CompanyCommunicator/ClientApp/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Source/CompanyCommunicator/ClientApp/package.json
@@ -1,6 +1,6 @@
{
"name": "company-communicator",
"version": "4.1.4",
"version": "4.1.5",
"private": true,
"dependencies": {
"@fluentui/react-northstar": "^0.52.0",
Expand Down
@@ -0,0 +1,215 @@
// <copyright file="NotificationDataRepositoryTests.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
// </copyright>

namespace Microsoft.Teams.App.CompanyCommunicator.Common.Test.Repositories.NotificationData
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using FluentAssertions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Repositories;
using Microsoft.Teams.Apps.CompanyCommunicator.Common.Repositories.NotificationData;
using Moq;
using Xunit;

/// <summary>
/// Notification Data Repository unit tests.
/// </summary>
public class NotificationDataRepositoryTests
{
private Mock<ILogger<NotificationDataRepository>> logger = new Mock<ILogger<NotificationDataRepository>>();
private Mock<IOptions<RepositoryOptions>> repositoryOptions = new Mock<IOptions<RepositoryOptions>>();
private TableRowKeyGenerator rowKeyGenerator = new TableRowKeyGenerator();

/// <summary>
/// Gets data for SaveExceptionInNotificationDataEntityAsync_SavesExceptionInfo and SaveWarningInNotificationDataEntityAsync_SavesWarningInfo.
/// </summary>
public static IEnumerable<object[]> SaveMessageTestCasesData
{
get
{
var testCases = new List<SaveMessageTestData>
{
new SaveMessageTestData
{
InitialMessage = null,
ShouldUpdateMessage = true,
FailMessage = "Should update null message.",
},
new SaveMessageTestData
{
InitialMessage = "Short message",
ShouldUpdateMessage = true,
FailMessage = "Should update message not exceeding max length.",
},
new SaveMessageTestData
{
InitialMessage = new string('x', NotificationDataRepository.MaxMessageLengthToSave - 1),
ShouldUpdateMessage = false,
FailMessage = "Should not update message that will exceed max length.",
},
new SaveMessageTestData
{
InitialMessage = new string('x', NotificationDataRepository.MaxMessageLengthToSave),
ShouldUpdateMessage = false,
FailMessage = "Should not update message that is already at max length.",
},
};
return testCases.Select(c => new object[] { c });
}
}

/// <summary>
/// Check if NotificationData repository can be instantiated successfully.
/// </summary>
[Fact]
public void CreateInstance_AllParameters_ShouldBeSuccess()
{
// Arrange
this.repositoryOptions.Setup(x => x.Value).Returns(new RepositoryOptions()
{
StorageAccountConnectionString = "UseDevelopmentStorage=true",
EnsureTableExists = false,
});
Action action = () => new NotificationDataRepository(this.logger.Object, this.repositoryOptions.Object, this.rowKeyGenerator);

// Act and Assert.
action.Should().NotThrow();
}

/// <summary>
/// Check that SaveExceptionInNotificationDataEntityAsync saves the exception info to table storage, up to a maximum length.
/// </summary>
/// <param name="testData">Test data.</param>
/// <returns>Tracking task.</returns>
[Theory]
[MemberData(nameof(SaveMessageTestCasesData))]
public async Task SaveExceptionInNotificationDataEntityAsync_SavesExceptionInfo(SaveMessageTestData testData)
{
const string testEntityId = "testEntityId";
const string testMessage = "New error message.";

// Arrange
var mockRepository = this.CreateMockableNotificationDataRepository();
var repository = mockRepository.Object;

mockRepository.Setup(t => t.GetAsync(NotificationDataTableNames.SentNotificationsPartition, testEntityId))
.Returns(Task.FromResult(new NotificationDataEntity
{
Id = testEntityId,
ErrorMessage = testData.InitialMessage,
}));
mockRepository.Setup(t => t.CreateOrUpdateAsync(It.IsAny<NotificationDataEntity>())).Returns(Task.CompletedTask);

// Act
await repository.SaveExceptionInNotificationDataEntityAsync(testEntityId, testMessage);

// Assert
mockRepository.Verify(t => t.CreateOrUpdateAsync(It.Is<NotificationDataEntity>(e =>
e.Id == testEntityId &&
e.Status == NotificationStatus.Failed.ToString())));

if (testData.ShouldUpdateMessage)
{
mockRepository.Verify(
t => t.CreateOrUpdateAsync(It.Is<NotificationDataEntity>(e =>
e.ErrorMessage != null && e.ErrorMessage.EndsWith(testMessage))),
testData.FailMessage);
}
else
{
mockRepository.Verify(
t => t.CreateOrUpdateAsync(It.Is<NotificationDataEntity>(e =>
e.ErrorMessage == testData.InitialMessage)),
testData.FailMessage);
}
}

/// <summary>
/// Check that SaveWarningInNotificationDataEntityAsync saves the warning info to table storage, up to a maximum length.
/// </summary>
/// <param name="testData">Test data.</param>
/// <returns>Tracking task.</returns>
[Theory]
[MemberData(nameof(SaveMessageTestCasesData))]
public async Task SaveWarningInNotificationDataEntityAsync_SavesWarningInfo(SaveMessageTestData testData)
{
const string testEntityId = "testEntityId";
const string testMessage = "New error message.";

// Arrange
var mockRepository = this.CreateMockableNotificationDataRepository();
var repository = mockRepository.Object;

mockRepository.Setup(t => t.GetAsync(NotificationDataTableNames.SentNotificationsPartition, testEntityId))
.Returns(Task.FromResult(new NotificationDataEntity
{
Id = testEntityId,
WarningMessage = testData.InitialMessage,
}));
mockRepository.Setup(t => t.CreateOrUpdateAsync(It.IsAny<NotificationDataEntity>())).Returns(Task.CompletedTask);

// Act
await repository.SaveWarningInNotificationDataEntityAsync(testEntityId, testMessage);

// Assert
mockRepository.Verify(t => t.CreateOrUpdateAsync(It.Is<NotificationDataEntity>(e =>
e.Id == testEntityId)));

if (testData.ShouldUpdateMessage)
{
mockRepository.Verify(
t => t.CreateOrUpdateAsync(It.Is<NotificationDataEntity>(e =>
e.WarningMessage != null && e.WarningMessage.EndsWith(testMessage))),
testData.FailMessage);
}
else
{
mockRepository.Verify(
t => t.CreateOrUpdateAsync(It.Is<NotificationDataEntity>(e =>
e.WarningMessage == testData.InitialMessage)),
testData.FailMessage);
}
}

private Mock<NotificationDataRepository> CreateMockableNotificationDataRepository()
{
this.repositoryOptions.Setup(x => x.Value).Returns(new RepositoryOptions()
{
StorageAccountConnectionString = "UseDevelopmentStorage=true",
EnsureTableExists = false,
});

var mock = new Mock<NotificationDataRepository>(this.logger.Object, this.repositoryOptions.Object, this.rowKeyGenerator);
mock.CallBase = true;

return mock;
}

/// <summary>
/// Data for tests that check message saving to table.
/// </summary>
public class SaveMessageTestData
{
/// <summary>
/// Gets or sets the initial message value.
/// </summary>
public string InitialMessage { get; set; }

/// <summary>
/// Gets or sets a value indicating whether the message should be updated.
/// </summary>
public bool ShouldUpdateMessage { get; set; }

/// <summary>
/// Gets or sets the message to print if the test case fails.
/// </summary>
public string FailMessage { get; set; }
}
}
}

0 comments on commit 54fd0c5

Please sign in to comment.