|
| 1 | +using System.Text; |
| 2 | +using System.Text.Json; |
| 3 | + |
1 | 4 | using FluentAssertions; |
2 | 5 |
|
3 | 6 | using Microsoft.AspNetCore.Builder; |
| 7 | +using Microsoft.AspNetCore.Http; |
4 | 8 |
|
5 | 9 | using Moq; |
6 | 10 |
|
7 | 11 | using Nexus.Engine.Extensions.Callback; |
8 | 12 | using Nexus.Engine.Extensions.Security; |
9 | 13 | using Nexus.Engine.Share; |
| 14 | +using Nexus.Engine.Share.Models; |
10 | 15 |
|
11 | 16 | using Xunit; |
12 | 17 |
|
@@ -85,4 +90,345 @@ public void ConfigureRoutes_CanBeCalledMultipleTimes() |
85 | 90 | _ = app.Should().NotBeNull(); |
86 | 91 | tokenValidator.Dispose(); |
87 | 92 | } |
| 93 | + |
| 94 | + /// <summary> |
| 95 | + /// Verifies that HandleExecuteAsync returns Problem for invalid request body (JSON deserialization throws). |
| 96 | + /// </summary> |
| 97 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 98 | + [Fact] |
| 99 | + public async Task HandleExecuteAsync_WithInvalidRequestBody_ReturnsProblem() |
| 100 | + { |
| 101 | + // Arrange |
| 102 | + var tokenValidator = new TokenValidator(); |
| 103 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 104 | + var context = new DefaultHttpContext(); |
| 105 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("invalid json")); |
| 106 | + |
| 107 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleExecuteAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 108 | + |
| 109 | + // Act |
| 110 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 111 | + |
| 112 | + // Assert |
| 113 | + _ = result.Should().NotBeNull(); |
| 114 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 115 | + _ = statusCode.Should().Be(500); |
| 116 | + tokenValidator.Dispose(); |
| 117 | + } |
| 118 | + |
| 119 | + /// <summary> |
| 120 | + /// Verifies that HandleExecuteAsync returns Unauthorized for invalid token. |
| 121 | + /// </summary> |
| 122 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 123 | + [Fact] |
| 124 | + public async Task HandleExecuteAsync_WithInvalidToken_ReturnsUnauthorized() |
| 125 | + { |
| 126 | + // Arrange |
| 127 | + var tokenValidator = new TokenValidator(); |
| 128 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 129 | + var request = new ExecuteCommandRequest { Command = "test" }; |
| 130 | + var json = JsonSerializer.Serialize(request); |
| 131 | + var context = new DefaultHttpContext(); |
| 132 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(json)); |
| 133 | + context.Request.Headers["Authorization"] = "Bearer invalid-token"; |
| 134 | + |
| 135 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleExecuteAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 136 | + |
| 137 | + // Act |
| 138 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 139 | + |
| 140 | + // Assert |
| 141 | + _ = result.Should().NotBeNull(); |
| 142 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 143 | + _ = statusCode.Should().Be(401); |
| 144 | + tokenValidator.Dispose(); |
| 145 | + } |
| 146 | + |
| 147 | + /// <summary> |
| 148 | + /// Verifies that HandleExecuteAsync handles exceptions gracefully. |
| 149 | + /// </summary> |
| 150 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 151 | + [Fact] |
| 152 | + public async Task HandleExecuteAsync_WithException_ReturnsProblem() |
| 153 | + { |
| 154 | + // Arrange |
| 155 | + _ = m_MockEngine.Setup(e => e.EnqueueCommand(It.IsAny<string>(), It.IsAny<string>())) |
| 156 | + .Throws(new InvalidOperationException("Test exception")); |
| 157 | + |
| 158 | + var tokenValidator = new TokenValidator(); |
| 159 | + var token = tokenValidator.GenerateToken("session-123", "cmd-456"); |
| 160 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 161 | + var request = new ExecuteCommandRequest { Command = "test" }; |
| 162 | + var json = JsonSerializer.Serialize(request); |
| 163 | + var context = new DefaultHttpContext(); |
| 164 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(json)); |
| 165 | + context.Request.Headers["Authorization"] = $"Bearer {token}"; |
| 166 | + |
| 167 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleExecuteAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 168 | + |
| 169 | + // Act |
| 170 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 171 | + |
| 172 | + // Assert |
| 173 | + _ = result.Should().NotBeNull(); |
| 174 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 175 | + _ = statusCode.Should().Be(500); |
| 176 | + tokenValidator.Dispose(); |
| 177 | + } |
| 178 | + |
| 179 | + /// <summary> |
| 180 | + /// Verifies that HandleQueueAsync returns Problem for invalid request body (JSON deserialization throws). |
| 181 | + /// </summary> |
| 182 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 183 | + [Fact] |
| 184 | + public async Task HandleQueueAsync_WithInvalidRequestBody_ReturnsProblem() |
| 185 | + { |
| 186 | + // Arrange |
| 187 | + var tokenValidator = new TokenValidator(); |
| 188 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 189 | + var context = new DefaultHttpContext(); |
| 190 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("invalid json")); |
| 191 | + |
| 192 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleQueueAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 193 | + |
| 194 | + // Act |
| 195 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 196 | + |
| 197 | + // Assert |
| 198 | + _ = result.Should().NotBeNull(); |
| 199 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 200 | + _ = statusCode.Should().Be(500); |
| 201 | + tokenValidator.Dispose(); |
| 202 | + } |
| 203 | + |
| 204 | + /// <summary> |
| 205 | + /// Verifies that HandleQueueAsync returns Unauthorized for invalid token. |
| 206 | + /// </summary> |
| 207 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 208 | + [Fact] |
| 209 | + public async Task HandleQueueAsync_WithInvalidToken_ReturnsUnauthorized() |
| 210 | + { |
| 211 | + // Arrange |
| 212 | + var tokenValidator = new TokenValidator(); |
| 213 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 214 | + var request = new QueueCommandRequest { Command = "test" }; |
| 215 | + var json = JsonSerializer.Serialize(request); |
| 216 | + var context = new DefaultHttpContext(); |
| 217 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(json)); |
| 218 | + context.Request.Headers["Authorization"] = "Bearer invalid-token"; |
| 219 | + |
| 220 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleQueueAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 221 | + |
| 222 | + // Act |
| 223 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 224 | + |
| 225 | + // Assert |
| 226 | + _ = result.Should().NotBeNull(); |
| 227 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 228 | + _ = statusCode.Should().Be(401); |
| 229 | + tokenValidator.Dispose(); |
| 230 | + } |
| 231 | + |
| 232 | + /// <summary> |
| 233 | + /// Verifies that HandleReadAsync returns Unauthorized for invalid token. |
| 234 | + /// </summary> |
| 235 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 236 | + [Fact] |
| 237 | + public async Task HandleReadAsync_WithInvalidToken_ReturnsUnauthorized() |
| 238 | + { |
| 239 | + // Arrange |
| 240 | + var tokenValidator = new TokenValidator(); |
| 241 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 242 | + var context = new DefaultHttpContext(); |
| 243 | + context.Request.Headers["Authorization"] = "Bearer invalid-token"; |
| 244 | + |
| 245 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleReadAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 246 | + |
| 247 | + // Act |
| 248 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context, "cmd-123" })!; |
| 249 | + |
| 250 | + // Assert |
| 251 | + _ = result.Should().NotBeNull(); |
| 252 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 253 | + _ = statusCode.Should().Be(401); |
| 254 | + tokenValidator.Dispose(); |
| 255 | + } |
| 256 | + |
| 257 | + /// <summary> |
| 258 | + /// Verifies that HandleStatus returns Unauthorized for invalid token. |
| 259 | + /// </summary> |
| 260 | + [Fact] |
| 261 | + public void HandleStatus_WithInvalidToken_ReturnsUnauthorized() |
| 262 | + { |
| 263 | + // Arrange |
| 264 | + var tokenValidator = new TokenValidator(); |
| 265 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 266 | + var context = new DefaultHttpContext(); |
| 267 | + context.Request.Headers["Authorization"] = "Bearer invalid-token"; |
| 268 | + |
| 269 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleStatus", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 270 | + |
| 271 | + // Act |
| 272 | + var result = (Microsoft.AspNetCore.Http.IResult)handleMethod!.Invoke(server, new object[] { context, "cmd-123" })!; |
| 273 | + |
| 274 | + // Assert |
| 275 | + _ = result.Should().NotBeNull(); |
| 276 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 277 | + _ = statusCode.Should().Be(401); |
| 278 | + tokenValidator.Dispose(); |
| 279 | + } |
| 280 | + |
| 281 | + /// <summary> |
| 282 | + /// Verifies that HandleStatus returns NotFound when command not found. |
| 283 | + /// </summary> |
| 284 | + [Fact] |
| 285 | + public void HandleStatus_WhenCommandNotFound_ReturnsNotFound() |
| 286 | + { |
| 287 | + // Arrange |
| 288 | + _ = m_MockEngine.Setup(e => e.GetCommandInfo(It.IsAny<string>(), It.IsAny<string>())) |
| 289 | + .Returns((CommandInfo?)null); |
| 290 | + |
| 291 | + var tokenValidator = new TokenValidator(); |
| 292 | + var token = tokenValidator.GenerateToken("session-123", "cmd-456"); |
| 293 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 294 | + var context = new DefaultHttpContext(); |
| 295 | + context.Request.Headers["Authorization"] = $"Bearer {token}"; |
| 296 | + |
| 297 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleStatus", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 298 | + |
| 299 | + // Act |
| 300 | + var result = (Microsoft.AspNetCore.Http.IResult)handleMethod!.Invoke(server, new object[] { context, "cmd-123" })!; |
| 301 | + |
| 302 | + // Assert |
| 303 | + _ = result.Should().NotBeNull(); |
| 304 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 305 | + _ = statusCode.Should().Be(404); |
| 306 | + tokenValidator.Dispose(); |
| 307 | + } |
| 308 | + |
| 309 | + /// <summary> |
| 310 | + /// Verifies that HandleBulkStatusAsync returns BadRequest for invalid request. |
| 311 | + /// </summary> |
| 312 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 313 | + [Fact] |
| 314 | + public async Task HandleBulkStatusAsync_WithInvalidRequest_ReturnsBadRequest() |
| 315 | + { |
| 316 | + // Arrange |
| 317 | + var tokenValidator = new TokenValidator(); |
| 318 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 319 | + var request = new BulkStatusRequest { CommandIds = new List<string>() }; |
| 320 | + var json = JsonSerializer.Serialize(request); |
| 321 | + var context = new DefaultHttpContext(); |
| 322 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(json)); |
| 323 | + |
| 324 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleBulkStatusAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 325 | + |
| 326 | + // Act |
| 327 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 328 | + |
| 329 | + // Assert |
| 330 | + _ = result.Should().NotBeNull(); |
| 331 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 332 | + _ = statusCode.Should().Be(400); |
| 333 | + tokenValidator.Dispose(); |
| 334 | + } |
| 335 | + |
| 336 | + /// <summary> |
| 337 | + /// Verifies that HandleBulkStatusAsync handles different command states correctly. |
| 338 | + /// </summary> |
| 339 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 340 | + [Fact] |
| 341 | + public async Task HandleBulkStatusAsync_WithDifferentCommandStates_HandlesCorrectly() |
| 342 | + { |
| 343 | + // Arrange |
| 344 | + var tokenValidator = new TokenValidator(); |
| 345 | + var token = tokenValidator.GenerateToken("session-123", "cmd-456"); |
| 346 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 347 | + |
| 348 | + var completedCommand = CommandInfo.Completed("session-123", "cmd-1", "test", DateTime.Now, DateTime.Now, DateTime.Now, "output", string.Empty, 12345); |
| 349 | + var failedCommand = CommandInfo.Failed("session-123", "cmd-2", "test", DateTime.Now, DateTime.Now, DateTime.Now, string.Empty, "error", 12346); |
| 350 | + var cancelledCommand = CommandInfo.Cancelled("session-123", "cmd-3", "test", DateTime.Now, DateTime.Now, DateTime.Now, string.Empty, "cancelled", 12347); |
| 351 | + |
| 352 | + _ = m_MockEngine.Setup(e => e.GetCommandInfo("session-123", "cmd-1")).Returns(completedCommand); |
| 353 | + _ = m_MockEngine.Setup(e => e.GetCommandInfo("session-123", "cmd-2")).Returns(failedCommand); |
| 354 | + _ = m_MockEngine.Setup(e => e.GetCommandInfo("session-123", "cmd-3")).Returns(cancelledCommand); |
| 355 | + |
| 356 | + var request = new BulkStatusRequest { CommandIds = new List<string> { "cmd-1", "cmd-2", "cmd-3" } }; |
| 357 | + var json = JsonSerializer.Serialize(request); |
| 358 | + var context = new DefaultHttpContext(); |
| 359 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(json)); |
| 360 | + context.Request.Headers["Authorization"] = $"Bearer {token}"; |
| 361 | + |
| 362 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleBulkStatusAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 363 | + |
| 364 | + // Act |
| 365 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 366 | + |
| 367 | + // Assert |
| 368 | + _ = result.Should().NotBeNull(); |
| 369 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 370 | + _ = statusCode.Should().Be(200); |
| 371 | + tokenValidator.Dispose(); |
| 372 | + } |
| 373 | + |
| 374 | + /// <summary> |
| 375 | + /// Verifies that HandleLogAsync returns Problem for invalid request body (JSON deserialization throws). |
| 376 | + /// </summary> |
| 377 | + /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns> |
| 378 | + [Fact] |
| 379 | + public async Task HandleLogAsync_WithInvalidRequestBody_ReturnsProblem() |
| 380 | + { |
| 381 | + // Arrange |
| 382 | + var tokenValidator = new TokenValidator(); |
| 383 | + var server = new CallbackServer(m_MockEngine.Object, tokenValidator); |
| 384 | + var context = new DefaultHttpContext(); |
| 385 | + context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("invalid json")); |
| 386 | + |
| 387 | + var handleMethod = typeof(CallbackServer).GetMethod("HandleLogAsync", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); |
| 388 | + |
| 389 | + // Act |
| 390 | + var result = await (Task<Microsoft.AspNetCore.Http.IResult>)handleMethod!.Invoke(server, new object[] { context })!; |
| 391 | + |
| 392 | + // Assert |
| 393 | + _ = result.Should().NotBeNull(); |
| 394 | + var statusCode = result.GetType().GetProperty("StatusCode")?.GetValue(result); |
| 395 | + _ = statusCode.Should().Be(500); |
| 396 | + tokenValidator.Dispose(); |
| 397 | + } |
| 398 | + |
| 399 | + /// <summary> |
| 400 | + /// Verifies that GetCommandNumber extracts command number correctly. |
| 401 | + /// </summary> |
| 402 | + [Fact] |
| 403 | + public void GetCommandNumber_WithValidCommandId_ReturnsNumber() |
| 404 | + { |
| 405 | + // Arrange |
| 406 | + var tokenValidator = new TokenValidator(); |
| 407 | + var getCommandNumberMethod = typeof(CallbackServer).GetMethod("GetCommandNumber", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); |
| 408 | + |
| 409 | + // Act |
| 410 | + var result = (int)getCommandNumberMethod!.Invoke(null, new object[] { "session-123", "cmd-session-123-456" })!; |
| 411 | + |
| 412 | + // Assert |
| 413 | + _ = result.Should().Be(456); |
| 414 | + tokenValidator.Dispose(); |
| 415 | + } |
| 416 | + |
| 417 | + /// <summary> |
| 418 | + /// Verifies that GetCommandNumber returns 0 for invalid command ID. |
| 419 | + /// </summary> |
| 420 | + [Fact] |
| 421 | + public void GetCommandNumber_WithInvalidCommandId_ReturnsZero() |
| 422 | + { |
| 423 | + // Arrange |
| 424 | + var tokenValidator = new TokenValidator(); |
| 425 | + var getCommandNumberMethod = typeof(CallbackServer).GetMethod("GetCommandNumber", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); |
| 426 | + |
| 427 | + // Act |
| 428 | + var result = (int)getCommandNumberMethod!.Invoke(null, new object[] { "session-123", "invalid-command-id" })!; |
| 429 | + |
| 430 | + // Assert |
| 431 | + _ = result.Should().Be(0); |
| 432 | + tokenValidator.Dispose(); |
| 433 | + } |
88 | 434 | } |
0 commit comments