Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VIH-10175 Refactor health check to improve startup and liveness #213

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,14 @@ public static AddNotificationRequest MapToNewHearingNotification(HearingDto hear
var parameters = InitHearingNotificationParams(hearing);

NotificationType notificationType;
if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase) && eJudFeatureEnabled && participant.HasEjdUsername())
var isJudge = participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase);

if (isJudge && eJudFeatureEnabled && participant.HasEjdUsername())
{
notificationType = NotificationType.HearingConfirmationEJudJudge;
parameters.Add(NotifyParams.Judge, participant.DisplayName);
}
else if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase) &&
!eJudFeatureEnabled)
else if (isJudge) // default to non ejud template for judges without ejud username
{
notificationType = NotificationType.HearingConfirmationJudge;
parameters.Add(NotifyParams.Judge, participant.DisplayName);
Expand Down Expand Up @@ -184,14 +185,15 @@ public static AddNotificationRequest MapToMultiDayHearingConfirmationNotificatio
{NotifyParams.NumberOfDays, days.ToString()}
};
NotificationType notificationType;
if (participant.UserRole.Contains(NotifyParams.Judge, StringComparison.InvariantCultureIgnoreCase) &&
eJudFeatureEnabled && participant.HasEjdUsername())

var isJudge = participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase);

if (isJudge && eJudFeatureEnabled && participant.HasEjdUsername())
{
notificationType = NotificationType.HearingConfirmationEJudJudgeMultiDay;
parameters.Add(NotifyParams.Judge, participant.DisplayName);
}
else if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase) &&
!eJudFeatureEnabled)
else if (isJudge)
{
notificationType = NotificationType.HearingConfirmationJudgeMultiDay;
parameters.Add(NotifyParams.Judge, participant.DisplayName);
Expand Down Expand Up @@ -241,13 +243,15 @@ public static AddNotificationRequest MapToDemoOrTestNotification(HearingDto hear
};

NotificationType notificationType;
if (participant.UserRole.Contains(RoleNames.JudicialOfficeHolder, StringComparison.InvariantCultureIgnoreCase) && eJudFeatureEnabled
&& participant.HasEjdUsername())
var isJudge = participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase);
var isJudicialOfficeHolder = participant.UserRole.Contains(RoleNames.JudicialOfficeHolder, StringComparison.InvariantCultureIgnoreCase);

if (isJudicialOfficeHolder && eJudFeatureEnabled && participant.HasEjdUsername())
{
notificationType = NotificationType.EJudJohDemoOrTest;
parameters.Add(NotifyParams.JudicialOfficeHolder, $"{participant.FirstName} {participant.LastName}");
}
else if (participant.UserRole.Contains(RoleNames.Judge, StringComparison.InvariantCultureIgnoreCase))
else if (isJudge)
{
var contactEmailForNonEJudJudgeUser = GetContactEmailForNonEJudJudgeUser(participant);
bool isEmailEjud = participant.HasEjdUsername();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,6 @@ public Task PushNewConferenceAdded(Guid conferenceId)

public Task PushParticipantsUpdatedMessage(Guid conferenceId, UpdateConferenceParticipantsRequest request)
{
DefaultContractResolver contractResolver = new DefaultContractResolver
{
NamingStrategy = new SnakeCaseNamingStrategy()
};

string json = JsonConvert.SerializeObject(request, new JsonSerializerSettings
{
ContractResolver = contractResolver,
Formatting = Formatting.Indented
});
PushParticipantsUpdatedMessageCount++;
return Task.FromResult(HttpStatusCode.OK);
}
Expand All @@ -62,5 +52,11 @@ public Task PushCloseConsultationBetweenEndpointAndParticipant(Guid conferenceId
{
return Task.FromResult(HttpStatusCode.OK);
}

public void ClearRequests()
{
PushParticipantsUpdatedMessageCount = PushNewConferenceAddedMessageCount =
PushAllocationToCsoUpdatedMessageCount = PushEndpointsUpdatedMessageCount = 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
using BookingQueueSubscriber.Services.MessageHandlers.Core;
using BookingQueueSubscriber.Services.NotificationApi;
using BookingQueueSubscriber.Services.VideoApi;
using BookingQueueSubscriber.Services.VideoWeb;
using BookingQueueSubscriber.UnitTests.MessageHandlers;
using Microsoft.Extensions.Logging;
using NotificationApi.Contract;

namespace BookingQueueSubscriber.UnitTests.BookingQueueSubscriberFunctionTests;

public class HearingIsReadyForVideoIntegrationMessageTests
{
private readonly IServiceProvider _serviceProvider = ServiceProviderFactory.ServiceProvider;
private VideoApiServiceFake _videoApiService;
private VideoWebServiceFake _videoWebService;
private NotificationServiceFake _notificationService;
private BookingQueueSubscriberFunction _sut;
private ILogger<BookingQueueSubscriberFunction> _logger;

[SetUp]
public void SetUp()
{
_logger = new Mock<ILogger<BookingQueueSubscriberFunction>>().Object;
_videoApiService = (VideoApiServiceFake) _serviceProvider.GetService<IVideoApiService>();
_videoWebService = (VideoWebServiceFake) _serviceProvider.GetService<IVideoWebService>();
_notificationService = (NotificationServiceFake) _serviceProvider.GetService<INotificationService>();
_sut = new BookingQueueSubscriberFunction(new MessageHandlerFactory(ServiceProviderFactory.ServiceProvider), _logger);
}

[TearDown]
public void TearDown()
{
_videoApiService.ClearRequests();
_videoWebService.ClearRequests();
_notificationService.EJudFetaureEnabled = false;
}

[Test]
public async Task should_process_a_single_day_hearing_ready_event_with_a_judge_only()
{
var judgeEmail = "[email protected]";
const string message = @"{
'$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.EventMessage, BookingsApi.Infrastructure.Services',
'id': 'e0bbb9ed-ce49-4e69-94e7-3e35e7010206',
'timestamp': '2023-09-15T09:03:50.889496Z',
'integration_event': {
'$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.Events.HearingIsReadyForVideoIntegrationEvent, BookingsApi.Infrastructure.Services',
'hearing': {
'$type': 'BookingsApi.Infrastructure.Services.Dtos.HearingDto, BookingsApi.Infrastructure.Services',
'hearing_id': 'e2ef8a71-6d22-486b-8876-a69aceac86d7',
'group_id': null,
'scheduled_date_time': '2023-09-15T09:08:46.636188Z',
'scheduled_duration': 5,
'case_type': 'Civil Money Claims',
'case_number': '6918/2815',
'case_name': 'Bookings Api Integration Automated 9532050',
'hearing_venue_name': 'Birmingham Civil and Family Justice Centre',
'record_audio': true,
'hearing_type': 'First Application'
},
'participants': [
{
'$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services',
'participant_id': 'c20b90b2-8fb1-4e65-b77b-fd381821ccad',
'fullname': 'Mrs Automation_Judge Judge_1',
'username': '[email protected]',
'first_name': 'Automation_Judge',
'last_name': 'Judge_1',
'contact_email': '[email protected]',
'contact_telephone': '01234567890',
'display_name': 'Automation_Judge Judge_1',
'hearing_role': 'Judge',
'user_role': 'Judge',
'case_group_type': 'judge',
'representee': '',
'linked_participants': [],
'contact_email_for_non_e_jud_judge_user': '',
'contact_phone_for_non_e_jud_judge_user': '',
'send_hearing_notification_if_new': true
}
],
'endpoints': []
}
}";
await _sut.Run(message);

_videoApiService.BookNewConferenceCount.Should().Be(1);
_videoWebService.PushNewConferenceAddedMessageCount.Should().Be(1);
_notificationService.NotificationRequests.Should()
.Contain(x =>
x.ContactEmail == judgeEmail && x.NotificationType == NotificationType.HearingConfirmationJudge);
}

[Test]
public async Task should_process_a_single_day_hearing_ready_event_with_a_judge_and_participants()
{
const string message = @"{
'$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.EventMessage, BookingsApi.Infrastructure.Services',
'id': '25839fbd-d19a-4ff8-908d-1c844b9171bc',
'timestamp': '2023-09-15T09:17:22.211731Z',
'integration_event': {
'$type': 'BookingsApi.Infrastructure.Services.IntegrationEvents.Events.HearingIsReadyForVideoIntegrationEvent, BookingsApi.Infrastructure.Services',
'hearing': {
'$type': 'BookingsApi.Infrastructure.Services.Dtos.HearingDto, BookingsApi.Infrastructure.Services',
'hearing_id': '3946edba-933e-49cb-a328-e350814b6fa2',
'group_id': null,
'scheduled_date_time': '2023-09-15T09:22:18.10116Z',
'scheduled_duration': 5,
'case_type': 'Civil Money Claims',
'case_number': '6918/2815',
'case_name': 'Bookings Api Integration Automated 9532050',
'hearing_venue_name': 'Birmingham Civil and Family Justice Centre',
'record_audio': true,
'hearing_type': 'First Application'
},
'participants': [
{
'$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services',
'participant_id': 'c0102934-9c49-4cea-bcfc-45fbf503f8a0',
'fullname': 'Mrs Automation_Respondent LitigantInPerson_1',
'username': 'automation_respondent_litigantinperson_1@hearings.reform.hmcts.net',
'first_name': 'Automation_Respondent',
'last_name': 'LitigantInPerson_1',
'contact_email': '[email protected]',
'contact_telephone': '01234567890',
'display_name': 'Automation_Respondent LitigantInPerson_1',
'hearing_role': 'Litigant in person',
'user_role': 'Individual',
'case_group_type': 'respondent',
'representee': '',
'linked_participants': [],
'contact_email_for_non_e_jud_judge_user': null,
'contact_phone_for_non_e_jud_judge_user': null,
'send_hearing_notification_if_new': true
},
{
'$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services',
'participant_id': '04850798-4b69-4d86-8fb7-5bafda64703d',
'fullname': 'Mrs Automation_Respondent Representative_1',
'username': '[email protected]',
'first_name': 'Automation_Respondent',
'last_name': 'Representative_1',
'contact_email': '[email protected]',
'contact_telephone': '01234567890',
'display_name': 'Automation_Respondent Representative_1',
'hearing_role': 'Representative',
'user_role': 'Representative',
'case_group_type': 'respondent',
'representee': 'Automation_Respondent LitigantInPerson_1',
'linked_participants': [],
'contact_email_for_non_e_jud_judge_user': null,
'contact_phone_for_non_e_jud_judge_user': null,
'send_hearing_notification_if_new': true
},
{
'$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services',
'participant_id': '9e30d442-53ca-458b-90d8-9588cee1ddde',
'fullname': 'Mrs Automation_Judge Judge_1',
'username': '[email protected]',
'first_name': 'Automation_Judge',
'last_name': 'Judge_1',
'contact_email': '[email protected]',
'contact_telephone': '01234567890',
'display_name': 'Automation_Judge Judge_1',
'hearing_role': 'Judge',
'user_role': 'Judge',
'case_group_type': 'judge',
'representee': '',
'linked_participants': [],
'contact_email_for_non_e_jud_judge_user': '',
'contact_phone_for_non_e_jud_judge_user': '',
'send_hearing_notification_if_new': true
},
{
'$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services',
'participant_id': 'd6275f3c-5b21-4362-a0ca-d8319a68492b',
'fullname': 'Mrs Automation_Applicant LitigantInPerson_1',
'username': 'automation_applicant_litigantinperson_1@hearings.reform.hmcts.net',
'first_name': 'Automation_Applicant',
'last_name': 'LitigantInPerson_1',
'contact_email': '[email protected]',
'contact_telephone': '01234567890',
'display_name': 'Automation_Applicant LitigantInPerson_1',
'hearing_role': 'Litigant in person',
'user_role': 'Individual',
'case_group_type': 'applicant',
'representee': '',
'linked_participants': [],
'contact_email_for_non_e_jud_judge_user': null,
'contact_phone_for_non_e_jud_judge_user': null,
'send_hearing_notification_if_new': true
},
{
'$type': 'BookingsApi.Infrastructure.Services.Dtos.ParticipantDto, BookingsApi.Infrastructure.Services',
'participant_id': 'c9ca299c-c83d-4a1b-bef9-efa8fbb1d57f',
'fullname': 'Mrs Automation_Applicant Representative_1',
'username': '[email protected]',
'first_name': 'Automation_Applicant',
'last_name': 'Representative_1',
'contact_email': '[email protected]',
'contact_telephone': '01234567890',
'display_name': 'Automation_Applicant Representative_1',
'hearing_role': 'Representative',
'user_role': 'Representative',
'case_group_type': 'applicant',
'representee': 'Automation_Applicant LitigantInPerson_1',
'linked_participants': [],
'contact_email_for_non_e_jud_judge_user': null,
'contact_phone_for_non_e_jud_judge_user': null,
'send_hearing_notification_if_new': true
}
],
'endpoints': []
}
}";
await _sut.Run(message);

_videoApiService.BookNewConferenceCount.Should().Be(1);
_videoWebService.PushNewConferenceAddedMessageCount.Should().Be(1);
_notificationService.NotificationRequests.Should().NotBeNullOrEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public void OneTimeSetup()
public void TearDown()
{
_videoApiService.ClearRequests();
_notificationService.EJudFetaureEnabled = false;
_bookingsApi.EJudFeatureEnabled = false;
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System.Net;
using System.Threading;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Microsoft.Extensions.Logging;

namespace BookingQueueSubscriber.UnitTests.HealthCheckFunctionTests;

public class HealthCheckTests
{
private HealthCheckFunction _sut;
private Mock<HealthCheckService> _healthCheckServiceMock;

[SetUp]
public void Setup()
{
_healthCheckServiceMock = new Mock<HealthCheckService>();
_sut = new HealthCheckFunction(_healthCheckServiceMock.Object);
}

[Test]
public async Task Should_return_200_when_health_check_is_healthy()
{
// Arrange
var healthReport = new HealthReport(new Dictionary<string, HealthReportEntry>
{
{"test", new HealthReportEntry(HealthStatus.Healthy, "test", TimeSpan.Zero, null, null)}
}, TimeSpan.Zero);
_healthCheckServiceMock.Setup(x=> x.CheckHealthAsync(It.IsAny<Func<HealthCheckRegistration,bool>?>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(healthReport);

// Act
var result = await _sut.HealthCheck(null, Mock.Of<ILogger>());

// Assert
var okResult = result as ObjectResult;
Assert.NotNull(okResult);
Assert.AreEqual((int) HttpStatusCode.OK, okResult.StatusCode);
}

[Test]
public async Task Should_return_503_when_health_check_is_unhealthy()
{
// Arrange
var healthReport = new HealthReport(new Dictionary<string, HealthReportEntry>
{
{"test", new HealthReportEntry(HealthStatus.Unhealthy, "test", TimeSpan.Zero, new Exception("Not Working Right Now"), null)}
}, TimeSpan.Zero);
_healthCheckServiceMock.Setup(x=> x.CheckHealthAsync(It.IsAny<Func<HealthCheckRegistration,bool>?>(), It.IsAny<CancellationToken>()))
.ReturnsAsync(healthReport);

// Act
var result = await _sut.HealthCheck(null, Mock.Of<ILogger>());

// Assert
var serviceUnavailableResult = result as ObjectResult;
Assert.NotNull(serviceUnavailableResult);
Assert.AreEqual((int) HttpStatusCode.ServiceUnavailable, serviceUnavailableResult.StatusCode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,11 @@
}
},
"ApplicationInsights:ConnectionString": "InstrumentationKey=TestInstrumentationKey;IngestionEndpoint=https://test.com/;LiveEndpoint=https://test.com/",
"VhServices:EnableVideoApiStub": true
"VhServices": {
"BookingsApiUrl": "https://localhost:5300",
"VideoApiUrl": "https://localhost:59390",
"UserApiUrl": "https://localhost:5200",
"NotificationApiUrl": "https://localhost:59390",
"EnableVideoApiStub": "true"
}
}
Loading