diff --git a/src/Auth0.ManagementApi/Clients/JobsClient.cs b/src/Auth0.ManagementApi/Clients/JobsClient.cs index 060480ede..ae2130efd 100644 --- a/src/Auth0.ManagementApi/Clients/JobsClient.cs +++ b/src/Auth0.ManagementApi/Clients/JobsClient.cs @@ -116,7 +116,7 @@ public Task GetErrorDetailsAsync(string id, CancellationToken cancella Connection.GetAsync( BuildUri($"jobs/{EncodePath(id)}/errors"), DefaultHeaders, cancellationToken: cancellationToken).Result; - if (string.IsNullOrEmpty(rawResponse)) return null; + if (string.IsNullOrEmpty(rawResponse)) return Task.FromResult(null!);; try { @@ -128,10 +128,16 @@ public Task GetErrorDetailsAsync(string id, CancellationToken cancella JobImportErrorDetails = jobImportErrorDetails }); } + + // Empty array means no errors + if (jobImportErrorDetails != null) + { + return Task.FromResult(null!); + } } - catch (JsonSerializationException ex) + catch (JsonSerializationException) { - // ignoring the exception to try to serialize to JobErrorDetails. + // ignoring the exception to try to deserialize to JobErrorDetails. } var jobErrorDetails = JsonConvert.DeserializeObject(rawResponse); @@ -143,6 +149,6 @@ public Task GetErrorDetailsAsync(string id, CancellationToken cancella }); } - return null; + return Task.FromResult(null!); } } \ No newline at end of file diff --git a/tests/Auth0.Core.UnitTests/JobsClientTests.cs b/tests/Auth0.Core.UnitTests/JobsClientTests.cs new file mode 100644 index 000000000..0261dcb0a --- /dev/null +++ b/tests/Auth0.Core.UnitTests/JobsClientTests.cs @@ -0,0 +1,99 @@ +using Auth0.ManagementApi.Clients; +using Auth0.ManagementApi.Models; +using FluentAssertions; +using Moq; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Auth0.ManagementApi; +using Newtonsoft.Json; +using Xunit; + +namespace Auth0.Core.UnitTests; + +public class JobsClientTests +{ + private static JobsClient CreateClient(string responseJson) + { + var mockConnection = new Mock(); + mockConnection + .Setup(c => c.GetAsync( + It.IsAny(), + It.IsAny>(), + It.IsAny(), + It.IsAny())) + .ReturnsAsync(responseJson); + + return new JobsClient( + mockConnection.Object, + new Uri("https://test.auth0.com"), + new Dictionary()); + } + + [Fact] + public async Task GetErrorDetailsAsync_Returns_Null_When_Response_Is_Empty_Array() + { + var client = CreateClient("[]"); + + var result = await client.GetErrorDetailsAsync("job_123"); + + result.Should().BeNull(); + } + + [Fact] + public async Task GetErrorDetailsAsync_Returns_Null_When_Response_Is_Null_Or_Empty() + { + var client = CreateClient(null); + + var result = await client.GetErrorDetailsAsync("job_123"); + + result.Should().BeNull(); + } + + [Fact] + public async Task GetErrorDetailsAsync_Returns_JobImportErrorDetails_When_Response_Is_Array_With_Errors() + { + var json = """ + [ + { + "user": { "email": "john@example.com" }, + "errors": [ + { "code": "INVALID_FORMAT", "message": "Invalid email", "path": "email" } + ] + } + ] + """; + var client = CreateClient(json); + + var result = await client.GetErrorDetailsAsync("job_123"); + + result.Should().NotBeNull(); + result!.JobImportErrorDetails.Should().NotBeNull(); + result.JobImportErrorDetails!.Length.Should().Be(1); + result.JobImportErrorDetails[0].Errors![0].Code.Should().Be("INVALID_FORMAT"); + result.JobErrorDetails.Should().BeNull(); + } + + [Fact] + public async Task GetErrorDetailsAsync_Returns_JobErrorDetails_When_Response_Is_Object() + { + var json = """ + { + "status": "failed", + "type": "users_import", + "id": "job_abc", + "status_details": "Failed to parse users file JSON when importing users." + } + """; + var client = CreateClient(json); + + var result = await client.GetErrorDetailsAsync("job_123"); + + result.Should().NotBeNull(); + result!.JobErrorDetails.Should().NotBeNull(); + result.JobErrorDetails!.StatusDetails.Should().Be("Failed to parse users file JSON when importing users."); + result.JobImportErrorDetails.Should().BeNull(); + } +}