diff --git a/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileParser.cs b/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileParser.cs index 7dbedb52..9dbb2de7 100644 --- a/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileParser.cs +++ b/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileParser.cs @@ -1,6 +1,7 @@ using CsvHelper; using CsvHelper.Configuration; using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models; +using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation; using System.Globalization; using System.Text; @@ -56,7 +57,10 @@ public ParsedFile Parse(Stream stream) break; default: - throw new InvalidOperationException($"Unknown record identifier: {recordIdentifier}"); + recordIdentifier ??= "No Record Identifier found"; + throw new FileParsingException( + ErrorCodes.UnknownRecordTypeIdentifier, + $"Unknown Record Identifier {recordIdentifier}"); } } @@ -98,7 +102,7 @@ private static FileDataRecord ParseDataRecord(CsvReader csv, List column { if (columnHeadings.Count == 0) { - throw new InvalidOperationException("Field headers (NBSSAPPT_FLDS) must appear before data records."); + throw new FileParsingException(ErrorCodes.MissingFieldHeadings, "Field headings are missing"); } const int dataFieldStartIndex = 1; @@ -140,4 +144,3 @@ public FileHeaderRecordMap() } } } - diff --git a/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileParsingException.cs b/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileParsingException.cs new file mode 100644 index 00000000..88864a5f --- /dev/null +++ b/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileParsingException.cs @@ -0,0 +1,19 @@ +namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents; + +public class FileParsingException : Exception +{ + public string Code { get; } + + public FileParsingException(string code, string message) + : base(message) + { + Code = code; + } + + public FileParsingException(string code, string message, Exception innerException) + : base(message, innerException) + { + Code = code; + } +} + diff --git a/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileTransformer.cs b/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileTransformer.cs index ce2897af..35a4a4fe 100644 --- a/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileTransformer.cs +++ b/src/ServiceLayer.Mesh/FileTypes/NbssAppointmentEvents/FileTransformer.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Logging; using ServiceLayer.Data.Models; using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation; @@ -6,22 +7,65 @@ namespace ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents; public class FileTransformer( IFileParser fileParser, IValidationRunner validationRunner, - IStagingPersister stagingPersister) + IStagingPersister stagingPersister, + ILogger logger) : FileTransformerBase { protected override MeshFileType HandlesFileType => MeshFileType.NbssAppointmentEvents; public override async Task> TransformFileAsync(Stream stream, MeshFile metaData) { - // TODO - wrap this parsing in a try-catch and return a List in case of any unforeseen parsing issues (file is totally unlike anything we expect) - var parsed = fileParser.Parse(stream); + try + { + var parsed = fileParser.Parse(stream); + var validationErrors = validationRunner.Validate(parsed); + + if (!validationErrors.Any()) + { + await stagingPersister.WriteStagedData(parsed, metaData); + } - var validationErrors = validationRunner.Validate(parsed); - if (!validationErrors.Any()) + return validationErrors; + } + catch (FileParsingException ex) { - await stagingPersister.WriteStagedData(parsed, metaData); + return HandleFileParsingException(ex); } + catch (Exception ex) + { + return HandleUnexpectedException(ex, metaData); + } + } + + private List HandleFileParsingException(FileParsingException ex) + { + logger.LogError("File parsing failed with validation error. Code: {ErrorCode}, Message: {ErrorMessage}", + ex.Code, ex.Message); + + return + [ + new ValidationError + { + Code = ex.Code, + Error = ex.Message, + Scope = ValidationErrorScope.File + } + ]; + } + + private IList HandleUnexpectedException(Exception ex, MeshFile metaData) + { + logger.LogError(ex, "System error occurred while parsing NBSS appointment file. File: {FileName}", + metaData.FileId); - return validationErrors; + return + [ + new ValidationError + { + Code = ErrorCodes.UnableToParseFile, + Error = "Unable to parse file", + Scope = ValidationErrorScope.File + } + ]; } } diff --git a/src/ServiceLayer.Mesh/Functions/FileExtractFunction.cs b/src/ServiceLayer.Mesh/Functions/FileExtractFunction.cs index f58c70bd..a8c62de0 100644 --- a/src/ServiceLayer.Mesh/Functions/FileExtractFunction.cs +++ b/src/ServiceLayer.Mesh/Functions/FileExtractFunction.cs @@ -109,7 +109,7 @@ private async Task ProcessFileExtraction(MeshFile file) private async Task HandleExtractionError(MeshFile file, FileExtractQueueMessage message, Exception ex) { - logger.LogError(ex, "An exception occurred during file extraction for fileId: {fileId}", message.FileId); + logger.LogError(ex, "An exception occurred during file extraction for fileId: {FileId}", message.FileId); file.Status = MeshFileStatus.FailedExtract; file.LastUpdatedUtc = DateTime.UtcNow; await serviceLayerDbContext.SaveChangesAsync(); diff --git a/src/ServiceLayer.Shared/Data/Models/MeshFileType.cs b/src/ServiceLayer.Shared/Data/Models/MeshFileType.cs index 32a8c197..7f842a49 100644 --- a/src/ServiceLayer.Shared/Data/Models/MeshFileType.cs +++ b/src/ServiceLayer.Shared/Data/Models/MeshFileType.cs @@ -2,5 +2,6 @@ namespace ServiceLayer.Data.Models; public enum MeshFileType { - NbssAppointmentEvents + NbssAppointmentEvents, + Unknown } diff --git a/tests/ServiceLayer.Mesh.Tests/FileTransformerTests.cs b/tests/ServiceLayer.Mesh.Tests/FileTransformerTests.cs new file mode 100644 index 00000000..27079e1b --- /dev/null +++ b/tests/ServiceLayer.Mesh.Tests/FileTransformerTests.cs @@ -0,0 +1,237 @@ +using Microsoft.Extensions.Logging; +using Moq; +using ServiceLayer.Data.Models; +using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents; +using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models; +using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation; +using ServiceLayer.TestUtilities; + +namespace ServiceLayer.Mesh.Tests.FileTypes.NbssAppointmentEvents; + +public class FileTransformerTests +{ + private readonly Mock _fileParserMock = new(); + private readonly Mock _validationRunnerMock = new(); + private readonly Mock _stagingPersisterMock = new(); + private readonly Mock> _loggerMock = new(); + private readonly FileTransformer _fileTransformer; + private readonly MeshFile _testMeshFile; + private readonly Stream _testStream; + private readonly ParsedFile parsedFile = new(); + + public FileTransformerTests() + { + _fileTransformer = new FileTransformer( + _fileParserMock.Object, + _validationRunnerMock.Object, + _stagingPersisterMock.Object, + _loggerMock.Object); + + _testMeshFile = new MeshFile + { + FileId = "test-file-123", + FileType = MeshFileType.NbssAppointmentEvents, + MailboxId = "testMailboxId", + Status = MeshFileStatus.Extracted + }; + + _testStream = new MemoryStream(); + } + + [Fact] + public void CanHandle_NbssAppointmentEventsFileType_ReturnsTrue() + { + // Act + var result = _fileTransformer.CanHandle(MeshFileType.NbssAppointmentEvents); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanHandle_OtherFileType_ReturnsFalse() + { + // Act + var result = _fileTransformer.CanHandle(MeshFileType.Unknown); + + // Assert + Assert.False(result); + } + + [Fact] + public async Task TransformFileAsync_ValidFileWithNoValidationErrors_ParsesValidatesAndPersists() + { + // Arrange + var validationErrors = new List(); + + _fileParserMock.Setup(p => p.Parse(_testStream)).Returns(parsedFile); + _validationRunnerMock.Setup(v => v.Validate(parsedFile)).Returns(validationErrors); + + // Act + var result = await _fileTransformer.TransformFileAsync(_testStream, _testMeshFile); + + // Assert + Assert.Empty(result); + _fileParserMock.Verify(p => p.Parse(_testStream), Times.Once); + _validationRunnerMock.Verify(v => v.Validate(parsedFile), Times.Once); + _stagingPersisterMock.Verify(s => s.WriteStagedData(parsedFile, _testMeshFile), Times.Once); + _loggerMock.VerifyNoLogs(LogLevel.Error); + } + + [Fact] + public async Task TransformFileAsync_ValidFileWithValidationErrors_DoesNotPersistData() + { + // Arrange + var validationErrors = new List + { + new() { Code = "TEST001", Error = "Test validation error", Scope = ValidationErrorScope.Record, RowNumber = 1 } + }; + + _fileParserMock.Setup(p => p.Parse(_testStream)).Returns(parsedFile); + _validationRunnerMock.Setup(v => v.Validate(parsedFile)).Returns(validationErrors); + + // Act + var result = await _fileTransformer.TransformFileAsync(_testStream, _testMeshFile); + + // Assert + Assert.Equal(validationErrors, result); + _fileParserMock.Verify(p => p.Parse(_testStream), Times.Once); + _validationRunnerMock.Verify(v => v.Validate(parsedFile), Times.Once); + _stagingPersisterMock.Verify(s => s.WriteStagedData(It.IsAny(), It.IsAny()), Times.Never); + _loggerMock.VerifyNoLogs(LogLevel.Error); + } + + [Fact] + public async Task TransformFileAsync_FileParsingExceptionThrown_ReturnsFileValidationError() + { + // Arrange + var fileParsingException = new FileParsingException(ErrorCodes.UnknownRecordTypeIdentifier, "Unknown record type identifier 'INVALID_TYPE'"); + + _fileParserMock.Setup(p => p.Parse(_testStream)).Throws(fileParsingException); + + // Act + var result = await _fileTransformer.TransformFileAsync(_testStream, _testMeshFile); + + // Assert + Assert.Single(result); + var validationError = result[0]; + Assert.Equal(ErrorCodes.UnknownRecordTypeIdentifier, validationError.Code); + Assert.Equal("Unknown record type identifier 'INVALID_TYPE'", validationError.Error); + Assert.Equal(ValidationErrorScope.File, validationError.Scope); + + _fileParserMock.Verify(p => p.Parse(_testStream), Times.Once); + _validationRunnerMock.Verify(v => v.Validate(It.IsAny()), Times.Never); + _stagingPersisterMock.Verify(s => s.WriteStagedData(It.IsAny(), It.IsAny()), Times.Never); + + _loggerMock.VerifyLogger(LogLevel.Error, + $"File parsing failed with validation error. Code: {ErrorCodes.UnknownRecordTypeIdentifier}, Message: Unknown record type identifier 'INVALID_TYPE'"); + } + + [Fact] + public async Task TransformFileAsync_UnexpectedExceptionThrown_ReturnsSystemValidationError() + { + // Arrange + var unexpectedException = new InvalidOperationException("Something went wrong"); + _fileParserMock.Setup(p => p.Parse(_testStream)).Throws(unexpectedException); + + // Act + var result = await _fileTransformer.TransformFileAsync(_testStream, _testMeshFile); + + // Assert + Assert.Single(result); + var validationError = result[0]; + Assert.Equal(ErrorCodes.UnableToParseFile, validationError.Code); + Assert.Equal("Unable to parse file", validationError.Error); + Assert.Equal(ValidationErrorScope.File, validationError.Scope); + + _fileParserMock.Verify(p => p.Parse(_testStream), Times.Once); + _validationRunnerMock.Verify(v => v.Validate(It.IsAny()), Times.Never); + _stagingPersisterMock.Verify(s => s.WriteStagedData(It.IsAny(), It.IsAny()), Times.Never); + + _loggerMock.VerifyLogger(LogLevel.Error, + $"System error occurred while parsing NBSS appointment file. File: {_testMeshFile.FileId}", + ex => ex == unexpectedException); + } + + [Fact] + public async Task TransformFileAsync_ValidationRunnerThrowsException_ReturnsSystemValidationError() + { + // Arrange + var validationException = new InvalidOperationException("Validation failed"); + + _fileParserMock.Setup(p => p.Parse(_testStream)).Returns(parsedFile); + _validationRunnerMock.Setup(v => v.Validate(parsedFile)).Throws(validationException); + + // Act + var result = await _fileTransformer.TransformFileAsync(_testStream, _testMeshFile); + + // Assert + Assert.Single(result); + var validationError = result[0]; + Assert.Equal(ErrorCodes.UnableToParseFile, validationError.Code); + Assert.Equal("Unable to parse file", validationError.Error); + Assert.Equal(ValidationErrorScope.File, validationError.Scope); + + _fileParserMock.Verify(p => p.Parse(_testStream), Times.Once); + _validationRunnerMock.Verify(v => v.Validate(parsedFile), Times.Once); + _stagingPersisterMock.Verify(s => s.WriteStagedData(It.IsAny(), It.IsAny()), Times.Never); + + _loggerMock.VerifyLogger(LogLevel.Error, + $"System error occurred while parsing NBSS appointment file. File: {_testMeshFile.FileId}", + ex => ex == validationException); + } + + [Fact] + public async Task TransformFileAsync_StagingPersisterThrowsException_ReturnsSystemValidationError() + { + // Arrange + var validationErrors = new List(); + var persistException = new InvalidOperationException("Database error"); + + _fileParserMock.Setup(p => p.Parse(_testStream)).Returns(parsedFile); + _validationRunnerMock.Setup(v => v.Validate(parsedFile)).Returns(validationErrors); + _stagingPersisterMock.Setup(s => s.WriteStagedData(parsedFile, _testMeshFile)).ThrowsAsync(persistException); + + // Act + var result = await _fileTransformer.TransformFileAsync(_testStream, _testMeshFile); + + // Assert + Assert.Single(result); + var validationError = result[0]; + Assert.Equal(ErrorCodes.UnableToParseFile, validationError.Code); + Assert.Equal("Unable to parse file", validationError.Error); + Assert.Equal(ValidationErrorScope.File, validationError.Scope); + + _fileParserMock.Verify(p => p.Parse(_testStream), Times.Once); + _validationRunnerMock.Verify(v => v.Validate(parsedFile), Times.Once); + _stagingPersisterMock.Verify(s => s.WriteStagedData(parsedFile, _testMeshFile), Times.Once); + + _loggerMock.VerifyLogger(LogLevel.Error, + $"System error occurred while parsing NBSS appointment file. File: {_testMeshFile.FileId}", + ex => ex == persistException); + } + + [Theory] + [InlineData(ErrorCodes.MissingFieldHeadings, "Field headings are missing")] + [InlineData(ErrorCodes.UnknownRecordTypeIdentifier, "Unknown record type 'INVALID'")] + [InlineData("CUSTOM001", "Custom validation error")] + public async Task TransformFileAsync_DifferentFileParsingExceptions_ReturnsCorrectValidationErrors(string errorCode, string errorMessage) + { + // Arrange + var fileParsingException = new FileParsingException(errorCode, errorMessage); + _fileParserMock.Setup(p => p.Parse(_testStream)).Throws(fileParsingException); + + // Act + var result = await _fileTransformer.TransformFileAsync(_testStream, _testMeshFile); + + // Assert + Assert.Single(result); + var validationError = result[0]; + Assert.Equal(errorCode, validationError.Code); + Assert.Equal(errorMessage, validationError.Error); + Assert.Equal(ValidationErrorScope.File, validationError.Scope); + + _loggerMock.VerifyLogger(LogLevel.Error, + $"File parsing failed with validation error. Code: {errorCode}, Message: {errorMessage}"); + } +} diff --git a/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/FileParserTests.cs b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/FileParserTests.cs index d5b5e4b9..7248ea4f 100644 --- a/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/FileParserTests.cs +++ b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/FileParserTests.cs @@ -4,6 +4,7 @@ using CsvHelper.Configuration; using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents; using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Models; +using ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.Validation; using static ServiceLayer.Mesh.FileTypes.NbssAppointmentEvents.FileParser; namespace ServiceLayer.Mesh.Tests.FileTypes.NbssAppointmentEvents; @@ -21,7 +22,7 @@ public FileParserTests() private FileStream GetTestFileStream(string fileName) { - string filePath = Path.Combine(_testDataPath, fileName); + var filePath = Path.Combine(_testDataPath, fileName); return File.OpenRead(filePath); } @@ -144,27 +145,29 @@ public void Parse_CompleteDataset_ParsesAllFieldsCorrectly() } [Fact] - public void Parse_MissingFieldsRecord_ThrowsInvalidOperationException() + public void Parse_MissingFieldsRecord_ThrowsFileParsingException() { // Arrange using var fileStream = GetTestFileStream("MissingFields.dat"); // Act & Assert - var exception = Assert.Throws(() => _fileParser.Parse(fileStream)); + var exception = Assert.Throws(() => _fileParser.Parse(fileStream)); - Assert.Equal("Field headers (NBSSAPPT_FLDS) must appear before data records.", exception.Message); + Assert.Equal(ErrorCodes.MissingFieldHeadings, exception.Code); + Assert.Equal("Field headings are missing", exception.Message); } [Fact] - public void Parse_UnknownRecordType_ThrowsInvalidOperationException() + public void Parse_UnknownRecordType_ThrowsFileParsingException() { // Arrange using var fileStream = GetTestFileStream("UnknownRecord.dat"); // Act & Assert - var exception = Assert.Throws(() => _fileParser.Parse(fileStream)); + var exception = Assert.Throws(() => _fileParser.Parse(fileStream)); - Assert.Equal("Unknown record identifier: UNKNOWN_TYPE", exception.Message); + Assert.Equal(ErrorCodes.UnknownRecordTypeIdentifier, exception.Code); + Assert.Equal("Unknown Record Identifier UNKNOWN_TYPE", exception.Message); } [Fact] @@ -319,6 +322,112 @@ public void VerifyFileTrailerRecordMap_MapsCorrectly() Assert.Equal("000002", result.RecordCount); } + + [Fact] + public void Parse_DataRecordBeforeFields_ThrowsFileParsingExceptionWithCorrectCode() + { + // Arrange + using var stream = GetTestFileStream("DataBeforeFields.dat"); + + // Act & Assert + var exception = Assert.Throws(() => _fileParser.Parse(stream)); + + Assert.Equal(ErrorCodes.MissingFieldHeadings, exception.Code); + Assert.Equal("Field headings are missing", exception.Message); + } + + [Fact] + public void Parse_UnknownRecordTypeWithNullIdentifier_ThrowsFileParsingExceptionWithNull() + { + // Arrange + using var stream = GetTestFileStream("NullRecordType.dat"); + + // Act & Assert + var exception = Assert.Throws(() => _fileParser.Parse(stream)); + + Assert.Equal(ErrorCodes.UnknownRecordTypeIdentifier, exception.Code); + Assert.Equal("Unknown Record Identifier ", exception.Message); + } + + [Fact] + public void Parse_UnknownRecordTypeWithEmptyString_ThrowsFileParsingExceptionWithNull() + { + // Arrange + using var stream = GetTestFileStream("EmptyRecordType.dat"); + + // Act & Assert + var exception = Assert.Throws(() => _fileParser.Parse(stream)); + + Assert.Equal(ErrorCodes.UnknownRecordTypeIdentifier, exception.Code); + Assert.Equal("Unknown Record Identifier ", exception.Message); + } + + [Fact] + public void Parse_FileParsingExceptionWithInnerException_PreservesInnerException() + { + // Arrange + var innerException = new InvalidOperationException("Inner error"); + + // Act + var exception = new FileParsingException("TEST001", "Test error", innerException); + + // Assert + Assert.Equal("TEST001", exception.Code); + Assert.Equal("Test error", exception.Message); + Assert.Equal(innerException, exception.InnerException); + Assert.Equal("Test error", exception.Message); + } + + [Fact] + public void Parse_FileWithOnlyHeader_CompletesSuccessfully() + { + // Arrange + using var stream = GetTestFileStream("HeaderMapping.dat"); + + // Act + var result = _fileParser.Parse(stream); + + // Assert + Assert.NotNull(result); + Assert.NotNull(result.FileHeader); + Assert.Equal("NBSSAPPT_HDR", result.FileHeader.RecordTypeIdentifier); + Assert.Null(result.FileTrailer); + Assert.Empty(result.DataRecords); + } + + [Fact] + public void Parse_FileWithOnlyTrailer_CompletesSuccessfully() + { + // Arrange + using var stream = GetTestFileStream("TrailerMapping.dat"); + + // Act + var result = _fileParser.Parse(stream); + + // Assert + Assert.NotNull(result); + Assert.Null(result.FileHeader); + Assert.NotNull(result.FileTrailer); + Assert.Equal("NBSSAPPT_END", result.FileTrailer.RecordTypeIdentifier); + Assert.Empty(result.DataRecords); + } + + [Fact] + public void Parse_FileWithOnlyFields_CompletesSuccessfully() + { + // Arrange + using var stream = GetTestFileStream("FieldsOnly.dat"); + + // Act + var result = _fileParser.Parse(stream); + + // Assert + Assert.NotNull(result); + Assert.Null(result.FileHeader); + Assert.Null(result.FileTrailer); + Assert.Empty(result.DataRecords); + } + // Helper methods private CsvReader CreateConfiguredCsvReader(string fileName) { diff --git a/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/DataBeforeFields.dat b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/DataBeforeFields.dat new file mode 100644 index 00000000..28950308 --- /dev/null +++ b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/DataBeforeFields.dat @@ -0,0 +1,4 @@ +"NBSSAPPT_HDR"|"00000107"|"20250317"|"133128"|"000001" +"NBSSAPPT_DATA"|"000001"|"KMK"|"B"|"BU003"|"B" +"NBSSAPPT_FLDS"|"Sequence"|"BSO"|"Action"|"Clinic Code"|"Status" +"NBSSAPPT_END"|"00000107"|"20250317"|"133129"|"000001" diff --git a/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/EmptyRecordType.dat b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/EmptyRecordType.dat new file mode 100644 index 00000000..519f9d17 --- /dev/null +++ b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/EmptyRecordType.dat @@ -0,0 +1,4 @@ +"NBSSAPPT_HDR"|"00000107"|"20250317"|"133128"|"000001" +"NBSSAPPT_FLDS"|"Sequence"|"BSO"|"Action"|"Clinic Code"|"Status" +""|"000001"|"KMK"|"B"|"BU003"|"B" +"NBSSAPPT_END"|"00000107"|"20250317"|"133129"|"000001" diff --git a/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/FieldsOnly.dat b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/FieldsOnly.dat new file mode 100644 index 00000000..bd864250 --- /dev/null +++ b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/FieldsOnly.dat @@ -0,0 +1 @@ +"NBSSAPPT_FLDS"|"Sequence"|"BSO"|"Action"|"Clinic Code"|"Status" diff --git a/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/NullRecordType.dat b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/NullRecordType.dat new file mode 100644 index 00000000..519f9d17 --- /dev/null +++ b/tests/ServiceLayer.Mesh.Tests/FileTypes/NbssAppointmentEvents/TestData/NullRecordType.dat @@ -0,0 +1,4 @@ +"NBSSAPPT_HDR"|"00000107"|"20250317"|"133128"|"000001" +"NBSSAPPT_FLDS"|"Sequence"|"BSO"|"Action"|"Clinic Code"|"Status" +""|"000001"|"KMK"|"B"|"BU003"|"B" +"NBSSAPPT_END"|"00000107"|"20250317"|"133129"|"000001"