1+ using System . Globalization ;
2+ using Google . Protobuf . WellKnownTypes ;
13using Microsoft . EntityFrameworkCore ;
24using Microsoft . Extensions . Logging ;
35using Moq ;
@@ -89,7 +91,7 @@ public async Task Run_FileNotFound_ExitsSilently()
8991 }
9092
9193 [ Fact ]
92- public async Task Run_FileInInvalidStatus_ExitsSilently ( )
94+ public async Task Run_FileStatusInvalid_ExitsSilently ( )
9395 {
9496 // Arrange
9597 var file = new MeshFile
@@ -113,7 +115,7 @@ public async Task Run_FileInInvalidStatus_ExitsSilently()
113115 x => x . Log (
114116 LogLevel . Warning ,
115117 It . IsAny < EventId > ( ) ,
116- It . Is < It . IsAnyType > ( ( v , t ) => v . ToString ( ) . StartsWith ( $ "File with id: { message . FileId } found in MeshFiles table but is not suitable for extraction. Status: { file . Status } , LastUpdatedUtc:" ) ) ,
118+ It . Is < It . IsAnyType > ( ( v , t ) => v . ToString ( ) == $ "File with id: { message . FileId } found in MeshFiles table but is not suitable for extraction. Status: { file . Status } , LastUpdatedUtc: { file . LastUpdatedUtc . ToTimestamp ( ) } ." ) ,
117119 null ,
118120 It . IsAny < Func < It . IsAnyType , Exception ? , string > > ( )
119121 ) , Times . Once ) ;
@@ -133,8 +135,9 @@ public async Task Run_FileInInvalidStatus_ExitsSilently()
133135 }
134136
135137 [ Fact ]
136- public async Task Run_FileExtractingButNotTimedOut_ExitsSilently ( )
138+ public async Task Run_FileStatusExtractingButNotTimedOut_ExitsSilently ( )
137139 {
140+ // Arrange
138141 var file = new MeshFile
139142 {
140143 FileType = MeshFileType . NbssAppointmentEvents ,
@@ -148,28 +151,53 @@ public async Task Run_FileExtractingButNotTimedOut_ExitsSilently()
148151
149152 var message = new FileExtractQueueMessage { FileId = "file-2" } ;
150153
151- var exception = await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => _function . Run ( message ) ) ;
152- Assert . Equal ( "File is not in expected status" , exception . Message ) ;
154+ // Act
155+ await _function . Run ( message ) ;
156+
157+ // Assert
158+ _loggerMock . Verify (
159+ x => x . Log (
160+ LogLevel . Warning ,
161+ It . IsAny < EventId > ( ) ,
162+ It . Is < It . IsAnyType > ( ( v , t ) => v . ToString ( ) == $ "File with id: { message . FileId } found in MeshFiles table but is not suitable for extraction. Status: { file . Status } , LastUpdatedUtc: { file . LastUpdatedUtc . ToTimestamp ( ) } .") ,
163+ null ,
164+ It . IsAny < Func < It . IsAnyType , Exception ? , string > > ( )
165+ ) , Times . Once ) ;
166+ _loggerMock . Verify (
167+ x => x . Log (
168+ LogLevel . Warning ,
169+ It . IsAny < EventId > ( ) ,
170+ It . Is < It . IsAnyType > ( ( v , t ) => v . ToString ( ) == "Exiting function." ) ,
171+ null ,
172+ It . IsAny < Func < It . IsAnyType , Exception ? , string > > ( )
173+ ) , Times . Once ) ;
174+
175+ _meshInboxServiceMock . Verify ( x => x . GetHeadMessageByIdAsync ( It . IsAny < string > ( ) , It . IsAny < string > ( ) ) , Times . Never ) ;
176+ _blobStoreMock . Verify ( x => x . UploadAsync ( It . IsAny < MeshFile > ( ) , It . IsAny < byte [ ] > ( ) ) , Times . Never ) ;
177+ _fileTransformQueueClientMock . Verify ( x => x . EnqueueFileTransformAsync ( It . IsAny < MeshFile > ( ) ) , Times . Never ) ;
178+ _fileTransformQueueClientMock . Verify ( x => x . SendToPoisonQueueAsync ( It . IsAny < FileTransformQueueMessage > ( ) ) , Times . Never ) ;
153179 }
154180
155181 [ Fact ]
156- public async Task Run_FileExtractSuccess_UploadsBlob ( )
182+ public async Task Run_FileValid_FileUploadedToBlobAndAcknowledgedAndEnqueued ( )
157183 {
158- var fileId = "file-3" ;
184+ // Arrange
185+ var originalLastUpdatedUtc = DateTime . UtcNow . AddHours ( - 1 ) ;
159186 var file = new MeshFile
160187 {
161188 FileType = MeshFileType . NbssAppointmentEvents ,
162189 MailboxId = "test-mailbox" ,
163- FileId = fileId ,
190+ FileId = "file-3" ,
164191 Status = MeshFileStatus . Discovered ,
165- LastUpdatedUtc = DateTime . UtcNow . AddHours ( - 1 )
192+ LastUpdatedUtc = originalLastUpdatedUtc
166193 } ;
167194 _dbContext . MeshFiles . Add ( file ) ;
168195 await _dbContext . SaveChangesAsync ( ) ;
169196
170197 var content = new byte [ ] { 1 , 2 , 3 } ;
198+ const string blobPath = "directory/fileName" ;
171199
172- _meshInboxServiceMock . Setup ( s => s . GetMessageByIdAsync ( "test-mailbox" , fileId ) )
200+ _meshInboxServiceMock . Setup ( s => s . GetMessageByIdAsync ( file . MailboxId , file . FileId ) )
173201 . ReturnsAsync ( new MeshResponse < GetMessageResponse >
174202 {
175203 IsSuccessful = true ,
@@ -178,38 +206,131 @@ public async Task Run_FileExtractSuccess_UploadsBlob()
178206 FileAttachment = new FileAttachment { Content = content }
179207 }
180208 } ) ;
209+ _blobStoreMock . Setup ( s => s . UploadAsync ( file , content ) ) . ReturnsAsync ( blobPath ) ;
210+ _meshInboxServiceMock . Setup ( s => s . AcknowledgeMessageByIdAsync ( file . MailboxId , file . FileId ) )
211+ . ReturnsAsync ( new MeshResponse < AcknowledgeMessageResponse >
212+ {
213+ IsSuccessful = true
214+ } ) ;
215+
216+ var message = new FileExtractQueueMessage { FileId = file . FileId } ;
217+
218+ // Act
219+ await _function . Run ( message ) ;
220+
221+ // Assert
222+ _blobStoreMock . Verify ( b => b . UploadAsync ( It . Is < MeshFile > ( f => f . FileId == file . FileId ) , content ) , Times . Once ) ;
223+ _meshInboxServiceMock . Verify ( m => m . AcknowledgeMessageByIdAsync ( file . MailboxId , file . FileId ) , Times . Once ) ;
224+ _fileTransformQueueClientMock . Verify ( q => q . EnqueueFileTransformAsync ( file ) , Times . Once ) ;
225+ var updatedFile = _dbContext . MeshFiles . First ( ) ;
226+ Assert . Equal ( blobPath , updatedFile . BlobPath ) ;
227+ Assert . Equal ( MeshFileStatus . Extracted , updatedFile . Status ) ;
228+ Assert . True ( updatedFile . LastUpdatedUtc > originalLastUpdatedUtc ) ;
229+ }
230+
231+ [ Fact ]
232+ public async Task Run_GetMessageFails_ErrorLoggedAndFileSentToPoisonQueue ( )
233+ {
234+ // Arrange
235+ var fileId = "file-4" ;
236+ var originalLastUpdatedUtc = DateTime . UtcNow . AddHours ( - 1 ) ;
237+ var file = new MeshFile
238+ {
239+ FileType = MeshFileType . NbssAppointmentEvents ,
240+ MailboxId = "test-mailbox" ,
241+ FileId = fileId ,
242+ Status = MeshFileStatus . Discovered ,
243+ LastUpdatedUtc = originalLastUpdatedUtc
244+ } ;
245+ _dbContext . MeshFiles . Add ( file ) ;
246+ await _dbContext . SaveChangesAsync ( ) ;
247+
248+ _meshInboxServiceMock . Setup ( s => s . GetMessageByIdAsync ( file . MailboxId , fileId ) )
249+ . ReturnsAsync ( new MeshResponse < GetMessageResponse >
250+ {
251+ IsSuccessful = false
252+ } ) ;
181253
182254 var message = new FileExtractQueueMessage { FileId = fileId } ;
183255
256+ // Act
184257 await _function . Run ( message ) ;
185258
186- _blobStoreMock . Verify ( b => b . UploadAsync ( It . Is < MeshFile > ( f => f . FileId == fileId ) , content ) , Times . Once ) ;
259+ // Assert
260+ _loggerMock . Verify (
261+ x => x . Log (
262+ LogLevel . Error ,
263+ It . IsAny < EventId > ( ) ,
264+ It . Is < It . IsAnyType > ( ( v , t ) => v . ToString ( ) == $ "An exception occurred during file extraction for fileId: { fileId } ") ,
265+ It . Is < InvalidOperationException > ( e => e . Message . StartsWith ( "Mesh extraction failed: " ) ) ,
266+ It . IsAny < Func < It . IsAnyType , Exception ? , string > > ( )
267+ ) , Times . Once ) ;
268+ _blobStoreMock . Verify ( b => b . UploadAsync ( It . IsAny < MeshFile > ( ) , It . IsAny < byte [ ] > ( ) ) , Times . Never ) ;
269+ _meshInboxServiceMock . Verify ( m => m . AcknowledgeMessageByIdAsync ( file . MailboxId , file . FileId ) , Times . Never ) ;
270+ _fileTransformQueueClientMock . Verify ( q => q . EnqueueFileTransformAsync ( It . IsAny < MeshFile > ( ) ) , Times . Never ) ;
271+ _fileExtractQueueClientMock . Verify ( q => q . SendToPoisonQueueAsync ( message ) , Times . Once ) ;
272+ var updatedFile = _dbContext . MeshFiles . First ( ) ;
273+ Assert . Null ( updatedFile . BlobPath ) ;
274+ Assert . Equal ( MeshFileStatus . FailedExtract , updatedFile . Status ) ;
275+ Assert . True ( updatedFile . LastUpdatedUtc > originalLastUpdatedUtc ) ;
187276 }
188277
189278 [ Fact ]
190- public async Task Run_MeshResponseFails_Throws ( )
279+ public async Task Run_AcknowledgeMessageFails_ErrorLoggedAndFileSentToPoisonQueue ( )
191280 {
281+ // Arrange
192282 var fileId = "file-4" ;
283+ var originalLastUpdatedUtc = DateTime . UtcNow . AddHours ( - 1 ) ;
193284 var file = new MeshFile
194285 {
195286 FileType = MeshFileType . NbssAppointmentEvents ,
196287 MailboxId = "test-mailbox" ,
197288 FileId = fileId ,
198289 Status = MeshFileStatus . Discovered ,
199- LastUpdatedUtc = DateTime . UtcNow . AddHours ( - 2 )
290+ LastUpdatedUtc = originalLastUpdatedUtc
200291 } ;
201292 _dbContext . MeshFiles . Add ( file ) ;
202293 await _dbContext . SaveChangesAsync ( ) ;
203294
204- _meshInboxServiceMock . Setup ( s => s . GetMessageByIdAsync ( "test-mailbox" , fileId ) )
295+ var content = new byte [ ] { 1 , 2 , 3 } ;
296+
297+ _meshInboxServiceMock . Setup ( s => s . GetMessageByIdAsync ( file . MailboxId , fileId ) )
205298 . ReturnsAsync ( new MeshResponse < GetMessageResponse >
206299 {
207- IsSuccessful = false ,
300+ IsSuccessful = true ,
301+ Response = new GetMessageResponse
302+ {
303+ FileAttachment = new FileAttachment { Content = content }
304+ }
305+ } ) ;
306+ _blobStoreMock . Setup ( s => s . UploadAsync ( file , content ) ) . ReturnsAsync ( "directory/fileName" ) ;
307+ _meshInboxServiceMock . Setup ( s => s . AcknowledgeMessageByIdAsync ( file . MailboxId , file . FileId ) )
308+ . ReturnsAsync ( new MeshResponse < AcknowledgeMessageResponse >
309+ {
310+ IsSuccessful = false
208311 } ) ;
209312
210313 var message = new FileExtractQueueMessage { FileId = fileId } ;
211314
212- var exception = await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => _function . Run ( message ) ) ;
213- Assert . Contains ( "Mesh extraction failed" , exception . Message ) ;
315+ // Act
316+ await _function . Run ( message ) ;
317+
318+ // Assert
319+ _loggerMock . Verify (
320+ x => x . Log (
321+ LogLevel . Error ,
322+ It . IsAny < EventId > ( ) ,
323+ It . Is < It . IsAnyType > ( ( v , t ) => v . ToString ( ) == $ "An exception occurred during file extraction for fileId: { fileId } ") ,
324+ It . Is < InvalidOperationException > ( e => e . Message . StartsWith ( "Mesh acknowledgement failed: " ) ) ,
325+ It . IsAny < Func < It . IsAnyType , Exception ? , string > > ( )
326+ ) , Times . Once ) ;
327+ _blobStoreMock . Verify ( b => b . UploadAsync ( file , content ) , Times . Once ) ;
328+ _meshInboxServiceMock . Verify ( m => m . AcknowledgeMessageByIdAsync ( file . MailboxId , file . FileId ) , Times . Once ) ;
329+ _fileTransformQueueClientMock . Verify ( q => q . EnqueueFileTransformAsync ( It . IsAny < MeshFile > ( ) ) , Times . Never ) ;
330+ _fileExtractQueueClientMock . Verify ( q => q . SendToPoisonQueueAsync ( message ) , Times . Once ) ;
331+ var updatedFile = _dbContext . MeshFiles . First ( ) ;
332+ Assert . Null ( updatedFile . BlobPath ) ;
333+ Assert . Equal ( MeshFileStatus . FailedExtract , updatedFile . Status ) ;
334+ Assert . True ( updatedFile . LastUpdatedUtc > originalLastUpdatedUtc ) ;
214335 }
215336}
0 commit comments