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,25 @@
using System.Text.RegularExpressions;
using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models;

namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;

public partial class ExtractIdValidator() :
HeaderFieldRegexValidator(h => h.ExtractId, "Extract ID", ExtractIdRegex(), ErrorCodes.MissingExtractId, ErrorCodes.InvalidExtractId)
{
[GeneratedRegex(@"^\d{8}$")]
private static partial Regex ExtractIdRegex();

protected override IEnumerable<ValidationError> RunAdditionalChecks(ParsedFile file, string value, bool hasErrored)
{
if (file.FileTrailer != null && file.FileHeader!.ExtractId != file.FileTrailer.ExtractId)
{
yield return new ValidationError
{
Field = "Extract ID",
Code = ErrorCodes.InconsistentExtractId,
Error = "Extract ID does not match value in header",
Scope = ValidationErrorScope.Trailer
};
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ public class HeaderFieldRegexValidator(
{
public IEnumerable<ValidationError> Validate(ParsedFile file)
{
if (file.FileHeader == null)
{
yield break;
}

var hasErrored = false;
var header = file.FileHeader!;
var value = fieldSelector.Compile().Invoke(header);

Expand All @@ -31,6 +37,7 @@ public IEnumerable<ValidationError> Validate(ParsedFile file)

if (!pattern.IsMatch(value))
{
hasErrored = true;
yield return new ValidationError
{
Scope = ValidationErrorScope.Header,
Expand All @@ -39,5 +46,15 @@ public IEnumerable<ValidationError> Validate(ParsedFile file)
Code = errorCodeInvalidFormat,
};
}

foreach (var additionalError in RunAdditionalChecks(file, value, hasErrored))
{
yield return additionalError;
}
}

protected virtual IEnumerable<ValidationError> RunAdditionalChecks(ParsedFile file, string value, bool hasErrored)
{
yield break;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models;

namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;

public class HeaderPresenceValidator : IFileValidator
{
public IEnumerable<ValidationError> Validate(ParsedFile file)
{
if (file.FileHeader == null)
{
yield return new ValidationError
{
Code = ErrorCodes.MissingHeader,
Error = "Header is missing",
Scope = ValidationErrorScope.File
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Text.RegularExpressions;
using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models;

namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;

public partial class RecordCountValidator() :
HeaderFieldRegexValidator(h => h.RecordCount, "Record count", RecordCountRegex(), ErrorCodes.MissingRecordCount, ErrorCodes.InvalidRecordCount)
{
[GeneratedRegex(@"^(?!000000)\d{6}$")]
private static partial Regex RecordCountRegex();

protected override IEnumerable<ValidationError> RunAdditionalChecks(ParsedFile file, string value, bool hasErrored)
{
if (file.FileTrailer != null && file.FileHeader!.RecordCount != file.FileTrailer.RecordCount)
{
yield return new ValidationError
{
Field = "Record count",
Code = ErrorCodes.InconsistentRecordCount,
Error = "Record count does not match value in header",
Scope = ValidationErrorScope.Trailer
};
}
else if (!hasErrored && file.DataRecords.Count != int.Parse(file.FileHeader!.RecordCount!))
{
yield return new ValidationError
{
Code = ErrorCodes.UnexpectedRecordCount,
Error = "Record count does not match value in header and trailer",
Scope = ValidationErrorScope.File
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models;

namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;

public class TrailerPresenceValidator : IFileValidator
{
public IEnumerable<ValidationError> Validate(ParsedFile file)
{
if (file.FileTrailer == null)
{
yield return new ValidationError
{
Code = ErrorCodes.MissingTrailer,
Error = "Trailer is missing",
Scope = ValidationErrorScope.File
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,12 @@ public static IEnumerable<IRecordValidator> GetAllRecordValidators()

public static IEnumerable<IFileValidator> GetAllFileValidators()
{
return [new FileValidator()];
return [
new HeaderPresenceValidator(),
new TrailerPresenceValidator(),
new ExtractIdValidator(),
new RecordCountValidator()
];
}

[GeneratedRegex(@"^[BCU]$", RegexOptions.Compiled)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation;

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

public class ExtractIdValidatorTests : ValidationTestBase
{
[Fact]
public void Validate_HeaderExtractIdMissing_ReturnsValidationError()
{
// Arrange
var file = ValidParsedFile;
file.FileHeader!.ExtractId = null;

// Act
var validationErrors = Validate(file);

// Assert
validationErrors.ShouldContainValidationError(
"Extract ID",
"Extract ID is missing",
ErrorCodes.MissingExtractId,
ValidationErrorScope.Header
);
}

[Theory]
[InlineData("1")] // Missing leading zeroes
[InlineData("100000000")] // Too large
[InlineData("")] // Blank
[InlineData("asdf")] // NaN
public void Validate_HeaderExtractIdInvalidFormat_ReturnsValidationError(string value)
{
// Arrange
var file = ValidParsedFile;
file.FileHeader!.ExtractId = value;

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

// Assert
validationErrors.ShouldContainValidationError(
"Extract ID",
"Extract ID is in an invalid format",
ErrorCodes.InvalidExtractId,
ValidationErrorScope.Header
);
}

[Theory]
[InlineData("00000000")]
[InlineData("00000001")]
[InlineData("99999999")]
public void Validate_HeaderExtractIdValidFormat_NoValidationErrorsReturned(string value)
{
// Arrange
var file = ValidParsedFile;
file.FileHeader!.ExtractId = value;
file.FileTrailer!.ExtractId = value;

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

// Assert
Assert.Empty(validationErrors);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("00000108")]
public void Validate_TrailerExtractIdMismatch_ReturnsValidationError(string? value)
{
// Arrange
var file = ValidParsedFile;
file.FileTrailer!.ExtractId = value;

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

// Assert
validationErrors.ShouldContainValidationError(
"Extract ID",
"Extract ID does not match value in header",
ErrorCodes.InconsistentExtractId,
ValidationErrorScope.Trailer
);
}
}
Loading
Loading