From db41dc3077705e60a2be241c38f7028fe393041c Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Mon, 9 Mar 2026 22:11:52 -0400 Subject: [PATCH 1/7] BX-69342: First wave of unit test improvements --- rosette_api/ApiClient.cs | 21 +- tests/ApiClientTests.cs | 424 +++++++++++++++++++++++++++++-- tests/CategoriesTests.cs | 12 + tests/EntitiesTests.cs | 14 +- tests/LanguageTests.cs | 8 + tests/NameDeduplicationTests.cs | 8 + tests/NameSimilarityTests.cs | 4 + tests/NameTests.cs | 12 + tests/NameTranslationTests.cs | 8 + tests/RelationshipsTests.cs | 12 + tests/ResponseTests.cs | 4 + tests/SentencesTests.cs | 12 + tests/SentimentTests.cs | 12 + tests/SimilarTermsTests.cs | 12 + tests/SimpleRecordModelsTests.cs | 332 ++++++++++++++++++++++++ tests/TokensTests.cs | 12 + tests/ValidEndpointTests.cs | 169 +++++++----- 17 files changed, 986 insertions(+), 90 deletions(-) create mode 100644 tests/SimpleRecordModelsTests.cs diff --git a/rosette_api/ApiClient.cs b/rosette_api/ApiClient.cs index 366de44..9012106 100644 --- a/rosette_api/ApiClient.cs +++ b/rosette_api/ApiClient.cs @@ -52,7 +52,7 @@ public class ApiClient : IDisposable /// /// Required Rosette API key public ApiClient(string apiKey) { - ArgumentNullException.ThrowIfNull(apiKey); + ArgumentException.ThrowIfNullOrWhiteSpace(apiKey); APIKey = apiKey; URI = "https://analytics.babelstreet.com/rest/v1/"; Client = null; @@ -70,6 +70,14 @@ public ApiClient(string apiKey) { /// Destination URL string /// RosetteAPI object public ApiClient UseAlternateURL(string urlString) { + ArgumentException.ThrowIfNullOrWhiteSpace(urlString); + + // Validate URL format by attempting to create Uri + if (!Uri.TryCreate(urlString, UriKind.Absolute, out _)) + { + throw new UriFormatException($"Invalid URL format: {urlString}"); + } + URI = urlString.EndsWith("/") ? urlString : urlString + "/"; return Prepare(); @@ -82,6 +90,7 @@ public ApiClient UseAlternateURL(string urlString) { /// A valid HttpClient /// RosetteAPI object public ApiClient AssignClient(HttpClient client) { + ArgumentNullException.ThrowIfNull(client); Client = client; return Prepare(); @@ -131,10 +140,11 @@ public ApiClient SetDebug(bool state=true) { /// Value of header /// RosetteAPI object public ApiClient AddCustomHeader(string headerName, string headerValue) { - if (!headerName.StartsWith("X-RosetteAPI-", StringComparison.OrdinalIgnoreCase)) + if (!headerName.StartsWith("X-RosetteAPI-", StringComparison.OrdinalIgnoreCase) || + headerName.Length <= "X-RosetteAPI-".Length) { throw new ArgumentException( - $"Custom header name must begin with 'X-RosetteAPI-'. Provided: {headerName}", + $"Custom header name must begin with 'X-RosetteAPI-' and have additional characters. Provided: {headerName}", nameof(headerName)); } if (_customHeaders.ContainsKey(headerName) && headerValue == null) @@ -169,7 +179,10 @@ private ApiClient Prepare(bool forceUpdate=false) { }); _disposeClient = true; } - Client.Timeout = TimeSpan.FromSeconds(Timeout); + // HttpClient.Timeout must be > 0 or Infinite + Client.Timeout = Timeout == 0 + ? System.Threading.Timeout.InfiniteTimeSpan + : TimeSpan.FromSeconds(Timeout); if (Client.BaseAddress == null) { Client.BaseAddress = new Uri(URI); // base address must be the rosette URI regardless of whether the client is external or internal diff --git a/tests/ApiClientTests.cs b/tests/ApiClientTests.cs index 73e2102..52ba197 100644 --- a/tests/ApiClientTests.cs +++ b/tests/ApiClientTests.cs @@ -6,17 +6,28 @@ public class ApiClientTests { private static readonly string _defaultUri = "https://analytics.babelstreet.com/rest/v1/"; private static readonly string _testKey = "testKey"; - + private static ApiClient Init() { return new ApiClient(_testKey); } + #region Basic Property Tests + [Fact] public void ApiKey_ReturnsProvidedKey_WhenInitialized() { ApiClient api = Init(); Assert.Equal(_testKey, api.APIKey); } + [Fact] + public void Version_IsNotEmpty_Always() { + Assert.NotEmpty(ApiClient.Version); + } + + #endregion + + #region Constructor Tests + [Fact] public void Constructor_ThrowsArgumentNullException_WhenApiKeyIsNull() { #pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. @@ -29,6 +40,22 @@ public void Constructor_ThrowsArgumentNullException_WhenApiKeyIsNull() { Assert.Equal("Value cannot be null. (Parameter 'apiKey')", ex.Message); } + [Fact] + public void Constructor_EmptyApiKey_ThrowsArgumentException() + { + Assert.Throws(() => new ApiClient("")); + } + + [Fact] + public void Constructor_WhitespaceApiKey_ThrowsArgumentException() + { + Assert.Throws(() => new ApiClient(" ")); + } + + #endregion + + #region URI and UseAlternateURL Tests + [Fact] public void URI_ReturnsDefaultAndAppendsTrailingSlash_WhenUsingAlternateUrl() { ApiClient api = Init(); @@ -40,6 +67,166 @@ public void URI_ReturnsDefaultAndAppendsTrailingSlash_WhenUsingAlternateUrl() { Assert.Equal(alternateUrl + "/", api.URI); } + [Fact] + public void UseAlternateURL_NullUrl_ThrowsArgumentNullException() + { + var api = new ApiClient("key"); + Assert.Throws(() => api.UseAlternateURL(null)); + } + + [Fact] + public void UseAlternateURL_InvalidUrl_ThrowsException() + { + var api = new ApiClient("key"); + Assert.Throws(() => api.UseAlternateURL("not a url")); + } + + [Fact] + public void UseAlternateURL_EmptyString_ThrowsArgumentException() + { + var api = Init(); + Assert.Throws(() => api.UseAlternateURL("")); + } + + [Fact] + public void UseAlternateURL_WhitespaceOnly_ThrowsArgumentException() + { + var api = Init(); + Assert.Throws(() => api.UseAlternateURL(" ")); + } + + [Fact] + public void UseAlternateURL_UrlWithoutScheme_ThrowsUriFormatException() + { + var api = Init(); + Assert.Throws(() => api.UseAlternateURL("example.com/api")); + } + + [Fact] + public void UseAlternateURL_MultipleTrailingSlashes_NormalizesToOneSlash() + { + var api = Init(); + api.UseAlternateURL("https://api.example.com////"); + // Code adds one slash if not present, doesn't normalize multiple slashes + Assert.Equal("https://api.example.com////", api.URI); + } + + [Fact] + public void UseAlternateURL_ValidUrlWithQueryString_PreservesQueryString() + { + var api = Init(); + string urlWithQuery = "https://api.example.com/v1?key=value"; + api.UseAlternateURL(urlWithQuery); + Assert.Equal(urlWithQuery + "/", api.URI); + } + + [Fact] + public void UseAlternateURL_CalledMultipleTimes_UpdatesURIEachTime() + { + var api = Init(); + + api.UseAlternateURL("https://url1.com"); + Assert.Equal("https://url1.com/", api.URI); + + api.UseAlternateURL("https://url2.com"); + Assert.Equal("https://url2.com/", api.URI); + } + + #endregion + + #region Client Configuration Tests + + [Fact] + public void Client_HasCorrectDefaultConfiguration_WhenInitialized() { + ApiClient api = Init(); + + Assert.Equal(_defaultUri, api.Client.BaseAddress.AbsoluteUri); + var acceptHeader = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"); + Assert.Contains(acceptHeader, api.Client.DefaultRequestHeaders.Accept); + foreach (string encodingType in new List() { "gzip", "deflate" }) { + var encodingHeader = new System.Net.Http.Headers.StringWithQualityHeaderValue(encodingType); + Assert.Contains(encodingHeader, api.Client.DefaultRequestHeaders.AcceptEncoding); + } + Assert.Equal(api.Timeout, api.Client.Timeout.TotalSeconds); + } + + #endregion + + #region AssignClient Tests + + [Fact] + public void AssignClient_NullClient_ThrowsArgumentNullException() + { + var api = new ApiClient("key"); + Assert.Throws(() => api.AssignClient(null)); + } + + [Fact] + public void AssignClient_WithDisposedClient_ThrowsObjectDisposedException() + { + var api = Init(); + var disposedClient = new HttpClient(); + disposedClient.Dispose(); + + // Assigning disposed client throws when Prepare() tries to set Timeout + Assert.Throws(() => api.AssignClient(disposedClient)); + } + + [Fact] + public void AssignClient_WithExistingBaseAddress_DoesNotOverwriteImmediately() + { + var api = Init(); + var otherUri = "https://other.com/api/"; + var client = new HttpClient { BaseAddress = new Uri(otherUri) }; + + api.AssignClient(client); + + // BaseAddress is set in Prepare() only if null + // User client's BaseAddress is preserved + Assert.Equal(otherUri, client.BaseAddress!.ToString()); + } + + [Fact] + public void AssignClient_PreservesUserClient_DoesNotDisposeOnApiDispose() + { + var api = Init(); + var userClient = new HttpClient(); + + api.AssignClient(userClient); + api.Dispose(); + + // User's client should still be usable (not disposed) + // Verify by checking BaseAddress is still accessible + var exception = Record.Exception(() => { var _ = userClient.BaseAddress; }); + Assert.Null(exception); + + // Clean up + userClient.Dispose(); + } + + [Fact] + public void AssignConcurrentConnections_AfterUserClient_ReplacesClientWithInternalOne() + { + var api = Init(); + var userClient = new HttpClient(); + + api.AssignClient(userClient); + var assignedClient = api.Client; + Assert.Same(userClient, assignedClient); + + // Changing concurrent connections forces new internal client + api.AssignConcurrentConnections(5); + + Assert.NotSame(userClient, api.Client); + + // Clean up + userClient.Dispose(); + } + + #endregion + + #region ConcurrentConnections Tests + [Fact] public void ConcurrentConnections_ReturnsCorrectValue_WhenDefaultAndAssigned() { ApiClient api = Init(); @@ -50,18 +237,46 @@ public void ConcurrentConnections_ReturnsCorrectValue_WhenDefaultAndAssigned() { } [Fact] - public void AddCustomHeader_ThrowsArgumentException_WhenHeaderNameIsInvalid() { - ApiClient api = Init(); - Exception ex = Assert.Throws(() => api.AddCustomHeader("BogusHeader", "BogusValue")); + public void AssignConcurrentConnections_One_ClampsToMinimumOfTwo() + { + var api = Init(); + api.AssignConcurrentConnections(1); - Assert.Contains(@"Custom header name must begin with 'X-RosetteAPI-'", ex.Message); + // Should be clamped to minimum of 2 + Assert.Equal(2, api.ConcurrentConnections); } [Fact] - public void Version_IsNotEmpty_Always() { - Assert.NotEmpty(ApiClient.Version); + public void AssignConcurrentConnections_Zero_ClampsToMinimumOfTwo() + { + var api = Init(); + api.AssignConcurrentConnections(0); + + Assert.Equal(2, api.ConcurrentConnections); } + [Fact] + public void AssignConcurrentConnections_NegativeValue_ClampsToMinimumOfTwo() + { + var api = Init(); + api.AssignConcurrentConnections(-5); + + Assert.Equal(2, api.ConcurrentConnections); + } + + [Fact] + public void AssignConcurrentConnections_LargeValue_AcceptsValue() + { + var api = Init(); + api.AssignConcurrentConnections(1000); + + Assert.Equal(1000, api.ConcurrentConnections); + } + + #endregion + + #region Timeout Tests + [Fact] public void Timeout_ReturnsCorrectValue_WhenDefaultAndAssigned() { ApiClient api = Init(); @@ -71,6 +286,60 @@ public void Timeout_ReturnsCorrectValue_WhenDefaultAndAssigned() { Assert.Equal(15, api.Timeout); } + [Fact] + public void AssignTimeout_Zero_SetsTimeoutToInfinite() + { + var api = Init(); + api.AssignTimeout(0); + + Assert.Equal(0, api.Timeout); + // HttpClient.Timeout of 0 gets set to Infinite + Assert.Equal(System.Threading.Timeout.InfiniteTimeSpan, api.Client!.Timeout); + } + + [Fact] + public void AssignTimeout_NegativeValue_ClampsToZeroAndSetsInfiniteTimeout() + { + var api = Init(); + api.AssignTimeout(-10); + + // Negative values should be clamped to 0 + Assert.Equal(0, api.Timeout); + Assert.Equal(System.Threading.Timeout.InfiniteTimeSpan, api.Client!.Timeout); + } + + [Fact] + public void AssignTimeout_VeryLargeValue_SetsTimeoutSuccessfully() + { + var api = Init(); + // Use a large but reasonable timeout value (1 day in seconds) + int largeTimeout = 86400; // 24 hours + + api.AssignTimeout(largeTimeout); + + Assert.Equal(largeTimeout, api.Timeout); + Assert.Equal(TimeSpan.FromSeconds(largeTimeout), api.Client!.Timeout); + } + + [Fact] + public void AssignTimeout_CalledMultipleTimes_UpdatesEachTime() + { + var api = Init(); + + api.AssignTimeout(10); + Assert.Equal(10, api.Timeout); + + api.AssignTimeout(20); + Assert.Equal(20, api.Timeout); + + api.AssignTimeout(30); + Assert.Equal(30, api.Timeout); + } + + #endregion + + #region Debug Tests + [Fact] public void Debug_TogglesToTrue_WhenSetDebugCalled() { ApiClient api = Init(); @@ -80,18 +349,141 @@ public void Debug_TogglesToTrue_WhenSetDebugCalled() { Assert.True(api.Debug); } + #endregion + + #region AddCustomHeader Tests + [Fact] - public void Client_HasCorrectDefaultConfiguration_WhenInitialized() { + public void AddCustomHeader_ThrowsArgumentException_WhenHeaderNameIsInvalid() { ApiClient api = Init(); + Exception ex = Assert.Throws(() => api.AddCustomHeader("BogusHeader", "BogusValue")); - Assert.Equal(_defaultUri, api.Client.BaseAddress.AbsoluteUri); - var acceptHeader = new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"); - Assert.Contains(acceptHeader, api.Client.DefaultRequestHeaders.Accept); - foreach (string encodingType in new List() { "gzip", "deflate" }) { - var encodingHeader = new System.Net.Http.Headers.StringWithQualityHeaderValue(encodingType); - Assert.Contains(encodingHeader, api.Client.DefaultRequestHeaders.AcceptEncoding); - } - Assert.Equal(api.Timeout, api.Client.Timeout.TotalSeconds); + Assert.Contains(@"Custom header name must begin with 'X-RosetteAPI-'", ex.Message); + } + + [Fact] + public void AddCustomHeader_JustPrefix_ThrowsArgumentException() + { + var api = Init(); + var exception = Assert.Throws(() => + api.AddCustomHeader("X-RosetteAPI-", "value")); + + Assert.Contains("X-RosetteAPI-", exception.Message); + } + + [Fact] + public void AddCustomHeader_EmptyHeaderValue_AddsHeaderSuccessfully() + { + var api = Init(); + + // Empty string is valid for HTTP headers + var exception = Record.Exception(() => + api.AddCustomHeader("X-RosetteAPI-Test", "")); + + Assert.Null(exception); + } + + [Fact] + public void AddCustomHeader_CaseInsensitivePrefix_AcceptsLowercase() + { + var api = Init(); + + // The check uses OrdinalIgnoreCase, so lowercase should work + var exception = Record.Exception(() => + api.AddCustomHeader("x-rosetteapi-custom", "value")); + + Assert.Null(exception); + } + + [Fact] + public void AddCustomHeader_NullValue_RemovesExistingHeader() + { + var api = Init(); + + api.AddCustomHeader("X-RosetteAPI-Test", "value"); + + // Setting to null should remove the header + var exception = Record.Exception(() => + api.AddCustomHeader("X-RosetteAPI-Test", null!)); + + Assert.Null(exception); + } + + [Fact] + public void AddCustomHeader_NullValueForNonExistentHeader_DoesNotThrow() + { + var api = Init(); + + // Removing non-existent header should not throw + var exception = Record.Exception(() => + api.AddCustomHeader("X-RosetteAPI-NonExistent", null!)); + + Assert.Null(exception); + } + + #endregion + + #region Dispose Pattern Tests + + [Fact] + public void Dispose_CalledTwice_DoesNotThrow() + { + var api = Init(); + api.Dispose(); + + var exception = Record.Exception(() => api.Dispose()); + Assert.Null(exception); + } + + [Fact] + public void Dispose_DisposesInternalClient_WhenNotUserProvided() + { + var api = Init(); + var client = api.Client; + + api.Dispose(); + + // In .NET the HttpClient might not immediately throw on property access after dispose + // Instead verify that operations fail + Assert.Throws(() => client!.GetAsync("http://test.com").GetAwaiter().GetResult()); + } + + [Fact] + public void Dispose_DoesNotDisposeUserClient_WhenUserProvided() + { + var api = Init(); + var userClient = new HttpClient(); + + api.AssignClient(userClient); + api.Dispose(); + + // User client should still be usable + var exception = Record.Exception(() => { var _ = userClient.BaseAddress; }); + Assert.Null(exception); + + // Clean up + userClient.Dispose(); + } + + #endregion + + #region Fluent API and State Management Tests + + [Fact] + public void FluentAPI_MultipleMethodCalls_AllReturnSameInstance() + { + var api = Init(); + + var result = api + .UseAlternateURL("https://test.com/") + .AssignTimeout(60) + .SetDebug() + .AddCustomHeader("X-RosetteAPI-Test", "value") + .AssignConcurrentConnections(5); + + Assert.Same(api, result); } + #endregion + } diff --git a/tests/CategoriesTests.cs b/tests/CategoriesTests.cs index 6d91b69..51b9b97 100644 --- a/tests/CategoriesTests.cs +++ b/tests/CategoriesTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class CategoriesTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -13,6 +15,10 @@ public void Constructor_SetsEndpointAndContent_WhenCalledWithText() Assert.Equal("Sample text for categorization", c.Content); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetLanguage_SetsLanguageProperty_WhenCalled() { @@ -32,6 +38,10 @@ public void SetGenre_SetsGenreProperty_WhenCalled() Assert.Equal("", c.Genre); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() { @@ -42,4 +52,6 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() Assert.Equal("eng", c.Language); Assert.Equal("value", c.Options["customOption"]); } + + #endregion } \ No newline at end of file diff --git a/tests/EntitiesTests.cs b/tests/EntitiesTests.cs index c83af7d..329eed2 100644 --- a/tests/EntitiesTests.cs +++ b/tests/EntitiesTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class EntitiesTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -14,6 +16,10 @@ public void Constructor_SetsEndpointAndContent_WhenCalledWithText() Assert.Equal(text, e.Content); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetLanguageAndGenre_SetsProperties_WhenCalled() { @@ -34,6 +40,10 @@ public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() Assert.Equal("rosette", e.UrlParameters["output"]); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() { @@ -47,4 +57,6 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() Assert.True((bool)e.Options["linkEntities"]); Assert.Equal("rosette", e.UrlParameters["output"]); } -} \ No newline at end of file + + #endregion +} diff --git a/tests/LanguageTests.cs b/tests/LanguageTests.cs index 082ec33..c2ce90b 100644 --- a/tests/LanguageTests.cs +++ b/tests/LanguageTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class LanguageTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -22,6 +24,10 @@ public void Constructor_SetsContentFromUri_WhenCalledWithUri() Assert.Equal("http://example.com/", l.Content.ToString()); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetContent_UpdatesContent_WhenCalled() { @@ -39,4 +45,6 @@ public void SetGenre_SetsGenreProperty_WhenCalled() Assert.Equal("", l.Genre); } + + #endregion } \ No newline at end of file diff --git a/tests/NameDeduplicationTests.cs b/tests/NameDeduplicationTests.cs index 97bd113..567b455 100644 --- a/tests/NameDeduplicationTests.cs +++ b/tests/NameDeduplicationTests.cs @@ -5,6 +5,8 @@ namespace Rosette.Api.Tests; public class NameDeduplicationTests { + #region Constructor Tests + [Fact] public void Constructor_SetsNamesAndThreshold_WhenCalledWithNames() { List names = new() @@ -17,6 +19,10 @@ public void Constructor_SetsNamesAndThreshold_WhenCalledWithNames() { Assert.Equal(0.75f, n.Threshold); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetProfileID_SetsProfileID_WhenCalled() { List names = new() @@ -40,4 +46,6 @@ public void SetThreshold_SetsThreshold_WhenCalled() { Assert.Equal(names, n.Names); Assert.Equal(0.8f, n.Threshold); } + + #endregion } diff --git a/tests/NameSimilarityTests.cs b/tests/NameSimilarityTests.cs index f57c9c4..5d6d3df 100644 --- a/tests/NameSimilarityTests.cs +++ b/tests/NameSimilarityTests.cs @@ -5,6 +5,8 @@ namespace Rosette.Api.Tests; public class NameSimilarityTests { + #region Constructor Tests + [Fact] public void Constructor_ThrowsArgumentNullException_WhenNamesAreNull() { var exception = Record.Exception(() => new NameSimilarity(null, null)); @@ -16,4 +18,6 @@ public void Constructor_ThrowsArgumentNullException_WhenNamesAreNull() { Assert.IsType(exception); Assert.Equal("Value cannot be null. (Parameter 'name2')", exception.Message); } + + #endregion } diff --git a/tests/NameTests.cs b/tests/NameTests.cs index 8d18ffa..affbb45 100644 --- a/tests/NameTests.cs +++ b/tests/NameTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class NameTests { + #region Constructor Tests + [Fact] public void Constructor_SetsTextAndNullProperties_WhenCreatingName() { Name rn = new("foo"); @@ -14,6 +16,10 @@ public void Constructor_SetsTextAndNullProperties_WhenCreatingName() { Assert.Null(rn.Gender); } + #endregion + + #region Property Setting Tests + [Fact] public void SetEntityType_SetsEntityType_WhenCalled() { Name rn = new Name("foo").SetEntityType("PERSON"); @@ -43,6 +49,10 @@ public void SetGender_SetsGender_WhenCalled() Assert.Equal(GenderType.Female, rn.Gender); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_SetsAllProperties_WhenChaining() { Name rn = new Name("foo") @@ -56,4 +66,6 @@ public void FluentAPI_SetsAllProperties_WhenChaining() { Assert.Equal("zho", rn.Script); Assert.Equal(GenderType.Male, rn.Gender); } + + #endregion } diff --git a/tests/NameTranslationTests.cs b/tests/NameTranslationTests.cs index a25c66a..c66eff8 100644 --- a/tests/NameTranslationTests.cs +++ b/tests/NameTranslationTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class NameTranslationTests { + #region Constructor Tests + [Fact] public void Constructor_SetsNameAndDefaults_WhenCalledWithName() { NameTranslation n = new("foo"); @@ -17,6 +19,10 @@ public void Constructor_SetsNameAndDefaults_WhenCalledWithName() { Assert.Empty(n.TargetScript); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_SetsAllProperties_WhenChaining() { NameTranslation n = new NameTranslation("foo") @@ -37,4 +43,6 @@ public void FluentAPI_SetsAllProperties_WhenChaining() { Assert.Equal("eng", n.TargetScript); } + #endregion + } diff --git a/tests/RelationshipsTests.cs b/tests/RelationshipsTests.cs index 8a58036..8f627a7 100644 --- a/tests/RelationshipsTests.cs +++ b/tests/RelationshipsTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests { public class RelationshipsTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -14,6 +16,10 @@ public void Constructor_SetsEndpointAndContent_WhenCalledWithText() Assert.Equal(text, r.Content); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetLanguage_SetsLanguageProperty_WhenCalled() { @@ -23,6 +29,10 @@ public void SetLanguage_SetsLanguageProperty_WhenCalled() Assert.Equal("eng", r.Language); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() { @@ -33,5 +43,7 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() Assert.Equal("eng", r.Language); Assert.Equal("high", r.Options["accuracy"]); } + + #endregion } } \ No newline at end of file diff --git a/tests/ResponseTests.cs b/tests/ResponseTests.cs index 50086db..5e56b90 100644 --- a/tests/ResponseTests.cs +++ b/tests/ResponseTests.cs @@ -6,6 +6,8 @@ namespace Rosette.Api.Tests; public class ResponseTests { + #region Constructor Tests + [Fact] public void Constructor_SetsStatusCodeAndContent_WhenHttpResponseIsOK() { Dictionary data = new() @@ -25,4 +27,6 @@ public void Constructor_SetsStatusCodeAndContent_WhenHttpResponseIsOK() { Assert.Equal(json, response.ContentAsJson()); } + + #endregion } diff --git a/tests/SentencesTests.cs b/tests/SentencesTests.cs index 9756684..c354960 100644 --- a/tests/SentencesTests.cs +++ b/tests/SentencesTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class SentencesTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -14,6 +16,10 @@ public void Constructor_SetsEndpointAndContent_WhenCalledWithText() Assert.Equal(text, s.Content); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetLanguage_SetsLanguageProperty_WhenCalled() { @@ -23,6 +29,10 @@ public void SetLanguage_SetsLanguageProperty_WhenCalled() Assert.Equal("eng", s.Language); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_AllowsMethodChaining_WhenSettingLanguage() { @@ -31,4 +41,6 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingLanguage() Assert.Equal("eng", s.Language); } + + #endregion } \ No newline at end of file diff --git a/tests/SentimentTests.cs b/tests/SentimentTests.cs index 26f55c3..068ad13 100644 --- a/tests/SentimentTests.cs +++ b/tests/SentimentTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class SentimentTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -13,6 +15,10 @@ public void Constructor_SetsEndpointAndContent_WhenCalledWithText() Assert.Equal("This is a great product!", s.Content); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetLanguage_SetsLanguageProperty_WhenCalled() { @@ -32,6 +38,10 @@ public void SetGenre_SetsGenreProperty_WhenCalled() Assert.Equal("", s.Genre); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() { @@ -42,4 +52,6 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() Assert.Equal("eng", s.Language); Assert.Equal(0.5, s.Options["sentiment.threshold"]); } + + #endregion } \ No newline at end of file diff --git a/tests/SimilarTermsTests.cs b/tests/SimilarTermsTests.cs index b657a1f..7277803 100644 --- a/tests/SimilarTermsTests.cs +++ b/tests/SimilarTermsTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class SimilarTermsTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -13,6 +15,10 @@ public void Constructor_SetsEndpointAndContent_WhenCalledWithText() Assert.Equal("happy", st.Content); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetLanguage_SetsLanguageProperty_WhenCalled() { @@ -23,6 +29,10 @@ public void SetLanguage_SetsLanguageProperty_WhenCalled() Assert.Equal("computer", st.Content); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() { @@ -33,4 +43,6 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() Assert.Equal("eng", st.Language); Assert.Equal(10, st.Options["count"]); } + + #endregion } \ No newline at end of file diff --git a/tests/SimpleRecordModelsTests.cs b/tests/SimpleRecordModelsTests.cs new file mode 100644 index 0000000..07b52ec --- /dev/null +++ b/tests/SimpleRecordModelsTests.cs @@ -0,0 +1,332 @@ +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +/// +/// Tests for simple record models: StringRecord, NumberRecord, BooleanRecord +/// +public class SimpleRecordModelsTests +{ + #region StringRecord Tests + + [Fact] + public void StringRecord_WithText_CreatesRecord() + { + // Arrange & Act + var record = new StringRecord { Text = "test value" }; + + // Assert + Assert.Equal("test value", record.Text); + } + + [Fact] + public void StringRecord_Text_CanBeUpdated() + { + // Arrange + var record = new StringRecord { Text = "initial" }; + + // Act + record.Text = "updated"; + + // Assert + Assert.Equal("updated", record.Text); + } + + [Fact] + public void StringRecord_HandlesSpecialCharacters() + { + // Arrange & Act + var record = new StringRecord { Text = "Special: @#$%^&*()" }; + + // Assert + Assert.Equal("Special: @#$%^&*()", record.Text); + } + + [Fact] + public void StringRecord_HandlesUnicode() + { + // Arrange & Act + var record = new StringRecord { Text = "Unicode: 你好世界 🌍" }; + + // Assert + Assert.Equal("Unicode: 你好世界 🌍", record.Text); + } + + [Fact] + public void StringRecord_ToString_ReturnsText() + { + // Arrange + var record = new StringRecord { Text = "test" }; + + // Act + var result = record.ToString(); + + // Assert + Assert.Equal("test", result); + } + + [Fact] + public void StringRecord_Equals_SameText_ReturnsTrue() + { + // Arrange + var record1 = new StringRecord { Text = "same" }; + var record2 = new StringRecord { Text = "same" }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void StringRecord_Equals_DifferentText_ReturnsFalse() + { + // Arrange + var record1 = new StringRecord { Text = "first" }; + var record2 = new StringRecord { Text = "second" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void StringRecord_GetHashCode_SameText_ReturnsSameHashCode() + { + // Arrange + var record1 = new StringRecord { Text = "same" }; + var record2 = new StringRecord { Text = "same" }; + + // Act & Assert + Assert.Equal(record1.GetHashCode(), record2.GetHashCode()); + } + + #endregion + + #region NumberRecord Tests + + [Fact] + public void NumberRecord_NoArgsConstructor_SetsNumberToZero() + { + // Arrange & Act + var record = new NumberRecord(); + + // Assert + Assert.Equal(0, record.Number); + } + + [Fact] + public void NumberRecord_Constructor_SetsNumber() + { + // Arrange & Act + var record = new NumberRecord(42.5); + + // Assert + Assert.Equal(42.5, record.Number); + } + + [Fact] + public void NumberRecord_Constructor_SetsNegativeValue() + { + // Arrange & Act + var record = new NumberRecord(-100.5); + + // Assert + Assert.Equal(-100.5, record.Number); + } + + [Fact] + public void NumberRecord_Number_CanBeUpdated() + { + // Arrange + var record = new NumberRecord(10); + + // Act + record.Number = 20; + + // Assert + Assert.Equal(20, record.Number); + } + + [Fact] + public void NumberRecord_ToString_ReturnsNumberAsString() + { + // Arrange + var record = new NumberRecord(42.5); + + // Act + var result = record.ToString(); + + // Assert + Assert.Equal("42.5", result); + } + + [Fact] + public void NumberRecord_Equals_SameNumber_ReturnsTrue() + { + // Arrange + var record1 = new NumberRecord(100); + var record2 = new NumberRecord(100); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void NumberRecord_Equals_DifferentNumber_ReturnsFalse() + { + // Arrange + var record1 = new NumberRecord(100); + var record2 = new NumberRecord(200); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void NumberRecord_GetHashCode_SameNumber_ReturnsSameHashCode() + { + // Arrange + var record1 = new NumberRecord(100); + var record2 = new NumberRecord(100); + + // Act & Assert + Assert.Equal(record1.GetHashCode(), record2.GetHashCode()); + } + + [Fact] + public void NumberRecord_HandlesLargeNumbers() + { + // Arrange & Act + var record = new NumberRecord(double.MaxValue); + + // Assert + Assert.Equal(double.MaxValue, record.Number); + } + + [Fact] + public void NumberRecord_HandlesSmallNumbers() + { + // Arrange & Act + var record = new NumberRecord(double.MinValue); + + // Assert + Assert.Equal(double.MinValue, record.Number); + } + + #endregion + + #region BooleanRecord Tests + + [Fact] + public void BooleanRecord_NoArgsConstructor_SetsBooleanToFalse() + { + // Arrange & Act + var record = new BooleanRecord(); + + // Assert + Assert.False(record.Boolean); + } + + [Fact] + public void BooleanRecord_Constructor_SetsTrue() + { + // Arrange & Act + var record = new BooleanRecord(true); + + // Assert + Assert.True(record.Boolean); + } + + [Fact] + public void BooleanRecord_Constructor_SetsFalse() + { + // Arrange & Act + var record = new BooleanRecord(false); + + // Assert + Assert.False(record.Boolean); + } + + [Fact] + public void BooleanRecord_Boolean_CanBeUpdated() + { + // Arrange + var record = new BooleanRecord(false); + + // Act + record.Boolean = true; + + // Assert + Assert.True(record.Boolean); + } + + [Fact] + public void BooleanRecord_ToString_True_ReturnsLowercaseTrue() + { + // Arrange + var record = new BooleanRecord(true); + + // Act + var result = record.ToString(); + + // Assert + Assert.Equal("true", result); + } + + [Fact] + public void BooleanRecord_ToString_False_ReturnsLowercaseFalse() + { + // Arrange + var record = new BooleanRecord(false); + + // Act + var result = record.ToString(); + + // Assert + Assert.Equal("false", result); + } + + [Fact] + public void BooleanRecord_Equals_BothTrue_ReturnsTrue() + { + // Arrange + var record1 = new BooleanRecord(true); + var record2 = new BooleanRecord(true); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void BooleanRecord_Equals_BothFalse_ReturnsTrue() + { + // Arrange + var record1 = new BooleanRecord(false); + var record2 = new BooleanRecord(false); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void BooleanRecord_Equals_TrueAndFalse_ReturnsFalse() + { + // Arrange + var record1 = new BooleanRecord(true); + var record2 = new BooleanRecord(false); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void BooleanRecord_GetHashCode_SameValue_ReturnsSameHashCode() + { + // Arrange + var record1 = new BooleanRecord(true); + var record2 = new BooleanRecord(true); + + // Act & Assert + Assert.Equal(record1.GetHashCode(), record2.GetHashCode()); + } + + #endregion +} diff --git a/tests/TokensTests.cs b/tests/TokensTests.cs index be742d0..52464ae 100644 --- a/tests/TokensTests.cs +++ b/tests/TokensTests.cs @@ -4,6 +4,8 @@ namespace Rosette.Api.Tests; public class TokensTests { + #region Constructor Tests + [Fact] public void Constructor_SetsEndpointAndContent_WhenCalledWithText() { @@ -13,6 +15,10 @@ public void Constructor_SetsEndpointAndContent_WhenCalledWithText() Assert.Equal("This is sample text", t.Content); } + #endregion + + #region Property Configuration Tests + [Fact] public void SetLanguage_SetsLanguageProperty_WhenCalled() { @@ -22,6 +28,10 @@ public void SetLanguage_SetsLanguageProperty_WhenCalled() Assert.Equal("eng", t.Language); } + #endregion + + #region Fluent API Tests + [Fact] public void FluentAPI_AllowsMethodChaining_WhenSettingLanguage() { @@ -30,4 +40,6 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingLanguage() Assert.Equal("jpn", t.Language); } + + #endregion } \ No newline at end of file diff --git a/tests/ValidEndpointTests.cs b/tests/ValidEndpointTests.cs index f2ded89..7aa1942 100644 --- a/tests/ValidEndpointTests.cs +++ b/tests/ValidEndpointTests.cs @@ -5,6 +5,7 @@ namespace Rosette.Api.Tests; public class ValidEndpointTests { + #region Address and Record Endpoints [Fact] public void Constructor_SetsEndpoint_WhenCreatingAddressSimilarity() @@ -15,6 +16,22 @@ public void Constructor_SetsEndpoint_WhenCreatingAddressSimilarity() Assert.Equal("address-similarity", asim.Endpoint); } + [Fact] + public void Constructor_SetsEndpoint_WhenCreatingRecordSimilarity() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + Assert.Equal("record-similarity", rs.Endpoint); + } + + #endregion + + #region Content Analysis Endpoints + [Fact] public void Constructor_SetsEndpointAndContent_WhenCreatingCategories() { @@ -41,12 +58,35 @@ public void Constructor_SetsEndpointAndContent_WhenCreatingEvents() } [Fact] - public void Constructor_SetsEndpoint_WhenCreatingInfo() + public void Constructor_SetsEndpointAndContent_WhenCreatingRelationships() { - Info i = new(); - Assert.Equal("info", i.Endpoint); + Relationships r = new("foo"); + + Assert.Equal("relationships", r.Endpoint); + Assert.Equal("foo", r.Content); + } + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCreatingSentiment() + { + Sentiment s = new("foo"); + Assert.Equal("sentiment", s.Endpoint); + Assert.Equal("foo", s.Content); + } + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCreatingTopics() + { + Topics t = new("foo"); + + Assert.Equal("topics", t.Endpoint); + Assert.Equal("foo", t.Content); } + #endregion + + #region Language and Text Processing Endpoints + [Fact] public void Constructor_SetsEndpointAndContent_WhenCreatingLanguage() { @@ -56,6 +96,36 @@ public void Constructor_SetsEndpointAndContent_WhenCreatingLanguage() Assert.Equal("foo", l.Content); } + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCreatingSentences() + { + Sentences s = new("foo"); + Assert.Equal("sentences", s.Endpoint); + Assert.Equal("foo", s.Content); + } + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCreatingTokens() + { + Tokens t = new("foo"); + + Assert.Equal("tokens", t.Endpoint); + Assert.Equal("foo", t.Content); + } + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCreatingTransliteration() + { + Transliteration t = new("foo"); + + Assert.Equal("transliteration", t.Endpoint); + Assert.Equal("foo", t.Content); + } + + #endregion + + #region Morphology Endpoints + [Theory] [InlineData(MorphologyFeature.complete)] [InlineData(MorphologyFeature.compoundComponents)] @@ -70,6 +140,10 @@ public void Constructor_SetsEndpointAndContent_WhenCreatingMorphologyWithFeature Assert.Equal("foo", m.Content); } + #endregion + + #region Name Endpoints + [Fact] public void Constructor_SetsEndpoint_WhenCreatingNameDeduplication() { @@ -98,33 +172,9 @@ public void Constructor_SetsEndpoint_WhenCreatingNameTranslation() Assert.Equal("name-translation", nt.Endpoint); } - [Fact] - public void Constructor_SetsEndpoint_WhenCreatingPing() - { - Ping p = new(); - Assert.Equal("ping", p.Endpoint); - } - - [Fact] - public void Constructor_SetsEndpoint_WhenCreatingRecordSimilarity() - { - var fields = new Dictionary(); - var properties = new RecordSimilarityProperties(); - var records = new RecordSimilarityRecords(); - - RecordSimilarity rs = new(fields, properties, records); + #endregion - Assert.Equal("record-similarity", rs.Endpoint); - } - - [Fact] - public void Constructor_SetsEndpointAndContent_WhenCreatingRelationships() - { - Relationships r = new("foo"); - - Assert.Equal("relationships", r.Endpoint); - Assert.Equal("foo", r.Content); - } + #region Semantics Endpoints [Fact] public void Constructor_SetsEndpointAndContent_WhenCreatingSemanticsVector() @@ -134,22 +184,6 @@ public void Constructor_SetsEndpointAndContent_WhenCreatingSemanticsVector() Assert.Equal("foo", s.Content); } - [Fact] - public void Constructor_SetsEndpointAndContent_WhenCreatingSentences() - { - Sentences s = new("foo"); - Assert.Equal("sentences", s.Endpoint); - Assert.Equal("foo", s.Content); - } - - [Fact] - public void Constructor_SetsEndpointAndContent_WhenCreatingSentiment() - { - Sentiment s = new("foo"); - Assert.Equal("sentiment", s.Endpoint); - Assert.Equal("foo", s.Content); - } - [Fact] public void Constructor_SetsEndpointAndContent_WhenCreatingSimilarTerms() { @@ -159,15 +193,6 @@ public void Constructor_SetsEndpointAndContent_WhenCreatingSimilarTerms() Assert.Equal("foo", st.Content); } - [Fact] - public void Constructor_SetsEndpointAndContent_WhenCreatingSyntaxDependencies() - { - SyntaxDependencies s = new("foo"); - - Assert.Equal("syntax/dependencies", s.Endpoint); - Assert.Equal("foo", s.Content); - } - [Fact] public void Constructor_SetsEndpointAndContent_WhenCreatingTextEmbedding() { @@ -177,30 +202,36 @@ public void Constructor_SetsEndpointAndContent_WhenCreatingTextEmbedding() Assert.Equal("foo", t.Content); } + #endregion + + #region Syntax Endpoints + [Fact] - public void Constructor_SetsEndpointAndContent_WhenCreatingTokens() + public void Constructor_SetsEndpointAndContent_WhenCreatingSyntaxDependencies() { - Tokens t = new("foo"); + SyntaxDependencies s = new("foo"); - Assert.Equal("tokens", t.Endpoint); - Assert.Equal("foo", t.Content); + Assert.Equal("syntax/dependencies", s.Endpoint); + Assert.Equal("foo", s.Content); } + #endregion + + #region Utility Endpoints + [Fact] - public void Constructor_SetsEndpointAndContent_WhenCreatingTopics() + public void Constructor_SetsEndpoint_WhenCreatingInfo() { - Topics t = new("foo"); - - Assert.Equal("topics", t.Endpoint); - Assert.Equal("foo", t.Content); + Info i = new(); + Assert.Equal("info", i.Endpoint); } [Fact] - public void Constructor_SetsEndpointAndContent_WhenCreatingTransliteration() + public void Constructor_SetsEndpoint_WhenCreatingPing() { - Transliteration t = new("foo"); - - Assert.Equal("transliteration", t.Endpoint); - Assert.Equal("foo", t.Content); + Ping p = new(); + Assert.Equal("ping", p.Endpoint); } -} \ No newline at end of file + + #endregion +} From 4e8e0d9892e3b98f811ed1ba1bd1af3897b5d8e6 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Mar 2026 06:53:08 -0400 Subject: [PATCH 2/7] BX-69342: Adds unit tests for additional endpoints --- tests/AddressSimilarityTests.cs | 276 +++++++++++++++++++++++++++++++ tests/EventsTests.cs | 149 +++++++++++++++++ tests/InfoTests.cs | 107 ++++++++++++ tests/MorphologyTests.cs | 148 +++++++++++++++++ tests/PingTests.cs | 106 ++++++++++++ tests/RecordSimilarityTests.cs | 231 ++++++++++++++++++++++++++ tests/SemanticsVectorTests.cs | 127 ++++++++++++++ tests/SyntaxDependenciesTests.cs | 103 ++++++++++++ tests/TextEmbeddingTests.cs | 149 +++++++++++++++++ tests/TopicsTests.cs | 127 ++++++++++++++ tests/TransliterationTests.cs | 134 +++++++++++++++ 11 files changed, 1657 insertions(+) create mode 100644 tests/AddressSimilarityTests.cs create mode 100644 tests/EventsTests.cs create mode 100644 tests/InfoTests.cs create mode 100644 tests/MorphologyTests.cs create mode 100644 tests/PingTests.cs create mode 100644 tests/RecordSimilarityTests.cs create mode 100644 tests/SemanticsVectorTests.cs create mode 100644 tests/SyntaxDependenciesTests.cs create mode 100644 tests/TextEmbeddingTests.cs create mode 100644 tests/TopicsTests.cs create mode 100644 tests/TransliterationTests.cs diff --git a/tests/AddressSimilarityTests.cs b/tests/AddressSimilarityTests.cs new file mode 100644 index 0000000..a443c2f --- /dev/null +++ b/tests/AddressSimilarityTests.cs @@ -0,0 +1,276 @@ +using Rosette.Api.Client.Endpoints; +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +public class AddressSimilarityTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpoint_WhenCalledWithUnfieldedAddresses() + { + var address1 = new UnfieldedAddressRecord { Address = "1600 Pennsylvania Avenue, Washington, D.C., 20500" }; + var address2 = new UnfieldedAddressRecord { Address = "160 Pennsylvana Avenue, Washington, D.C., 20500" }; + + AddressSimilarity asim = new(address1, address2); + + Assert.Equal("address-similarity", asim.Endpoint); + } + + [Fact] + public void Constructor_SetsEndpoint_WhenCalledWithFieldedAddresses() + { + var address1 = new FieldedAddressRecord( + houseNumber: "1600", + road: "Pennsylvania Avenue NW", + city: "Washington", + state: "DC", + postcode: "20500" + ); + var address2 = new FieldedAddressRecord( + houseNumber: "1600", + road: "Pennsylvania Ave N.W.", + city: "Washington", + state: "DC", + postcode: "20500" + ); + + AddressSimilarity asim = new(address1, address2); + + Assert.Equal("address-similarity", asim.Endpoint); + } + + [Fact] + public void Constructor_SetsEndpoint_WhenCalledWithMixedAddresses() + { + var address1 = new UnfieldedAddressRecord { Address = "1600 Pennsylvania Avenue, Washington, D.C., 20500" }; + var address2 = new FieldedAddressRecord( + houseNumber: "1600", + road: "Pennsylvania Avenue NW", + city: "Washington", + state: "DC", + postcode: "20500" + ); + + AddressSimilarity asim = new(address1, address2); + + Assert.Equal("address-similarity", asim.Endpoint); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenAddress1IsNull() + { + var address2 = new UnfieldedAddressRecord { Address = "Some address" }; + + Assert.Throws(() => new AddressSimilarity(null!, address2)); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenAddress2IsNull() + { + var address1 = new UnfieldedAddressRecord { Address = "Some address" }; + + Assert.Throws(() => new AddressSimilarity(address1, null!)); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenBothAddressesAreNull() + { + Assert.Throws(() => new AddressSimilarity(null!, null!)); + } + + #endregion + + #region Parameter Tests + + [Fact] + public void Constructor_AddsAddress1ToParams_WhenCalled() + { + var address1 = new UnfieldedAddressRecord { Address = "Address 1" }; + var address2 = new UnfieldedAddressRecord { Address = "Address 2" }; + + AddressSimilarity asim = new(address1, address2); + + Assert.Contains("address1", asim.Params.Keys); + Assert.Equal(address1, asim.Params["address1"]); + } + + [Fact] + public void Constructor_AddsAddress2ToParams_WhenCalled() + { + var address1 = new UnfieldedAddressRecord { Address = "Address 1" }; + var address2 = new UnfieldedAddressRecord { Address = "Address 2" }; + + AddressSimilarity asim = new(address1, address2); + + Assert.Contains("address2", asim.Params.Keys); + Assert.Equal(address2, asim.Params["address2"]); + } + + #endregion + + #region UnfieldedAddressRecord Tests + + [Fact] + public void Constructor_AcceptsUnfieldedAddress_WithSimpleAddress() + { + var address1 = new UnfieldedAddressRecord { Address = "123 Main St" }; + var address2 = new UnfieldedAddressRecord { Address = "123 Main Street" }; + + AddressSimilarity asim = new(address1, address2); + + var storedAddress1 = (UnfieldedAddressRecord)asim.Params["address1"]; + Assert.Equal("123 Main St", storedAddress1.Address); + } + + [Fact] + public void Constructor_AcceptsUnfieldedAddress_WithComplexAddress() + { + var address1 = new UnfieldedAddressRecord + { + Address = "Suite 500, 1234 Technology Drive, San Francisco, CA 94102, United States" + }; + var address2 = new UnfieldedAddressRecord + { + Address = "1234 Technology Dr, Suite 500, San Francisco, California 94102" + }; + + AddressSimilarity asim = new(address1, address2); + + Assert.NotNull(asim.Params["address1"]); + Assert.NotNull(asim.Params["address2"]); + } + + [Fact] + public void Constructor_AcceptsUnfieldedAddress_WithInternationalAddress() + { + var address1 = new UnfieldedAddressRecord { Address = "10 Downing Street, London, SW1A 2AA, UK" }; + var address2 = new UnfieldedAddressRecord { Address = "10 Downing St, Westminster, London SW1A 2AA" }; + + AddressSimilarity asim = new(address1, address2); + + Assert.Equal("address-similarity", asim.Endpoint); + } + + #endregion + + #region FieldedAddressRecord Tests + + [Fact] + public void Constructor_AcceptsFieldedAddress_WithAllFields() + { + var address1 = new FieldedAddressRecord( + houseNumber: "1600", + road: "Pennsylvania Avenue NW", + city: "Washington", + state: "DC", + postcode: "20500", + country: "United States" + ); + var address2 = new FieldedAddressRecord( + houseNumber: "1600", + road: "Pennsylvania Ave", + city: "Washington", + state: "DC", + postcode: "20500" + ); + + AddressSimilarity asim = new(address1, address2); + + var storedAddress1 = (FieldedAddressRecord)asim.Params["address1"]; + Assert.Equal("1600", storedAddress1.HouseNumber); + Assert.Equal("Pennsylvania Avenue NW", storedAddress1.Road); + } + + [Fact] + public void Constructor_AcceptsFieldedAddress_WithMinimalFields() + { + var address1 = new FieldedAddressRecord(city: "Boston"); + var address2 = new FieldedAddressRecord(city: "Boston"); + + AddressSimilarity asim = new(address1, address2); + + Assert.Equal("address-similarity", asim.Endpoint); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_AddsOptionToOptions_WhenCalled() + { + var address1 = new UnfieldedAddressRecord { Address = "Address 1" }; + var address2 = new UnfieldedAddressRecord { Address = "Address 2" }; + + AddressSimilarity asim = new AddressSimilarity(address1, address2) + .SetOption("explain", true); + + Assert.True((bool)asim.Options["explain"]); + } + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + var address1 = new UnfieldedAddressRecord { Address = "Address 1" }; + var address2 = new UnfieldedAddressRecord { Address = "Address 2" }; + + AddressSimilarity asim = new AddressSimilarity(address1, address2) + .SetOption("explain", true) + .SetOption("debug", "detailed"); + + Assert.True((bool)asim.Options["explain"]); + Assert.Equal("detailed", asim.Options["debug"]); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingOptions() + { + var address1 = new UnfieldedAddressRecord { Address = "123 Main St" }; + var address2 = new UnfieldedAddressRecord { Address = "123 Main Street" }; + + AddressSimilarity asim = new AddressSimilarity(address1, address2) + .SetOption("explain", true); + + Assert.True((bool)asim.Options["explain"]); + } + + #endregion + + #region Real-World Address Tests + + [Fact] + public void Constructor_HandlesTypicalUSAddress_WhenCalled() + { + var address1 = new UnfieldedAddressRecord { Address = "1 Apple Park Way, Cupertino, CA 95014" }; + var address2 = new FieldedAddressRecord( + houseNumber: "1", + road: "Apple Park Way", + city: "Cupertino", + state: "CA", + postcode: "95014" + ); + + AddressSimilarity asim = new(address1, address2); + + Assert.Equal("address-similarity", asim.Endpoint); + } + + [Fact] + public void Constructor_HandlesAbbreviationsAndSpellingVariations_WhenCalled() + { + var address1 = new UnfieldedAddressRecord { Address = "1600 Penn Ave NW" }; + var address2 = new UnfieldedAddressRecord { Address = "1600 Pennsylvania Avenue Northwest" }; + + AddressSimilarity asim = new(address1, address2); + + Assert.Equal("address-similarity", asim.Endpoint); + } + + #endregion +} diff --git a/tests/EventsTests.cs b/tests/EventsTests.cs new file mode 100644 index 0000000..33c22ec --- /dev/null +++ b/tests/EventsTests.cs @@ -0,0 +1,149 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class EventsTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCalledWithText() + { + string text = "Bill Gates went to the store."; + Events e = new(text); + + Assert.Equal("events", e.Endpoint); + Assert.Equal(text, e.Content); + } + + #endregion + + #region Property Configuration Tests + + [Fact] + public void SetLanguage_UpdatesLanguage_WhenCalled() + { + Events e = new Events("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", e.Language); + } + + [Fact] + public void SetGenre_UpdatesGenre_WhenCalled() + { + Events e = new Events("Sample text") + .SetGenre("social-media"); + + Assert.Equal("", e.Genre); + } + + [Fact] + public void SetFileContentType_UpdatesFileContentType_WhenCalled() + { + Events e = new Events("Sample text") + .SetFileContentType("text/plain"); + + Assert.Equal("text/plain", e.FileContentType); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() + { + Events e = new Events("The CEO announced a merger yesterday.") + .SetLanguage("eng") + .SetGenre("news") + .SetOption("output", "rosette"); + + Assert.Equal("eng", e.Language); + Assert.Equal("", e.Genre); + Assert.Equal("rosette", e.Options["output"]); + } + + #endregion + + #region Content Type Tests + + [Fact] + public void Constructor_AcceptsUri_AsContent() + { + Uri uri = new("https://example.com/article"); + Events e = new(uri); + + Assert.Equal(uri, e.Content); + } + + [Fact] + public void Constructor_AcceptsString_AsContent() + { + string text = "The company launched a new product last week."; + Events e = new(text); + + Assert.Equal(text, e.Content); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + Events e = new Events("Sample text") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", e.UrlParameters["output"]); + } + + #endregion + + #region Negation Option Tests + + [Fact] + public void SetOption_SupportsNegationOption_WhenCalledWithOnlyPositive() + { + Events e = new Events("Bill Gates did not go to the store.") + .SetOption("negation", "ONLY_POSITIVE"); + + Assert.Equal("ONLY_POSITIVE", e.Options["negation"]); + } + + [Fact] + public void SetOption_SupportsNegationOption_WhenCalledWithOnlyNegative() + { + Events e = new Events("Bill Gates did not go to the store.") + .SetOption("negation", "ONLY_NEGATIVE"); + + Assert.Equal("ONLY_NEGATIVE", e.Options["negation"]); + } + + [Fact] + public void SetOption_SupportsNegationOption_WhenCalledWithBoth() + { + Events e = new Events("Bill Gates did not go to the store.") + .SetOption("negation", "BOTH"); + + Assert.Equal("BOTH", e.Options["negation"]); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + Events e = new Events("Sample text") + .SetOption("negation", "ONLY_POSITIVE") + .SetOption("output", "rosette"); + + Assert.Equal("ONLY_POSITIVE", e.Options["negation"]); + Assert.Equal("rosette", e.Options["output"]); + } + + #endregion +} diff --git a/tests/InfoTests.cs b/tests/InfoTests.cs new file mode 100644 index 0000000..cc9707c --- /dev/null +++ b/tests/InfoTests.cs @@ -0,0 +1,107 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class InfoTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpoint_WhenCalled() + { + Info i = new(); + + Assert.Equal("info", i.Endpoint); + } + + #endregion + + #region Endpoint Validation Tests + + [Fact] + public void Endpoint_ReturnsCorrectValue_Always() + { + Info i = new(); + + Assert.Equal("info", i.Endpoint); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_AddsOptionToOptions_WhenCalled() + { + Info i = new Info() + .SetOption("test", "value"); + + Assert.Equal("value", i.Options["test"]); + } + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + Info i = new Info() + .SetOption("option1", "value1") + .SetOption("option2", "value2"); + + Assert.Equal("value1", i.Options["option1"]); + Assert.Equal("value2", i.Options["option2"]); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + Info i = new Info() + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", i.UrlParameters["output"]); + } + + [Fact] + public void SetUrlParameter_SupportsMultipleParameters_WhenCalledMultipleTimes() + { + Info i = new Info() + .SetUrlParameter("output", "rosette") + .SetUrlParameter("format", "json"); + + Assert.Equal("rosette", i.UrlParameters["output"]); + Assert.Equal("json", i.UrlParameters["format"]); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingProperties() + { + Info i = new Info() + .SetOption("test", "value") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("value", i.Options["test"]); + Assert.Equal("rosette", i.UrlParameters["output"]); + } + + #endregion + + #region Instance Creation Tests + + [Fact] + public void Constructor_CreatesNewInstance_WhenCalled() + { + Info i1 = new(); + Info i2 = new(); + + Assert.NotSame(i1, i2); + } + + #endregion + +} diff --git a/tests/MorphologyTests.cs b/tests/MorphologyTests.cs new file mode 100644 index 0000000..cb71c6b --- /dev/null +++ b/tests/MorphologyTests.cs @@ -0,0 +1,148 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class MorphologyTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCalledWithDefaultFeature() + { + string text = "The quick brown fox jumped over the lazy dog."; + Morphology m = new(text); + + Assert.Equal("morphology/complete", m.Endpoint); + Assert.Equal(text, m.Content); + } + + [Theory] + [InlineData(MorphologyFeature.complete, "morphology/complete")] + [InlineData(MorphologyFeature.lemmas, "morphology/lemmas")] + [InlineData(MorphologyFeature.partsOfSpeech, "morphology/parts-of-speech")] + [InlineData(MorphologyFeature.compoundComponents, "morphology/compound-components")] + [InlineData(MorphologyFeature.hanReadings, "morphology/han-readings")] + public void Constructor_SetsCorrectEndpoint_ForEachFeature(MorphologyFeature feature, string expectedEndpoint) + { + Morphology m = new("Sample text", feature); + + Assert.Equal(expectedEndpoint, m.Endpoint); + } + + #endregion + + #region Property Configuration Tests + + [Fact] + public void SetContent_UpdatesContent_WhenCalled() + { + Morphology m = new("initial content"); + string newContent = "updated content"; + + m.SetContent(newContent); + + Assert.Equal(newContent, m.Content); + } + + [Fact] + public void SetLanguage_UpdatesLanguage_WhenCalled() + { + Morphology m = new("Sample text"); + string language = "eng"; + + m.SetLanguage(language); + + Assert.Equal("eng", m.Language); + } + + [Fact] + public void SetGenre_UpdatesGenre_WhenCalled() + { + Morphology m = new("Sample text"); + string genre = "social-media"; + + m.SetGenre(genre); + + Assert.Equal("social-media", m.Genre); + } + + [Fact] + public void SetFileContentType_UpdatesFileContentType_WhenCalled() + { + Morphology m = new("Sample text"); + string fileContentType = "text/plain"; + + m.SetFileContentType(fileContentType); + + Assert.Equal("text/plain", m.FileContentType); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() + { + Morphology m = new Morphology("The geese went back to get a rest", MorphologyFeature.lemmas) + .SetLanguage("eng") + .SetGenre("news") + .SetOption("debug", true); + + Assert.Equal("morphology/lemmas", m.Endpoint); + Assert.Equal("eng", m.Language); + Assert.Equal("news", m.Genre); + Assert.True((bool)m.Options["debug"]); + } + + #endregion + + #region Feature-Specific Tests + + [Fact] + public void Constructor_WithLemmasFeature_SetsLemmasEndpoint() + { + Morphology m = new("banking on their return", MorphologyFeature.lemmas); + + Assert.Equal("morphology/lemmas", m.Endpoint); + } + + [Fact] + public void Constructor_WithPartsOfSpeechFeature_SetsPartsOfSpeechEndpoint() + { + Morphology m = new("The cat sat on the mat", MorphologyFeature.partsOfSpeech); + + Assert.Equal("morphology/parts-of-speech", m.Endpoint); + } + + [Fact] + public void Constructor_WithCompoundComponentsFeature_SetsCompoundComponentsEndpoint() + { + Morphology m = new("Rechtsschutzversicherungsgesellschaften", MorphologyFeature.compoundComponents); + + Assert.Equal("morphology/compound-components", m.Endpoint); + } + + [Fact] + public void Constructor_WithHanReadingsFeature_SetsHanReadingsEndpoint() + { + Morphology m = new("北京大学生物系主任办公室内部会议", MorphologyFeature.hanReadings); + + Assert.Equal("morphology/han-readings", m.Endpoint); + } + + #endregion + + #region Content Type Tests + + [Fact] + public void Constructor_AcceptsUri_AsContent() + { + Uri uri = new("https://example.com/text"); + Morphology m = new(uri, MorphologyFeature.complete); + + Assert.Equal(uri, m.Content); + } + + #endregion +} diff --git a/tests/PingTests.cs b/tests/PingTests.cs new file mode 100644 index 0000000..979175d --- /dev/null +++ b/tests/PingTests.cs @@ -0,0 +1,106 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class PingTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpoint_WhenCalled() + { + Ping p = new(); + + Assert.Equal("ping", p.Endpoint); + } + + #endregion + + #region Endpoint Validation Tests + + [Fact] + public void Endpoint_ReturnsCorrectValue_Always() + { + Ping p = new(); + + Assert.Equal("ping", p.Endpoint); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_AddsOptionToOptions_WhenCalled() + { + Ping p = new Ping() + .SetOption("test", "value"); + + Assert.Equal("value", p.Options["test"]); + } + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + Ping p = new Ping() + .SetOption("option1", "value1") + .SetOption("option2", "value2"); + + Assert.Equal("value1", p.Options["option1"]); + Assert.Equal("value2", p.Options["option2"]); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + Ping p = new Ping() + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", p.UrlParameters["output"]); + } + + [Fact] + public void SetUrlParameter_SupportsMultipleParameters_WhenCalledMultipleTimes() + { + Ping p = new Ping() + .SetUrlParameter("output", "rosette") + .SetUrlParameter("format", "json"); + + Assert.Equal("rosette", p.UrlParameters["output"]); + Assert.Equal("json", p.UrlParameters["format"]); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingProperties() + { + Ping p = new Ping() + .SetOption("test", "value") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("value", p.Options["test"]); + Assert.Equal("rosette", p.UrlParameters["output"]); + } + + #endregion + + #region Instance Creation Tests + + [Fact] + public void Constructor_CreatesNewInstance_WhenCalled() + { + Ping p1 = new(); + Ping p2 = new(); + + Assert.NotSame(p1, p2); + } + + #endregion +} diff --git a/tests/RecordSimilarityTests.cs b/tests/RecordSimilarityTests.cs new file mode 100644 index 0000000..387e5b1 --- /dev/null +++ b/tests/RecordSimilarityTests.cs @@ -0,0 +1,231 @@ +using Rosette.Api.Client.Endpoints; +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +public class RecordSimilarityTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpoint_WhenCalledWithValidParameters() + { + var fields = new Dictionary + { + { "name", new RecordSimilarityFieldInfo("rni_name", 1.0, 0.0) } + }; + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + Assert.Equal("record-similarity", rs.Endpoint); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenFieldsIsNull() + { + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + Assert.Throws(() => new RecordSimilarity(null!, properties, records)); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenPropertiesIsNull() + { + var fields = new Dictionary(); + var records = new RecordSimilarityRecords(); + + Assert.Throws(() => new RecordSimilarity(fields, null!, records)); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenRecordsIsNull() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + + Assert.Throws(() => new RecordSimilarity(fields, properties, null!)); + } + + #endregion + + #region Parameter Tests + + [Fact] + public void Constructor_AddsFieldsToParams_WhenCalled() + { + var fields = new Dictionary + { + { "name", new RecordSimilarityFieldInfo("rni_name", 1.0, 0.0) } + }; + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + Assert.Contains("fields", rs.Params.Keys); + Assert.Equal(fields, rs.Params["fields"]); + } + + [Fact] + public void Constructor_AddsPropertiesToParams_WhenCalled() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + Assert.Contains("properties", rs.Params.Keys); + Assert.Equal(properties, rs.Params["properties"]); + } + + [Fact] + public void Constructor_AddsRecordsToParams_WhenCalled() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + Assert.Contains("records", rs.Params.Keys); + Assert.Equal(records, rs.Params["records"]); + } + + #endregion + + #region Field Configuration Tests + + [Fact] + public void Constructor_AcceptsMultipleFields_WhenCalled() + { + var fields = new Dictionary + { + { "name", new RecordSimilarityFieldInfo("rni_name", 1.0, 0.0) }, + { "address", new RecordSimilarityFieldInfo("rni_address", 0.8, 0.0) }, + { "birthDate", new RecordSimilarityFieldInfo("rni_date", 0.6, 0.0) } + }; + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + var storedFields = (Dictionary)rs.Params["fields"]; + Assert.Equal(3, storedFields.Count); + Assert.Contains("name", storedFields.Keys); + Assert.Contains("address", storedFields.Keys); + Assert.Contains("birthDate", storedFields.Keys); + } + + [Fact] + public void Constructor_AcceptsFieldInfoWithNullWeight_WhenCalled() + { + var fields = new Dictionary + { + { "name", new RecordSimilarityFieldInfo("rni_name", null, 0.0) } + }; + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + var storedFields = (Dictionary)rs.Params["fields"]; + Assert.Null(storedFields["name"].Weight); + } + + [Fact] + public void Constructor_AcceptsFieldInfoWithNullScoreIfNull_WhenCalled() + { + var fields = new Dictionary + { + { "name", new RecordSimilarityFieldInfo("rni_name", 1.0, null) } + }; + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + var storedFields = (Dictionary)rs.Params["fields"]; + Assert.Null(storedFields["name"].ScoreIfNull); + } + + #endregion + + #region Field Type Tests + + [Theory] + [InlineData("rni_name")] + [InlineData("rni_address")] + [InlineData("rni_date")] + [InlineData("string")] + [InlineData("number")] + [InlineData("boolean")] + public void Constructor_AcceptsDifferentFieldTypes_WhenCalled(string fieldType) + { + var fields = new Dictionary + { + { "testField", new RecordSimilarityFieldInfo(fieldType, 1.0, 0.0) } + }; + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + var storedFields = (Dictionary)rs.Params["fields"]; + Assert.Equal(fieldType, storedFields["testField"].Type); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_AddsOptionToOptions_WhenCalled() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new RecordSimilarity(fields, properties, records) + .SetOption("debug", true); + + Assert.True((bool)rs.Options["debug"]); + } + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new RecordSimilarity(fields, properties, records) + .SetOption("debug", true) + .SetOption("explain", "detailed"); + + Assert.True((bool)rs.Options["debug"]); + Assert.Equal("detailed", rs.Options["explain"]); + } + + #endregion + + #region Empty Collection Tests + + [Fact] + public void Constructor_AcceptsEmptyFields_WhenCalled() + { + var fields = new Dictionary(); + var properties = new RecordSimilarityProperties(); + var records = new RecordSimilarityRecords(); + + RecordSimilarity rs = new(fields, properties, records); + + var storedFields = (Dictionary)rs.Params["fields"]; + Assert.Empty(storedFields); + } + + #endregion +} diff --git a/tests/SemanticsVectorTests.cs b/tests/SemanticsVectorTests.cs new file mode 100644 index 0000000..5950bec --- /dev/null +++ b/tests/SemanticsVectorTests.cs @@ -0,0 +1,127 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class SemanticsVectorTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCalledWithText() + { + string text = "Cambridge, Massachusetts"; + SemanticsVector sv = new(text); + + Assert.Equal("semantics/vector", sv.Endpoint); + Assert.Equal(text, sv.Content); + } + + #endregion + + #region Property Configuration Tests + + [Fact] + public void SetLanguage_UpdatesLanguage_WhenCalled() + { + SemanticsVector sv = new SemanticsVector("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", sv.Language); + } + + [Fact] + public void SetGenre_UpdatesGenre_WhenCalled() + { + SemanticsVector sv = new SemanticsVector("Sample text") + .SetGenre("social-media"); + + Assert.Equal("", sv.Genre); + } + + [Fact] + public void SetFileContentType_UpdatesFileContentType_WhenCalled() + { + SemanticsVector sv = new SemanticsVector("Sample text") + .SetFileContentType("text/plain"); + + Assert.Equal("text/plain", sv.FileContentType); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() + { + SemanticsVector sv = new SemanticsVector("New York City") + .SetLanguage("eng") + .SetGenre("news") + .SetOption("vectorType", "embedding"); + + Assert.Equal("eng", sv.Language); + Assert.Equal("", sv.Genre); + Assert.Equal("embedding", sv.Options["vectorType"]); + } + + #endregion + + #region Content Type Tests + + [Fact] + public void Constructor_AcceptsUri_AsContent() + { + Uri uri = new("https://example.com/text"); + SemanticsVector sv = new(uri); + + Assert.Equal(uri, sv.Content); + } + + [Fact] + public void Constructor_AcceptsString_AsContent() + { + string text = "San Francisco, California"; + SemanticsVector sv = new(text); + + Assert.Equal(text, sv.Content); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + SemanticsVector sv = new SemanticsVector("Sample text") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", sv.UrlParameters["output"]); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_AddsOptionToOptions_WhenCalled() + { + SemanticsVector sv = new SemanticsVector("Sample text") + .SetOption("vectorType", "dense"); + + Assert.Equal("dense", sv.Options["vectorType"]); + } + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + SemanticsVector sv = new SemanticsVector("Sample text") + .SetOption("vectorType", "dense") + .SetOption("normalize", true); + + Assert.Equal("dense", sv.Options["vectorType"]); + Assert.True((bool)sv.Options["normalize"]); + } + + #endregion +} diff --git a/tests/SyntaxDependenciesTests.cs b/tests/SyntaxDependenciesTests.cs new file mode 100644 index 0000000..9d19deb --- /dev/null +++ b/tests/SyntaxDependenciesTests.cs @@ -0,0 +1,103 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class SyntaxDependenciesTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCalledWithText() + { + string text = "Yoshinori Ohsumi won the Nobel Prize."; + SyntaxDependencies sd = new(text); + + Assert.Equal("syntax/dependencies", sd.Endpoint); + Assert.Equal(text, sd.Content); + } + + #endregion + + #region Property Configuration Tests + + [Fact] + public void SetLanguage_UpdatesLanguage_WhenCalled() + { + SyntaxDependencies sd = new SyntaxDependencies("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", sd.Language); + } + + [Fact] + public void SetGenre_UpdatesGenre_WhenCalled() + { + SyntaxDependencies sd = new SyntaxDependencies("Sample text") + .SetGenre("social-media"); + + Assert.Equal("", sd.Genre); + } + + [Fact] + public void SetFileContentType_UpdatesFileContentType_WhenCalled() + { + SyntaxDependencies sd = new SyntaxDependencies("Sample text") + .SetFileContentType("text/plain"); + + Assert.Equal("text/plain", sd.FileContentType); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() + { + SyntaxDependencies sd = new SyntaxDependencies("The cat sat on the mat.") + .SetLanguage("eng") + .SetGenre("news") + .SetOption("output", "rosette"); + + Assert.Equal("eng", sd.Language); + Assert.Equal("", sd.Genre); + Assert.Equal("rosette", sd.Options["output"]); + } + + #endregion + + #region Content Type Tests + + [Fact] + public void Constructor_AcceptsUri_AsContent() + { + Uri uri = new("https://example.com/text"); + SyntaxDependencies sd = new(uri); + + Assert.Equal(uri, sd.Content); + } + + [Fact] + public void Constructor_AcceptsString_AsContent() + { + string text = "A complex sentence with multiple dependencies."; + SyntaxDependencies sd = new(text); + + Assert.Equal(text, sd.Content); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + SyntaxDependencies sd = new SyntaxDependencies("Sample text") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", sd.UrlParameters["output"]); + } + + #endregion +} diff --git a/tests/TextEmbeddingTests.cs b/tests/TextEmbeddingTests.cs new file mode 100644 index 0000000..71bc87e --- /dev/null +++ b/tests/TextEmbeddingTests.cs @@ -0,0 +1,149 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class TextEmbeddingTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCalledWithText() + { + string text = "Cambridge, Massachusetts"; + TextEmbedding te = new(text); + + Assert.Equal("text-embedding", te.Endpoint); + Assert.Equal(text, te.Content); + } + + #endregion + + #region Property Configuration Tests + + [Fact] + public void SetLanguage_UpdatesLanguage_WhenCalled() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", te.Language); + } + + [Fact] + public void SetGenre_UpdatesGenre_WhenCalled() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetGenre("social-media"); + + Assert.Equal("", te.Genre); + } + + [Fact] + public void SetFileContentType_UpdatesFileContentType_WhenCalled() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetFileContentType("text/plain"); + + Assert.Equal("text/plain", te.FileContentType); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() + { + TextEmbedding te = new TextEmbedding("Boston, Massachusetts") + .SetLanguage("eng") + .SetGenre("news") + .SetOption("model", "multilingual"); + + Assert.Equal("eng", te.Language); + Assert.Equal("", te.Genre); + Assert.Equal("multilingual", te.Options["model"]); + } + + #endregion + + #region Content Type Tests + + [Fact] + public void Constructor_AcceptsUri_AsContent() + { + Uri uri = new("https://example.com/text"); + TextEmbedding te = new(uri); + + Assert.Equal(uri, te.Content); + } + + [Fact] + public void Constructor_AcceptsString_AsContent() + { + string text = "New York City"; + TextEmbedding te = new(text); + + Assert.Equal(text, te.Content); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", te.UrlParameters["output"]); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_AddsOptionToOptions_WhenCalled() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetOption("model", "english"); + + Assert.Equal("english", te.Options["model"]); + } + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetOption("model", "multilingual") + .SetOption("pooling", "mean"); + + Assert.Equal("multilingual", te.Options["model"]); + Assert.Equal("mean", te.Options["pooling"]); + } + + #endregion + + #region Model-Specific Tests + + [Fact] + public void SetOption_AcceptsMultilingualModel_WhenCalled() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetOption("model", "multilingual"); + + Assert.Equal("multilingual", te.Options["model"]); + } + + [Fact] + public void SetOption_AcceptsEnglishModel_WhenCalled() + { + TextEmbedding te = new TextEmbedding("Sample text") + .SetOption("model", "english"); + + Assert.Equal("english", te.Options["model"]); + } + + #endregion +} diff --git a/tests/TopicsTests.cs b/tests/TopicsTests.cs new file mode 100644 index 0000000..0930211 --- /dev/null +++ b/tests/TopicsTests.cs @@ -0,0 +1,127 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class TopicsTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCalledWithText() + { + string text = "Lily Collins stars in the new Tolkien biopic."; + Topics t = new(text); + + Assert.Equal("topics", t.Endpoint); + Assert.Equal(text, t.Content); + } + + #endregion + + #region Property Configuration Tests + + [Fact] + public void SetLanguage_UpdatesLanguage_WhenCalled() + { + Topics t = new Topics("Sample text") + .SetLanguage("eng"); + + Assert.Equal("eng", t.Language); + } + + [Fact] + public void SetGenre_UpdatesGenre_WhenCalled() + { + Topics t = new Topics("Sample text") + .SetGenre("social-media"); + + Assert.Equal("", t.Genre); + } + + [Fact] + public void SetFileContentType_UpdatesFileContentType_WhenCalled() + { + Topics t = new Topics("Sample text") + .SetFileContentType("text/plain"); + + Assert.Equal("text/plain", t.FileContentType); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() + { + Topics t = new Topics("Article about climate change and renewable energy.") + .SetLanguage("eng") + .SetGenre("news") + .SetOption("maxTopics", 5); + + Assert.Equal("eng", t.Language); + Assert.Equal("", t.Genre); + Assert.Equal(5, t.Options["maxTopics"]); + } + + #endregion + + #region Content Type Tests + + [Fact] + public void Constructor_AcceptsUri_AsContent() + { + Uri uri = new("https://example.com/article"); + Topics t = new(uri); + + Assert.Equal(uri, t.Content); + } + + [Fact] + public void Constructor_AcceptsString_AsContent() + { + string text = "Technology companies are investing in artificial intelligence."; + Topics t = new(text); + + Assert.Equal(text, t.Content); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + Topics t = new Topics("Sample text") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", t.UrlParameters["output"]); + } + + #endregion + + #region Options Tests + + [Fact] + public void SetOption_AddsOptionToOptions_WhenCalled() + { + Topics t = new Topics("Sample text") + .SetOption("maxTopics", 10); + + Assert.Equal(10, t.Options["maxTopics"]); + } + + [Fact] + public void SetOption_SupportsMultipleOptions_WhenCalledMultipleTimes() + { + Topics t = new Topics("Sample text") + .SetOption("maxTopics", 10) + .SetOption("minConfidence", 0.75); + + Assert.Equal(10, t.Options["maxTopics"]); + Assert.Equal(0.75, t.Options["minConfidence"]); + } + + #endregion +} diff --git a/tests/TransliterationTests.cs b/tests/TransliterationTests.cs new file mode 100644 index 0000000..fbf13a8 --- /dev/null +++ b/tests/TransliterationTests.cs @@ -0,0 +1,134 @@ +using Rosette.Api.Client.Endpoints; + +namespace Rosette.Api.Tests; + +public class TransliterationTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_SetsEndpointAndContent_WhenCalledWithText() + { + string text = "ana r2ye7 el gam3a el sa3a 3 el 3asr"; + Transliteration tr = new(text); + + Assert.Equal("transliteration", tr.Endpoint); + Assert.Equal(text, tr.Content); + } + + #endregion + + #region Property Configuration Tests + + [Fact] + public void SetLanguage_UpdatesLanguage_WhenCalled() + { + Transliteration tr = new Transliteration("Sample text") + .SetLanguage("ara"); + + Assert.Equal("ara", tr.Language); + } + + [Fact] + public void SetGenre_UpdatesGenre_WhenCalled() + { + Transliteration tr = new Transliteration("Sample text") + .SetGenre("social-media"); + + Assert.Equal("", tr.Genre); + } + + [Fact] + public void SetFileContentType_UpdatesFileContentType_WhenCalled() + { + Transliteration tr = new Transliteration("Sample text") + .SetFileContentType("text/plain"); + + Assert.Equal("text/plain", tr.FileContentType); + } + + #endregion + + #region Fluent API Tests + + [Fact] + public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() + { + Transliteration tr = new Transliteration("ana r2ye7 el gam3a") + .SetLanguage("ara") + .SetGenre("social-media") + .SetOption("romanization", "native"); + + Assert.Equal("ara", tr.Language); + Assert.Equal("", tr.Genre); + Assert.Equal("native", tr.Options["romanization"]); + } + + #endregion + + #region Language-Specific Tests + + [Fact] + public void SetLanguage_AcceptsArabic_WhenCalled() + { + Transliteration tr = new Transliteration("arabic text") + .SetLanguage("ara"); + + Assert.Equal("ara", tr.Language); + } + + [Fact] + public void SetLanguage_AcceptsJapanese_WhenCalled() + { + Transliteration tr = new Transliteration("japanese text") + .SetLanguage("jpn"); + + Assert.Equal("jpn", tr.Language); + } + + [Fact] + public void SetLanguage_AcceptsChinese_WhenCalled() + { + Transliteration tr = new Transliteration("chinese text") + .SetLanguage("zho"); + + Assert.Equal("zho", tr.Language); + } + + #endregion + + #region Content Type Tests + + [Fact] + public void Constructor_AcceptsUri_AsContent() + { + Uri uri = new("https://example.com/text"); + Transliteration tr = new(uri); + + Assert.Equal(uri, tr.Content); + } + + [Fact] + public void Constructor_AcceptsString_AsContent() + { + string text = "Text to transliterate"; + Transliteration tr = new(text); + + Assert.Equal(text, tr.Content); + } + + #endregion + + #region URL Parameter Tests + + [Fact] + public void SetUrlParameter_AddsParameterToUrlParameters_WhenCalled() + { + Transliteration tr = new Transliteration("Sample text") + .SetUrlParameter("output", "rosette"); + + Assert.Equal("rosette", tr.UrlParameters["output"]); + } + + #endregion +} From 07a5b51e48fb0d9311ac1aed3ed1e739e1cf3701 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Mar 2026 08:38:31 -0400 Subject: [PATCH 3/7] BX-69342: Added unit tests for Utilities.cs --- rosette_api/Utilities.cs | 2 +- tests/UtilitiesTests.cs | 556 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 557 insertions(+), 1 deletion(-) create mode 100644 tests/UtilitiesTests.cs diff --git a/rosette_api/Utilities.cs b/rosette_api/Utilities.cs index ff2adc3..8757d88 100644 --- a/rosette_api/Utilities.cs +++ b/rosette_api/Utilities.cs @@ -25,7 +25,7 @@ public static bool DictionaryEquals(this IDictionary { return false; } - if (!kvp.Value.Equals(secondValue)) + if (!EqualityComparer.Default.Equals(kvp.Value, secondValue)) { return false; } diff --git a/tests/UtilitiesTests.cs b/tests/UtilitiesTests.cs new file mode 100644 index 0000000..cce2c6b --- /dev/null +++ b/tests/UtilitiesTests.cs @@ -0,0 +1,556 @@ +using Rosette.Api.Client; + +namespace Rosette.Api.Tests; + +/// +/// Tests for the Utilities class extension methods +/// +public class UtilitiesTests +{ + #region DictionaryEquals - Null Handling Tests + + [Fact] + public void DictionaryEquals_BothNull_ReturnsTrue() + { + // Arrange + Dictionary? first = null; + Dictionary? second = null; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_FirstNull_ReturnsFalse() + { + // Arrange + Dictionary? first = null; + Dictionary second = new() { { "key", "value" } }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + [Fact] + public void DictionaryEquals_SecondNull_ReturnsFalse() + { + // Arrange + Dictionary first = new() { { "key", "value" } }; + Dictionary? second = null; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + #endregion + + #region DictionaryEquals - Empty Dictionary Tests + + [Fact] + public void DictionaryEquals_BothEmpty_ReturnsTrue() + { + // Arrange + Dictionary first = new(); + Dictionary second = new(); + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_OneEmptyOneNot_ReturnsFalse() + { + // Arrange + Dictionary first = new(); + Dictionary second = new() { { "key", "value" } }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + #endregion + + #region DictionaryEquals - Count Tests + + [Fact] + public void DictionaryEquals_DifferentCounts_ReturnsFalse() + { + // Arrange + Dictionary first = new() + { + { "key1", "value1" } + }; + Dictionary second = new() + { + { "key1", "value1" }, + { "key2", "value2" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + #endregion + + #region DictionaryEquals - Content Comparison Tests + + [Fact] + public void DictionaryEquals_SameContent_ReturnsTrue() + { + // Arrange + Dictionary first = new() + { + { "name", "John" }, + { "age", "30" }, + { "city", "Boston" } + }; + Dictionary second = new() + { + { "name", "John" }, + { "age", "30" }, + { "city", "Boston" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_SameContentDifferentOrder_ReturnsTrue() + { + // Arrange + Dictionary first = new() + { + { "name", "John" }, + { "age", "30" }, + { "city", "Boston" } + }; + Dictionary second = new() + { + { "city", "Boston" }, + { "name", "John" }, + { "age", "30" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_DifferentKeys_ReturnsFalse() + { + // Arrange + Dictionary first = new() + { + { "key1", "value1" }, + { "key2", "value2" } + }; + Dictionary second = new() + { + { "key1", "value1" }, + { "key3", "value2" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + [Fact] + public void DictionaryEquals_DifferentValues_ReturnsFalse() + { + // Arrange + Dictionary first = new() + { + { "key1", "value1" }, + { "key2", "value2" } + }; + Dictionary second = new() + { + { "key1", "value1" }, + { "key2", "different" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + [Fact] + public void DictionaryEquals_KeyNotInSecond_ReturnsFalse() + { + // Arrange + Dictionary first = new() + { + { "key1", "value1" }, + { "uniqueKey", "value2" } + }; + Dictionary second = new() + { + { "key1", "value1" }, + { "key2", "value2" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + #endregion + + #region DictionaryEquals - Generic Type Tests + + [Fact] + public void DictionaryEquals_IntKeys_WorksCorrectly() + { + // Arrange + Dictionary first = new() + { + { 1, "one" }, + { 2, "two" }, + { 3, "three" } + }; + Dictionary second = new() + { + { 1, "one" }, + { 2, "two" }, + { 3, "three" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_IntValues_WorksCorrectly() + { + // Arrange + Dictionary first = new() + { + { "one", 1 }, + { "two", 2 }, + { "three", 3 } + }; + Dictionary second = new() + { + { "one", 1 }, + { "two", 2 }, + { "three", 3 } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_IntValuesDifferent_ReturnsFalse() + { + // Arrange + Dictionary first = new() + { + { "one", 1 }, + { "two", 2 } + }; + Dictionary second = new() + { + { "one", 1 }, + { "two", 999 } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + [Fact] + public void DictionaryEquals_DoubleValues_WorksCorrectly() + { + // Arrange + Dictionary first = new() + { + { "pi", 3.14159 }, + { "e", 2.71828 } + }; + Dictionary second = new() + { + { "pi", 3.14159 }, + { "e", 2.71828 } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_BooleanValues_WorksCorrectly() + { + // Arrange + Dictionary first = new() + { + { "isActive", true }, + { "isDeleted", false } + }; + Dictionary second = new() + { + { "isActive", true }, + { "isDeleted", false } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + #endregion + + #region DictionaryEquals - Complex Object Tests + + [Fact] + public void DictionaryEquals_ObjectValues_UsesObjectEquals() + { + // Arrange + var obj1 = new TestObject { Id = 1, Name = "Test" }; + var obj2 = new TestObject { Id = 1, Name = "Test" }; + + Dictionary first = new() + { + { "key1", obj1 } + }; + Dictionary second = new() + { + { "key1", obj2 } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_ObjectValuesDifferent_ReturnsFalse() + { + // Arrange + var obj1 = new TestObject { Id = 1, Name = "Test" }; + var obj2 = new TestObject { Id = 2, Name = "Different" }; + + Dictionary first = new() + { + { "key1", obj1 } + }; + Dictionary second = new() + { + { "key1", obj2 } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + #endregion + + #region DictionaryEquals - Special Cases + + [Fact] + public void DictionaryEquals_EmptyStringKeys_WorksCorrectly() + { + // Arrange + Dictionary first = new() + { + { "", "empty key value" }, + { "normal", "value" } + }; + Dictionary second = new() + { + { "", "empty key value" }, + { "normal", "value" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_EmptyStringValues_WorksCorrectly() + { + // Arrange + Dictionary first = new() + { + { "key1", "" }, + { "key2", "value" } + }; + Dictionary second = new() + { + { "key1", "" }, + { "key2", "value" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_NullStringValue_WorksCorrectly() + { + // Arrange + Dictionary first = new() + { + { "key1", null }, + { "key2", "value" } + }; + Dictionary second = new() + { + { "key1", null }, + { "key2", "value" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_OneNullOneEmptyString_ReturnsFalse() + { + // Arrange + Dictionary first = new() + { + { "key1", null } + }; + Dictionary second = new() + { + { "key1", "" } + }; + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + [Fact] + public void DictionaryEquals_LargeDictionaries_WorksCorrectly() + { + // Arrange + Dictionary first = new(); + Dictionary second = new(); + + for (int i = 0; i < 1000; i++) + { + first[$"key{i}"] = i; + second[$"key{i}"] = i; + } + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.True(result); + } + + [Fact] + public void DictionaryEquals_LargeDictionariesOneDifferent_ReturnsFalse() + { + // Arrange + Dictionary first = new(); + Dictionary second = new(); + + for (int i = 0; i < 1000; i++) + { + first[$"key{i}"] = i; + second[$"key{i}"] = i; + } + second["key500"] = 999; // Change one value + + // Act + bool result = first.DictionaryEquals(second); + + // Assert + Assert.False(result); + } + + #endregion + + #region Helper Classes + + /// + /// Test object with custom Equals implementation + /// + private class TestObject + { + public int Id { get; set; } + public string Name { get; set; } = string.Empty; + + public override bool Equals(object? obj) + { + if (obj is TestObject other) + { + return Id == other.Id && Name == other.Name; + } + return false; + } + + public override int GetHashCode() + { + return HashCode.Combine(Id, Name); + } + } + + #endregion +} From ff4ac42e9586f2e5dd79007f5966352919e4c4c5 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Mar 2026 10:59:04 -0400 Subject: [PATCH 4/7] BX-69342: Adds tests for the *Record classes --- tests/BooleanRecordTests.cs | 255 +++++++++++++ tests/FieldedAddressRecordTests.cs | 516 +++++++++++++++++++++++++++ tests/FieldedDateRecordTests.cs | 295 +++++++++++++++ tests/FieldedNameRecordTests.cs | 374 +++++++++++++++++++ tests/NumberRecordTests.cs | 367 +++++++++++++++++++ tests/StringRecordTests.cs | 281 +++++++++++++++ tests/UnfieldedAddressRecordTests.cs | 222 ++++++++++++ tests/UnfieldedDateRecordTests.cs | 213 +++++++++++ tests/UnfieldedNameRecordTests.cs | 202 +++++++++++ tests/UnknownFieldRecordTests.cs | 418 ++++++++++++++++++++++ 10 files changed, 3143 insertions(+) create mode 100644 tests/BooleanRecordTests.cs create mode 100644 tests/FieldedAddressRecordTests.cs create mode 100644 tests/FieldedDateRecordTests.cs create mode 100644 tests/FieldedNameRecordTests.cs create mode 100644 tests/NumberRecordTests.cs create mode 100644 tests/StringRecordTests.cs create mode 100644 tests/UnfieldedAddressRecordTests.cs create mode 100644 tests/UnfieldedDateRecordTests.cs create mode 100644 tests/UnfieldedNameRecordTests.cs create mode 100644 tests/UnknownFieldRecordTests.cs diff --git a/tests/BooleanRecordTests.cs b/tests/BooleanRecordTests.cs new file mode 100644 index 0000000..0b337a7 --- /dev/null +++ b/tests/BooleanRecordTests.cs @@ -0,0 +1,255 @@ +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +/// +/// Tests for BooleanRecord class +/// +public class BooleanRecordTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_CreatesFalseRecord() + { + // Act + var record = new BooleanRecord(); + + // Assert + Assert.False(record.Boolean); + } + + [Fact] + public void Constructor_WithTrue_SetsBooleanToTrue() + { + // Act + var record = new BooleanRecord(true); + + // Assert + Assert.True(record.Boolean); + } + + [Fact] + public void Constructor_WithFalse_SetsBooleanToFalse() + { + // Act + var record = new BooleanRecord(false); + + // Assert + Assert.False(record.Boolean); + } + + #endregion + + #region Property Tests + + [Fact] + public void Boolean_CanBeSetToTrue() + { + // Arrange + var record = new BooleanRecord(); + + // Act + record.Boolean = true; + + // Assert + Assert.True(record.Boolean); + } + + [Fact] + public void Boolean_CanBeSetToFalse() + { + // Arrange + var record = new BooleanRecord(true); + + // Act + record.Boolean = false; + + // Assert + Assert.False(record.Boolean); + } + + [Fact] + public void Boolean_CanBeToggledMultipleTimes() + { + // Arrange + var record = new BooleanRecord(); + + // Act & Assert + record.Boolean = true; + Assert.True(record.Boolean); + + record.Boolean = false; + Assert.False(record.Boolean); + + record.Boolean = true; + Assert.True(record.Boolean); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_BothTrue_ReturnsTrue() + { + // Arrange + var record1 = new BooleanRecord(true); + var record2 = new BooleanRecord(true); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_BothFalse_ReturnsTrue() + { + // Arrange + var record1 = new BooleanRecord(false); + var record2 = new BooleanRecord(false); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_OneTrueOneFalse_ReturnsFalse() + { + // Arrange + var record1 = new BooleanRecord(true); + var record2 = new BooleanRecord(false); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new BooleanRecord(true); + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new BooleanRecord(true); + var differentType = true; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var record = new BooleanRecord(true); + + // Act & Assert + Assert.True(record.Equals(record)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_BothTrue_ReturnsSameHashCode() + { + // Arrange + var record1 = new BooleanRecord(true); + var record2 = new BooleanRecord(true); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_BothFalse_ReturnsSameHashCode() + { + // Arrange + var record1 = new BooleanRecord(false); + var record2 = new BooleanRecord(false); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_TrueAndFalse_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new BooleanRecord(true); + var record2 = new BooleanRecord(false); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_True_ReturnsLowercaseTrue() + { + // Arrange + var record = new BooleanRecord(true); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("true", result); + } + + [Fact] + public void ToString_False_ReturnsLowercaseFalse() + { + // Arrange + var record = new BooleanRecord(false); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("false", result); + } + + [Fact] + public void ToString_ReturnsLowercaseFormat() + { + // Arrange + var trueRecord = new BooleanRecord(true); + var falseRecord = new BooleanRecord(false); + + // Act + string trueResult = trueRecord.ToString(); + string falseResult = falseRecord.ToString(); + + // Assert - Verify lowercase (not "True" or "False") + Assert.Equal("true", trueResult); + Assert.Equal("false", falseResult); + Assert.NotEqual("True", trueResult); + Assert.NotEqual("False", falseResult); + } + + #endregion +} diff --git a/tests/FieldedAddressRecordTests.cs b/tests/FieldedAddressRecordTests.cs new file mode 100644 index 0000000..1665627 --- /dev/null +++ b/tests/FieldedAddressRecordTests.cs @@ -0,0 +1,516 @@ +using Rosette.Api.Client.Models; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for FieldedAddressRecord class +/// +public class FieldedAddressRecordTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_CreatesEmptyRecord() + { + // Act + var record = new FieldedAddressRecord(); + + // Assert + Assert.Null(record.House); + Assert.Null(record.HouseNumber); + Assert.Null(record.Road); + Assert.Null(record.City); + Assert.Null(record.State); + Assert.Null(record.Country); + Assert.Null(record.Postcode); + } + + [Fact] + public void Constructor_WithAllParameters_SetsAllProperties() + { + // Act + var record = new FieldedAddressRecord( + house: "White House", + houseNumber: "1600", + road: "Pennsylvania Avenue NW", + city: "Washington", + state: "DC", + country: "USA", + postcode: "20500" + ); + + // Assert + Assert.Equal("White House", record.House); + Assert.Equal("1600", record.HouseNumber); + Assert.Equal("Pennsylvania Avenue NW", record.Road); + Assert.Equal("Washington", record.City); + Assert.Equal("DC", record.State); + Assert.Equal("USA", record.Country); + Assert.Equal("20500", record.Postcode); + } + + [Fact] + public void Constructor_WithSubsetOfParameters_SetsSpecifiedProperties() + { + // Act + var record = new FieldedAddressRecord( + houseNumber: "123", + road: "Main Street", + city: "Springfield" + ); + + // Assert + Assert.Equal("123", record.HouseNumber); + Assert.Equal("Main Street", record.Road); + Assert.Equal("Springfield", record.City); + Assert.Null(record.House); + Assert.Null(record.State); + Assert.Null(record.Country); + } + + #endregion + + #region Property Tests - Basic Fields + + [Fact] + public void House_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.House = "Empire State Building"; + + // Assert + Assert.Equal("Empire State Building", record.House); + } + + [Fact] + public void HouseNumber_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.HouseNumber = "350"; + + // Assert + Assert.Equal("350", record.HouseNumber); + } + + [Fact] + public void Road_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Road = "5th Avenue"; + + // Assert + Assert.Equal("5th Avenue", record.Road); + } + + [Fact] + public void City_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.City = "New York"; + + // Assert + Assert.Equal("New York", record.City); + } + + [Fact] + public void State_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.State = "NY"; + + // Assert + Assert.Equal("NY", record.State); + } + + [Fact] + public void Country_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Country = "United States"; + + // Assert + Assert.Equal("United States", record.Country); + } + + [Fact] + public void Postcode_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Postcode = "10118"; + + // Assert + Assert.Equal("10118", record.Postcode); + } + + #endregion + + #region Property Tests - Extended Fields + + [Fact] + public void Unit_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Unit = "Apt 3B"; + + // Assert + Assert.Equal("Apt 3B", record.Unit); + } + + [Fact] + public void Level_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Level = "Floor 5"; + + // Assert + Assert.Equal("Floor 5", record.Level); + } + + [Fact] + public void Staircase_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Staircase = "Staircase A"; + + // Assert + Assert.Equal("Staircase A", record.Staircase); + } + + [Fact] + public void Entrance_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Entrance = "North Entrance"; + + // Assert + Assert.Equal("North Entrance", record.Entrance); + } + + [Fact] + public void Suburb_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Suburb = "Brooklyn"; + + // Assert + Assert.Equal("Brooklyn", record.Suburb); + } + + [Fact] + public void CityDistrict_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.CityDistrict = "Manhattan"; + + // Assert + Assert.Equal("Manhattan", record.CityDistrict); + } + + [Fact] + public void Island_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.Island = "Long Island"; + + // Assert + Assert.Equal("Long Island", record.Island); + } + + [Fact] + public void StateDistrict_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.StateDistrict = "Southern District"; + + // Assert + Assert.Equal("Southern District", record.StateDistrict); + } + + [Fact] + public void CountryRegion_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.CountryRegion = "New England"; + + // Assert + Assert.Equal("New England", record.CountryRegion); + } + + [Fact] + public void WorldRegion_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.WorldRegion = "North America"; + + // Assert + Assert.Equal("North America", record.WorldRegion); + } + + [Fact] + public void PoBox_CanBeSetAndRetrieved() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + record.PoBox = "PO Box 123"; + + // Assert + Assert.Equal("PO Box 123", record.PoBox); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameProperties_ReturnsTrue() + { + // Arrange + var record1 = new FieldedAddressRecord( + houseNumber: "123", + road: "Main St", + city: "Springfield", + state: "IL", + country: "USA", + postcode: "62701" + ); + var record2 = new FieldedAddressRecord( + houseNumber: "123", + road: "Main St", + city: "Springfield", + state: "IL", + country: "USA", + postcode: "62701" + ); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_AllNullProperties_ReturnsTrue() + { + // Arrange + var record1 = new FieldedAddressRecord(); + var record2 = new FieldedAddressRecord(); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentHouseNumber_ReturnsFalse() + { + // Arrange + var record1 = new FieldedAddressRecord(houseNumber: "123"); + var record2 = new FieldedAddressRecord(houseNumber: "456"); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentCity_ReturnsFalse() + { + // Arrange + var record1 = new FieldedAddressRecord(city: "Springfield"); + var record2 = new FieldedAddressRecord(city: "Boston"); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_OnePropertySetOneNull_ReturnsFalse() + { + // Arrange + var record1 = new FieldedAddressRecord(city: "Springfield"); + var record2 = new FieldedAddressRecord(); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new FieldedAddressRecord(); + var differentType = "address"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameProperties_ReturnsSameHashCode() + { + // Arrange + var record1 = new FieldedAddressRecord( + houseNumber: "123", + road: "Main St", + city: "Springfield" + ); + var record2 = new FieldedAddressRecord( + houseNumber: "123", + road: "Main St", + city: "Springfield" + ); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentProperties_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new FieldedAddressRecord(city: "Springfield"); + var record2 = new FieldedAddressRecord(city: "Boston"); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsValidJson() + { + // Arrange + var record = new FieldedAddressRecord( + houseNumber: "123", + road: "Main St", + city: "Springfield" + ); + + // Act + string json = record.ToString(); + + // Assert + Assert.NotEmpty(json); + Assert.Contains("123", json); + Assert.Contains("Main St", json); + Assert.Contains("Springfield", json); + } + + [Fact] + public void ToString_CanBeDeserialized() + { + // Arrange + var original = new FieldedAddressRecord( + houseNumber: "123", + road: "Main St", + city: "Springfield", + state: "IL", + country: "USA", + postcode: "62701" + ); + + // Act + string json = original.ToString(); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + Assert.Equal(original.HouseNumber, deserialized.HouseNumber); + Assert.Equal(original.Road, deserialized.Road); + Assert.Equal(original.City, deserialized.City); + Assert.Equal(original.State, deserialized.State); + Assert.Equal(original.Country, deserialized.Country); + Assert.Equal(original.Postcode, deserialized.Postcode); + } + + [Fact] + public void ToString_EmptyRecord_ProducesValidJson() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + string json = record.ToString(); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + } + + #endregion +} diff --git a/tests/FieldedDateRecordTests.cs b/tests/FieldedDateRecordTests.cs new file mode 100644 index 0000000..dcaf4dc --- /dev/null +++ b/tests/FieldedDateRecordTests.cs @@ -0,0 +1,295 @@ +using Rosette.Api.Client.Models; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for FieldedDateRecord class +/// +public class FieldedDateRecordTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_CreatesRecordWithEmptyDate() + { + // Act + var record = new FieldedDateRecord { Date = string.Empty }; + + // Assert + Assert.Equal(string.Empty, record.Date); + Assert.Null(record.Format); + } + + [Fact] + public void Constructor_WithDateOnly_SetsDateProperty() + { + // Arrange + const string date = "2024-03-10"; + + // Act + var record = new FieldedDateRecord { Date = date }; + + // Assert + Assert.Equal(date, record.Date); + Assert.Null(record.Format); + } + + [Fact] + public void Constructor_WithDateAndFormat_SetsBothProperties() + { + // Arrange + const string date = "03/10/2024"; + const string format = "MM/dd/yyyy"; + + // Act + var record = new FieldedDateRecord + { + Date = date, + Format = format + }; + + // Assert + Assert.Equal(date, record.Date); + Assert.Equal(format, record.Format); + } + + #endregion + + #region Property Tests + + [Fact] + public void Date_CanBeSet_WithValidValue() + { + // Arrange + var record = new FieldedDateRecord { Date = "2024-01-01" }; + + // Act + record.Date = "2024-12-31"; + + // Assert + Assert.Equal("2024-12-31", record.Date); + } + + [Fact] + public void Format_CanBeSet_WithValidValue() + { + // Arrange + var record = new FieldedDateRecord { Date = "2024-03-10" }; + + // Act + record.Format = "yyyy-MM-dd"; + + // Assert + Assert.Equal("yyyy-MM-dd", record.Format); + } + + [Fact] + public void Format_AcceptsJavaDateTimeFormatterPatterns() + { + // Arrange + var formats = new[] + { + "yyyy-MM-dd", + "MM/dd/yyyy", + "dd.MM.yyyy", + "yyyy/MM/dd", + "yyyyMMdd", + "dd-MMM-yyyy" + }; + + // Act & Assert + foreach (var format in formats) + { + var record = new FieldedDateRecord { Date = "2024-03-10", Format = format }; + Assert.Equal(format, record.Format); + } + } + + [Fact] + public void Date_AcceptsDifferentDateFormats() + { + // Arrange + var dates = new[] + { + "2024-03-10", + "03/10/2024", + "March 10, 2024", + "10-Mar-2024", + "20240310" + }; + + // Act & Assert + foreach (var date in dates) + { + var record = new FieldedDateRecord { Date = date }; + Assert.Equal(date, record.Date); + } + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameDateAndFormat_ReturnsTrue() + { + // Arrange + var record1 = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + var record2 = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_SameDateNoFormat_ReturnsTrue() + { + // Arrange + var record1 = new FieldedDateRecord { Date = "2024-03-10" }; + var record2 = new FieldedDateRecord { Date = "2024-03-10" }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentDate_ReturnsFalse() + { + // Arrange + var record1 = new FieldedDateRecord { Date = "2024-03-10" }; + var record2 = new FieldedDateRecord { Date = "2024-03-11" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentFormat_ReturnsFalse() + { + // Arrange + var record1 = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + var record2 = new FieldedDateRecord { Date = "2024-03-10", Format = "MM/dd/yyyy" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_OneWithFormatOneWithout_ReturnsFalse() + { + // Arrange + var record1 = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + var record2 = new FieldedDateRecord { Date = "2024-03-10" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new FieldedDateRecord { Date = "2024-03-10" }; + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new FieldedDateRecord { Date = "2024-03-10" }; + var differentType = "2024-03-10"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameProperties_ReturnsSameHashCode() + { + // Arrange + var record1 = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + var record2 = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentProperties_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new FieldedDateRecord { Date = "2024-03-10" }; + var record2 = new FieldedDateRecord { Date = "2024-03-11" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsValidJson() + { + // Arrange + var record = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + + // Act + string json = record.ToString(); + + // Assert + Assert.NotEmpty(json); + Assert.Contains("\"date\"", json); + Assert.Contains("2024-03-10", json); + } + + [Fact] + public void ToString_CanBeDeserialized() + { + // Arrange + var original = new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" }; + + // Act + string json = original.ToString(); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + Assert.Equal(original.Date, deserialized.Date); + Assert.Equal(original.Format, deserialized.Format); + } + + [Fact] + public void ToString_NullFormat_ProducesValidJson() + { + // Arrange + var record = new FieldedDateRecord { Date = "2024-03-10" }; + + // Act + string json = record.ToString(); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + Assert.Equal("2024-03-10", deserialized.Date); + } + + #endregion +} diff --git a/tests/FieldedNameRecordTests.cs b/tests/FieldedNameRecordTests.cs new file mode 100644 index 0000000..50463c9 --- /dev/null +++ b/tests/FieldedNameRecordTests.cs @@ -0,0 +1,374 @@ +using Rosette.Api.Client.Models; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for FieldedNameRecord class +/// +public class FieldedNameRecordTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_CreatesRecordWithEmptyText() + { + // Act + var record = new FieldedNameRecord { Text = string.Empty }; + + // Assert + Assert.Equal(string.Empty, record.Text); + Assert.Null(record.Language); + Assert.Null(record.LanguageOfOrigin); + Assert.Null(record.Script); + Assert.Null(record.EntityType); + } + + [Fact] + public void Constructor_WithTextOnly_SetsTextProperty() + { + // Arrange + const string text = "John Smith"; + + // Act + var record = new FieldedNameRecord { Text = text }; + + // Assert + Assert.Equal(text, record.Text); + Assert.Null(record.Language); + Assert.Null(record.LanguageOfOrigin); + Assert.Null(record.Script); + Assert.Null(record.EntityType); + } + + [Fact] + public void Constructor_WithAllParameters_SetsAllProperties() + { + // Arrange + const string text = "John Smith"; + const string language = "eng"; + const string languageOfOrigin = "eng"; + const string script = "Latn"; + const string entityType = "PERSON"; + + // Act + var record = new FieldedNameRecord + { + Text = text, + Language = language, + LanguageOfOrigin = languageOfOrigin, + Script = script, + EntityType = entityType + }; + + // Assert + Assert.Equal(text, record.Text); + Assert.Equal(language, record.Language); + Assert.Equal(languageOfOrigin, record.LanguageOfOrigin); + Assert.Equal(script, record.Script); + Assert.Equal(entityType, record.EntityType); + } + + #endregion + + #region Property Tests + + [Fact] + public void Properties_CanBeSet_Individually() + { + // Arrange + var record = new FieldedNameRecord { Text = "Test" }; + + // Act + record.Language = "eng"; + record.LanguageOfOrigin = "spa"; + record.Script = "Latn"; + record.EntityType = "PERSON"; + + // Assert + Assert.Equal("eng", record.Language); + Assert.Equal("spa", record.LanguageOfOrigin); + Assert.Equal("Latn", record.Script); + Assert.Equal("PERSON", record.EntityType); + } + + [Fact] + public void EntityType_AcceptsPersonType() + { + // Arrange & Act + var record = new FieldedNameRecord { Text = "John Smith", EntityType = "PERSON" }; + + // Assert + Assert.Equal("PERSON", record.EntityType); + } + + [Fact] + public void EntityType_AcceptsLocationType() + { + // Arrange & Act + var record = new FieldedNameRecord { Text = "New York", EntityType = "LOCATION" }; + + // Assert + Assert.Equal("LOCATION", record.EntityType); + } + + [Fact] + public void EntityType_AcceptsOrganizationType() + { + // Arrange & Act + var record = new FieldedNameRecord { Text = "Microsoft", EntityType = "ORGANIZATION" }; + + // Assert + Assert.Equal("ORGANIZATION", record.EntityType); + } + + [Fact] + public void Script_AcceptsISO15924Codes() + { + // Arrange + var scripts = new[] { "Latn", "Cyrl", "Arab", "Hans", "Hant", "Jpan", "Kore" }; + + // Act & Assert + foreach (var script in scripts) + { + var record = new FieldedNameRecord { Text = "Test", Script = script }; + Assert.Equal(script, record.Script); + } + } + + [Fact] + public void Language_AcceptsISO6393Codes() + { + // Arrange + var languages = new[] { "eng", "spa", "fra", "deu", "jpn", "cmn", "ara" }; + + // Act & Assert + foreach (var language in languages) + { + var record = new FieldedNameRecord { Text = "Test", Language = language }; + Assert.Equal(language, record.Language); + } + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameProperties_ReturnsTrue() + { + // Arrange + var record1 = new FieldedNameRecord + { + Text = "John Smith", + Language = "eng", + LanguageOfOrigin = "eng", + Script = "Latn", + EntityType = "PERSON" + }; + var record2 = new FieldedNameRecord + { + Text = "John Smith", + Language = "eng", + LanguageOfOrigin = "eng", + Script = "Latn", + EntityType = "PERSON" + }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentText_ReturnsFalse() + { + // Arrange + var record1 = new FieldedNameRecord { Text = "John Smith" }; + var record2 = new FieldedNameRecord { Text = "Jane Doe" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentLanguage_ReturnsFalse() + { + // Arrange + var record1 = new FieldedNameRecord { Text = "Test", Language = "eng" }; + var record2 = new FieldedNameRecord { Text = "Test", Language = "spa" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentLanguageOfOrigin_ReturnsFalse() + { + // Arrange + var record1 = new FieldedNameRecord { Text = "Test", LanguageOfOrigin = "eng" }; + var record2 = new FieldedNameRecord { Text = "Test", LanguageOfOrigin = "spa" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentScript_ReturnsFalse() + { + // Arrange + var record1 = new FieldedNameRecord { Text = "Test", Script = "Latn" }; + var record2 = new FieldedNameRecord { Text = "Test", Script = "Cyrl" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentEntityType_ReturnsFalse() + { + // Arrange + var record1 = new FieldedNameRecord { Text = "Test", EntityType = "PERSON" }; + var record2 = new FieldedNameRecord { Text = "Test", EntityType = "LOCATION" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new FieldedNameRecord { Text = "Test" }; + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new FieldedNameRecord { Text = "Test" }; + var differentType = "Test"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameProperties_ReturnsSameHashCode() + { + // Arrange + var record1 = new FieldedNameRecord + { + Text = "John Smith", + Language = "eng", + LanguageOfOrigin = "eng", + Script = "Latn", + EntityType = "PERSON" + }; + var record2 = new FieldedNameRecord + { + Text = "John Smith", + Language = "eng", + LanguageOfOrigin = "eng", + Script = "Latn", + EntityType = "PERSON" + }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentProperties_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new FieldedNameRecord { Text = "John Smith" }; + var record2 = new FieldedNameRecord { Text = "Jane Doe" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsValidJson() + { + // Arrange + var record = new FieldedNameRecord + { + Text = "John Smith", + Language = "eng", + LanguageOfOrigin = "eng", + Script = "Latn", + EntityType = "PERSON" + }; + + // Act + string json = record.ToString(); + + // Assert + Assert.NotEmpty(json); + Assert.Contains("\"text\"", json); + Assert.Contains("John Smith", json); + } + + [Fact] + public void ToString_CanBeDeserialized() + { + // Arrange + var original = new FieldedNameRecord + { + Text = "John Smith", + Language = "eng", + LanguageOfOrigin = "eng", + Script = "Latn", + EntityType = "PERSON" + }; + + // Act + string json = original.ToString(); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + Assert.Equal(original.Text, deserialized.Text); + Assert.Equal(original.Language, deserialized.Language); + Assert.Equal(original.LanguageOfOrigin, deserialized.LanguageOfOrigin); + Assert.Equal(original.Script, deserialized.Script); + Assert.Equal(original.EntityType, deserialized.EntityType); + } + + [Fact] + public void ToString_NullOptionalFields_ProducesValidJson() + { + // Arrange + var record = new FieldedNameRecord { Text = "Test" }; + + // Act + string json = record.ToString(); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + Assert.Equal("Test", deserialized.Text); + } + + #endregion +} diff --git a/tests/NumberRecordTests.cs b/tests/NumberRecordTests.cs new file mode 100644 index 0000000..2809223 --- /dev/null +++ b/tests/NumberRecordTests.cs @@ -0,0 +1,367 @@ +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +/// +/// Tests for NumberRecord class +/// +public class NumberRecordTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_CreatesZeroRecord() + { + // Act + var record = new NumberRecord(); + + // Assert + Assert.Equal(0.0, record.Number); + } + + [Fact] + public void Constructor_WithPositiveNumber_SetsNumber() + { + // Act + var record = new NumberRecord(42.5); + + // Assert + Assert.Equal(42.5, record.Number); + } + + [Fact] + public void Constructor_WithNegativeNumber_SetsNumber() + { + // Act + var record = new NumberRecord(-17.3); + + // Assert + Assert.Equal(-17.3, record.Number); + } + + [Fact] + public void Constructor_WithZero_SetsNumber() + { + // Act + var record = new NumberRecord(0.0); + + // Assert + Assert.Equal(0.0, record.Number); + } + + [Fact] + public void Constructor_WithInteger_SetsNumber() + { + // Act + var record = new NumberRecord(100); + + // Assert + Assert.Equal(100.0, record.Number); + } + + #endregion + + #region Property Tests + + [Fact] + public void Number_CanBeSet_ToPositiveValue() + { + // Arrange + var record = new NumberRecord(); + + // Act + record.Number = 123.456; + + // Assert + Assert.Equal(123.456, record.Number); + } + + [Fact] + public void Number_CanBeSet_ToNegativeValue() + { + // Arrange + var record = new NumberRecord(); + + // Act + record.Number = -999.99; + + // Assert + Assert.Equal(-999.99, record.Number); + } + + [Fact] + public void Number_HandlesVeryLargeNumbers() + { + // Arrange & Act + var record = new NumberRecord(double.MaxValue); + + // Assert + Assert.Equal(double.MaxValue, record.Number); + } + + [Fact] + public void Number_HandlesVerySmallNumbers() + { + // Arrange & Act + var record = new NumberRecord(double.MinValue); + + // Assert + Assert.Equal(double.MinValue, record.Number); + } + + [Fact] + public void Number_HandlesInfinity() + { + // Arrange & Act + var positiveInfinity = new NumberRecord(double.PositiveInfinity); + var negativeInfinity = new NumberRecord(double.NegativeInfinity); + + // Assert + Assert.Equal(double.PositiveInfinity, positiveInfinity.Number); + Assert.Equal(double.NegativeInfinity, negativeInfinity.Number); + } + + [Fact] + public void Number_HandlesNaN() + { + // Arrange & Act + var record = new NumberRecord(double.NaN); + + // Assert + Assert.True(double.IsNaN(record.Number)); + } + + [Fact] + public void Number_HandlesScientificNotation() + { + // Arrange & Act + var record = new NumberRecord(1.23e10); + + // Assert + Assert.Equal(1.23e10, record.Number); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameNumber_ReturnsTrue() + { + // Arrange + var record1 = new NumberRecord(42.5); + var record2 = new NumberRecord(42.5); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentNumber_ReturnsFalse() + { + // Arrange + var record1 = new NumberRecord(42.5); + var record2 = new NumberRecord(43.5); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_BothZero_ReturnsTrue() + { + // Arrange + var record1 = new NumberRecord(0.0); + var record2 = new NumberRecord(0.0); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_NegativeZeroAndPositiveZero_ReturnsTrue() + { + // Arrange + var record1 = new NumberRecord(-0.0); + var record2 = new NumberRecord(0.0); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_BothNaN_ReturnsTrue() + { + // Arrange + var record1 = new NumberRecord(double.NaN); + var record2 = new NumberRecord(double.NaN); + + // Act & Assert + // Note: NaN == NaN is false in IEEE 754, but our Equals uses == operator + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new NumberRecord(42.5); + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new NumberRecord(42.5); + var differentType = 42.5; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var record = new NumberRecord(42.5); + + // Act & Assert + Assert.True(record.Equals(record)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameNumber_ReturnsSameHashCode() + { + // Arrange + var record1 = new NumberRecord(42.5); + var record2 = new NumberRecord(42.5); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentNumber_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new NumberRecord(42.5); + var record2 = new NumberRecord(43.5); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_ZeroValue_ReturnsConsistentHashCode() + { + // Arrange + var record1 = new NumberRecord(0.0); + var record2 = new NumberRecord(0.0); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_PositiveNumber_ReturnsNumberAsString() + { + // Arrange + var record = new NumberRecord(42.5); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("42.5", result); + } + + [Fact] + public void ToString_NegativeNumber_ReturnsNumberAsString() + { + // Arrange + var record = new NumberRecord(-17.3); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("-17.3", result); + } + + [Fact] + public void ToString_Zero_ReturnsZero() + { + // Arrange + var record = new NumberRecord(0.0); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("0", result); + } + + [Fact] + public void ToString_Integer_ReturnsInteger() + { + // Arrange + var record = new NumberRecord(100); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("100", result); + } + + [Fact] + public void ToString_ScientificNotation_ReturnsScientificNotation() + { + // Arrange + var record = new NumberRecord(1.23e10); + + // Act + string result = record.ToString(); + + // Assert + Assert.Contains("12300000000", result); + } + + [Fact] + public void ToString_NaN_ReturnsNaN() + { + // Arrange + var record = new NumberRecord(double.NaN); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("NaN", result); + } + + #endregion +} diff --git a/tests/StringRecordTests.cs b/tests/StringRecordTests.cs new file mode 100644 index 0000000..f070b8e --- /dev/null +++ b/tests/StringRecordTests.cs @@ -0,0 +1,281 @@ +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +/// +/// Tests for StringRecord class +/// +public class StringRecordTests +{ + #region Property Tests + + [Fact] + public void Text_CanBeSetAndRetrieved() + { + // Arrange & Act + var record = new StringRecord { Text = "Hello World" }; + + // Assert + Assert.Equal("Hello World", record.Text); + } + + [Fact] + public void Text_CanBeUpdated() + { + // Arrange + var record = new StringRecord { Text = "Initial" }; + + // Act + record.Text = "New Value"; + + // Assert + Assert.Equal("New Value", record.Text); + } + + [Fact] + public void Text_HandlesLongStrings() + { + // Arrange + var longString = new string('A', 10000); + + // Act + var record = new StringRecord { Text = longString }; + + // Assert + Assert.Equal(longString, record.Text); + Assert.Equal(10000, record.Text.Length); + } + + [Fact] + public void Text_HandlesSpecialCharacters() + { + // Arrange & Act + var record = new StringRecord { Text = "Special: !@#$%^&*()_+-={}[]|\\:;\"'<>,.?/~`" }; + + // Assert + Assert.Equal("Special: !@#$%^&*()_+-={}[]|\\:;\"'<>,.?/~`", record.Text); + } + + [Fact] + public void Text_HandlesUnicodeCharacters() + { + // Arrange & Act + var record = new StringRecord { Text = "Unicode: 你好世界 مرحبا العالم" }; + + // Assert + Assert.Equal("Unicode: 你好世界 مرحبا العالم", record.Text); + } + + [Fact] + public void Text_HandlesEmojis() + { + // Arrange & Act + var record = new StringRecord { Text = "Emojis: 😀🎉🌟❤️" }; + + // Assert + Assert.Equal("Emojis: 😀🎉🌟❤️", record.Text); + } + + [Fact] + public void Text_HandlesMultilineText() + { + // Arrange & Act + var record = new StringRecord { Text = "Line 1\nLine 2\nLine 3" }; + + // Assert + Assert.Equal("Line 1\nLine 2\nLine 3", record.Text); + } + + [Fact] + public void Text_HandlesTabs() + { + // Arrange & Act + var record = new StringRecord { Text = "Column1\tColumn2\tColumn3" }; + + // Assert + Assert.Equal("Column1\tColumn2\tColumn3", record.Text); + } + + [Fact] + public void Text_HandlesControlCharacters() + { + // Arrange & Act + var record = new StringRecord { Text = "Text\r\nWith\r\nControl\r\nCharacters" }; + + // Assert + Assert.Equal("Text\r\nWith\r\nControl\r\nCharacters", record.Text); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameText_ReturnsTrue() + { + // Arrange + var record1 = new StringRecord { Text = "Test" }; + var record2 = new StringRecord { Text = "Test" }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentText_ReturnsFalse() + { + // Arrange + var record1 = new StringRecord { Text = "Test1" }; + var record2 = new StringRecord { Text = "Test2" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_CaseSensitive_ReturnsFalse() + { + // Arrange + var record1 = new StringRecord { Text = "Test" }; + var record2 = new StringRecord { Text = "test" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new StringRecord { Text = "Test" }; + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new StringRecord { Text = "Test" }; + var differentType = "Test"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var record = new StringRecord { Text = "Test" }; + + // Act & Assert + Assert.True(record.Equals(record)); + } + + [Fact] + public void Equals_WhitespaceMatters() + { + // Arrange + var record1 = new StringRecord { Text = "Test " }; + var record2 = new StringRecord { Text = "Test" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameText_ReturnsSameHashCode() + { + // Arrange + var record1 = new StringRecord { Text = "Test" }; + var record2 = new StringRecord { Text = "Test" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentText_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new StringRecord { Text = "Test1" }; + var record2 = new StringRecord { Text = "Test2" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_EmptyText_ReturnsConsistentValue() + { + // Arrange + var record1 = new StringRecord { Text = string.Empty }; + var record2 = new StringRecord { Text = string.Empty }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsText() + { + // Arrange + var record = new StringRecord { Text = "Hello World" }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("Hello World", result); + } + + [Fact] + public void ToString_EmptyText_ReturnsEmptyString() + { + // Arrange + var record = new StringRecord { Text = string.Empty }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal(string.Empty, result); + } + + [Fact] + public void ToString_PreservesFormatting() + { + // Arrange + var record = new StringRecord { Text = "Line 1\n\tLine 2\nLine 3" }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("Line 1\n\tLine 2\nLine 3", result); + } + + #endregion +} diff --git a/tests/UnfieldedAddressRecordTests.cs b/tests/UnfieldedAddressRecordTests.cs new file mode 100644 index 0000000..2236c36 --- /dev/null +++ b/tests/UnfieldedAddressRecordTests.cs @@ -0,0 +1,222 @@ +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +/// +/// Tests for UnfieldedAddressRecord class +/// +public class UnfieldedAddressRecordTests +{ + #region Property Tests + + [Fact] + public void Address_CanBeSetAndRetrieved() + { + // Arrange & Act + var record = new UnfieldedAddressRecord { Address = "123 Main Street, Springfield, IL 62701" }; + + // Assert + Assert.Equal("123 Main Street, Springfield, IL 62701", record.Address); + } + + [Fact] + public void Address_CanBeUpdated() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = "Initial Address" }; + + // Act + record.Address = "456 Oak Avenue, Boston, MA 02101"; + + // Assert + Assert.Equal("456 Oak Avenue, Boston, MA 02101", record.Address); + } + + [Fact] + public void Address_HandlesComplexAddress() + { + // Arrange & Act + var record = new UnfieldedAddressRecord { Address = "Apt 3B, 789 Pine St, Suite 200, New York, NY 10001" }; + + // Assert + Assert.Equal("Apt 3B, 789 Pine St, Suite 200, New York, NY 10001", record.Address); + } + + [Fact] + public void Address_HandlesInternationalAddress() + { + // Arrange & Act + var record = new UnfieldedAddressRecord { Address = "10 Downing Street, London, SW1A 2AA, United Kingdom" }; + + // Assert + Assert.Equal("10 Downing Street, London, SW1A 2AA, United Kingdom", record.Address); + } + + [Fact] + public void Address_HandlesNonLatinCharacters() + { + // Arrange & Act + var record = new UnfieldedAddressRecord { Address = "東京都渋谷区神南1丁目" }; + + // Assert + Assert.Equal("東京都渋谷区神南1丁目", record.Address); + } + + [Fact] + public void Address_HandlesSpecialCharacters() + { + // Arrange & Act + var record = new UnfieldedAddressRecord { Address = "123 O'Brien St. #456" }; + + // Assert + Assert.Equal("123 O'Brien St. #456", record.Address); + } + + [Fact] + public void Address_HandlesMultiLineAddress() + { + // Arrange & Act + var record = new UnfieldedAddressRecord { Address = "Building A\nFloor 5\n100 Main St\nCity, State 12345" }; + + // Assert + Assert.Equal("Building A\nFloor 5\n100 Main St\nCity, State 12345", record.Address); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameAddress_ReturnsTrue() + { + // Arrange + var record1 = new UnfieldedAddressRecord { Address = "123 Main St" }; + var record2 = new UnfieldedAddressRecord { Address = "123 Main St" }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentAddress_ReturnsFalse() + { + // Arrange + var record1 = new UnfieldedAddressRecord { Address = "123 Main St" }; + var record2 = new UnfieldedAddressRecord { Address = "456 Oak Ave" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = "123 Main St" }; + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = "123 Main St" }; + var differentType = "123 Main St"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = "123 Main St" }; + + // Act & Assert + Assert.True(record.Equals(record)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameAddress_ReturnsSameHashCode() + { + // Arrange + var record1 = new UnfieldedAddressRecord { Address = "123 Main St" }; + var record2 = new UnfieldedAddressRecord { Address = "123 Main St" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentAddress_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new UnfieldedAddressRecord { Address = "123 Main St" }; + var record2 = new UnfieldedAddressRecord { Address = "456 Oak Ave" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_EmptyAddress_ReturnsConsistentValue() + { + // Arrange + var record1 = new UnfieldedAddressRecord { Address = string.Empty }; + var record2 = new UnfieldedAddressRecord { Address = string.Empty }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsAddress() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = "123 Main St" }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("123 Main St", result); + } + + [Fact] + public void ToString_EmptyAddress_ReturnsEmptyString() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = string.Empty }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal(string.Empty, result); + } + + #endregion +} diff --git a/tests/UnfieldedDateRecordTests.cs b/tests/UnfieldedDateRecordTests.cs new file mode 100644 index 0000000..558498c --- /dev/null +++ b/tests/UnfieldedDateRecordTests.cs @@ -0,0 +1,213 @@ +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +/// +/// Tests for UnfieldedDateRecord class +/// +public class UnfieldedDateRecordTests +{ + #region Property Tests + + [Fact] + public void Date_CanBeSetAndRetrieved() + { + // Arrange & Act + var record = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Assert + Assert.Equal("2024-03-10", record.Date); + } + + [Fact] + public void Date_CanBeUpdated() + { + // Arrange + var record = new UnfieldedDateRecord { Date = "2024-01-01" }; + + // Act + record.Date = "2024-12-31"; + + // Assert + Assert.Equal("2024-12-31", record.Date); + } + + [Fact] + public void Date_AcceptsDifferentFormats() + { + // Arrange + var formats = new[] + { + "2024-03-10", + "03/10/2024", + "March 10, 2024", + "10-Mar-2024", + "20240310" + }; + + // Act & Assert + foreach (var format in formats) + { + var record = new UnfieldedDateRecord { Date = format }; + Assert.Equal(format, record.Date); + } + } + + [Fact] + public void Date_HandlesPartialDates() + { + // Arrange & Act + var record = new UnfieldedDateRecord { Date = "2024-03" }; + + // Assert + Assert.Equal("2024-03", record.Date); + } + + [Fact] + public void Date_HandlesRelativeDates() + { + // Arrange & Act + var record = new UnfieldedDateRecord { Date = "yesterday" }; + + // Assert + Assert.Equal("yesterday", record.Date); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameDate_ReturnsTrue() + { + // Arrange + var record1 = new UnfieldedDateRecord { Date = "2024-03-10" }; + var record2 = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentDate_ReturnsFalse() + { + // Arrange + var record1 = new UnfieldedDateRecord { Date = "2024-03-10" }; + var record2 = new UnfieldedDateRecord { Date = "2024-03-11" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new UnfieldedDateRecord { Date = "2024-03-10" }; + var differentType = "2024-03-10"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var record = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Act & Assert + Assert.True(record.Equals(record)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameDate_ReturnsSameHashCode() + { + // Arrange + var record1 = new UnfieldedDateRecord { Date = "2024-03-10" }; + var record2 = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentDate_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new UnfieldedDateRecord { Date = "2024-03-10" }; + var record2 = new UnfieldedDateRecord { Date = "2024-03-11" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_EmptyDate_ReturnsConsistentValue() + { + // Arrange + var record1 = new UnfieldedDateRecord { Date = string.Empty }; + var record2 = new UnfieldedDateRecord { Date = string.Empty }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsDate() + { + // Arrange + var record = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("2024-03-10", result); + } + + [Fact] + public void ToString_EmptyDate_ReturnsEmptyString() + { + // Arrange + var record = new UnfieldedDateRecord { Date = string.Empty }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal(string.Empty, result); + } + + #endregion +} diff --git a/tests/UnfieldedNameRecordTests.cs b/tests/UnfieldedNameRecordTests.cs new file mode 100644 index 0000000..3de2247 --- /dev/null +++ b/tests/UnfieldedNameRecordTests.cs @@ -0,0 +1,202 @@ +using Rosette.Api.Client.Models; + +namespace Rosette.Api.Tests; + +/// +/// Tests for UnfieldedNameRecord class +/// +public class UnfieldedNameRecordTests +{ + #region Property Tests + + [Fact] + public void Text_CanBeSetAndRetrieved() + { + // Arrange & Act + var record = new UnfieldedNameRecord { Text = "John Smith" }; + + // Assert + Assert.Equal("John Smith", record.Text); + } + + [Fact] + public void Text_CanBeUpdated() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "Initial" }; + + // Act + record.Text = "Updated"; + + // Assert + Assert.Equal("Updated", record.Text); + } + + [Fact] + public void Text_HandlesSpecialCharacters() + { + // Arrange & Act + var record = new UnfieldedNameRecord { Text = "O'Brien-Smith" }; + + // Assert + Assert.Equal("O'Brien-Smith", record.Text); + } + + [Fact] + public void Text_HandlesUnicodeCharacters() + { + // Arrange & Act + var record = new UnfieldedNameRecord { Text = "José García" }; + + // Assert + Assert.Equal("José García", record.Text); + } + + [Fact] + public void Text_HandlesNonLatinScripts() + { + // Arrange & Act + var record = new UnfieldedNameRecord { Text = "山田太郎" }; + + // Assert + Assert.Equal("山田太郎", record.Text); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameText_ReturnsTrue() + { + // Arrange + var record1 = new UnfieldedNameRecord { Text = "John Smith" }; + var record2 = new UnfieldedNameRecord { Text = "John Smith" }; + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentText_ReturnsFalse() + { + // Arrange + var record1 = new UnfieldedNameRecord { Text = "John Smith" }; + var record2 = new UnfieldedNameRecord { Text = "Jane Doe" }; + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "John Smith" }; + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "John Smith" }; + var differentType = "John Smith"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "John Smith" }; + + // Act & Assert + Assert.True(record.Equals(record)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameText_ReturnsSameHashCode() + { + // Arrange + var record1 = new UnfieldedNameRecord { Text = "John Smith" }; + var record2 = new UnfieldedNameRecord { Text = "John Smith" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentText_ReturnsDifferentHashCode() + { + // Arrange + var record1 = new UnfieldedNameRecord { Text = "John Smith" }; + var record2 = new UnfieldedNameRecord { Text = "Jane Doe" }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_EmptyText_ReturnsConsistentValue() + { + // Arrange + var record1 = new UnfieldedNameRecord { Text = string.Empty }; + var record2 = new UnfieldedNameRecord { Text = string.Empty }; + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsText() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "John Smith" }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("John Smith", result); + } + + [Fact] + public void ToString_EmptyText_ReturnsEmptyString() + { + // Arrange + var record = new UnfieldedNameRecord { Text = string.Empty }; + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal(string.Empty, result); + } + + #endregion +} diff --git a/tests/UnknownFieldRecordTests.cs b/tests/UnknownFieldRecordTests.cs new file mode 100644 index 0000000..2774736 --- /dev/null +++ b/tests/UnknownFieldRecordTests.cs @@ -0,0 +1,418 @@ +using Rosette.Api.Client.Models; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Rosette.Api.Tests; + +/// +/// Tests for UnknownFieldRecord class +/// +public class UnknownFieldRecordTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_WithNull_CreatesRecordWithNullData() + { + // Act + var record = new UnknownFieldRecord(null); + + // Assert + Assert.Null(record.Data); + } + + [Fact] + public void Constructor_WithJsonObject_SetsDataProperty() + { + // Arrange + var jsonObject = new JsonObject + { + ["key"] = "value", + ["number"] = 42 + }; + + // Act + var record = new UnknownFieldRecord(jsonObject); + + // Assert + Assert.NotNull(record.Data); + Assert.Equal("value", record.Data["key"]?.GetValue()); + Assert.Equal(42, record.Data["number"]?.GetValue()); + } + + [Fact] + public void Constructor_WithJsonArray_SetsDataProperty() + { + // Arrange + var jsonArray = new JsonArray { "item1", "item2", "item3" }; + + // Act + var record = new UnknownFieldRecord(jsonArray); + + // Assert + Assert.NotNull(record.Data); + Assert.Equal(3, record.Data.AsArray().Count); + } + + [Fact] + public void Constructor_WithJsonValue_SetsDataProperty() + { + // Arrange + var jsonValue = JsonValue.Create("simple string"); + + // Act + var record = new UnknownFieldRecord(jsonValue); + + // Assert + Assert.NotNull(record.Data); + Assert.Equal("simple string", record.Data.GetValue()); + } + + #endregion + + #region Property Tests + + [Fact] + public void Data_IsReadOnly() + { + // Arrange + var jsonObject = new JsonObject { ["key"] = "value" }; + var record = new UnknownFieldRecord(jsonObject); + + // Assert - Data property should be get-only + Assert.NotNull(record.Data); + // Attempting to set Data would not compile + } + + [Fact] + public void Data_PreservesComplexStructure() + { + // Arrange + var jsonObject = new JsonObject + { + ["name"] = "Test", + ["nested"] = new JsonObject + { + ["field1"] = "value1", + ["field2"] = 123 + }, + ["array"] = new JsonArray { 1, 2, 3 } + }; + + // Act + var record = new UnknownFieldRecord(jsonObject); + + // Assert + Assert.NotNull(record.Data); + Assert.Equal("Test", record.Data["name"]?.GetValue()); + Assert.Equal("value1", record.Data["nested"]?["field1"]?.GetValue()); + Assert.Equal(3, record.Data["array"]?.AsArray().Count); + } + + [Fact] + public void Data_HandlesEmptyObject() + { + // Arrange + var jsonObject = new JsonObject(); + + // Act + var record = new UnknownFieldRecord(jsonObject); + + // Assert + Assert.NotNull(record.Data); + Assert.Empty(record.Data.AsObject()); + } + + [Fact] + public void Data_HandlesEmptyArray() + { + // Arrange + var jsonArray = new JsonArray(); + + // Act + var record = new UnknownFieldRecord(jsonArray); + + // Assert + Assert.NotNull(record.Data); + Assert.Empty(record.Data.AsArray()); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_BothNull_ReturnsTrue() + { + // Arrange + var record1 = new UnknownFieldRecord(null); + var record2 = new UnknownFieldRecord(null); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_SameJsonObject_ReturnsTrue() + { + // Arrange + var jsonObject1 = new JsonObject { ["key"] = "value" }; + var jsonObject2 = new JsonObject { ["key"] = "value" }; + var record1 = new UnknownFieldRecord(jsonObject1); + var record2 = new UnknownFieldRecord(jsonObject2); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentJsonObject_ReturnsFalse() + { + // Arrange + var jsonObject1 = new JsonObject { ["key"] = "value1" }; + var jsonObject2 = new JsonObject { ["key"] = "value2" }; + var record1 = new UnknownFieldRecord(jsonObject1); + var record2 = new UnknownFieldRecord(jsonObject2); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_SameJsonArray_ReturnsTrue() + { + // Arrange + var jsonArray1 = new JsonArray { 1, 2, 3 }; + var jsonArray2 = new JsonArray { 1, 2, 3 }; + var record1 = new UnknownFieldRecord(jsonArray1); + var record2 = new UnknownFieldRecord(jsonArray2); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + [Fact] + public void Equals_DifferentJsonArray_ReturnsFalse() + { + // Arrange + var jsonArray1 = new JsonArray { 1, 2, 3 }; + var jsonArray2 = new JsonArray { 4, 5, 6 }; + var record1 = new UnknownFieldRecord(jsonArray1); + var record2 = new UnknownFieldRecord(jsonArray2); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_OneNullOneWithData_ReturnsFalse() + { + // Arrange + var record1 = new UnknownFieldRecord(null); + var record2 = new UnknownFieldRecord(new JsonObject { ["key"] = "value" }); + + // Act & Assert + Assert.False(record1.Equals(record2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var record = new UnknownFieldRecord(new JsonObject { ["key"] = "value" }); + + // Act & Assert + Assert.False(record.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var record = new UnknownFieldRecord(new JsonObject { ["key"] = "value" }); + var differentType = "different"; + + // Act & Assert + Assert.False(record.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var record = new UnknownFieldRecord(new JsonObject { ["key"] = "value" }); + + // Act & Assert + Assert.True(record.Equals(record)); + } + + [Fact] + public void Equals_NestedStructure_ComparesCorrectly() + { + // Arrange + var json1 = new JsonObject + { + ["nested"] = new JsonObject { ["value"] = 123 } + }; + var json2 = new JsonObject + { + ["nested"] = new JsonObject { ["value"] = 123 } + }; + var record1 = new UnknownFieldRecord(json1); + var record2 = new UnknownFieldRecord(json2); + + // Act & Assert + Assert.True(record1.Equals(record2)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameData_ReturnsSameHashCode() + { + // Arrange - use same object reference + var jsonObject = new JsonObject { ["key"] = "value" }; + var record1 = new UnknownFieldRecord(jsonObject); + var record2 = new UnknownFieldRecord(jsonObject); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentData_ReturnsDifferentHashCode() + { + // Arrange + var jsonObject1 = new JsonObject { ["key"] = "value1" }; + var jsonObject2 = new JsonObject { ["key"] = "value2" }; + var record1 = new UnknownFieldRecord(jsonObject1); + var record2 = new UnknownFieldRecord(jsonObject2); + + // Act + int hash1 = record1.GetHashCode(); + int hash2 = record2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_NullData_ReturnsZero() + { + // Arrange + var record = new UnknownFieldRecord(null); + + // Act + int hash = record.GetHashCode(); + + // Assert + Assert.Equal(0, hash); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_Null_ReturnsNull() + { + // Arrange + var record = new UnknownFieldRecord(null); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("null", result); + } + + [Fact] + public void ToString_JsonObject_ReturnsJsonString() + { + // Arrange + var jsonObject = new JsonObject { ["key"] = "value" }; + var record = new UnknownFieldRecord(jsonObject); + + // Act + string result = record.ToString(); + + // Assert + Assert.NotEmpty(result); + Assert.Contains("key", result); + Assert.Contains("value", result); + } + + [Fact] + public void ToString_JsonArray_ReturnsJsonString() + { + // Arrange + var jsonArray = new JsonArray { 1, 2, 3 }; + var record = new UnknownFieldRecord(jsonArray); + + // Act + string result = record.ToString(); + + // Assert + Assert.NotEmpty(result); + Assert.Contains("1", result); + Assert.Contains("2", result); + Assert.Contains("3", result); + } + + [Fact] + public void ToString_ProducesValidJson() + { + // Arrange + var jsonObject = new JsonObject + { + ["name"] = "Test", + ["value"] = 42 + }; + var record = new UnknownFieldRecord(jsonObject); + + // Act + string result = record.ToString(); + + // Assert - Should be able to parse it back + var parsed = JsonNode.Parse(result); + Assert.NotNull(parsed); + Assert.Equal("Test", parsed["name"]?.GetValue()); + Assert.Equal(42, parsed["value"]?.GetValue()); + } + + [Fact] + public void ToString_EmptyObject_ReturnsEmptyJsonObject() + { + // Arrange + var jsonObject = new JsonObject(); + var record = new UnknownFieldRecord(jsonObject); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("{}", result); + } + + [Fact] + public void ToString_EmptyArray_ReturnsEmptyJsonArray() + { + // Arrange + var jsonArray = new JsonArray(); + var record = new UnknownFieldRecord(jsonArray); + + // Act + string result = record.ToString(); + + // Assert + Assert.Equal("[]", result); + } + + #endregion +} From 7731b9303e8ddc832e989f7d93df9441fccd5984 Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Mar 2026 11:52:36 -0400 Subject: [PATCH 5/7] BX-69342: Added unit tests for JSON conversions --- .../RecordSimilarityFieldConverter.cs | 32 +- .../UnfieldedRecordSimilarityConverter.cs | 40 +- tests/RecordSimilarityFieldConverterTests.cs | 349 +++++++++++ .../RecordSimilarityRecordsConverterTests.cs | 378 ++++++++++++ ...UnfieldedRecordSimilarityConverterTests.cs | 571 ++++++++++++++++++ 5 files changed, 1335 insertions(+), 35 deletions(-) create mode 100644 tests/RecordSimilarityFieldConverterTests.cs create mode 100644 tests/RecordSimilarityRecordsConverterTests.cs create mode 100644 tests/UnfieldedRecordSimilarityConverterTests.cs diff --git a/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs b/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs index 12a8a8c..be8c59a 100644 --- a/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs +++ b/rosette_api/Models/JsonConverter/RecordSimilarityFieldConverter.cs @@ -20,15 +20,6 @@ public override void Write(Utf8JsonWriter writer, RecordSimilarityField value, J { switch (value) { - case UnfieldedNameRecord unfieldedName: - writer.WriteStringValue(unfieldedName.Text); - break; - case UnfieldedDateRecord unfieldedDate: - writer.WriteStringValue(unfieldedDate.Date); - break; - case UnfieldedAddressRecord unfieldedAddress: - writer.WriteStringValue(unfieldedAddress.Address); - break; case FieldedNameRecord fieldedName: JsonSerializer.Serialize(writer, fieldedName, options); break; @@ -38,24 +29,11 @@ public override void Write(Utf8JsonWriter writer, RecordSimilarityField value, J case FieldedAddressRecord fieldedAddress: JsonSerializer.Serialize(writer, fieldedAddress, options); break; - case NumberRecord numberRecord: - writer.WriteNumberValue(numberRecord.Number); - break; - case BooleanRecord booleanRecord: - writer.WriteBooleanValue(booleanRecord.Boolean); - break; - case StringRecord stringRecord: - writer.WriteStringValue(stringRecord.Text); - break; - case UnknownFieldRecord unknownField: - if (unknownField.Data != null) - { - unknownField.Data.WriteTo(writer, options); - } - else - { - writer.WriteNullValue(); - } + // Unfielded records are handled by UnfieldedRecordSimilarityConverter + case UnfieldedNameRecord or UnfieldedDateRecord or UnfieldedAddressRecord: + case NumberRecord or BooleanRecord or StringRecord or UnknownFieldRecord: + // Use the UnfieldedRecordSimilarityConverter for these types + JsonSerializer.Serialize(writer, value, value.GetType(), options); break; default: JsonSerializer.Serialize(writer, value, value.GetType(), options); diff --git a/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs b/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs index e4ff1e2..91d2241 100644 --- a/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs +++ b/rosette_api/Models/JsonConverter/UnfieldedRecordSimilarityConverter.cs @@ -18,10 +18,13 @@ public UnfieldedRecordSimilarityConverter() public override bool CanConvert(Type typeToConvert) { - return typeToConvert == typeof(UnknownFieldRecord) || + return typeToConvert == typeof(UnfieldedNameRecord) || + typeToConvert == typeof(UnfieldedDateRecord) || + typeToConvert == typeof(UnfieldedAddressRecord) || typeToConvert == typeof(NumberRecord) || typeToConvert == typeof(BooleanRecord) || - typeToConvert == typeof(UnfieldedAddressRecord) || + typeToConvert == typeof(StringRecord) || + typeToConvert == typeof(UnknownFieldRecord) || typeToConvert == typeof(object); } @@ -31,9 +34,14 @@ public override bool CanConvert(Type typeToConvert) { JsonTokenType.Number => new NumberRecord { Number = reader.GetDouble() }, JsonTokenType.True or JsonTokenType.False => new BooleanRecord { Boolean = reader.GetBoolean() }, - JsonTokenType.String => typeToConvert == typeof(UnfieldedAddressRecord) - ? new UnfieldedAddressRecord { Address = reader.GetString() ?? string.Empty } - : reader.GetString(), + JsonTokenType.String => typeToConvert switch + { + Type t when t == typeof(UnfieldedNameRecord) => new UnfieldedNameRecord { Text = reader.GetString() ?? string.Empty }, + Type t when t == typeof(UnfieldedDateRecord) => new UnfieldedDateRecord { Date = reader.GetString() ?? string.Empty }, + Type t when t == typeof(UnfieldedAddressRecord) => new UnfieldedAddressRecord { Address = reader.GetString() ?? string.Empty }, + Type t when t == typeof(StringRecord) => new StringRecord { Text = reader.GetString() ?? string.Empty }, + _ => reader.GetString() + }, JsonTokenType.StartObject => JsonSerializer.Deserialize(ref reader, options), _ => null }; @@ -43,18 +51,34 @@ public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOp { switch (value) { + case UnfieldedNameRecord unfieldedName: + writer.WriteStringValue(unfieldedName.Text); + break; + case UnfieldedDateRecord unfieldedDate: + writer.WriteStringValue(unfieldedDate.Date); + break; case UnfieldedAddressRecord unfieldedAddress: writer.WriteStringValue(unfieldedAddress.Address); break; - case UnknownFieldRecord unknownField: - JsonSerializer.Serialize(writer, unknownField.Data, options); - break; case NumberRecord numberRecord: writer.WriteNumberValue(numberRecord.Number); break; case BooleanRecord booleanRecord: writer.WriteBooleanValue(booleanRecord.Boolean); break; + case StringRecord stringRecord: + writer.WriteStringValue(stringRecord.Text); + break; + case UnknownFieldRecord unknownField: + if (unknownField.Data != null) + { + unknownField.Data.WriteTo(writer, options); + } + else + { + writer.WriteNullValue(); + } + break; case string stringValue: writer.WriteStringValue(stringValue); break; diff --git a/tests/RecordSimilarityFieldConverterTests.cs b/tests/RecordSimilarityFieldConverterTests.cs new file mode 100644 index 0000000..272c27b --- /dev/null +++ b/tests/RecordSimilarityFieldConverterTests.cs @@ -0,0 +1,349 @@ +using Rosette.Api.Client.Models; +using Rosette.Api.Client.Models.JsonConverter; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for RecordSimilarityFieldConverter class +/// +public class RecordSimilarityFieldConverterTests +{ + private readonly JsonSerializerOptions _options; + + public RecordSimilarityFieldConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new RecordSimilarityFieldConverter()); + _options.Converters.Add(new UnfieldedRecordSimilarityConverter()); + } + + #region Read Tests + + [Fact] + public void Read_ThrowsNotSupportedException() + { + // Arrange + var converter = new RecordSimilarityFieldConverter(); + string json = "{}"; + + // Act & Assert + NotSupportedException exception = null!; + try + { + var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(json)); + reader.Read(); + converter.Read(ref reader, typeof(RecordSimilarityField), _options); + } + catch (NotSupportedException ex) + { + exception = ex; + } + + Assert.NotNull(exception); + Assert.Contains("Deserialization of RecordSimilarityField is not supported", exception.Message); + Assert.Contains("type information is lost", exception.Message); + } + + #endregion + + #region Write Tests - FieldedNameRecord + + [Fact] + public void Write_FieldedNameRecord_SerializesAsObject() + { + // Arrange + var record = new FieldedNameRecord + { + Text = "John Smith", + Language = "eng", + EntityType = "PERSON" + }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("\"text\"", json); + Assert.Contains("John Smith", json); + Assert.Contains("\"language\"", json); + Assert.Contains("eng", json); + Assert.Contains("\"entityType\"", json); + Assert.Contains("PERSON", json); + } + + [Fact] + public void Write_FieldedNameRecord_WithAllProperties_SerializesCompletely() + { + // Arrange + var record = new FieldedNameRecord + { + Text = "José García", + Language = "spa", + LanguageOfOrigin = "spa", + Script = "Latn", + EntityType = "PERSON" + }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert - JSON may escape Unicode + Assert.Contains("Jos", json); + Assert.Contains("Garc", json); + Assert.Contains("spa", json); + Assert.Contains("Latn", json); + Assert.Contains("PERSON", json); + } + + [Fact] + public void Write_FieldedNameRecord_WithNullOptionalFields_SerializesTextOnly() + { + // Arrange + var record = new FieldedNameRecord { Text = "Test Name" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("\"text\"", json); + Assert.Contains("Test Name", json); + } + + #endregion + + #region Write Tests - FieldedDateRecord + + [Fact] + public void Write_FieldedDateRecord_SerializesAsObject() + { + // Arrange + var record = new FieldedDateRecord + { + Date = "2024-03-10", + Format = "yyyy-MM-dd" + }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("\"date\"", json); + Assert.Contains("2024-03-10", json); + Assert.Contains("\"format\"", json); + Assert.Contains("yyyy-MM-dd", json); + } + + [Fact] + public void Write_FieldedDateRecord_WithoutFormat_SerializesDateOnly() + { + // Arrange + var record = new FieldedDateRecord { Date = "2024-03-10" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("\"date\"", json); + Assert.Contains("2024-03-10", json); + } + + #endregion + + #region Write Tests - FieldedAddressRecord + + [Fact] + public void Write_FieldedAddressRecord_SerializesAsObject() + { + // Arrange + var record = new FieldedAddressRecord + { + HouseNumber = "123", + Road = "Main Street", + City = "Springfield", + State = "IL", + Postcode = "62701" + }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("123", json); + Assert.Contains("Main Street", json); + Assert.Contains("Springfield", json); + Assert.Contains("IL", json); + Assert.Contains("62701", json); + } + + [Fact] + public void Write_FieldedAddressRecord_WithAllFields_SerializesCompletely() + { + // Arrange + var record = new FieldedAddressRecord + { + House = "White House", + HouseNumber = "1600", + Road = "Pennsylvania Avenue NW", + City = "Washington", + State = "DC", + Country = "USA", + Postcode = "20500" + }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("White House", json); + Assert.Contains("1600", json); + Assert.Contains("Pennsylvania Avenue NW", json); + Assert.Contains("Washington", json); + Assert.Contains("DC", json); + Assert.Contains("USA", json); + Assert.Contains("20500", json); + } + + [Fact] + public void Write_FieldedAddressRecord_Empty_SerializesAsEmptyObject() + { + // Arrange + var record = new FieldedAddressRecord(); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("{", json); + Assert.Contains("}", json); + } + + #endregion + + #region Write Tests - Unfielded Records Delegation + + [Fact] + public void Write_UnfieldedNameRecord_DelegatesToUnfieldedConverter() + { + // Arrange + RecordSimilarityField record = new UnfieldedNameRecord { Text = "John Smith" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"John Smith\"", json); + } + + [Fact] + public void Write_UnfieldedDateRecord_DelegatesToUnfieldedConverter() + { + // Arrange + RecordSimilarityField record = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"2024-03-10\"", json); + } + + [Fact] + public void Write_UnfieldedAddressRecord_DelegatesToUnfieldedConverter() + { + // Arrange + RecordSimilarityField record = new UnfieldedAddressRecord { Address = "123 Main St" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"123 Main St\"", json); + } + + [Fact] + public void Write_NumberRecord_DelegatesToUnfieldedConverter() + { + // Arrange + RecordSimilarityField record = new NumberRecord(42); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("42", json); + } + + [Fact] + public void Write_BooleanRecord_DelegatesToUnfieldedConverter() + { + // Arrange + RecordSimilarityField record = new BooleanRecord(true); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("true", json); + } + + [Fact] + public void Write_StringRecord_DelegatesToUnfieldedConverter() + { + // Arrange + RecordSimilarityField record = new StringRecord { Text = "Test" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"Test\"", json); + } + + #endregion + + #region Integration Tests + + [Fact] + public void Serialize_MixedFieldedAndUnfieldedRecords_HandlesCorrectly() + { + // Arrange + var records = new List + { + new FieldedNameRecord { Text = "John", Language = "eng" }, + new UnfieldedNameRecord { Text = "Jane" }, + new NumberRecord(42), + new BooleanRecord(true) + }; + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("John", json); + Assert.Contains("eng", json); + Assert.Contains("Jane", json); + Assert.Contains("42", json); + Assert.Contains("true", json); + } + + [Fact] + public void Serialize_FieldedRecord_ProducesValidJson() + { + // Arrange + var record = new FieldedNameRecord + { + Text = "Test", + Language = "eng" + }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert - Should be able to parse as valid JSON + var parsed = JsonDocument.Parse(json); + Assert.NotNull(parsed); + } + + #endregion +} diff --git a/tests/RecordSimilarityRecordsConverterTests.cs b/tests/RecordSimilarityRecordsConverterTests.cs new file mode 100644 index 0000000..8175b87 --- /dev/null +++ b/tests/RecordSimilarityRecordsConverterTests.cs @@ -0,0 +1,378 @@ +using Rosette.Api.Client.Models; +using Rosette.Api.Client.Models.JsonConverter; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for RecordSimilarityRecordsConverter class +/// +public class RecordSimilarityRecordsConverterTests +{ + private readonly JsonSerializerOptions _options; + + public RecordSimilarityRecordsConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new RecordSimilarityRecordsConverter()); + _options.Converters.Add(new RecordSimilarityFieldConverter()); + _options.Converters.Add(new UnfieldedRecordSimilarityConverter()); + } + + #region Write Tests - Basic Functionality + + [Fact] + public void Write_EmptyRecords_SerializesCorrectly() + { + // Arrange + var records = new RecordSimilarityRecords( + new List>(), + new List>() + ); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("\"left\"", json); + Assert.Contains("\"right\"", json); + Assert.Contains("[]", json); + } + + [Fact] + public void Write_SingleRecord_SerializesCorrectly() + { + // Arrange + var leftRecords = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "John Smith" } }, + { "age", new NumberRecord(30) } + } + }; + var rightRecords = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Jane Doe" } }, + { "age", new NumberRecord(28) } + } + }; + var records = new RecordSimilarityRecords(leftRecords, rightRecords); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("\"left\"", json); + Assert.Contains("\"right\"", json); + Assert.Contains("John Smith", json); + Assert.Contains("Jane Doe", json); + Assert.Contains("30", json); + Assert.Contains("28", json); + } + + [Fact] + public void Write_MultipleRecords_SerializesAll() + { + // Arrange + var leftRecords = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Person 1" } } + }, + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Person 2" } } + } + }; + var rightRecords = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Person 3" } } + } + }; + var records = new RecordSimilarityRecords(leftRecords, rightRecords); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("Person 1", json); + Assert.Contains("Person 2", json); + Assert.Contains("Person 3", json); + } + + #endregion + + #region Write Tests - Mixed Field Types + + [Fact] + public void Write_FieldedAndUnfieldedRecords_SerializesBoth() + { + // Arrange + var leftRecords = new List> + { + new Dictionary + { + { "name", new FieldedNameRecord { Text = "John", Language = "eng" } }, + { "simpleText", new UnfieldedNameRecord { Text = "Simple" } }, + { "count", new NumberRecord(5) }, + { "active", new BooleanRecord(true) } + } + }; + var rightRecords = new List>(); + var records = new RecordSimilarityRecords(leftRecords, rightRecords); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("John", json); + Assert.Contains("eng", json); + Assert.Contains("Simple", json); + Assert.Contains("5", json); + Assert.Contains("true", json); + } + + [Fact] + public void Write_DatesAndAddresses_SerializesCorrectly() + { + // Arrange + var leftRecords = new List> + { + new Dictionary + { + { "date", new UnfieldedDateRecord { Date = "2024-03-10" } }, + { "address", new UnfieldedAddressRecord { Address = "123 Main St" } }, + { "fieldedDate", new FieldedDateRecord { Date = "2024-03-10", Format = "yyyy-MM-dd" } } + } + }; + var rightRecords = new List>(); + var records = new RecordSimilarityRecords(leftRecords, rightRecords); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("2024-03-10", json); + Assert.Contains("123 Main St", json); + Assert.Contains("yyyy-MM-dd", json); + } + + #endregion + + #region Write Tests - Complex Scenarios + + [Fact] + public void Write_ComplexFieldedAddress_SerializesAllProperties() + { + // Arrange + var leftRecords = new List> + { + new Dictionary + { + { + "address", + new FieldedAddressRecord + { + HouseNumber = "123", + Road = "Main Street", + City = "Springfield", + State = "IL", + Postcode = "62701", + Country = "USA" + } + } + } + }; + var rightRecords = new List>(); + var records = new RecordSimilarityRecords(leftRecords, rightRecords); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("123", json); + Assert.Contains("Main Street", json); + Assert.Contains("Springfield", json); + Assert.Contains("IL", json); + Assert.Contains("62701", json); + Assert.Contains("USA", json); + } + + [Fact] + public void Write_StringRecord_SerializesAsString() + { + // Arrange + var leftRecords = new List> + { + new Dictionary + { + { "text", new StringRecord { Text = "Sample Text" } } + } + }; + var rightRecords = new List>(); + var records = new RecordSimilarityRecords(leftRecords, rightRecords); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("Sample Text", json); + } + + #endregion + + #region Read Tests + + [Fact] + public void Read_EmptyRecords_DeserializesCorrectly() + { + // Arrange + string json = "{\"left\":[],\"right\":[]}"; + + // Act + var result = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Empty(result.Left); + Assert.Empty(result.Right); + } + + [Fact] + public void Read_AnyNonEmptyRecords_ThrowsNotSupported() + { + // Arrange - Any records with fields will fail because RecordSimilarityField deserialization is not supported + string json = @"{ + ""left"": [ + { + ""age"": 30, + ""active"": true + } + ], + ""right"": [] + }"; + + // Act & Assert - Reading throws because RecordSimilarityField.Read is not supported + Assert.Throws(() => + JsonSerializer.Deserialize(json, _options)); + } + + #endregion + + #region Round-Trip Tests + + [Fact] + public void RoundTrip_SimpleRecords_SerializationWorks() + { + // Arrange - Note: Deserialization is not supported, only testing serialization + var original = new RecordSimilarityRecords( + new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "John" } }, + { "age", new NumberRecord(30) } + } + }, + new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Jane" } }, + { "age", new NumberRecord(28) } + } + } + ); + + // Act + string json = JsonSerializer.Serialize(original, _options); + + // Assert - Verify JSON structure + Assert.Contains("\"left\"", json); + Assert.Contains("\"right\"", json); + Assert.Contains("John", json); + Assert.Contains("Jane", json); + Assert.Contains("30", json); + Assert.Contains("28", json); + } + + [Fact] + public void RoundTrip_EmptyRecords_SerializationWorks() + { + // Arrange - Note: Deserialization is not supported, only testing serialization + var original = new RecordSimilarityRecords( + new List>(), + new List>() + ); + + // Act + string json = JsonSerializer.Serialize(original, _options); + + // Assert - Verify JSON structure + Assert.Contains("\"left\"", json); + Assert.Contains("\"right\"", json); + Assert.Contains("[]", json); + } + + #endregion + + #region Validation Tests + + [Fact] + public void Serialize_ProducesValidJson() + { + // Arrange + var records = new RecordSimilarityRecords( + new List> + { + new Dictionary + { + { "field1", new UnfieldedNameRecord { Text = "Value1" } } + } + }, + new List>() + ); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert - Should be valid JSON + var parsed = JsonDocument.Parse(json); + Assert.NotNull(parsed); + Assert.True(parsed.RootElement.TryGetProperty("left", out _)); + Assert.True(parsed.RootElement.TryGetProperty("right", out _)); + } + + [Fact] + public void Write_HasCorrectStructure() + { + // Arrange + var records = new RecordSimilarityRecords( + new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Test" } } + } + }, + new List>() + ); + + // Act + string json = JsonSerializer.Serialize(records, _options); + var doc = JsonDocument.Parse(json); + + // Assert + Assert.Equal(JsonValueKind.Object, doc.RootElement.ValueKind); + Assert.Equal(JsonValueKind.Array, doc.RootElement.GetProperty("left").ValueKind); + Assert.Equal(JsonValueKind.Array, doc.RootElement.GetProperty("right").ValueKind); + } + + #endregion +} diff --git a/tests/UnfieldedRecordSimilarityConverterTests.cs b/tests/UnfieldedRecordSimilarityConverterTests.cs new file mode 100644 index 0000000..0bc58f5 --- /dev/null +++ b/tests/UnfieldedRecordSimilarityConverterTests.cs @@ -0,0 +1,571 @@ +using Rosette.Api.Client.Models; +using Rosette.Api.Client.Models.JsonConverter; +using System.Text.Json; +using System.Text.Json.Nodes; + +namespace Rosette.Api.Tests; + +/// +/// Tests for UnfieldedRecordSimilarityConverter class +/// +public class UnfieldedRecordSimilarityConverterTests +{ + private readonly JsonSerializerOptions _options; + + public UnfieldedRecordSimilarityConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new UnfieldedRecordSimilarityConverter()); + } + + #region CanConvert Tests + + [Fact] + public void CanConvert_UnfieldedNameRecord_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(UnfieldedNameRecord)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_UnfieldedDateRecord_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(UnfieldedDateRecord)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_UnfieldedAddressRecord_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(UnfieldedAddressRecord)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_NumberRecord_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(NumberRecord)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_BooleanRecord_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(BooleanRecord)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_StringRecord_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(StringRecord)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_UnknownFieldRecord_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(UnknownFieldRecord)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_Object_ReturnsTrue() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(object)); + + // Assert + Assert.True(result); + } + + [Fact] + public void CanConvert_UnsupportedType_ReturnsFalse() + { + // Arrange + var converter = new UnfieldedRecordSimilarityConverter(); + + // Act + bool result = converter.CanConvert(typeof(FieldedNameRecord)); + + // Assert + Assert.False(result); + } + + #endregion + + #region Write Tests - UnfieldedNameRecord + + [Fact] + public void Write_UnfieldedNameRecord_SerializesAsString() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "John Smith" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"John Smith\"", json); + } + + [Fact] + public void Write_UnfieldedNameRecord_WithSpecialCharacters_SerializesCorrectly() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "O'Brien \"Quote\"" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert - JSON escapes special characters + Assert.Contains("Brien", json); + Assert.Contains("Quote", json); + } + + [Fact] + public void Write_UnfieldedNameRecord_WithUnicode_SerializesCorrectly() + { + // Arrange + var record = new UnfieldedNameRecord { Text = "José García" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert - JSON may escape Unicode characters + Assert.Contains("Jos", json); + Assert.Contains("Garc", json); + } + + #endregion + + #region Write Tests - UnfieldedDateRecord + + [Fact] + public void Write_UnfieldedDateRecord_SerializesAsString() + { + // Arrange + var record = new UnfieldedDateRecord { Date = "2024-03-10" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"2024-03-10\"", json); + } + + [Fact] + public void Write_UnfieldedDateRecord_DifferentFormat_SerializesAsProvided() + { + // Arrange + var record = new UnfieldedDateRecord { Date = "March 10, 2024" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"March 10, 2024\"", json); + } + + #endregion + + #region Write Tests - UnfieldedAddressRecord + + [Fact] + public void Write_UnfieldedAddressRecord_SerializesAsString() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = "123 Main St, Springfield, IL" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"123 Main St, Springfield, IL\"", json); + } + + [Fact] + public void Write_UnfieldedAddressRecord_WithNewlines_SerializesCorrectly() + { + // Arrange + var record = new UnfieldedAddressRecord { Address = "123 Main St\nSpringfield, IL\n62701" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("123 Main St", json); + Assert.Contains("Springfield", json); + } + + #endregion + + #region Write Tests - NumberRecord + + [Fact] + public void Write_NumberRecord_SerializesAsNumber() + { + // Arrange + var record = new NumberRecord(42.5); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("42.5", json); + } + + [Fact] + public void Write_NumberRecord_Integer_SerializesWithoutDecimal() + { + // Arrange + var record = new NumberRecord(100); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("100", json); + } + + [Fact] + public void Write_NumberRecord_NegativeNumber_SerializesCorrectly() + { + // Arrange + var record = new NumberRecord(-17.3); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("-17.3", json); + } + + [Fact] + public void Write_NumberRecord_Zero_SerializesAsZero() + { + // Arrange + var record = new NumberRecord(0); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("0", json); + } + + #endregion + + #region Write Tests - BooleanRecord + + [Fact] + public void Write_BooleanRecord_True_SerializesAsTrue() + { + // Arrange + var record = new BooleanRecord(true); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("true", json); + } + + [Fact] + public void Write_BooleanRecord_False_SerializesAsFalse() + { + // Arrange + var record = new BooleanRecord(false); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("false", json); + } + + #endregion + + #region Write Tests - StringRecord + + [Fact] + public void Write_StringRecord_SerializesAsString() + { + // Arrange + var record = new StringRecord { Text = "Hello World" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("\"Hello World\"", json); + } + + [Fact] + public void Write_StringRecord_WithSpecialCharacters_SerializesCorrectly() + { + // Arrange + var record = new StringRecord { Text = "Line 1\nLine 2\tTab" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("Line 1", json); + Assert.Contains("Line 2", json); + } + + #endregion + + #region Write Tests - UnknownFieldRecord + + [Fact] + public void Write_UnknownFieldRecord_WithJsonObject_SerializesAsObject() + { + // Arrange + var jsonObject = new JsonObject + { + ["key1"] = "value1", + ["key2"] = 42 + }; + var record = new UnknownFieldRecord(jsonObject); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("key1", json); + Assert.Contains("value1", json); + Assert.Contains("key2", json); + Assert.Contains("42", json); + } + + [Fact] + public void Write_UnknownFieldRecord_WithJsonArray_SerializesAsArray() + { + // Arrange + var jsonArray = new JsonArray { "item1", "item2", 42 }; + var record = new UnknownFieldRecord(jsonArray); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Contains("item1", json); + Assert.Contains("item2", json); + Assert.Contains("42", json); + } + + [Fact] + public void Write_UnknownFieldRecord_WithNull_SerializesAsNull() + { + // Arrange + var record = new UnknownFieldRecord(null); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.Equal("null", json); + } + + #endregion + + #region Write Tests - String Value + + [Fact] + public void Write_StringValue_SerializesAsString() + { + // Arrange + object value = "plain string"; + + // Act + string json = JsonSerializer.Serialize(value, _options); + + // Assert + Assert.Equal("\"plain string\"", json); + } + + #endregion + + #region Read Tests - NumberRecord + + [Fact] + public void Read_NumberValue_CreatesNumberRecord() + { + // Arrange + string json = "42.5"; + + // Act + var result = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.IsType(result); + var numberRecord = (NumberRecord)result; + Assert.Equal(42.5, numberRecord.Number); + } + + [Fact] + public void Read_IntegerValue_CreatesNumberRecord() + { + // Arrange + string json = "100"; + + // Act + var result = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.IsType(result); + var numberRecord = (NumberRecord)result; + Assert.Equal(100, numberRecord.Number); + } + + #endregion + + #region Read Tests - BooleanRecord + + [Fact] + public void Read_TrueValue_CreatesBooleanRecord() + { + // Arrange + string json = "true"; + + // Act + var result = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.IsType(result); + var booleanRecord = (BooleanRecord)result; + Assert.True(booleanRecord.Boolean); + } + + [Fact] + public void Read_FalseValue_CreatesBooleanRecord() + { + // Arrange + string json = "false"; + + // Act + var result = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.IsType(result); + var booleanRecord = (BooleanRecord)result; + Assert.False(booleanRecord.Boolean); + } + + #endregion + + #region Read Tests - String Values + + [Fact] + public void Read_StringValue_ReturnsString() + { + // Arrange + string json = "\"simple string\""; + + // Act + var result = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.IsType(result); + Assert.Equal("simple string", result); + } + + [Fact] + public void Read_StringValue_WithSpecificType_CreatesCorrectRecord() + { + // Arrange + string json = "\"123 Main St\""; + + // Act + var result = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.NotNull(result); + Assert.Equal("123 Main St", result.Address); + } + + #endregion + + #region Round-Trip Tests + + [Fact] + public void RoundTrip_NumberRecord_PreservesValue() + { + // Arrange + var original = new NumberRecord(42.5); + + // Act + string json = JsonSerializer.Serialize(original, _options); + var deserialized = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.IsType(deserialized); + var result = (NumberRecord)deserialized; + Assert.Equal(original.Number, result.Number); + } + + [Fact] + public void RoundTrip_BooleanRecord_PreservesValue() + { + // Arrange + var original = new BooleanRecord(true); + + // Act + string json = JsonSerializer.Serialize(original, _options); + var deserialized = JsonSerializer.Deserialize(json, _options); + + // Assert + Assert.IsType(deserialized); + var result = (BooleanRecord)deserialized; + Assert.Equal(original.Boolean, result.Boolean); + } + + #endregion +} From 672233beb0b3f37ada89b0cd6acb5428f45bee1c Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Mar 2026 13:48:09 -0400 Subject: [PATCH 6/7] BX-69342: Adds more tests to increase branch coverage --- tests/NameSimilarityTests.cs | 66 ++ tests/NameTests.cs | 131 ++++ tests/RecordSimilarityFieldConverterTests.cs | 117 ++++ tests/RecordSimilarityFieldInfoTests.cs | 494 ++++++++++++++ tests/RecordSimilarityPropertiesTests.cs | 465 +++++++++++++ tests/RecordSimilarityRecordsEnhancedTests.cs | 616 ++++++++++++++++++ tests/ResponseTests.cs | 531 +++++++++++++++ 7 files changed, 2420 insertions(+) create mode 100644 tests/RecordSimilarityFieldInfoTests.cs create mode 100644 tests/RecordSimilarityPropertiesTests.cs create mode 100644 tests/RecordSimilarityRecordsEnhancedTests.cs diff --git a/tests/NameSimilarityTests.cs b/tests/NameSimilarityTests.cs index 5d6d3df..b93a093 100644 --- a/tests/NameSimilarityTests.cs +++ b/tests/NameSimilarityTests.cs @@ -19,5 +19,71 @@ public void Constructor_ThrowsArgumentNullException_WhenNamesAreNull() { Assert.Equal("Value cannot be null. (Parameter 'name2')", exception.Message); } + [Fact] + public void Constructor_SetsEndpointCorrectly_WhenCalledWithValidNames() + { + // Arrange + var name1 = new Name("John Smith"); + var name2 = new Name("Jon Smyth"); + + // Act + var endpoint = new NameSimilarity(name1, name2); + + // Assert + Assert.Equal("name-similarity", endpoint.Endpoint); + } + + [Fact] + public void Constructor_SetsParamsCorrectly_WhenBothNamesProvided() + { + // Arrange + var name1 = new Name("Alice Johnson"); + var name2 = new Name("Alicia Johnston"); + + // Act + var endpoint = new NameSimilarity(name1, name2); + + // Assert + Assert.Contains("name1", endpoint.Params.Keys); + Assert.Contains("name2", endpoint.Params.Keys); + Assert.Equal(name1, endpoint.Params["name1"]); + Assert.Equal(name2, endpoint.Params["name2"]); + } + + [Fact] + public void Constructor_HandlesNamesWithLanguage_WhenProvided() + { + // Arrange + var name1 = new Name("José García", language: "spa"); + var name2 = new Name("Jose Garcia", language: "eng"); + + // Act + var endpoint = new NameSimilarity(name1, name2); + + // Assert + Assert.Equal(name1, endpoint.Params["name1"]); + Assert.Equal(name2, endpoint.Params["name2"]); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenName1IsNull() + { + // Arrange + var name2 = new Name("Test Name"); + + // Act & Assert + Assert.Throws(() => new NameSimilarity(null, name2)); + } + + [Fact] + public void Constructor_ThrowsArgumentNullException_WhenName2IsNull() + { + // Arrange + var name1 = new Name("Test Name"); + + // Act & Assert + Assert.Throws(() => new NameSimilarity(name1, null)); + } + #endregion } diff --git a/tests/NameTests.cs b/tests/NameTests.cs index affbb45..46cd166 100644 --- a/tests/NameTests.cs +++ b/tests/NameTests.cs @@ -16,6 +16,24 @@ public void Constructor_SetsTextAndNullProperties_WhenCreatingName() { Assert.Null(rn.Gender); } + [Fact] + public void Constructor_ThrowsArgumentException_WhenTextIsNull() + { + Assert.Throws(() => new Name(null!)); + } + + [Fact] + public void Constructor_ThrowsArgumentException_WhenTextIsEmpty() + { + Assert.Throws(() => new Name(string.Empty)); + } + + [Fact] + public void Constructor_ThrowsArgumentException_WhenTextIsWhitespace() + { + Assert.Throws(() => new Name(" ")); + } + #endregion #region Property Setting Tests @@ -27,6 +45,77 @@ public void SetEntityType_SetsEntityType_WhenCalled() { Assert.Equal("PERSON", rn.EntityType); } + [Fact] + public void SetEntityType_WithEnum_SetsEntityType_WhenCalled() + { + var name = new Name("foo").SetEntityType(Client.Models.EntityType.Person); + Assert.Equal("PERSON", name.EntityType); + } + + [Fact] + public void SetEntityType_WithEnum_SetsLocation_WhenCalled() + { + var name = new Name("foo").SetEntityType(Client.Models.EntityType.Location); + Assert.Equal("LOCATION", name.EntityType); + } + + [Fact] + public void SetEntityType_WithEnum_SetsOrganization_WhenCalled() + { + var name = new Name("foo").SetEntityType(Client.Models.EntityType.Organization); + Assert.Equal("ORGANIZATION", name.EntityType); + } + + [Fact] + public void SetEntityType_ThrowsArgumentException_WhenTypeIsNull() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetEntityType(null!)); + } + + [Fact] + public void SetEntityType_ThrowsArgumentException_WhenTypeIsEmpty() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetEntityType(string.Empty)); + } + + [Fact] + public void SetEntityType_ThrowsArgumentException_WhenTypeIsWhitespace() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetEntityType(" ")); + } + + [Fact] + public void SetEntityType_ThrowsArgumentException_WhenTypeIsInvalid() + { + var name = new Name("foo"); + var exception = Assert.Throws(() => name.SetEntityType("INVALID_TYPE")); + Assert.Contains("Entity type must be one of", exception.Message); + } + + [Fact] + public void SetEntityType_AcceptsLocation_WhenCalled() + { + var name = new Name("foo").SetEntityType("LOCATION"); + Assert.Equal("LOCATION", name.EntityType); + } + + [Fact] + public void SetEntityType_AcceptsOrganization_WhenCalled() + { + var name = new Name("foo").SetEntityType("ORGANIZATION"); + Assert.Equal("ORGANIZATION", name.EntityType); + } + + [Fact] + public void SetEntityType_IsCaseInsensitive_WhenCalled() + { + var name = new Name("foo").SetEntityType("person"); + Assert.Equal("PERSON", name.EntityType); + } + [Fact] public void SetLanguage_SetsLanguage_WhenCalled() { Name rn = new Name("foo").SetLanguage("eng"); @@ -34,6 +123,27 @@ public void SetLanguage_SetsLanguage_WhenCalled() { Assert.Equal("eng", rn.Language); } + [Fact] + public void SetLanguage_ThrowsArgumentException_WhenLanguageIsNull() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetLanguage(null!)); + } + + [Fact] + public void SetLanguage_ThrowsArgumentException_WhenLanguageIsEmpty() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetLanguage(string.Empty)); + } + + [Fact] + public void SetLanguage_ThrowsArgumentException_WhenLanguageIsWhitespace() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetLanguage(" ")); + } + [Fact] public void SetScript_SetsScript_WhenCalled() { Name rn = new Name("foo").SetScript("zho"); @@ -41,6 +151,27 @@ public void SetScript_SetsScript_WhenCalled() { Assert.Equal("zho", rn.Script); } + [Fact] + public void SetScript_ThrowsArgumentException_WhenScriptIsNull() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetScript(null!)); + } + + [Fact] + public void SetScript_ThrowsArgumentException_WhenScriptIsEmpty() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetScript(string.Empty)); + } + + [Fact] + public void SetScript_ThrowsArgumentException_WhenScriptIsWhitespace() + { + var name = new Name("foo"); + Assert.Throws(() => name.SetScript(" ")); + } + [Fact] public void SetGender_SetsGender_WhenCalled() { diff --git a/tests/RecordSimilarityFieldConverterTests.cs b/tests/RecordSimilarityFieldConverterTests.cs index 272c27b..7786009 100644 --- a/tests/RecordSimilarityFieldConverterTests.cs +++ b/tests/RecordSimilarityFieldConverterTests.cs @@ -300,6 +300,123 @@ public void Write_StringRecord_DelegatesToUnfieldedConverter() Assert.Equal("\"Test\"", json); } + [Fact] + public void Write_UnknownFieldRecord_DelegatesToDefaultCase() + { + // Arrange + RecordSimilarityField record = new UnknownFieldRecord(null); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + Assert.Contains("null", json); + } + + #endregion + + #region Write Tests - Switch Case Coverage + + [Fact] + public void Write_UnfieldedNameRecord_ExecutesUnfieldedCaseBranch() + { + // Arrange - Test the specific switch case for UnfieldedNameRecord + RecordSimilarityField record = new UnfieldedNameRecord { Text = "Jane Doe" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + Assert.Contains("Jane Doe", json); + } + + [Fact] + public void Write_UnfieldedDateRecord_ExecutesUnfieldedCaseBranch() + { + // Arrange - Test the specific switch case for UnfieldedDateRecord + RecordSimilarityField record = new UnfieldedDateRecord { Date = "2026-03-10" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + Assert.Contains("2026-03-10", json); + } + + [Fact] + public void Write_UnfieldedAddressRecord_ExecutesUnfieldedCaseBranch() + { + // Arrange - Test the specific switch case for UnfieldedAddressRecord + RecordSimilarityField record = new UnfieldedAddressRecord { Address = "456 Oak Avenue" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + Assert.Contains("456 Oak Avenue", json); + } + + [Fact] + public void Write_NumberRecord_ExecutesSimpleRecordCaseBranch() + { + // Arrange - Test the specific switch case for NumberRecord + RecordSimilarityField record = new NumberRecord(99); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + Assert.Equal("99", json); + } + + [Fact] + public void Write_BooleanRecord_False_ExecutesSimpleRecordCaseBranch() + { + // Arrange - Test the specific switch case for BooleanRecord with false value + RecordSimilarityField record = new BooleanRecord(false); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + Assert.Equal("false", json); + } + + [Fact] + public void Write_StringRecord_WithSpecialCharacters_ExecutesSimpleRecordCaseBranch() + { + // Arrange - Test the specific switch case for StringRecord with special characters + RecordSimilarityField record = new StringRecord { Text = "Test with special chars" }; + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + Assert.Contains("Test with", json); + } + + [Fact] + public void Write_UnknownFieldRecord_ExecutesDefaultCaseBranch() + { + // Arrange - Test the default case of the switch statement + RecordSimilarityField record = new UnknownFieldRecord(System.Text.Json.Nodes.JsonNode.Parse("{}")); + + // Act + string json = JsonSerializer.Serialize(record, _options); + + // Assert + Assert.NotNull(json); + // UnknownFieldRecord should serialize the JsonNode data + Assert.Contains("{", json); + } + #endregion #region Integration Tests diff --git a/tests/RecordSimilarityFieldInfoTests.cs b/tests/RecordSimilarityFieldInfoTests.cs new file mode 100644 index 0000000..dc80e78 --- /dev/null +++ b/tests/RecordSimilarityFieldInfoTests.cs @@ -0,0 +1,494 @@ +using Rosette.Api.Client.Models; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for RecordSimilarityFieldInfo class +/// +public class RecordSimilarityFieldInfoTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_SetsDefaultValues() + { + // Act + var fieldInfo = new RecordSimilarityFieldInfo(); + + // Assert + Assert.Null(fieldInfo.Type); + Assert.Null(fieldInfo.Weight); + Assert.Null(fieldInfo.ScoreIfNull); + } + + [Fact] + public void Constructor_WithAllParameters_SetsAllValues() + { + // Act + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Assert + Assert.Equal("rni_name", fieldInfo.Type); + Assert.Equal(0.8, fieldInfo.Weight); + Assert.Equal(0.5, fieldInfo.ScoreIfNull); + } + + [Fact] + public void Constructor_WithNullValues_AcceptsNulls() + { + // Act + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", null, null); + + // Assert + Assert.Equal("rni_name", fieldInfo.Type); + Assert.Null(fieldInfo.Weight); + Assert.Null(fieldInfo.ScoreIfNull); + } + + [Fact] + public void Constructor_WithZeroWeight_AcceptsZero() + { + // Act + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 0.0, 0.0); + + // Assert + Assert.Equal(0.0, fieldInfo.Weight); + Assert.Equal(0.0, fieldInfo.ScoreIfNull); + } + + [Fact] + public void Constructor_WithMaxWeight_AcceptsOne() + { + // Act + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 1.0, 1.0); + + // Assert + Assert.Equal(1.0, fieldInfo.Weight); + Assert.Equal(1.0, fieldInfo.ScoreIfNull); + } + + #endregion + + #region Property Tests + + [Fact] + public void Type_CanBeSet_ToValidAlgorithm() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo(); + var validTypes = new[] { "rni_name", "rni_date", "rni_address", "string" }; + + // Act & Assert + foreach (var type in validTypes) + { + fieldInfo.Type = type; + Assert.Equal(type, fieldInfo.Type); + } + } + + [Fact] + public void Type_CanBeSetToEmptyString() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo(); + + // Act + fieldInfo.Type = string.Empty; + + // Assert + Assert.Equal(string.Empty, fieldInfo.Type); + } + + [Fact] + public void Weight_CanBeSet_ToValidRange() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo(); + + // Act & Assert + fieldInfo.Weight = 0.0; + Assert.Equal(0.0, fieldInfo.Weight); + + fieldInfo.Weight = 0.5; + Assert.Equal(0.5, fieldInfo.Weight); + + fieldInfo.Weight = 1.0; + Assert.Equal(1.0, fieldInfo.Weight); + } + + [Fact] + public void Weight_CanBeSetToNull() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("type", 0.8, null); + + // Act + fieldInfo.Weight = null; + + // Assert + Assert.Null(fieldInfo.Weight); + } + + [Fact] + public void ScoreIfNull_CanBeSet_ToValidRange() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo(); + + // Act & Assert + fieldInfo.ScoreIfNull = 0.0; + Assert.Equal(0.0, fieldInfo.ScoreIfNull); + + fieldInfo.ScoreIfNull = 0.5; + Assert.Equal(0.5, fieldInfo.ScoreIfNull); + + fieldInfo.ScoreIfNull = 1.0; + Assert.Equal(1.0, fieldInfo.ScoreIfNull); + } + + [Fact] + public void ScoreIfNull_CanBeSetToNull() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("type", null, 0.5); + + // Act + fieldInfo.ScoreIfNull = null; + + // Assert + Assert.Null(fieldInfo.ScoreIfNull); + } + + [Fact] + public void Properties_CanBeModifiedIndependently() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo(); + + // Act + fieldInfo.Type = "rni_name"; + Assert.Equal("rni_name", fieldInfo.Type); + Assert.Null(fieldInfo.Weight); + + fieldInfo.Weight = 0.8; + Assert.Equal(0.8, fieldInfo.Weight); + Assert.Null(fieldInfo.ScoreIfNull); + + fieldInfo.ScoreIfNull = 0.5; + Assert.Equal(0.5, fieldInfo.ScoreIfNull); + + // Assert all are set + Assert.Equal("rni_name", fieldInfo.Type); + Assert.Equal(0.8, fieldInfo.Weight); + Assert.Equal(0.5, fieldInfo.ScoreIfNull); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameValues_ReturnsTrue() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act & Assert + Assert.True(fieldInfo1.Equals(fieldInfo2)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_date", 0.8, 0.5); + + // Act & Assert + Assert.False(fieldInfo1.Equals(fieldInfo2)); + } + + [Fact] + public void Equals_DifferentWeight_ReturnsFalse() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_name", 0.7, 0.5); + + // Act & Assert + Assert.False(fieldInfo1.Equals(fieldInfo2)); + } + + [Fact] + public void Equals_DifferentScoreIfNull_ReturnsFalse() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.4); + + // Act & Assert + Assert.False(fieldInfo1.Equals(fieldInfo2)); + } + + [Fact] + public void Equals_BothNullWeights_ReturnsTrue() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", null, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_name", null, 0.5); + + // Act & Assert + Assert.True(fieldInfo1.Equals(fieldInfo2)); + } + + [Fact] + public void Equals_OneNullWeightOneNot_ReturnsFalse() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", null, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act & Assert + Assert.False(fieldInfo1.Equals(fieldInfo2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act & Assert + Assert.False(fieldInfo.Equals(null)); + } + + [Fact] + public void Equals_DifferentObjectType_ReturnsFalse() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + var differentType = "string"; + + // Act & Assert + Assert.False(fieldInfo.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act & Assert + Assert.True(fieldInfo.Equals(fieldInfo)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameValues_ReturnsSameHashCode() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act + int hash1 = fieldInfo1.GetHashCode(); + int hash2 = fieldInfo2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentValues_ReturnsDifferentHashCode() + { + // Arrange + var fieldInfo1 = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + var fieldInfo2 = new RecordSimilarityFieldInfo("rni_date", 0.7, 0.4); + + // Act + int hash1 = fieldInfo1.GetHashCode(); + int hash2 = fieldInfo2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_NullValues_ReturnsConsistentValue() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("type", null, null); + + // Act + int hash1 = fieldInfo.GetHashCode(); + int hash2 = fieldInfo.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_ReturnsValidJson() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act + string json = fieldInfo.ToString(); + + // Assert + Assert.Contains("\"type\"", json); + Assert.Contains("rni_name", json); + Assert.Contains("\"weight\"", json); + Assert.Contains("0.8", json); + Assert.Contains("\"scoreIfNull\"", json); + Assert.Contains("0.5", json); + } + + [Fact] + public void ToString_WithNullValues_ProducesValidJson() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", null, null); + + // Act + string json = fieldInfo.ToString(); + + // Assert + Assert.Contains("\"type\"", json); + Assert.Contains("rni_name", json); + } + + [Fact] + public void ToString_CanBeParsedBack() + { + // Arrange + var original = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act + string json = original.ToString(); + var parsed = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(parsed); + Assert.Equal(original.Type, parsed.Type); + Assert.Equal(original.Weight, parsed.Weight); + Assert.Equal(original.ScoreIfNull, parsed.ScoreIfNull); + } + + #endregion + + #region Serialization Tests + + [Fact] + public void Serialization_WithAllProperties_ProducesValidJson() + { + // Arrange + var fieldInfo = new RecordSimilarityFieldInfo("rni_name", 0.8, 0.5); + + // Act + string json = JsonSerializer.Serialize(fieldInfo); + + // Assert + Assert.Contains("\"type\"", json); + Assert.Contains("\"weight\"", json); + Assert.Contains("\"scoreIfNull\"", json); + } + + [Fact] + public void Deserialization_FromJson_CreatesCorrectObject() + { + // Arrange + string json = @"{ + ""type"": ""rni_date"", + ""weight"": 0.9, + ""scoreIfNull"": 0.3 + }"; + + // Act + var fieldInfo = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(fieldInfo); + Assert.Equal("rni_date", fieldInfo.Type); + Assert.Equal(0.9, fieldInfo.Weight); + Assert.Equal(0.3, fieldInfo.ScoreIfNull); + } + + [Fact] + public void RoundTrip_PreservesAllData() + { + // Arrange + var original = new RecordSimilarityFieldInfo("rni_address", 0.75, 0.25); + + // Act + string json = JsonSerializer.Serialize(original); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + Assert.Equal(original.Type, deserialized.Type); + Assert.Equal(original.Weight, deserialized.Weight); + Assert.Equal(original.ScoreIfNull, deserialized.ScoreIfNull); + } + + #endregion + + #region Validation Tests + + [Fact] + public void Type_SupportsCommonAlgorithms() + { + // Arrange + var algorithms = new[] + { + "rni_name", + "rni_date", + "rni_address", + "string", + "number", + "boolean" + }; + + // Act & Assert + foreach (var algorithm in algorithms) + { + var fieldInfo = new RecordSimilarityFieldInfo(algorithm, 0.8, 0.5); + Assert.Equal(algorithm, fieldInfo.Type); + } + } + + [Fact] + public void Weight_AcceptsBoundaryValues() + { + // Act & Assert + var fieldInfo1 = new RecordSimilarityFieldInfo("type", 0.0, null); + Assert.Equal(0.0, fieldInfo1.Weight); + + var fieldInfo2 = new RecordSimilarityFieldInfo("type", 1.0, null); + Assert.Equal(1.0, fieldInfo2.Weight); + } + + [Fact] + public void ScoreIfNull_AcceptsBoundaryValues() + { + // Act & Assert + var fieldInfo1 = new RecordSimilarityFieldInfo("type", null, 0.0); + Assert.Equal(0.0, fieldInfo1.ScoreIfNull); + + var fieldInfo2 = new RecordSimilarityFieldInfo("type", null, 1.0); + Assert.Equal(1.0, fieldInfo2.ScoreIfNull); + } + + #endregion +} diff --git a/tests/RecordSimilarityPropertiesTests.cs b/tests/RecordSimilarityPropertiesTests.cs new file mode 100644 index 0000000..040b17c --- /dev/null +++ b/tests/RecordSimilarityPropertiesTests.cs @@ -0,0 +1,465 @@ +using Rosette.Api.Client.Models; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for RecordSimilarityProperties class +/// +public class RecordSimilarityPropertiesTests +{ + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_SetsDefaultValues() + { + // Act + var props = new RecordSimilarityProperties(); + + // Assert + Assert.Equal(0.0, props.Threshold); + Assert.Null(props.IncludeExplainInfo); + Assert.Null(props.Parameters); + Assert.Null(props.ParameterUniverse); + } + + [Fact] + public void Constructor_WithIncludeExplainInfo_SetsCorrectValues() + { + // Act + var props = new RecordSimilarityProperties(true); + + // Assert + Assert.True(props.IncludeExplainInfo); + Assert.Equal(0.0, props.Threshold); + Assert.NotNull(props.Parameters); + Assert.Empty(props.Parameters); + Assert.Equal(string.Empty, props.ParameterUniverse); + } + + [Fact] + public void Constructor_WithIncludeExplainInfoFalse_SetsCorrectValues() + { + // Act + var props = new RecordSimilarityProperties(false); + + // Assert + Assert.False(props.IncludeExplainInfo); + Assert.Equal(0.0, props.Threshold); + Assert.NotNull(props.Parameters); + Assert.Empty(props.Parameters); + Assert.Equal(string.Empty, props.ParameterUniverse); + } + + [Fact] + public void Constructor_WithAllParameters_SetsAllValues() + { + // Arrange + var parameters = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + + // Act + var props = new RecordSimilarityProperties(0.75, true, parameters, "test-universe"); + + // Assert + Assert.Equal(0.75, props.Threshold); + Assert.True(props.IncludeExplainInfo); + Assert.Equal(parameters, props.Parameters); + Assert.Equal("test-universe", props.ParameterUniverse); + } + + [Fact] + public void Constructor_WithNullParameters_AcceptsNull() + { + // Act + var props = new RecordSimilarityProperties(0.5, false, null, null); + + // Assert + Assert.Equal(0.5, props.Threshold); + Assert.False(props.IncludeExplainInfo); + Assert.Null(props.Parameters); + Assert.Null(props.ParameterUniverse); + } + + #endregion + + #region Property Tests + + [Fact] + public void Threshold_CanBeSet_ToValidValue() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + props.Threshold = 0.85; + + // Assert + Assert.Equal(0.85, props.Threshold); + } + + [Fact] + public void Threshold_AcceptsZero() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + props.Threshold = 0.0; + + // Assert + Assert.Equal(0.0, props.Threshold); + } + + [Fact] + public void Threshold_AcceptsOne() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + props.Threshold = 1.0; + + // Assert + Assert.Equal(1.0, props.Threshold); + } + + [Fact] + public void Threshold_CanBeNull() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + props.Threshold = null; + + // Assert + Assert.Null(props.Threshold); + } + + [Fact] + public void IncludeExplainInfo_CanBeToggled() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act & Assert + props.IncludeExplainInfo = true; + Assert.True(props.IncludeExplainInfo); + + props.IncludeExplainInfo = false; + Assert.False(props.IncludeExplainInfo); + + props.IncludeExplainInfo = null; + Assert.Null(props.IncludeExplainInfo); + } + + [Fact] + public void Parameters_CanBeSetAndModified() + { + // Arrange + var props = new RecordSimilarityProperties(); + var parameters = new Dictionary + { + { "param1", "value1" } + }; + + // Act + props.Parameters = parameters; + props.Parameters["param2"] = "value2"; + + // Assert + Assert.Equal(2, props.Parameters.Count); + Assert.Equal("value1", props.Parameters["param1"]); + Assert.Equal("value2", props.Parameters["param2"]); + } + + [Fact] + public void Parameters_CanBeEmpty() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + props.Parameters = new Dictionary(); + + // Assert + Assert.NotNull(props.Parameters); + Assert.Empty(props.Parameters); + } + + [Fact] + public void ParameterUniverse_CanBeSet() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + props.ParameterUniverse = "production-universe"; + + // Assert + Assert.Equal("production-universe", props.ParameterUniverse); + } + + [Fact] + public void ParameterUniverse_CanBeEmptyString() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + props.ParameterUniverse = string.Empty; + + // Assert + Assert.Equal(string.Empty, props.ParameterUniverse); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameValues_ReturnsTrue() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, + new Dictionary { { "key", "value" } }, "universe"); + var props2 = new RecordSimilarityProperties(0.75, true, + new Dictionary { { "key", "value" } }, "universe"); + + // Act & Assert + Assert.True(props1.Equals(props2)); + } + + [Fact] + public void Equals_DifferentThreshold_ReturnsFalse() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, null, null); + var props2 = new RecordSimilarityProperties(0.80, true, null, null); + + // Act & Assert + Assert.False(props1.Equals(props2)); + } + + [Fact] + public void Equals_DifferentIncludeExplainInfo_ReturnsFalse() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, null, null); + var props2 = new RecordSimilarityProperties(0.75, false, null, null); + + // Act & Assert + Assert.False(props1.Equals(props2)); + } + + [Fact] + public void Equals_DifferentParameters_ReturnsFalse() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, + new Dictionary { { "key", "value1" } }, null); + var props2 = new RecordSimilarityProperties(0.75, true, + new Dictionary { { "key", "value2" } }, null); + + // Act & Assert + Assert.False(props1.Equals(props2)); + } + + [Fact] + public void Equals_DifferentParameterUniverse_ReturnsFalse() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, null, "universe1"); + var props2 = new RecordSimilarityProperties(0.75, true, null, "universe2"); + + // Act & Assert + Assert.False(props1.Equals(props2)); + } + + [Fact] + public void Equals_BothParametersNull_ReturnsTrue() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, null, "universe"); + var props2 = new RecordSimilarityProperties(0.75, true, null, "universe"); + + // Act & Assert + Assert.True(props1.Equals(props2)); + } + + [Fact] + public void Equals_OneParameterNullOneNot_ReturnsFalse() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, null, null); + var props2 = new RecordSimilarityProperties(0.75, true, + new Dictionary(), null); + + // Act & Assert + Assert.False(props1.Equals(props2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act & Assert + Assert.False(props.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var props = new RecordSimilarityProperties(); + var differentType = "string"; + + // Act & Assert + Assert.False(props.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var props = new RecordSimilarityProperties(0.75, true, null, null); + + // Act & Assert + Assert.True(props.Equals(props)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameValues_ReturnsSameHashCode() + { + // Arrange - Use same dictionary reference for consistent hash + var parameters = new Dictionary { { "key", "value" } }; + var props1 = new RecordSimilarityProperties(0.75, true, parameters, "universe"); + var props2 = new RecordSimilarityProperties(0.75, true, parameters, "universe"); + + // Act + int hash1 = props1.GetHashCode(); + int hash2 = props2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_DifferentValues_ReturnsDifferentHashCode() + { + // Arrange + var props1 = new RecordSimilarityProperties(0.75, true, null, null); + var props2 = new RecordSimilarityProperties(0.80, false, null, null); + + // Act + int hash1 = props1.GetHashCode(); + int hash2 = props2.GetHashCode(); + + // Assert + Assert.NotEqual(hash1, hash2); + } + + [Fact] + public void GetHashCode_NullParameters_ReturnsConsistentValue() + { + // Arrange + var props = new RecordSimilarityProperties(0.75, true, null, null); + + // Act + int hash1 = props.GetHashCode(); + int hash2 = props.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + #endregion + + #region Serialization Tests + + [Fact] + public void Serialization_WithAllProperties_ProducesValidJson() + { + // Arrange + var props = new RecordSimilarityProperties(0.75, true, + new Dictionary { { "key", "value" } }, "test-universe"); + + // Act + string json = JsonSerializer.Serialize(props); + + // Assert + Assert.Contains("\"threshold\"", json); + Assert.Contains("0.75", json); + Assert.Contains("\"includeExplainInfo\"", json); + Assert.Contains("true", json); + Assert.Contains("\"parameters\"", json); + Assert.Contains("\"parameterUniverse\"", json); + Assert.Contains("test-universe", json); + } + + [Fact] + public void Serialization_WithDefaults_ProducesValidJson() + { + // Arrange + var props = new RecordSimilarityProperties(); + + // Act + string json = JsonSerializer.Serialize(props); + + // Assert + Assert.Contains("\"threshold\"", json); + } + + [Fact] + public void Deserialization_FromJson_CreatesCorrectObject() + { + // Arrange + string json = @"{ + ""threshold"": 0.85, + ""includeExplainInfo"": true, + ""parameters"": {""key1"": ""value1""}, + ""parameterUniverse"": ""test"" + }"; + + // Act + var props = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(props); + Assert.Equal(0.85, props.Threshold); + Assert.True(props.IncludeExplainInfo); + Assert.Single(props.Parameters); + Assert.Equal("value1", props.Parameters["key1"]); + Assert.Equal("test", props.ParameterUniverse); + } + + [Fact] + public void RoundTrip_PreservesAllData() + { + // Arrange + var original = new RecordSimilarityProperties(0.75, true, + new Dictionary { { "key", "value" } }, "universe"); + + // Act + string json = JsonSerializer.Serialize(original); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert + Assert.NotNull(deserialized); + Assert.Equal(original.Threshold, deserialized.Threshold); + Assert.Equal(original.IncludeExplainInfo, deserialized.IncludeExplainInfo); + Assert.Equal(original.ParameterUniverse, deserialized.ParameterUniverse); + } + + #endregion +} diff --git a/tests/RecordSimilarityRecordsEnhancedTests.cs b/tests/RecordSimilarityRecordsEnhancedTests.cs new file mode 100644 index 0000000..7a7c3a5 --- /dev/null +++ b/tests/RecordSimilarityRecordsEnhancedTests.cs @@ -0,0 +1,616 @@ +using Rosette.Api.Client.Models; +using Rosette.Api.Client.Models.JsonConverter; +using System.Text.Json; + +namespace Rosette.Api.Tests; + +/// +/// Tests for RecordSimilarityRecords class +/// +public class RecordSimilarityRecordsEnhancedTests +{ + private readonly JsonSerializerOptions _options; + + public RecordSimilarityRecordsEnhancedTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new RecordSimilarityRecordsConverter()); + _options.Converters.Add(new RecordSimilarityFieldConverter()); + _options.Converters.Add(new UnfieldedRecordSimilarityConverter()); + } + + #region Constructor Tests + + [Fact] + public void Constructor_NoArgs_SetsDefaultValues() + { + // Act + var records = new RecordSimilarityRecords(); + + // Assert + Assert.Null(records.Left); + Assert.Null(records.Right); + } + + [Fact] + public void Constructor_WithEmptyLists_StoresEmptyLists() + { + // Arrange + var left = new List>(); + var right = new List>(); + + // Act + var records = new RecordSimilarityRecords(left, right); + + // Assert + Assert.NotNull(records.Left); + Assert.NotNull(records.Right); + Assert.Empty(records.Left); + Assert.Empty(records.Right); + } + + [Fact] + public void Constructor_WithData_StoresDataCorrectly() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "John" } } + } + }; + var right = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Jane" } } + } + }; + + // Act + var records = new RecordSimilarityRecords(left, right); + + // Assert + Assert.Single(records.Left); + Assert.Single(records.Right); + Assert.Single(records.Left[0]); + Assert.Single(records.Right[0]); + } + + [Fact] + public void Constructor_WithNullLists_AcceptsNulls() + { + // Act + var records = new RecordSimilarityRecords(null, null); + + // Assert + Assert.Null(records.Left); + Assert.Null(records.Right); + } + + [Fact] + public void Constructor_WithMultipleRecords_StoresAll() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "field1", new UnfieldedNameRecord { Text = "Value1" } } + }, + new Dictionary + { + { "field2", new UnfieldedNameRecord { Text = "Value2" } } + }, + new Dictionary + { + { "field3", new UnfieldedNameRecord { Text = "Value3" } } + } + }; + var right = new List>(); + + // Act + var records = new RecordSimilarityRecords(left, right); + + // Assert + Assert.Equal(3, records.Left.Count); + Assert.Empty(records.Right); + } + + #endregion + + #region Property Tests + + [Fact] + public void Left_CanBeModified_AfterConstruction() + { + // Arrange + var records = new RecordSimilarityRecords(); + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Test" } } + } + }; + + // Act + records.Left = left; + + // Assert + Assert.NotNull(records.Left); + Assert.Single(records.Left); + } + + [Fact] + public void Right_CanBeModified_AfterConstruction() + { + // Arrange + var records = new RecordSimilarityRecords(); + var right = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Test" } } + } + }; + + // Act + records.Right = right; + + // Assert + Assert.NotNull(records.Right); + Assert.Single(records.Right); + } + + [Fact] + public void Left_And_Right_AreIndependent() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Left" } } + } + }; + var right = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Right" } } + } + }; + var records = new RecordSimilarityRecords(left, right); + + // Act - Modify left + records.Left.Add(new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Left2" } } + }); + + // Assert - Right unchanged + Assert.Equal(2, records.Left.Count); + Assert.Single(records.Right); + } + + [Fact] + public void Records_CanContainMultipleFields() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "John" } }, + { "age", new NumberRecord(30) }, + { "active", new BooleanRecord(true) }, + { "address", new UnfieldedAddressRecord { Address = "123 Main" } } + } + }; + var records = new RecordSimilarityRecords(left, new List>()); + + // Act & Assert + Assert.Equal(4, records.Left[0].Count); + Assert.IsType(records.Left[0]["name"]); + Assert.IsType(records.Left[0]["age"]); + Assert.IsType(records.Left[0]["active"]); + Assert.IsType(records.Left[0]["address"]); + } + + #endregion + + #region Equals Tests + + [Fact] + public void Equals_SameValues_ReturnsTrue() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "John" } } + } + }; + var records1 = new RecordSimilarityRecords(left, new List>()); + var records2 = new RecordSimilarityRecords(left, new List>()); + + // Act & Assert + Assert.True(records1.Equals(records2)); + } + + [Fact] + public void Equals_DifferentLeftCount_ReturnsFalse() + { + // Arrange + var left1 = new List> + { + new Dictionary() + }; + var left2 = new List> + { + new Dictionary(), + new Dictionary() + }; + var records1 = new RecordSimilarityRecords(left1, new List>()); + var records2 = new RecordSimilarityRecords(left2, new List>()); + + // Act & Assert + Assert.False(records1.Equals(records2)); + } + + [Fact] + public void Equals_BothLeftNull_ReturnsTrue() + { + // Arrange + var records1 = new RecordSimilarityRecords(null, new List>()); + var records2 = new RecordSimilarityRecords(null, new List>()); + + // Act & Assert + Assert.True(records1.Equals(records2)); + } + + [Fact] + public void Equals_OneLeftNullOneNot_ReturnsFalse() + { + // Arrange + var records1 = new RecordSimilarityRecords(null, new List>()); + var records2 = new RecordSimilarityRecords( + new List>(), + new List>()); + + // Act & Assert + Assert.False(records1.Equals(records2)); + } + + [Fact] + public void Equals_BothRightNull_ReturnsTrue() + { + // Arrange + var records1 = new RecordSimilarityRecords(new List>(), null); + var records2 = new RecordSimilarityRecords(new List>(), null); + + // Act & Assert + Assert.True(records1.Equals(records2)); + } + + [Fact] + public void Equals_Null_ReturnsFalse() + { + // Arrange + var records = new RecordSimilarityRecords(); + + // Act & Assert + Assert.False(records.Equals(null)); + } + + [Fact] + public void Equals_DifferentType_ReturnsFalse() + { + // Arrange + var records = new RecordSimilarityRecords(); + var differentType = "string"; + + // Act & Assert + Assert.False(records.Equals(differentType)); + } + + [Fact] + public void Equals_SameReference_ReturnsTrue() + { + // Arrange + var records = new RecordSimilarityRecords( + new List>(), + new List>()); + + // Act & Assert + Assert.True(records.Equals(records)); + } + + #endregion + + #region GetHashCode Tests + + [Fact] + public void GetHashCode_SameValues_ReturnsSameHashCode() + { + // Arrange - Use same list references for consistent hash + var left = new List> + { + new Dictionary() + }; + var right = new List>(); + var records1 = new RecordSimilarityRecords(left, right); + var records2 = new RecordSimilarityRecords(left, right); + + // Act + int hash1 = records1.GetHashCode(); + int hash2 = records2.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_NullValues_ReturnsConsistentValue() + { + // Arrange + var records = new RecordSimilarityRecords(null, null); + + // Act + int hash1 = records.GetHashCode(); + int hash2 = records.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + } + + [Fact] + public void GetHashCode_CalledMultipleTimes_ReturnsSameValue() + { + // Arrange + var records = new RecordSimilarityRecords( + new List>(), + new List>()); + + // Act + int hash1 = records.GetHashCode(); + int hash2 = records.GetHashCode(); + int hash3 = records.GetHashCode(); + + // Assert + Assert.Equal(hash1, hash2); + Assert.Equal(hash2, hash3); + } + + #endregion + + #region ToString Tests + + [Fact] + public void ToString_EmptyRecords_ReturnsValidJson() + { + // Arrange + var records = new RecordSimilarityRecords( + new List>(), + new List>()); + + // Act + string json = records.ToString(); + + // Assert + Assert.Contains("\"left\"", json); + Assert.Contains("\"right\"", json); + Assert.Contains("[]", json); + } + + [Fact] + public void ToString_WithRecords_ReturnsValidJson() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "John" } } + } + }; + var records = new RecordSimilarityRecords(left, new List>()); + + // Act + string json = records.ToString(); + + // Assert + Assert.Contains("\"left\"", json); + Assert.Contains("\"right\"", json); + Assert.Contains("John", json); + } + + [Fact] + public void ToString_ProducesValidParsableJson() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Test" } } + } + }; + var records = new RecordSimilarityRecords(left, new List>()); + + // Act + string json = records.ToString(); + + // Assert - Should be valid JSON + var parsed = JsonDocument.Parse(json); + Assert.NotNull(parsed); + } + + #endregion + + #region Serialization Tests + + [Fact] + public void Serialization_EmptyRecords_ProducesValidJson() + { + // Arrange + var records = new RecordSimilarityRecords( + new List>(), + new List>()); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("\"left\"", json); + Assert.Contains("\"right\"", json); + } + + [Fact] + public void Serialization_WithFieldedRecords_SerializesCorrectly() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new FieldedNameRecord { Text = "John", Language = "eng" } } + } + }; + var records = new RecordSimilarityRecords(left, new List>()); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("John", json); + Assert.Contains("eng", json); + } + + [Fact] + public void Serialization_WithMixedFieldTypes_SerializesAll() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "John" } }, + { "age", new NumberRecord(30) }, + { "active", new BooleanRecord(true) }, + { "fieldedName", new FieldedNameRecord { Text = "Jane", Language = "eng" } } + } + }; + var records = new RecordSimilarityRecords(left, new List>()); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("John", json); + Assert.Contains("30", json); + Assert.Contains("true", json); + Assert.Contains("Jane", json); + Assert.Contains("eng", json); + } + + [Fact] + public void Serialization_MultipleRecords_PreservesOrder() + { + // Arrange + var left = new List> + { + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "First" } } + }, + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Second" } } + }, + new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "Third" } } + } + }; + var records = new RecordSimilarityRecords(left, new List>()); + + // Act + string json = JsonSerializer.Serialize(records, _options); + + // Assert + Assert.Contains("First", json); + Assert.Contains("Second", json); + Assert.Contains("Third", json); + + // Verify order + int firstPos = json.IndexOf("First"); + int secondPos = json.IndexOf("Second"); + int thirdPos = json.IndexOf("Third"); + Assert.True(firstPos < secondPos); + Assert.True(secondPos < thirdPos); + } + + #endregion + + #region Collection Manipulation Tests + + [Fact] + public void AddRecord_ToLeft_IncreasesCount() + { + // Arrange + var records = new RecordSimilarityRecords( + new List>(), + new List>()); + + // Act + records.Left.Add(new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "New" } } + }); + + // Assert + Assert.Single(records.Left); + } + + [Fact] + public void AddRecord_ToRight_IncreasesCount() + { + // Arrange + var records = new RecordSimilarityRecords( + new List>(), + new List>()); + + // Act + records.Right.Add(new Dictionary + { + { "name", new UnfieldedNameRecord { Text = "New" } } + }); + + // Assert + Assert.Single(records.Right); + } + + [Fact] + public void ClearRecords_RemovesAllEntries() + { + // Arrange + var left = new List> + { + new Dictionary(), + new Dictionary() + }; + var records = new RecordSimilarityRecords(left, new List>()); + + // Act + records.Left.Clear(); + + // Assert + Assert.Empty(records.Left); + } + + #endregion +} diff --git a/tests/ResponseTests.cs b/tests/ResponseTests.cs index 5e56b90..648eb6a 100644 --- a/tests/ResponseTests.cs +++ b/tests/ResponseTests.cs @@ -28,5 +28,536 @@ public void Constructor_SetsStatusCodeAndContent_WhenHttpResponseIsOK() { } + [Fact] + public void ContentAsJson_WithPrettyTrue_ReturnsFormattedJson() + { + // Arrange + Dictionary data = new() + { + { "key1", "value1" }, + { "key2", "value2" } + }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK); + msg.Content = new StringContent(json); + + Response response = new(msg); + + // Act + string prettyJson = (string)response.ContentAsJson(pretty: true); + + // Assert + Assert.Contains("\n", prettyJson); // Pretty JSON should have newlines + Assert.Contains("key1", prettyJson); + Assert.Contains("value1", prettyJson); + } + + [Fact] + public void ContentAsJson_WithPrettyFalse_ReturnsCompactJson() + { + // Arrange + Dictionary data = new() + { + { "key1", "value1" } + }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK); + msg.Content = new StringContent(json); + + Response response = new(msg); + + // Act + string compactJson = (string)response.ContentAsJson(pretty: false); + + // Assert + Assert.NotNull(compactJson); + Assert.Contains("key1", compactJson); + } + + #endregion + + #region Error Response Tests + + [Fact] + public void Constructor_ThrowsHttpRequestException_WhenStatusCodeIsNotSuccess() + { + // Arrange + HttpResponseMessage msg = new(HttpStatusCode.BadRequest) + { + ReasonPhrase = "Bad Request", + Content = new StringContent("Invalid request parameters") + }; + + // Act & Assert + var exception = Assert.Throws(() => new Response(msg)); + Assert.Contains("400", exception.Message); + Assert.Contains("Bad Request", exception.Message); + Assert.Contains("Invalid request parameters", exception.Message); + } + + [Fact] + public void Constructor_ThrowsHttpRequestException_WhenStatusCodeIs404() + { + // Arrange + HttpResponseMessage msg = new(HttpStatusCode.NotFound) + { + ReasonPhrase = "Not Found", + Content = new StringContent("Resource not found") + }; + + // Act & Assert + var exception = Assert.Throws(() => new Response(msg)); + Assert.Contains("404", exception.Message); + Assert.Contains("Not Found", exception.Message); + } + + [Fact] + public void Constructor_ThrowsHttpRequestException_WhenStatusCodeIs500() + { + // Arrange + HttpResponseMessage msg = new(HttpStatusCode.InternalServerError) + { + ReasonPhrase = "Internal Server Error", + Content = new StringContent("Server error occurred") + }; + + // Act & Assert + var exception = Assert.Throws(() => new Response(msg)); + Assert.Contains("500", exception.Message); + Assert.Contains("Internal Server Error", exception.Message); + } + + [Fact] + public void Constructor_ThrowsHttpRequestException_WhenStatusCodeIs401() + { + // Arrange + HttpResponseMessage msg = new(HttpStatusCode.Unauthorized) + { + ReasonPhrase = "Unauthorized", + Content = new StringContent("Invalid API key") + }; + + // Act & Assert + var exception = Assert.Throws(() => new Response(msg)); + Assert.Contains("401", exception.Message); + Assert.Contains("Unauthorized", exception.Message); + Assert.Contains("Invalid API key", exception.Message); + } + + [Fact] + public async Task CreateAsync_ThrowsHttpRequestException_WhenStatusCodeIsNotSuccess() + { + // Arrange + HttpResponseMessage msg = new(HttpStatusCode.BadRequest) + { + ReasonPhrase = "Bad Request", + Content = new StringContent("Invalid parameters") + }; + + // Act & Assert + var exception = await Assert.ThrowsAsync( + async () => await Response.CreateAsync(msg)); + Assert.Contains("400", exception.Message); + Assert.Contains("Bad Request", exception.Message); + Assert.Contains("Invalid parameters", exception.Message); + } + + [Fact] + public async Task CreateAsync_ThrowsHttpRequestException_WhenStatusCodeIs503() + { + // Arrange + HttpResponseMessage msg = new(HttpStatusCode.ServiceUnavailable) + { + ReasonPhrase = "Service Unavailable", + Content = new StringContent("Service temporarily unavailable") + }; + + // Act & Assert + var exception = await Assert.ThrowsAsync( + async () => await Response.CreateAsync(msg)); + Assert.Contains("503", exception.Message); + Assert.Contains("Service Unavailable", exception.Message); + } + + #endregion + + #region Gzip Decompression Tests + + [Fact] + public void Constructor_DecompressesGzipContent_WhenContentIsGzipped() + { + // Arrange + Dictionary data = new() + { + { "result", "success" }, + { "message", "Data decompressed successfully" } + }; + string json = JsonSerializer.Serialize(data); + + // Create gzipped content + byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); + byte[] gzipBytes; + using (var outputStream = new MemoryStream()) + { + using (var gzipStream = new System.IO.Compression.GZipStream(outputStream, System.IO.Compression.CompressionMode.Compress)) + { + gzipStream.Write(jsonBytes, 0, jsonBytes.Length); + } + gzipBytes = outputStream.ToArray(); + } + + // Verify gzip header + Assert.Equal(0x1f, gzipBytes[0]); + Assert.Equal(0x8b, gzipBytes[1]); + Assert.Equal(0x08, gzipBytes[2]); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new ByteArrayContent(gzipBytes) + }; + + // Act + Response response = new(msg); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Contains("result", response.Content.Keys); + Assert.Equal("success", response.Content["result"].ToString()); + } + + [Fact] + public async Task CreateAsync_DecompressesGzipContent_WhenContentIsGzipped() + { + // Arrange + Dictionary data = new() + { + { "status", "ok" }, + { "compressed", "true" } + }; + string json = JsonSerializer.Serialize(data); + + // Create gzipped content + byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); + byte[] gzipBytes; + using (var outputStream = new MemoryStream()) + { + using (var gzipStream = new System.IO.Compression.GZipStream(outputStream, System.IO.Compression.CompressionMode.Compress)) + { + gzipStream.Write(jsonBytes, 0, jsonBytes.Length); + } + gzipBytes = outputStream.ToArray(); + } + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new ByteArrayContent(gzipBytes) + }; + + // Act + Response response = await Response.CreateAsync(msg); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Contains("status", response.Content.Keys); + Assert.Equal("ok", response.Content["status"].ToString()); + Assert.Equal("true", response.Content["compressed"].ToString()); + } + + [Fact] + public void Constructor_HandlesLargeGzipContent_WhenContentIsLarge() + { + // Arrange - Create a large JSON object + var largeData = new Dictionary(); + for (int i = 0; i < 1000; i++) + { + largeData.Add($"key{i}", $"value{i}_{new string('x', 100)}"); + } + string json = JsonSerializer.Serialize(largeData); + + // Create gzipped content + byte[] jsonBytes = System.Text.Encoding.UTF8.GetBytes(json); + byte[] gzipBytes; + using (var outputStream = new MemoryStream()) + { + using (var gzipStream = new System.IO.Compression.GZipStream(outputStream, System.IO.Compression.CompressionMode.Compress)) + { + gzipStream.Write(jsonBytes, 0, jsonBytes.Length); + } + gzipBytes = outputStream.ToArray(); + } + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new ByteArrayContent(gzipBytes) + }; + + // Act + Response response = new(msg); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Equal(1000, response.Content.Count); + Assert.Contains("key500", response.Content.Keys); + } + + [Fact] + public void Constructor_HandlesUncompressedContent_WhenContentIsNotGzipped() + { + // Arrange + Dictionary data = new() + { + { "compressed", "false" } + }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent(json) + }; + + // Act + Response response = new(msg); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Contains("compressed", response.Content.Keys); + Assert.Equal("false", response.Content["compressed"].ToString()); + } + + #endregion + + #region Internal Method Tests via Reflection + + [Fact] + public void ContentToString_ReturnsEmptyString_WhenHttpContentIsNull() + { + // Act - Use reflection to access internal method + var methodInfo = typeof(Response).GetMethod("ContentToString", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + Assert.NotNull(methodInfo); + + string result = (string)methodInfo.Invoke(null, new object?[] { null })!; + + // Assert + Assert.Equal(string.Empty, result); + } + + [Fact] + public async Task ContentToStringAsync_ReturnsEmptyString_WhenHttpContentIsNull() + { + // Act - Use reflection to access internal method + var methodInfo = typeof(Response).GetMethod("ContentToStringAsync", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + Assert.NotNull(methodInfo); + + var task = (Task)methodInfo.Invoke(null, new object?[] { null, CancellationToken.None })!; + string result = await task; + + // Assert + Assert.Equal(string.Empty, result); + } + + [Fact] + public async Task ContentToStringAsync_ReturnsContent_WhenHttpContentIsNotNull() + { + // Arrange + var content = new StringContent("Test content"); + + // Act - Use reflection to access internal method + var methodInfo = typeof(Response).GetMethod("ContentToStringAsync", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + Assert.NotNull(methodInfo); + + var task = (Task)methodInfo.Invoke(null, new object?[] { content, CancellationToken.None })!; + string result = await task; + + // Assert + Assert.Equal("Test content", result); + } + + [Fact] + public async Task ContentToStringAsync_WithCancellationToken_ReturnsContent() + { + // Arrange + var content = new StringContent("Test with cancellation token"); + var cts = new CancellationTokenSource(); + + // Act - Use reflection to access internal method + var methodInfo = typeof(Response).GetMethod("ContentToStringAsync", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static); + Assert.NotNull(methodInfo); + + var task = (Task)methodInfo.Invoke(null, new object?[] { content, cts.Token })!; + string result = await task; + + // Assert + Assert.Equal("Test with cancellation token", result); + } + + #endregion + + #region Headers Tests + + [Fact] + public void Constructor_ProcessesResponseHeaders_WhenHeadersExist() + { + // Arrange + Dictionary data = new() { { "test", "data" } }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent(json) + }; + msg.Headers.Add("X-Custom-Header", "CustomValue"); + msg.Headers.Add("X-Request-Id", "12345"); + + // Act + Response response = new(msg); + + // Assert + Assert.Contains("X-Custom-Header", response.Headers.Keys); + Assert.Equal("CustomValue", response.Headers["X-Custom-Header"]); + // Note: Header names might be normalized (X-Request-Id becomes X-Request-ID) + Assert.True(response.Headers.ContainsKey("X-Request-Id") || response.Headers.ContainsKey("X-Request-ID")); + } + + [Fact] + public void Constructor_ProcessesContentHeaders_WhenContentHeadersExist() + { + // Arrange + Dictionary data = new() { { "test", "data" } }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent(json, System.Text.Encoding.UTF8, "application/json") + }; + + // Act + Response response = new(msg); + + // Assert + Assert.Contains("Content-Type", response.Headers.Keys); + Assert.Contains("application/json", response.Headers["Content-Type"]); + } + + [Fact] + public async Task CreateAsync_ProcessesHeaders_WhenHeadersExist() + { + // Arrange + Dictionary data = new() { { "async", "test" } }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent(json) + }; + msg.Headers.Add("X-Async-Header", "AsyncValue"); + + // Act + Response response = await Response.CreateAsync(msg); + + // Assert + Assert.Contains("X-Async-Header", response.Headers.Keys); + Assert.Equal("AsyncValue", response.Headers["X-Async-Header"]); + } + + #endregion + + #region Edge Case Tests + + [Fact] + public void Constructor_HandlesEmptyContent_WhenContentIsEmpty() + { + // Arrange + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent("{}") + }; + + // Act + Response response = new(msg); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Empty(response.Content); + } + + [Fact] + public void Constructor_HandlesMultipleHeaders_WhenManyHeadersExist() + { + // Arrange + Dictionary data = new() { { "data", "test" } }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent(json) + }; + msg.Headers.Add("Header1", "Value1"); + msg.Headers.Add("Header2", "Value2"); + msg.Headers.Add("Header3", "Value3"); + + // Act + Response response = new(msg); + + // Assert + Assert.True(response.Headers.Count >= 3); + Assert.Equal("Value1", response.Headers["Header1"]); + Assert.Equal("Value2", response.Headers["Header2"]); + Assert.Equal("Value3", response.Headers["Header3"]); + } + + [Fact] + public void Constructor_HandlesComplexJsonContent_WhenContentIsNested() + { + // Arrange + var complexData = new + { + user = new { id = 1, name = "John" }, + metadata = new { created = "2026-03-10", version = "1.0" }, + items = new[] { "item1", "item2", "item3" } + }; + string json = JsonSerializer.Serialize(complexData); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent(json) + }; + + // Act + Response response = new(msg); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Contains("user", response.Content.Keys); + Assert.Contains("metadata", response.Content.Keys); + Assert.Contains("items", response.Content.Keys); + } + + [Fact] + public async Task CreateAsync_WithCancellationToken_CompletesSuccessfully() + { + // Arrange + Dictionary data = new() { { "status", "success" } }; + string json = JsonSerializer.Serialize(data); + + HttpResponseMessage msg = new(HttpStatusCode.OK) + { + Content = new StringContent(json) + }; + var cts = new CancellationTokenSource(); + + // Act + Response response = await Response.CreateAsync(msg, cts.Token); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, response.StatusCode); + Assert.Contains("status", response.Content.Keys); + } + #endregion } From c456a07ec0f8b118ffe1b70a410b2e3dd77b50ba Mon Sep 17 00:00:00 2001 From: Curtis Ransom Date: Tue, 10 Mar 2026 13:59:44 -0400 Subject: [PATCH 7/7] BX-69342: Reorganizes test files and namespaces to reflect the objects in the client api project --- tests/EndpointBaseTests.cs | 629 ------------------ .../{ => Endpoints}/AddressSimilarityTests.cs | 2 +- tests/{ => Endpoints}/CategoriesTests.cs | 4 +- .../Core}/ContentEndpointBaseTests.cs | 6 +- tests/Endpoints/Core/EndpointBaseTests.cs | 628 +++++++++++++++++ .../Core}/EndpointExecutorTests.cs | 4 +- .../Core}/ValidEndpointTests.cs | 4 +- tests/{ => Endpoints}/EntitiesTests.cs | 2 +- tests/{ => Endpoints}/EventsTests.cs | 2 +- tests/{ => Endpoints}/InfoTests.cs | 2 +- tests/{ => Endpoints}/LanguageTests.cs | 4 +- tests/{ => Endpoints}/MorphologyTests.cs | 2 +- .../{ => Endpoints}/NameDeduplicationTests.cs | 4 +- tests/{ => Endpoints}/NameSimilarityTests.cs | 4 +- tests/{ => Endpoints}/NameTranslationTests.cs | 4 +- tests/{ => Endpoints}/PingTests.cs | 2 +- .../{ => Endpoints}/RecordSimilarityTests.cs | 2 +- tests/{ => Endpoints}/RelationshipsTests.cs | 2 +- tests/{ => Endpoints}/SemanticsVectorTests.cs | 2 +- tests/{ => Endpoints}/SentencesTests.cs | 4 +- tests/{ => Endpoints}/SentimentTests.cs | 4 +- tests/{ => Endpoints}/SimilarTermsTests.cs | 4 +- .../SyntaxDependenciesTests.cs | 2 +- tests/{ => Endpoints}/TextEmbeddingTests.cs | 2 +- tests/{ => Endpoints}/TokensTests.cs | 4 +- tests/{ => Endpoints}/TopicsTests.cs | 2 +- tests/{ => Endpoints}/TransliterationTests.cs | 2 +- tests/{ => Models}/BooleanRecordTests.cs | 2 +- .../{ => Models}/FieldedAddressRecordTests.cs | 2 +- tests/{ => Models}/FieldedDateRecordTests.cs | 2 +- tests/{ => Models}/FieldedNameRecordTests.cs | 2 +- .../RecordSimilarityFieldConverterTests.cs | 2 +- .../RecordSimilarityRecordsConverterTests.cs | 2 +- ...UnfieldedRecordSimilarityConverterTests.cs | 2 +- tests/{ => Models}/NameTests.cs | 4 +- tests/{ => Models}/NumberRecordTests.cs | 2 +- .../RecordSimilarityFieldInfoTests.cs | 2 +- .../RecordSimilarityPropertiesTests.cs | 2 +- .../RecordSimilarityRecordsEnhancedTests.cs | 2 +- tests/{ => Models}/ResponseTests.cs | 4 +- tests/{ => Models}/SimpleRecordModelsTests.cs | 2 +- tests/{ => Models}/StringRecordTests.cs | 2 +- .../UnfieldedAddressRecordTests.cs | 2 +- .../{ => Models}/UnfieldedDateRecordTests.cs | 2 +- .../{ => Models}/UnfieldedNameRecordTests.cs | 2 +- tests/{ => Models}/UnknownFieldRecordTests.cs | 2 +- 46 files changed, 687 insertions(+), 688 deletions(-) delete mode 100644 tests/EndpointBaseTests.cs rename tests/{ => Endpoints}/AddressSimilarityTests.cs (99%) rename tests/{ => Endpoints}/CategoriesTests.cs (96%) rename tests/{ => Endpoints/Core}/ContentEndpointBaseTests.cs (98%) create mode 100644 tests/Endpoints/Core/EndpointBaseTests.cs rename tests/{ => Endpoints/Core}/EndpointExecutorTests.cs (99%) rename tests/{ => Endpoints/Core}/ValidEndpointTests.cs (98%) rename tests/{ => Endpoints}/EntitiesTests.cs (97%) rename tests/{ => Endpoints}/EventsTests.cs (98%) rename tests/{ => Endpoints}/InfoTests.cs (98%) rename tests/{ => Endpoints}/LanguageTests.cs (96%) rename tests/{ => Endpoints}/MorphologyTests.cs (99%) rename tests/{ => Endpoints}/NameDeduplicationTests.cs (94%) rename tests/{ => Endpoints}/NameSimilarityTests.cs (97%) rename tests/{ => Endpoints}/NameTranslationTests.cs (94%) rename tests/{ => Endpoints}/PingTests.cs (98%) rename tests/{ => Endpoints}/RecordSimilarityTests.cs (99%) rename tests/{ => Endpoints}/RelationshipsTests.cs (99%) rename tests/{ => Endpoints}/SemanticsVectorTests.cs (98%) rename tests/{ => Endpoints}/SentencesTests.cs (95%) rename tests/{ => Endpoints}/SentimentTests.cs (96%) rename tests/{ => Endpoints}/SimilarTermsTests.cs (96%) rename tests/{ => Endpoints}/SyntaxDependenciesTests.cs (98%) rename tests/{ => Endpoints}/TextEmbeddingTests.cs (98%) rename tests/{ => Endpoints}/TokensTests.cs (95%) rename tests/{ => Endpoints}/TopicsTests.cs (98%) rename tests/{ => Endpoints}/TransliterationTests.cs (98%) rename tests/{ => Models}/BooleanRecordTests.cs (99%) rename tests/{ => Models}/FieldedAddressRecordTests.cs (99%) rename tests/{ => Models}/FieldedDateRecordTests.cs (99%) rename tests/{ => Models}/FieldedNameRecordTests.cs (99%) rename tests/{ => Models/JsonConverter}/RecordSimilarityFieldConverterTests.cs (99%) rename tests/{ => Models/JsonConverter}/RecordSimilarityRecordsConverterTests.cs (99%) rename tests/{ => Models/JsonConverter}/UnfieldedRecordSimilarityConverterTests.cs (99%) rename tests/{ => Models}/NameTests.cs (98%) rename tests/{ => Models}/NumberRecordTests.cs (99%) rename tests/{ => Models}/RecordSimilarityFieldInfoTests.cs (99%) rename tests/{ => Models}/RecordSimilarityPropertiesTests.cs (99%) rename tests/{ => Models}/RecordSimilarityRecordsEnhancedTests.cs (99%) rename tests/{ => Models}/ResponseTests.cs (99%) rename tests/{ => Models}/SimpleRecordModelsTests.cs (99%) rename tests/{ => Models}/StringRecordTests.cs (99%) rename tests/{ => Models}/UnfieldedAddressRecordTests.cs (99%) rename tests/{ => Models}/UnfieldedDateRecordTests.cs (99%) rename tests/{ => Models}/UnfieldedNameRecordTests.cs (99%) rename tests/{ => Models}/UnknownFieldRecordTests.cs (99%) diff --git a/tests/EndpointBaseTests.cs b/tests/EndpointBaseTests.cs deleted file mode 100644 index 4a7acfb..0000000 --- a/tests/EndpointBaseTests.cs +++ /dev/null @@ -1,629 +0,0 @@ -using RichardSzalay.MockHttp; -using Rosette.Api.Client; -using Rosette.Api.Client.Endpoints; -using Rosette.Api.Client.Models; -using System.Net; - -namespace Rosette.Api.Tests -{ - public class EndpointBaseTests - { - private static readonly string _defaultUri = "https://analytics.babelstreet.com/rest/v1/*"; - - [Fact] - public void Constructor_ValidEndpoint_SetsEndpointName() - { - // Arrange & Act - var endpoint = new Entities("test content"); - - // Assert - Assert.Equal("entities", endpoint.Endpoint); - } - - [Fact] - public void Options_InitialState_IsEmpty() - { - // Arrange & Act - var endpoint = new Entities("foo"); - - // Assert - Assert.Empty(endpoint.Options); - } - - [Fact] - public void Params_InitialState_IsNotEmpty() - { - // Arrange & Act - var endpoint = new Entities("foo"); - - // Assert - Assert.NotEmpty(endpoint.Params); - } - - [Fact] - public void UrlParameters_InitialState_IsEmpty() - { - // Arrange & Act - var endpoint = new Entities("foo"); - - // Assert - Assert.Empty(endpoint.UrlParameters); - } - - [Fact] - public void SetOption_ValidOption_AddsToOptions() - { - // Arrange - Entities e = new("foo"); - - // Act - e.SetOption("test", "value"); - - // Assert - Assert.Equal("value", e.Options["test"]); - } - - [Fact] - public void SetOption_MultipleOptions_AddsAllOptions() - { - // Arrange - Entities e = new("foo"); - - // Act - e.SetOption("test", "value"); - e.SetOption("test2", "value2"); - - // Assert - Assert.Equal("value", e.Options["test"]); - Assert.Equal("value2", e.Options["test2"]); - } - - [Fact] - public void SetOption_NullOptionName_ThrowsArgumentNullException() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - Assert.Throws(() => e.SetOption(null!, "value")); - } - - [Fact] - public void SetOption_EmptyOptionName_ThrowsArgumentException() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - Assert.Throws(() => e.SetOption(string.Empty, "value")); - } - - [Fact] - public void SetOption_NullOptionValue_ThrowsArgumentNullException() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - Assert.Throws(() => e.SetOption("test", null!)); - } - - [Fact] - public void SetOption_ReturnsInstance_ForFluentAPI() - { - // Arrange - Entities e = new("foo"); - - // Act - var result = e.SetOption("test", "value"); - - // Assert - Assert.Same(e, result); - } - - [Fact] - public void RemoveOption_ExistingOption_RemovesOption() - { - // Arrange - Entities e = new Entities("foo") - .SetOption("test", "value"); - - // Act - e.RemoveOption("test"); - - // Assert - Assert.False(e.Options.ContainsKey("test")); - } - - [Fact] - public void RemoveOption_NonExistingOption_DoesNotThrow() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - var exception = Record.Exception(() => e.RemoveOption("nonexistent")); - Assert.Null(exception); - } - - [Fact] - public void RemoveOption_NullKey_ThrowsArgumentNullException() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - Assert.Throws(() => e.RemoveOption(null!)); - } - - [Fact] - public void RemoveOption_ReturnsInstance_ForFluentAPI() - { - // Arrange - Entities e = new Entities("foo") - .SetOption("test", "value"); - - // Act - var result = e.RemoveOption("test"); - - // Assert - Assert.Same(e, result); - } - - [Fact] - public void ClearOptions_WithOptions_RemovesAllOptions() - { - // Arrange - Entities e = new Entities("foo") - .SetOption("test1", "value1") - .SetOption("test2", "value2"); - - // Act - e.ClearOptions(); - - // Assert - Assert.Empty(e.Options); - } - - [Fact] - public void ClearOptions_ReturnsInstance_ForFluentAPI() - { - // Arrange - Entities e = new("foo"); - - // Act - var result = e.ClearOptions(); - - // Assert - Assert.Same(e, result); - } - - [Fact] - public void SetUrlParameter_ValidParameter_AddsToUrlParameters() - { - // Arrange - Entities e = new("foo"); - - // Act - e.SetUrlParameter("test", "value"); - - // Assert - Assert.Equal("value", e.UrlParameters["test"]); - } - - [Fact] - public void SetUrlParameter_NullKey_ThrowsArgumentNullException() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - Assert.Throws(() => e.SetUrlParameter(null!, "value")); - } - - [Fact] - public void SetUrlParameter_NullValue_ThrowsArgumentNullException() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - Assert.Throws(() => e.SetUrlParameter("key", null!)); - } - - [Fact] - public void SetUrlParameter_ReturnsInstance_ForFluentAPI() - { - // Arrange - Entities e = new("foo"); - - // Act - var result = e.SetUrlParameter("test", "value"); - - // Assert - Assert.Same(e, result); - } - - [Fact] - public void RemoveUrlParameter_ExistingParameter_RemovesParameter() - { - // Arrange - Entities e = new Entities("foo") - .SetUrlParameter("test", "value"); - - // Act - e.RemoveUrlParameter("test"); - - // Assert - Assert.Empty(e.UrlParameters); - } - - [Fact] - public void RemoveUrlParameter_NullKey_ThrowsArgumentNullException() - { - // Arrange - Entities e = new("foo"); - - // Act & Assert - Assert.Throws(() => e.RemoveUrlParameter(null!)); - } - - [Fact] - public void RemoveUrlParameter_ReturnsInstance_ForFluentAPI() - { - // Arrange - Entities e = new Entities("foo") - .SetUrlParameter("test", "value"); - - // Act - var result = e.RemoveUrlParameter("test"); - - // Assert - Assert.Same(e, result); - } - - [Fact] - public void FluentAPI_ChainMultipleMethods_AllSettersReturnSameInstance() - { - // Arrange & Act - var endpoint = new Entities("content") - .SetOption("opt1", "val1") - .SetOption("opt2", "val2") - .SetUrlParameter("param", "value") - .SetLanguage("eng"); - - // Assert - Assert.Equal("val1", endpoint.Options["opt1"]); - Assert.Equal("val2", endpoint.Options["opt2"]); - Assert.Equal("value", endpoint.UrlParameters["param"]); - Assert.Equal("eng", endpoint.Language); - } - - [Fact] - public void Call_ValidEndpoint_ReturnsResponse() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - - // Act - Response result = endpoint.Call(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - Assert.NotNull(result.Content); - } - - #region Async Tests - - [Fact] - public async Task CallAsync_ValidEndpoint_ReturnsResponse() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - - // Act - Response result = await endpoint.CallAsync(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - Assert.NotNull(result.Content); - } - - [Fact] - public async Task CallAsync_WithCancellationToken_ReturnsResponse() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - using var cts = new CancellationTokenSource(); - - // Act - Response result = await endpoint.CallAsync(api, cts.Token); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - Assert.NotNull(result.Content); - } - - [Fact] - public async Task CallAsync_CancelledToken_ThrowsOperationCanceledException() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(async () => - { - await Task.Delay(100); - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent("{\"test\": \"OK\"}") - }; - }); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - var cts = new CancellationTokenSource(); - cts.Cancel(); // Cancel immediately - - // Act & Assert - await Assert.ThrowsAsync( - async () => await endpoint.CallAsync(api, cts.Token)); - } - - [Fact] - public async Task CallAsync_WithTimeout_ThrowsOperationCanceledException() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(async () => - { - await Task.Delay(5000); // 5 second delay - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = new StringContent("{\"test\": \"OK\"}") - }; - }); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)); // 100ms timeout - - // Act & Assert - await Assert.ThrowsAnyAsync( - async () => await endpoint.CallAsync(api, cts.Token)); - } - - [Fact] - public async Task CallAsync_WithOptions_SendsOptionsToServer() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new Entities("test content") - .SetOption("linkEntities", true); - - // Act - Response result = await endpoint.CallAsync(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - } - - [Fact] - public async Task CallAsync_WithUrlParameters_AppendsQueryString() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When("https://analytics.babelstreet.com/rest/v1/entities?output=rosette") - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new Entities("test content") - .SetUrlParameter("output", "rosette"); - - // Act - Response result = await endpoint.CallAsync(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - } - - [Fact] - public async Task CallAsync_ErrorResponse_ThrowsHttpRequestException() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.BadRequest, "application/json", "{\"message\": \"Bad Request\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - - // Act & Assert - await Assert.ThrowsAsync( - async () => await endpoint.CallAsync(api)); - } - - [Fact] - public async Task CallAsync_NotFoundResponse_ThrowsHttpRequestException() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.NotFound, "application/json", "{\"message\": \"Not Found\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - - // Act & Assert - await Assert.ThrowsAsync( - async () => await endpoint.CallAsync(api)); - } - - [Fact] - public async Task CallAsync_UnauthorizedResponse_ThrowsHttpRequestException() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.Unauthorized, "application/json", "{\"message\": \"Unauthorized\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - - // Act & Assert - await Assert.ThrowsAsync( - async () => await endpoint.CallAsync(api)); - } - - [Fact] - public async Task CallAsync_WithLanguage_SendsLanguageParameter() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new Entities("test content") - .SetLanguage("eng"); - - // Act - Response result = await endpoint.CallAsync(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - } - - [Fact] - public async Task CallAsync_MultipleCallsToSameEndpoint_EachReturnsResponse() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint = new("test content"); - - // Act - Response result1 = await endpoint.CallAsync(api); - Response result2 = await endpoint.CallAsync(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result1.StatusCode); - Assert.Equal((int)HttpStatusCode.OK, result2.StatusCode); - Assert.NotSame(result1, result2); // Different instances - } - - [Fact] - public async Task CallAsync_ConcurrentCalls_BothComplete() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When(_defaultUri) - .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Entities endpoint1 = new("content1"); - Entities endpoint2 = new("content2"); - - // Act - var task1 = endpoint1.CallAsync(api); - var task2 = endpoint2.CallAsync(api); - var results = await Task.WhenAll(task1, task2); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, results[0].StatusCode); - Assert.Equal((int)HttpStatusCode.OK, results[1].StatusCode); - } - - [Fact] - public async Task CallAsync_InfoEndpoint_UsesGetCall() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When("https://analytics.babelstreet.com/rest/v1/info") - .Respond(HttpStatusCode.OK, "application/json", "{\"name\": \"Rosette API\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Info endpoint = new(); - - // Act - Response result = await endpoint.CallAsync(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - Assert.NotNull(result.Content); - } - - [Fact] - public async Task CallAsync_PingEndpoint_UsesGetCall() - { - // Arrange - ApiClient api = new("testkey"); - var mockHttp = new MockHttpMessageHandler(); - mockHttp.When("https://analytics.babelstreet.com/rest/v1/ping") - .Respond(HttpStatusCode.OK, "application/json", "{\"message\": \"pong\"}"); - var client = mockHttp.ToHttpClient(); - api.AssignClient(client); - - Ping endpoint = new(); - - // Act - Response result = await endpoint.CallAsync(api); - - // Assert - Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); - Assert.NotNull(result.Content); - } - - #endregion - } -} \ No newline at end of file diff --git a/tests/AddressSimilarityTests.cs b/tests/Endpoints/AddressSimilarityTests.cs similarity index 99% rename from tests/AddressSimilarityTests.cs rename to tests/Endpoints/AddressSimilarityTests.cs index a443c2f..4cdabfd 100644 --- a/tests/AddressSimilarityTests.cs +++ b/tests/Endpoints/AddressSimilarityTests.cs @@ -1,7 +1,7 @@ using Rosette.Api.Client.Endpoints; using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class AddressSimilarityTests { diff --git a/tests/CategoriesTests.cs b/tests/Endpoints/CategoriesTests.cs similarity index 96% rename from tests/CategoriesTests.cs rename to tests/Endpoints/CategoriesTests.cs index 51b9b97..41051ae 100644 --- a/tests/CategoriesTests.cs +++ b/tests/Endpoints/CategoriesTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class CategoriesTests { @@ -54,4 +54,4 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() } #endregion -} \ No newline at end of file +} diff --git a/tests/ContentEndpointBaseTests.cs b/tests/Endpoints/Core/ContentEndpointBaseTests.cs similarity index 98% rename from tests/ContentEndpointBaseTests.cs rename to tests/Endpoints/Core/ContentEndpointBaseTests.cs index 1639b54..c7e2dda 100644 --- a/tests/ContentEndpointBaseTests.cs +++ b/tests/Endpoints/Core/ContentEndpointBaseTests.cs @@ -1,6 +1,6 @@ -using Rosette.Api.Client.Endpoints.Core; +using Rosette.Api.Client.Endpoints.Core; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints.Core; // Concrete test implementation of ContentEndpointBase for testing internal class TestContentEndpoint : ContentEndpointBase @@ -308,4 +308,4 @@ public void UrlParameters_SetUrlParameter_CanSetQueryParameters() // Assert Assert.Equal("rosette", endpoint.UrlParameters["output"]); } -} \ No newline at end of file +} diff --git a/tests/Endpoints/Core/EndpointBaseTests.cs b/tests/Endpoints/Core/EndpointBaseTests.cs new file mode 100644 index 0000000..b7ce17b --- /dev/null +++ b/tests/Endpoints/Core/EndpointBaseTests.cs @@ -0,0 +1,628 @@ +using RichardSzalay.MockHttp; +using Rosette.Api.Client; +using Rosette.Api.Client.Endpoints; +using Rosette.Api.Client.Models; +using System.Net; + +namespace Rosette.Api.Tests.Endpoints.Core; + +public class EndpointBaseTests +{ + private static readonly string _defaultUri = "https://analytics.babelstreet.com/rest/v1/*"; + + [Fact] + public void Constructor_ValidEndpoint_SetsEndpointName() + { + // Arrange & Act + var endpoint = new Entities("test content"); + + // Assert + Assert.Equal("entities", endpoint.Endpoint); + } + + [Fact] + public void Options_InitialState_IsEmpty() + { + // Arrange & Act + var endpoint = new Entities("foo"); + + // Assert + Assert.Empty(endpoint.Options); + } + + [Fact] + public void Params_InitialState_IsNotEmpty() + { + // Arrange & Act + var endpoint = new Entities("foo"); + + // Assert + Assert.NotEmpty(endpoint.Params); + } + + [Fact] + public void UrlParameters_InitialState_IsEmpty() + { + // Arrange & Act + var endpoint = new Entities("foo"); + + // Assert + Assert.Empty(endpoint.UrlParameters); + } + + [Fact] + public void SetOption_ValidOption_AddsToOptions() + { + // Arrange + Entities e = new("foo"); + + // Act + e.SetOption("test", "value"); + + // Assert + Assert.Equal("value", e.Options["test"]); + } + + [Fact] + public void SetOption_MultipleOptions_AddsAllOptions() + { + // Arrange + Entities e = new("foo"); + + // Act + e.SetOption("test", "value"); + e.SetOption("test2", "value2"); + + // Assert + Assert.Equal("value", e.Options["test"]); + Assert.Equal("value2", e.Options["test2"]); + } + + [Fact] + public void SetOption_NullOptionName_ThrowsArgumentNullException() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + Assert.Throws(() => e.SetOption(null!, "value")); + } + + [Fact] + public void SetOption_EmptyOptionName_ThrowsArgumentException() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + Assert.Throws(() => e.SetOption(string.Empty, "value")); + } + + [Fact] + public void SetOption_NullOptionValue_ThrowsArgumentNullException() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + Assert.Throws(() => e.SetOption("test", null!)); + } + + [Fact] + public void SetOption_ReturnsInstance_ForFluentAPI() + { + // Arrange + Entities e = new("foo"); + + // Act + var result = e.SetOption("test", "value"); + + // Assert + Assert.Same(e, result); + } + + [Fact] + public void RemoveOption_ExistingOption_RemovesOption() + { + // Arrange + Entities e = new Entities("foo") + .SetOption("test", "value"); + + // Act + e.RemoveOption("test"); + + // Assert + Assert.False(e.Options.ContainsKey("test")); + } + + [Fact] + public void RemoveOption_NonExistingOption_DoesNotThrow() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + var exception = Record.Exception(() => e.RemoveOption("nonexistent")); + Assert.Null(exception); + } + + [Fact] + public void RemoveOption_NullKey_ThrowsArgumentNullException() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + Assert.Throws(() => e.RemoveOption(null!)); + } + + [Fact] + public void RemoveOption_ReturnsInstance_ForFluentAPI() + { + // Arrange + Entities e = new Entities("foo") + .SetOption("test", "value"); + + // Act + var result = e.RemoveOption("test"); + + // Assert + Assert.Same(e, result); + } + + [Fact] + public void ClearOptions_WithOptions_RemovesAllOptions() + { + // Arrange + Entities e = new Entities("foo") + .SetOption("test1", "value1") + .SetOption("test2", "value2"); + + // Act + e.ClearOptions(); + + // Assert + Assert.Empty(e.Options); + } + + [Fact] + public void ClearOptions_ReturnsInstance_ForFluentAPI() + { + // Arrange + Entities e = new("foo"); + + // Act + var result = e.ClearOptions(); + + // Assert + Assert.Same(e, result); + } + + [Fact] + public void SetUrlParameter_ValidParameter_AddsToUrlParameters() + { + // Arrange + Entities e = new("foo"); + + // Act + e.SetUrlParameter("test", "value"); + + // Assert + Assert.Equal("value", e.UrlParameters["test"]); + } + + [Fact] + public void SetUrlParameter_NullKey_ThrowsArgumentNullException() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + Assert.Throws(() => e.SetUrlParameter(null!, "value")); + } + + [Fact] + public void SetUrlParameter_NullValue_ThrowsArgumentNullException() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + Assert.Throws(() => e.SetUrlParameter("key", null!)); + } + + [Fact] + public void SetUrlParameter_ReturnsInstance_ForFluentAPI() + { + // Arrange + Entities e = new("foo"); + + // Act + var result = e.SetUrlParameter("test", "value"); + + // Assert + Assert.Same(e, result); + } + + [Fact] + public void RemoveUrlParameter_ExistingParameter_RemovesParameter() + { + // Arrange + Entities e = new Entities("foo") + .SetUrlParameter("test", "value"); + + // Act + e.RemoveUrlParameter("test"); + + // Assert + Assert.Empty(e.UrlParameters); + } + + [Fact] + public void RemoveUrlParameter_NullKey_ThrowsArgumentNullException() + { + // Arrange + Entities e = new("foo"); + + // Act & Assert + Assert.Throws(() => e.RemoveUrlParameter(null!)); + } + + [Fact] + public void RemoveUrlParameter_ReturnsInstance_ForFluentAPI() + { + // Arrange + Entities e = new Entities("foo") + .SetUrlParameter("test", "value"); + + // Act + var result = e.RemoveUrlParameter("test"); + + // Assert + Assert.Same(e, result); + } + + [Fact] + public void FluentAPI_ChainMultipleMethods_AllSettersReturnSameInstance() + { + // Arrange & Act + var endpoint = new Entities("content") + .SetOption("opt1", "val1") + .SetOption("opt2", "val2") + .SetUrlParameter("param", "value") + .SetLanguage("eng"); + + // Assert + Assert.Equal("val1", endpoint.Options["opt1"]); + Assert.Equal("val2", endpoint.Options["opt2"]); + Assert.Equal("value", endpoint.UrlParameters["param"]); + Assert.Equal("eng", endpoint.Language); + } + + [Fact] + public void Call_ValidEndpoint_ReturnsResponse() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + + // Act + Response result = endpoint.Call(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + Assert.NotNull(result.Content); + } + + #region Async Tests + + [Fact] + public async Task CallAsync_ValidEndpoint_ReturnsResponse() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + + // Act + Response result = await endpoint.CallAsync(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + Assert.NotNull(result.Content); + } + + [Fact] + public async Task CallAsync_WithCancellationToken_ReturnsResponse() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + using var cts = new CancellationTokenSource(); + + // Act + Response result = await endpoint.CallAsync(api, cts.Token); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + Assert.NotNull(result.Content); + } + + [Fact] + public async Task CallAsync_CancelledToken_ThrowsOperationCanceledException() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(async () => + { + await Task.Delay(100); + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{\"test\": \"OK\"}") + }; + }); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + var cts = new CancellationTokenSource(); + cts.Cancel(); // Cancel immediately + + // Act & Assert + await Assert.ThrowsAsync( + async () => await endpoint.CallAsync(api, cts.Token)); + } + + [Fact] + public async Task CallAsync_WithTimeout_ThrowsOperationCanceledException() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(async () => + { + await Task.Delay(5000); // 5 second delay + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent("{\"test\": \"OK\"}") + }; + }); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(100)); // 100ms timeout + + // Act & Assert + await Assert.ThrowsAnyAsync( + async () => await endpoint.CallAsync(api, cts.Token)); + } + + [Fact] + public async Task CallAsync_WithOptions_SendsOptionsToServer() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new Entities("test content") + .SetOption("linkEntities", true); + + // Act + Response result = await endpoint.CallAsync(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + } + + [Fact] + public async Task CallAsync_WithUrlParameters_AppendsQueryString() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When("https://analytics.babelstreet.com/rest/v1/entities?output=rosette") + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new Entities("test content") + .SetUrlParameter("output", "rosette"); + + // Act + Response result = await endpoint.CallAsync(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + } + + [Fact] + public async Task CallAsync_ErrorResponse_ThrowsHttpRequestException() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.BadRequest, "application/json", "{\"message\": \"Bad Request\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + + // Act & Assert + await Assert.ThrowsAsync( + async () => await endpoint.CallAsync(api)); + } + + [Fact] + public async Task CallAsync_NotFoundResponse_ThrowsHttpRequestException() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.NotFound, "application/json", "{\"message\": \"Not Found\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + + // Act & Assert + await Assert.ThrowsAsync( + async () => await endpoint.CallAsync(api)); + } + + [Fact] + public async Task CallAsync_UnauthorizedResponse_ThrowsHttpRequestException() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.Unauthorized, "application/json", "{\"message\": \"Unauthorized\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + + // Act & Assert + await Assert.ThrowsAsync( + async () => await endpoint.CallAsync(api)); + } + + [Fact] + public async Task CallAsync_WithLanguage_SendsLanguageParameter() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new Entities("test content") + .SetLanguage("eng"); + + // Act + Response result = await endpoint.CallAsync(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + } + + [Fact] + public async Task CallAsync_MultipleCallsToSameEndpoint_EachReturnsResponse() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint = new("test content"); + + // Act + Response result1 = await endpoint.CallAsync(api); + Response result2 = await endpoint.CallAsync(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result1.StatusCode); + Assert.Equal((int)HttpStatusCode.OK, result2.StatusCode); + Assert.NotSame(result1, result2); // Different instances + } + + [Fact] + public async Task CallAsync_ConcurrentCalls_BothComplete() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When(_defaultUri) + .Respond(HttpStatusCode.OK, "application/json", "{\"test\": \"OK\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Entities endpoint1 = new("content1"); + Entities endpoint2 = new("content2"); + + // Act + var task1 = endpoint1.CallAsync(api); + var task2 = endpoint2.CallAsync(api); + var results = await Task.WhenAll(task1, task2); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, results[0].StatusCode); + Assert.Equal((int)HttpStatusCode.OK, results[1].StatusCode); + } + + [Fact] + public async Task CallAsync_InfoEndpoint_UsesGetCall() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When("https://analytics.babelstreet.com/rest/v1/info") + .Respond(HttpStatusCode.OK, "application/json", "{\"name\": \"Rosette API\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Info endpoint = new(); + + // Act + Response result = await endpoint.CallAsync(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + Assert.NotNull(result.Content); + } + + [Fact] + public async Task CallAsync_PingEndpoint_UsesGetCall() + { + // Arrange + ApiClient api = new("testkey"); + var mockHttp = new MockHttpMessageHandler(); + mockHttp.When("https://analytics.babelstreet.com/rest/v1/ping") + .Respond(HttpStatusCode.OK, "application/json", "{\"message\": \"pong\"}"); + var client = mockHttp.ToHttpClient(); + api.AssignClient(client); + + Ping endpoint = new(); + + // Act + Response result = await endpoint.CallAsync(api); + + // Assert + Assert.Equal((int)HttpStatusCode.OK, result.StatusCode); + Assert.NotNull(result.Content); + } + + #endregion +} diff --git a/tests/EndpointExecutorTests.cs b/tests/Endpoints/Core/EndpointExecutorTests.cs similarity index 99% rename from tests/EndpointExecutorTests.cs rename to tests/Endpoints/Core/EndpointExecutorTests.cs index 9634d15..b2dd2db 100644 --- a/tests/EndpointExecutorTests.cs +++ b/tests/Endpoints/Core/EndpointExecutorTests.cs @@ -1,4 +1,4 @@ -using RichardSzalay.MockHttp; +using RichardSzalay.MockHttp; using System.Collections.Specialized; using System.Net; using System.Text.Json; @@ -6,7 +6,7 @@ using Rosette.Api.Client.Models; using Rosette.Api.Client; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints.Core; public class EndpointExecutorTests { diff --git a/tests/ValidEndpointTests.cs b/tests/Endpoints/Core/ValidEndpointTests.cs similarity index 98% rename from tests/ValidEndpointTests.cs rename to tests/Endpoints/Core/ValidEndpointTests.cs index 7aa1942..6fa6cc6 100644 --- a/tests/ValidEndpointTests.cs +++ b/tests/Endpoints/Core/ValidEndpointTests.cs @@ -1,7 +1,7 @@ -using Rosette.Api.Client.Endpoints; +using Rosette.Api.Client.Endpoints; using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints.Core; public class ValidEndpointTests { diff --git a/tests/EntitiesTests.cs b/tests/Endpoints/EntitiesTests.cs similarity index 97% rename from tests/EntitiesTests.cs rename to tests/Endpoints/EntitiesTests.cs index 329eed2..072b4d6 100644 --- a/tests/EntitiesTests.cs +++ b/tests/Endpoints/EntitiesTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class EntitiesTests { diff --git a/tests/EventsTests.cs b/tests/Endpoints/EventsTests.cs similarity index 98% rename from tests/EventsTests.cs rename to tests/Endpoints/EventsTests.cs index 33c22ec..4eb87e1 100644 --- a/tests/EventsTests.cs +++ b/tests/Endpoints/EventsTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class EventsTests { diff --git a/tests/InfoTests.cs b/tests/Endpoints/InfoTests.cs similarity index 98% rename from tests/InfoTests.cs rename to tests/Endpoints/InfoTests.cs index cc9707c..a5674e9 100644 --- a/tests/InfoTests.cs +++ b/tests/Endpoints/InfoTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class InfoTests { diff --git a/tests/LanguageTests.cs b/tests/Endpoints/LanguageTests.cs similarity index 96% rename from tests/LanguageTests.cs rename to tests/Endpoints/LanguageTests.cs index c2ce90b..4a6a7af 100644 --- a/tests/LanguageTests.cs +++ b/tests/Endpoints/LanguageTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class LanguageTests { @@ -47,4 +47,4 @@ public void SetGenre_SetsGenreProperty_WhenCalled() } #endregion -} \ No newline at end of file +} diff --git a/tests/MorphologyTests.cs b/tests/Endpoints/MorphologyTests.cs similarity index 99% rename from tests/MorphologyTests.cs rename to tests/Endpoints/MorphologyTests.cs index cb71c6b..82cc402 100644 --- a/tests/MorphologyTests.cs +++ b/tests/Endpoints/MorphologyTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class MorphologyTests { diff --git a/tests/NameDeduplicationTests.cs b/tests/Endpoints/NameDeduplicationTests.cs similarity index 94% rename from tests/NameDeduplicationTests.cs rename to tests/Endpoints/NameDeduplicationTests.cs index 567b455..dd9f9ca 100644 --- a/tests/NameDeduplicationTests.cs +++ b/tests/Endpoints/NameDeduplicationTests.cs @@ -1,7 +1,7 @@ -using Rosette.Api.Client.Endpoints; +using Rosette.Api.Client.Endpoints; using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class NameDeduplicationTests { diff --git a/tests/NameSimilarityTests.cs b/tests/Endpoints/NameSimilarityTests.cs similarity index 97% rename from tests/NameSimilarityTests.cs rename to tests/Endpoints/NameSimilarityTests.cs index b93a093..d2a17c2 100644 --- a/tests/NameSimilarityTests.cs +++ b/tests/Endpoints/NameSimilarityTests.cs @@ -1,7 +1,7 @@ -using Rosette.Api.Client.Endpoints; +using Rosette.Api.Client.Endpoints; using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class NameSimilarityTests { diff --git a/tests/NameTranslationTests.cs b/tests/Endpoints/NameTranslationTests.cs similarity index 94% rename from tests/NameTranslationTests.cs rename to tests/Endpoints/NameTranslationTests.cs index c66eff8..5720756 100644 --- a/tests/NameTranslationTests.cs +++ b/tests/Endpoints/NameTranslationTests.cs @@ -1,6 +1,6 @@ -using Rosette.Api.Client.Endpoints; +using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class NameTranslationTests { diff --git a/tests/PingTests.cs b/tests/Endpoints/PingTests.cs similarity index 98% rename from tests/PingTests.cs rename to tests/Endpoints/PingTests.cs index 979175d..4c90a64 100644 --- a/tests/PingTests.cs +++ b/tests/Endpoints/PingTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class PingTests { diff --git a/tests/RecordSimilarityTests.cs b/tests/Endpoints/RecordSimilarityTests.cs similarity index 99% rename from tests/RecordSimilarityTests.cs rename to tests/Endpoints/RecordSimilarityTests.cs index 387e5b1..34c0892 100644 --- a/tests/RecordSimilarityTests.cs +++ b/tests/Endpoints/RecordSimilarityTests.cs @@ -1,7 +1,7 @@ using Rosette.Api.Client.Endpoints; using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class RecordSimilarityTests { diff --git a/tests/RelationshipsTests.cs b/tests/Endpoints/RelationshipsTests.cs similarity index 99% rename from tests/RelationshipsTests.cs rename to tests/Endpoints/RelationshipsTests.cs index 8f627a7..659bbf3 100644 --- a/tests/RelationshipsTests.cs +++ b/tests/Endpoints/RelationshipsTests.cs @@ -46,4 +46,4 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() #endregion } -} \ No newline at end of file +} diff --git a/tests/SemanticsVectorTests.cs b/tests/Endpoints/SemanticsVectorTests.cs similarity index 98% rename from tests/SemanticsVectorTests.cs rename to tests/Endpoints/SemanticsVectorTests.cs index 5950bec..9893ffd 100644 --- a/tests/SemanticsVectorTests.cs +++ b/tests/Endpoints/SemanticsVectorTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class SemanticsVectorTests { diff --git a/tests/SentencesTests.cs b/tests/Endpoints/SentencesTests.cs similarity index 95% rename from tests/SentencesTests.cs rename to tests/Endpoints/SentencesTests.cs index c354960..eec5d37 100644 --- a/tests/SentencesTests.cs +++ b/tests/Endpoints/SentencesTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class SentencesTests { @@ -43,4 +43,4 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingLanguage() } #endregion -} \ No newline at end of file +} diff --git a/tests/SentimentTests.cs b/tests/Endpoints/SentimentTests.cs similarity index 96% rename from tests/SentimentTests.cs rename to tests/Endpoints/SentimentTests.cs index 068ad13..9380ad8 100644 --- a/tests/SentimentTests.cs +++ b/tests/Endpoints/SentimentTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class SentimentTests { @@ -54,4 +54,4 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() } #endregion -} \ No newline at end of file +} diff --git a/tests/SimilarTermsTests.cs b/tests/Endpoints/SimilarTermsTests.cs similarity index 96% rename from tests/SimilarTermsTests.cs rename to tests/Endpoints/SimilarTermsTests.cs index 7277803..f533217 100644 --- a/tests/SimilarTermsTests.cs +++ b/tests/Endpoints/SimilarTermsTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class SimilarTermsTests { @@ -45,4 +45,4 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingMultipleProperties() } #endregion -} \ No newline at end of file +} diff --git a/tests/SyntaxDependenciesTests.cs b/tests/Endpoints/SyntaxDependenciesTests.cs similarity index 98% rename from tests/SyntaxDependenciesTests.cs rename to tests/Endpoints/SyntaxDependenciesTests.cs index 9d19deb..0d9812b 100644 --- a/tests/SyntaxDependenciesTests.cs +++ b/tests/Endpoints/SyntaxDependenciesTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class SyntaxDependenciesTests { diff --git a/tests/TextEmbeddingTests.cs b/tests/Endpoints/TextEmbeddingTests.cs similarity index 98% rename from tests/TextEmbeddingTests.cs rename to tests/Endpoints/TextEmbeddingTests.cs index 71bc87e..122689b 100644 --- a/tests/TextEmbeddingTests.cs +++ b/tests/Endpoints/TextEmbeddingTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class TextEmbeddingTests { diff --git a/tests/TokensTests.cs b/tests/Endpoints/TokensTests.cs similarity index 95% rename from tests/TokensTests.cs rename to tests/Endpoints/TokensTests.cs index 52464ae..3c73d2f 100644 --- a/tests/TokensTests.cs +++ b/tests/Endpoints/TokensTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class TokensTests { @@ -42,4 +42,4 @@ public void FluentAPI_AllowsMethodChaining_WhenSettingLanguage() } #endregion -} \ No newline at end of file +} diff --git a/tests/TopicsTests.cs b/tests/Endpoints/TopicsTests.cs similarity index 98% rename from tests/TopicsTests.cs rename to tests/Endpoints/TopicsTests.cs index 0930211..0d930f4 100644 --- a/tests/TopicsTests.cs +++ b/tests/Endpoints/TopicsTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class TopicsTests { diff --git a/tests/TransliterationTests.cs b/tests/Endpoints/TransliterationTests.cs similarity index 98% rename from tests/TransliterationTests.cs rename to tests/Endpoints/TransliterationTests.cs index fbf13a8..3b0d073 100644 --- a/tests/TransliterationTests.cs +++ b/tests/Endpoints/TransliterationTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Endpoints; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Endpoints; public class TransliterationTests { diff --git a/tests/BooleanRecordTests.cs b/tests/Models/BooleanRecordTests.cs similarity index 99% rename from tests/BooleanRecordTests.cs rename to tests/Models/BooleanRecordTests.cs index 0b337a7..7143ef3 100644 --- a/tests/BooleanRecordTests.cs +++ b/tests/Models/BooleanRecordTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for BooleanRecord class diff --git a/tests/FieldedAddressRecordTests.cs b/tests/Models/FieldedAddressRecordTests.cs similarity index 99% rename from tests/FieldedAddressRecordTests.cs rename to tests/Models/FieldedAddressRecordTests.cs index 1665627..d805983 100644 --- a/tests/FieldedAddressRecordTests.cs +++ b/tests/Models/FieldedAddressRecordTests.cs @@ -1,7 +1,7 @@ using Rosette.Api.Client.Models; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for FieldedAddressRecord class diff --git a/tests/FieldedDateRecordTests.cs b/tests/Models/FieldedDateRecordTests.cs similarity index 99% rename from tests/FieldedDateRecordTests.cs rename to tests/Models/FieldedDateRecordTests.cs index dcaf4dc..692a491 100644 --- a/tests/FieldedDateRecordTests.cs +++ b/tests/Models/FieldedDateRecordTests.cs @@ -1,7 +1,7 @@ using Rosette.Api.Client.Models; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for FieldedDateRecord class diff --git a/tests/FieldedNameRecordTests.cs b/tests/Models/FieldedNameRecordTests.cs similarity index 99% rename from tests/FieldedNameRecordTests.cs rename to tests/Models/FieldedNameRecordTests.cs index 50463c9..36a530a 100644 --- a/tests/FieldedNameRecordTests.cs +++ b/tests/Models/FieldedNameRecordTests.cs @@ -1,7 +1,7 @@ using Rosette.Api.Client.Models; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for FieldedNameRecord class diff --git a/tests/RecordSimilarityFieldConverterTests.cs b/tests/Models/JsonConverter/RecordSimilarityFieldConverterTests.cs similarity index 99% rename from tests/RecordSimilarityFieldConverterTests.cs rename to tests/Models/JsonConverter/RecordSimilarityFieldConverterTests.cs index 7786009..b54c510 100644 --- a/tests/RecordSimilarityFieldConverterTests.cs +++ b/tests/Models/JsonConverter/RecordSimilarityFieldConverterTests.cs @@ -2,7 +2,7 @@ using Rosette.Api.Client.Models.JsonConverter; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models.JsonConverter; /// /// Tests for RecordSimilarityFieldConverter class diff --git a/tests/RecordSimilarityRecordsConverterTests.cs b/tests/Models/JsonConverter/RecordSimilarityRecordsConverterTests.cs similarity index 99% rename from tests/RecordSimilarityRecordsConverterTests.cs rename to tests/Models/JsonConverter/RecordSimilarityRecordsConverterTests.cs index 8175b87..82d1bb5 100644 --- a/tests/RecordSimilarityRecordsConverterTests.cs +++ b/tests/Models/JsonConverter/RecordSimilarityRecordsConverterTests.cs @@ -2,7 +2,7 @@ using Rosette.Api.Client.Models.JsonConverter; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models.JsonConverter; /// /// Tests for RecordSimilarityRecordsConverter class diff --git a/tests/UnfieldedRecordSimilarityConverterTests.cs b/tests/Models/JsonConverter/UnfieldedRecordSimilarityConverterTests.cs similarity index 99% rename from tests/UnfieldedRecordSimilarityConverterTests.cs rename to tests/Models/JsonConverter/UnfieldedRecordSimilarityConverterTests.cs index 0bc58f5..cfe33f5 100644 --- a/tests/UnfieldedRecordSimilarityConverterTests.cs +++ b/tests/Models/JsonConverter/UnfieldedRecordSimilarityConverterTests.cs @@ -3,7 +3,7 @@ using System.Text.Json; using System.Text.Json.Nodes; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models.JsonConverter; /// /// Tests for UnfieldedRecordSimilarityConverter class diff --git a/tests/NameTests.cs b/tests/Models/NameTests.cs similarity index 98% rename from tests/NameTests.cs rename to tests/Models/NameTests.cs index 46cd166..fe23e6f 100644 --- a/tests/NameTests.cs +++ b/tests/Models/NameTests.cs @@ -1,6 +1,6 @@ -using Rosette.Api.Client.Models; +using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; public class NameTests { diff --git a/tests/NumberRecordTests.cs b/tests/Models/NumberRecordTests.cs similarity index 99% rename from tests/NumberRecordTests.cs rename to tests/Models/NumberRecordTests.cs index 2809223..c5d08e3 100644 --- a/tests/NumberRecordTests.cs +++ b/tests/Models/NumberRecordTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for NumberRecord class diff --git a/tests/RecordSimilarityFieldInfoTests.cs b/tests/Models/RecordSimilarityFieldInfoTests.cs similarity index 99% rename from tests/RecordSimilarityFieldInfoTests.cs rename to tests/Models/RecordSimilarityFieldInfoTests.cs index dc80e78..94163cf 100644 --- a/tests/RecordSimilarityFieldInfoTests.cs +++ b/tests/Models/RecordSimilarityFieldInfoTests.cs @@ -1,7 +1,7 @@ using Rosette.Api.Client.Models; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for RecordSimilarityFieldInfo class diff --git a/tests/RecordSimilarityPropertiesTests.cs b/tests/Models/RecordSimilarityPropertiesTests.cs similarity index 99% rename from tests/RecordSimilarityPropertiesTests.cs rename to tests/Models/RecordSimilarityPropertiesTests.cs index 040b17c..763b35c 100644 --- a/tests/RecordSimilarityPropertiesTests.cs +++ b/tests/Models/RecordSimilarityPropertiesTests.cs @@ -1,7 +1,7 @@ using Rosette.Api.Client.Models; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for RecordSimilarityProperties class diff --git a/tests/RecordSimilarityRecordsEnhancedTests.cs b/tests/Models/RecordSimilarityRecordsEnhancedTests.cs similarity index 99% rename from tests/RecordSimilarityRecordsEnhancedTests.cs rename to tests/Models/RecordSimilarityRecordsEnhancedTests.cs index 7a7c3a5..a2d86dc 100644 --- a/tests/RecordSimilarityRecordsEnhancedTests.cs +++ b/tests/Models/RecordSimilarityRecordsEnhancedTests.cs @@ -2,7 +2,7 @@ using Rosette.Api.Client.Models.JsonConverter; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for RecordSimilarityRecords class diff --git a/tests/ResponseTests.cs b/tests/Models/ResponseTests.cs similarity index 99% rename from tests/ResponseTests.cs rename to tests/Models/ResponseTests.cs index 648eb6a..b3cf4e9 100644 --- a/tests/ResponseTests.cs +++ b/tests/Models/ResponseTests.cs @@ -1,8 +1,8 @@ -using Rosette.Api.Client.Models; +using Rosette.Api.Client.Models; using System.Net; using System.Text.Json; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; public class ResponseTests { diff --git a/tests/SimpleRecordModelsTests.cs b/tests/Models/SimpleRecordModelsTests.cs similarity index 99% rename from tests/SimpleRecordModelsTests.cs rename to tests/Models/SimpleRecordModelsTests.cs index 07b52ec..e0c8c8d 100644 --- a/tests/SimpleRecordModelsTests.cs +++ b/tests/Models/SimpleRecordModelsTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for simple record models: StringRecord, NumberRecord, BooleanRecord diff --git a/tests/StringRecordTests.cs b/tests/Models/StringRecordTests.cs similarity index 99% rename from tests/StringRecordTests.cs rename to tests/Models/StringRecordTests.cs index f070b8e..458ca64 100644 --- a/tests/StringRecordTests.cs +++ b/tests/Models/StringRecordTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for StringRecord class diff --git a/tests/UnfieldedAddressRecordTests.cs b/tests/Models/UnfieldedAddressRecordTests.cs similarity index 99% rename from tests/UnfieldedAddressRecordTests.cs rename to tests/Models/UnfieldedAddressRecordTests.cs index 2236c36..b3827af 100644 --- a/tests/UnfieldedAddressRecordTests.cs +++ b/tests/Models/UnfieldedAddressRecordTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for UnfieldedAddressRecord class diff --git a/tests/UnfieldedDateRecordTests.cs b/tests/Models/UnfieldedDateRecordTests.cs similarity index 99% rename from tests/UnfieldedDateRecordTests.cs rename to tests/Models/UnfieldedDateRecordTests.cs index 558498c..a44951d 100644 --- a/tests/UnfieldedDateRecordTests.cs +++ b/tests/Models/UnfieldedDateRecordTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for UnfieldedDateRecord class diff --git a/tests/UnfieldedNameRecordTests.cs b/tests/Models/UnfieldedNameRecordTests.cs similarity index 99% rename from tests/UnfieldedNameRecordTests.cs rename to tests/Models/UnfieldedNameRecordTests.cs index 3de2247..4244308 100644 --- a/tests/UnfieldedNameRecordTests.cs +++ b/tests/Models/UnfieldedNameRecordTests.cs @@ -1,6 +1,6 @@ using Rosette.Api.Client.Models; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for UnfieldedNameRecord class diff --git a/tests/UnknownFieldRecordTests.cs b/tests/Models/UnknownFieldRecordTests.cs similarity index 99% rename from tests/UnknownFieldRecordTests.cs rename to tests/Models/UnknownFieldRecordTests.cs index 2774736..4cffb79 100644 --- a/tests/UnknownFieldRecordTests.cs +++ b/tests/Models/UnknownFieldRecordTests.cs @@ -2,7 +2,7 @@ using System.Text.Json; using System.Text.Json.Nodes; -namespace Rosette.Api.Tests; +namespace Rosette.Api.Tests.Models; /// /// Tests for UnknownFieldRecord class