Skip to content
This repository was archived by the owner on Jul 28, 2025. It is now read-only.
Merged
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
@@ -0,0 +1,38 @@
using System.Text.RegularExpressions;

namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;

public partial class NhsNumValidator() :
RegexValidator("NHS Num", NhsNumberRegex(), ErrorCodes.MissingNhsNum, ErrorCodes.InvalidNhsNum)
{
protected override IEnumerable<ValidationError> RunAdditionalChecks(int rowNumber, string value)
{
if (!HasValidCheckDigit(value))
{
yield return new ValidationError
{
RowNumber = rowNumber,
Field = FieldName,
Error = "NHS Num has invalid check digit",
Code = ErrorCodes.InvalidNhsNumCheckDigit
};
}
}

private static bool HasValidCheckDigit(string value)
{
var weightedSum = 0;
for (var i = 0; i < 9; i++)
{
weightedSum += (10 - i) * (value[i] - '0');
}

var remainder = weightedSum % 11;
var expectedCheckDigit = (11 - remainder) % 11;

return expectedCheckDigit == value[9] - '0';
}

[GeneratedRegex(@"^\d{10}$", RegexOptions.Compiled)]
private static partial Regex NhsNumberRegex();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ public class RegexValidator(
string errorCodeInvalidFormat)
: IRecordValidator
{
protected string FieldName { get; } = fieldName;

public IEnumerable<ValidationError> Validate(FileDataRecord fileDataRecord)
{
var value = fileDataRecord[fieldName];
var value = fileDataRecord[FieldName];

if (value == null)
{
yield return new ValidationError
{
RowNumber = fileDataRecord.RowNumber,
Field = fieldName,
Error = $"{fieldName} is missing",
Field = FieldName,
Error = $"{FieldName} is missing",
Code = errorCodeMissing,
};
yield break;
Expand All @@ -31,10 +33,21 @@ public IEnumerable<ValidationError> Validate(FileDataRecord fileDataRecord)
yield return new ValidationError
{
RowNumber = fileDataRecord.RowNumber,
Field = fieldName,
Error = $"{fieldName} is in an invalid format",
Field = FieldName,
Error = $"{FieldName} is in an invalid format",
Code = errorCodeInvalidFormat,
};
yield break;
}

foreach (var additionalError in RunAdditionalChecks(fileDataRecord.RowNumber, value))
{
yield return additionalError;
}
}

protected virtual IEnumerable<ValidationError> RunAdditionalChecks(int rowNumber, string value)
{
yield break;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static IEnumerable<IRecordValidator> GetAllRecordValidators()
ErrorCodes.InvalidAttendedNotScr),
new MaxLengthValidator("Appointment ID", 27, ErrorCodes.MissingAppointmentId,
ErrorCodes.InvalidAppointmentId),
new NhsNumValidator(),
new RegexValidator("Episode Type", EpisodeTypeRegex(), ErrorCodes.MissingEpisodeType,
ErrorCodes.InvalidEpisodeType),
new MaxLengthValidator("Batch ID", 9, ErrorCodes.MissingBatchId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;

namespace ServiceLayer.Mesh.Tests.FileTypes.NbssAppointmentEvents.Validation;

public class NhsNumValidatorTests : ValidationTestBase
{
[Fact]
public void Validate_NhsNumMissing_ReturnsValidationError()
{
// Arrange
var file = ParsedFileWithModifiedRecord(r => r.Fields.Remove("NHS Num"));

// Act
var validationErrors = Validate(file);

// Assert
validationErrors.ShouldContainValidationError(
"NHS Num",
"NHS Num is missing",
ErrorCodes.MissingNhsNum
);
}

[Theory]
[InlineData("308 407 5425")] // we don't anticipate spaces
[InlineData("857320211")] // too few character
[InlineData("90238807571")] // Too many characters
[InlineData("159278895S")] // invalid characters
public void Validate_NhsNumInvalidFormat_ReturnsValidationError(string value)
{
// Arrange
var file = ParsedFileWithModifiedRecord(r => r.Fields["NHS Num"] = value);

// Act
var validationErrors = Validate(file).ToList();

// Assert
validationErrors.ShouldContainValidationError(
"NHS Num",
"NHS Num is in an invalid format",
ErrorCodes.InvalidNhsNum
);
}

[Theory]
[InlineData("3244700471")]
[InlineData("7326012282")]
[InlineData("6245827145")]
[InlineData("4745895257")]
public void Validate_NhsNumInvalidCheckDigit_ReturnsValidationError(string value)
{
// Arrange
var file = ParsedFileWithModifiedRecord(r => r.Fields["NHS Num"] = value);

// Act
var validationErrors = Validate(file).ToList();

// Assert
validationErrors.ShouldContainValidationError(
"NHS Num",
"NHS Num has invalid check digit",
ErrorCodes.InvalidNhsNumCheckDigit
);
}

[Theory]
[InlineData("4941273230")]
[InlineData("6451357219")]
[InlineData("3365582983")]
[InlineData("8799244780")]
public void Validate_NHSNumValidFormat_NoValidationErrorsReturned(string value)
{
// Arrange
var file = ParsedFileWithModifiedRecord(r => r.Fields["NHS Num"] = value);

// Act
var validationErrors = Validate(file).ToList();

// Assert
Assert.Empty(validationErrors);
}
}
Loading