diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ArgumentGuardsTests.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ArgumentGuardsTests.cs deleted file mode 100644 index 82d12f65..00000000 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ArgumentGuardsTests.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace RarelySimple.AvatarScriptLink.Objects.Helpers.Tests -{ - /// - /// Tests for internal ArgumentGuards validation helpers. - /// - [TestClass] - public class ArgumentGuardsTests - { - [TestMethod] - public void ValidateFieldNumber_ArgumentGuards_WithNullFieldNumber_ThrowsArgumentNullException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateFieldNumber(null!, "fieldNumber")); - - Assert.AreEqual("fieldNumber", ex.ParamName); - } - - [TestMethod] - public void ValidateFieldNumber_ArgumentGuards_WithEmptyFieldNumber_ThrowsArgumentException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateFieldNumber(string.Empty, "fieldNumber")); - - Assert.AreEqual("fieldNumber", ex.ParamName); - } - - [TestMethod] - public void ValidateFieldNumber_ArgumentGuards_WithValidFieldNumber_DoesNotThrow() - { - ArgumentGuards.ValidateFieldNumber("100", "fieldNumber"); - } - - [TestMethod] - public void ValidateAndNormalizeFieldNumbers_ArgumentGuards_WithNullFieldNumbers_ThrowsArgumentNullException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateAndNormalizeFieldNumbers(null!, "fieldNumbers")); - - Assert.AreEqual("fieldNumbers", ex.ParamName); - } - - [TestMethod] - public void ValidateAndNormalizeFieldNumbers_ArgumentGuards_WithEmptyFieldNumbers_ThrowsArgumentException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateAndNormalizeFieldNumbers(new List(), "fieldNumbers")); - - Assert.AreEqual("fieldNumbers", ex.ParamName); - } - - [TestMethod] - public void ValidateAndNormalizeFieldNumbers_ArgumentGuards_WithOnlyEmptyValues_ThrowsArgumentException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateAndNormalizeFieldNumbers(new List { string.Empty, null!, string.Empty }, "fieldNumbers")); - - Assert.AreEqual("fieldNumbers", ex.ParamName); - } - - [TestMethod] - public void ValidateAndNormalizeFieldNumbers_ArgumentGuards_WithDuplicatesAndEmptyValues_ReturnsDistinctNonEmptyValues() - { - var result = ArgumentGuards.ValidateAndNormalizeFieldNumbers( - new List { "100", string.Empty, "100", "101", "101" }, - "fieldNumbers"); - - CollectionAssert.AreEqual(new List { "100", "101" }, result); - } - - [TestMethod] - public void ValidateAndGetFieldNumbers_ArgumentGuards_WithNullFieldObjects_ThrowsArgumentNullException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateAndGetFieldNumbers(null!, "fieldObjects")); - - Assert.AreEqual("fieldObjects", ex.ParamName); - } - - [TestMethod] - public void ValidateAndGetFieldNumbers_ArgumentGuards_WithEmptyFieldObjects_ThrowsArgumentException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateAndGetFieldNumbers(new List(), "fieldObjects")); - - Assert.AreEqual("fieldObjects", ex.ParamName); - } - - [TestMethod] - public void ValidateAndGetFieldNumbers_ArgumentGuards_WithNoValidFieldNumbers_ThrowsArgumentException() - { - var ex = Assert.ThrowsException( - () => ArgumentGuards.ValidateAndGetFieldNumbers( - new List { null!, new FieldObject { FieldNumber = string.Empty }, new FieldObject { FieldNumber = null! } }, - "fieldObjects")); - - Assert.AreEqual("fieldObjects", ex.ParamName); - } - - [TestMethod] - public void ValidateAndGetFieldNumbers_ArgumentGuards_WithDuplicatesAndInvalidEntries_ReturnsDistinctFieldNumbers() - { - var result = ArgumentGuards.ValidateAndGetFieldNumbers( - new List - { - new FieldObject { FieldNumber = "100" }, - null!, - new FieldObject { FieldNumber = string.Empty }, - new FieldObject { FieldNumber = "100" }, - new FieldObject { FieldNumber = "101" } - }, - "fieldObjects"); - - CollectionAssert.AreEqual(new List { "100", "101" }, result); - } - } -} diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ScriptLinkParsingTests.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ScriptLinkParsingTests.cs new file mode 100644 index 00000000..96a2b098 --- /dev/null +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ScriptLinkParsingTests.cs @@ -0,0 +1,609 @@ +namespace RarelySimple.AvatarScriptLink.Objects.Helpers.Tests +{ + [TestClass] + public class ScriptLinkParsingTests + { + #region SafeGetInt Tests + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_Zero_ReturnsZero() + { + // Arrange + string intString = "0"; + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_PositiveInteger_ReturnsValue() + { + // Arrange + string intString = "42"; + int expected = 42; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_NegativeInteger_ReturnsValue() + { + // Arrange + string intString = "-42"; + int expected = -42; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_DecimalString_ReturnsZero() + { + // Arrange + string intString = "10.5"; + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_AlphabeticString_ReturnsZero() + { + // Arrange + string intString = "abc"; + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_SpecialCharacters_ReturnsZero() + { + // Arrange + string intString = "@#$"; + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_NullString_ReturnsZero() + { + // Arrange + string? intString = null; + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_EmptyString_ReturnsZero() + { + // Arrange + string intString = ""; + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_LargeNumber_ReturnsValue() + { + // Arrange + string intString = "2147483647"; // Max int + int expected = 2147483647; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_WhitespaceString_ReturnsZero() + { + // Arrange + string intString = " "; + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetInt")] + public void SafeGetInt_OverflowValue_ReturnsZero() + { + // Arrange + string intString = "2147483648"; // int.MaxValue + 1 + int expected = 0; + + // Act + int actual = ScriptLinkParsing.SafeGetInt(intString); + + // Assert + Assert.AreEqual(expected, actual); + } + + #endregion + + #region SafeGetBool Tests + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_One_ReturnsTrue() + { + // Arrange + string boolString = "1"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsTrue(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_Zero_ReturnsFalse() + { + // Arrange + string boolString = "0"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_TUppercase_ReturnsTrue() + { + // Arrange + string boolString = "T"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsTrue(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_TrueLowercase_ReturnsTrue() + { + // Arrange + string boolString = "true"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsTrue(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_TrueUppercase_ReturnsTrue() + { + // Arrange + string boolString = "TRUE"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsTrue(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_YUppercase_ReturnsTrue() + { + // Arrange + string boolString = "Y"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsTrue(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_YesLowercase_ReturnsTrue() + { + // Arrange + string boolString = "yes"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsTrue(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_YesMixedCase_ReturnsTrue() + { + // Arrange + string boolString = "Yes"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsTrue(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_FUppercase_ReturnsFalse() + { + // Arrange + string boolString = "F"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_FalseLowercase_ReturnsFalse() + { + // Arrange + string boolString = "false"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_FalseUppercase_ReturnsFalse() + { + // Arrange + string boolString = "FALSE"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_NUppercase_ReturnsFalse() + { + // Arrange + string boolString = "N"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_NoLowercase_ReturnsFalse() + { + // Arrange + string boolString = "no"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_NoUppercase_ReturnsFalse() + { + // Arrange + string boolString = "NO"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_InvalidString_ReturnsFalse() + { + // Arrange + string boolString = "not a bool"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_NullString_ReturnsFalse() + { + // Arrange + string? boolString = null; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_EmptyString_ReturnsFalse() + { + // Arrange + string boolString = ""; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_TwoString_ReturnsFalse() + { + // Arrange + string boolString = "2"; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetBool")] + public void SafeGetBool_WhitespaceString_ReturnsFalse() + { + // Arrange + string boolString = " "; + + // Act + bool actual = ScriptLinkParsing.SafeGetBool(boolString); + + // Assert + Assert.IsFalse(actual); + } + + #endregion + + #region SafeGetDateTime Tests + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_ValidDateMMDDYYYYSlash_ReturnsDate() + { + // Arrange + string dateTimeString = "10/10/2010"; + DateTime expected = new(2010, 10, 10, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_ValidDateMMDDYYYYDash_ReturnsDate() + { + // Arrange + string dateTimeString = "10-10-2010"; + DateTime expected = new(2010, 10, 10, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_ValidDateMMDDYYDash_ReturnsDate() + { + // Arrange + string dateTimeString = "10-10-10"; + DateTime expected = new(2010, 10, 10, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_ValidDateTextFormat_ReturnsDate() + { + // Arrange + string dateTimeString = "October 10, 2010"; + DateTime expected = new(2010, 10, 10, 0, 0, 0, DateTimeKind.Unspecified); + var originalCulture = Thread.CurrentThread.CurrentCulture; + + try + { + Thread.CurrentThread.CurrentCulture = System.Globalization.CultureInfo.GetCultureInfo("en-US"); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + finally + { + Thread.CurrentThread.CurrentCulture = originalCulture; + } + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_InvalidDateString_ReturnsMinDate() + { + // Arrange + string dateTimeString = "not a date"; + DateTime expected = new(1, 1, 1, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_NullString_ReturnsMinDate() + { + // Arrange + string? dateTimeString = null; + DateTime expected = new(1, 1, 1, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_EmptyString_ReturnsMinDate() + { + // Arrange + string dateTimeString = ""; + DateTime expected = new(1, 1, 1, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_ValidDateWithTime_ReturnsDateTime() + { + // Arrange + string dateTimeString = "10/10/2010 14:30:00"; + DateTime expected = new(2010, 10, 10, 14, 30, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_WhitespaceString_ReturnsMinDate() + { + // Arrange + string dateTimeString = " "; + DateTime expected = new(1, 1, 1, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + [TestMethod] + [TestCategory("ScriptLinkParsing-SafeGetDateTime")] + public void SafeGetDateTime_ValidIso8601Format_ReturnsDate() + { + // Arrange + string dateTimeString = "2010-10-10"; + DateTime expected = new(2010, 10, 10, 0, 0, 0, DateTimeKind.Unspecified); + + // Act + DateTime actual = ScriptLinkParsing.SafeGetDateTime(dateTimeString); + + // Assert + Assert.AreEqual(expected, actual); + } + + #endregion + } +} diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ScriptLinkValidationTests.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ScriptLinkValidationTests.cs new file mode 100644 index 00000000..cc12c537 --- /dev/null +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers.Tests/ScriptLinkValidationTests.cs @@ -0,0 +1,524 @@ +using System; + +namespace RarelySimple.AvatarScriptLink.Objects.Helpers.Tests +{ + [TestClass] + public class ScriptLinkValidationTests + { + #region IsValidUrl(string) Tests + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidHttpUrl_ReturnsTrue() + { + // Arrange + string url = "http://example.com"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidHttpsUrl_ReturnsTrue() + { + // Arrange + string url = "https://example.com"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidHttpUrlWithPath_ReturnsTrue() + { + // Arrange + string url = "http://example.com/path"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidHttpUrlWithQueryString_ReturnsTrue() + { + // Arrange + string url = "http://example.com/?e=123"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidHttpsUrlWithQueryString_ReturnsTrue() + { + // Arrange + string url = "https://example.com/?e=123"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_RelativeUrl_ReturnsFalse() + { + // Arrange + string url = "example.com"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_RelativeUrlWithSpaces_ReturnsFalse() + { + // Arrange + string url = "example domain.com"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_NullString_ReturnsFalse() + { + // Arrange + string? url = null; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_EmptyString_ReturnsFalse() + { + // Arrange + string url = ""; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_WhitespaceString_ReturnsFalse() + { + // Arrange + string url = " "; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidFileScheme_ReturnsTrue() + { + // Arrange + string url = "file:///C:/path/to/file.txt"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidUrlWithFragment_ReturnsTrue() + { + // Arrange + string url = "https://example.com/page#section"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidUrlWithPort_ReturnsTrue() + { + // Arrange + string url = "https://example.com:8443/path"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl")] + public void IsValidUrl_ValidFtpUrl_ReturnsTrue() + { + // Arrange + string url = "ftp://files.example.com/report.csv"; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(url); + + // Assert + Assert.IsTrue(result); + } + + #endregion + + #region IsValidUrl(Uri) Tests + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl-Uri")] + public void IsValidUrl_ValidUri_ReturnsTrue() + { + // Arrange + var uri = new Uri("http://example.com"); + + // Act + bool result = ScriptLinkValidation.IsValidUrl(uri); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl-Uri")] + public void IsValidUrl_AbsoluteUri_ReturnsTrue() + { + // Arrange + var uri = new Uri("https://example.com/path?query=value"); + + // Act + bool result = ScriptLinkValidation.IsValidUrl(uri); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl-Uri")] + public void IsValidUrl_RelativeUri_ReturnsFalse() + { + // Arrange + var uri = new Uri("/path/to/resource", UriKind.Relative); + + // Act + bool result = ScriptLinkValidation.IsValidUrl(uri); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidUrl-Uri")] + public void IsValidUrl_NullUri_ReturnsFalse() + { + // Arrange + Uri? uri = null; + + // Act + bool result = ScriptLinkValidation.IsValidUrl(uri); + + // Assert + Assert.IsFalse(result); + } + + #endregion + + #region IsValidOpenFormString Tests + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_SimpleForm_ReturnsTrue() + { + // Arrange + string openForm = "PR0001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_FormWithModule_ReturnsTrue() + { + // Arrange + string openForm = "[PM]PR0001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_RadplusForm_ReturnsTrue() + { + // Arrange + string openForm = "RADplus_Imaging001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_CWSModule_ReturnsTrue() + { + // Arrange + string openForm = "[CWS]RADplus_Imaging001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_MSOModule_ReturnsTrue() + { + // Arrange + string openForm = "[MSO]PR0001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_FormWithMessage_ReturnsTrue() + { + // Arrange + string openForm = "PR0001|Error occurred"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_FormWithMessageAndPatientId_ReturnsTrue() + { + // Arrange + string openForm = "PR0001|Error occurred|12345"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_FormWithAllComponents_ReturnsTrue() + { + // Arrange + string openForm = "[PM]PR0001|Error occurred|12345|1"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_MultipleForms_ReturnsTrue() + { + // Arrange + string openForm = "PR0001&PR0002"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_InvalidFormName_ReturnsFalse() + { + // Arrange + string openForm = "0001"; // Missing letter prefix + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_InvalidModule_ReturnsFalse() + { + // Arrange + string openForm = "[XX]PR0001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_NullString_ReturnsFalse() + { + // Arrange + string? openForm = null; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_EmptyString_ReturnsFalse() + { + // Arrange + string openForm = ""; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_WhitespaceString_ReturnsFalse() + { + // Arrange + string openForm = " "; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_LowercaseFormName_ReturnsFalse() + { + // Arrange + string openForm = "pr0001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_NumbersOnlyFormName_ReturnsFalse() + { + // Arrange + string openForm = "0001"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_MultipleFormsWithSpacesAroundAmpersand_ReturnsTrue() + { + // Arrange + string openForm = "PR0001 & PR0002"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsTrue(result); + } + + [TestMethod] + [TestCategory("ScriptLinkValidation-IsValidOpenFormString")] + public void IsValidOpenFormString_TabInMessage_ReturnsFalse() + { + // Arrange + string openForm = "PR0001|Error\there"; + + // Act + bool result = ScriptLinkValidation.IsValidOpenFormString(openForm); + + // Assert + Assert.IsFalse(result); + } + + #endregion + } +} diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/FormObjectHelpers.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/FormObjectHelpers.cs index 35ac68c4..6c7e60c0 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/FormObjectHelpers.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/FormObjectHelpers.cs @@ -155,12 +155,12 @@ public static FormObject AddRowObject(this FormObject formObject, RowObject rowO if (!formObject.MultipleIteration && formObject.CurrentRow != null) { - throw new ArgumentException("Cannot add another row to a non-multiple-iteration form.", nameof(formObject)); + throw new ArgumentException(StructuralMutationMessages.CannotAddAnotherRowToNonMultipleIterationForm, nameof(formObject)); } if (!string.IsNullOrEmpty(rowObject.RowId) && formObject.IsRowPresent(rowObject.RowId)) { - throw new ArgumentException("A row with the provided RowId already exists.", nameof(rowObject)); + throw new ArgumentException(StructuralMutationMessages.RowObjectAlreadyExists, nameof(rowObject)); } if (formObject.CurrentRow == null) @@ -565,7 +565,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && formObject.OtherRows.Any(r => r.IsFieldPresent(fieldNumber))); if (!hasFieldInForm) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); if (formObject.CurrentRow.IsFieldPresent(fieldNumber)) { @@ -602,7 +602,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && fieldsToSet.Any(f => formObject.OtherRows.Any(r => r.IsFieldPresent(f)))); if (!hasAnyField) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); var currentRowFieldNumbers = fieldsToSet.Where(formObject.CurrentRow.IsFieldPresent).ToList(); if (currentRowFieldNumbers.Count > 0) @@ -644,7 +644,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && formObject.OtherRows.Any(r => r.IsFieldPresent(fieldNumber))); if (!hasFieldInForm) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); if (formObject.CurrentRow.IsFieldPresent(fieldNumber)) { @@ -681,7 +681,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && fieldsToSet.Any(f => formObject.OtherRows.Any(r => r.IsFieldPresent(f)))); if (!hasAnyField) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); var currentRowFieldNumbers = fieldsToSet.Where(formObject.CurrentRow.IsFieldPresent).ToList(); if (currentRowFieldNumbers.Count > 0) @@ -723,7 +723,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && formObject.OtherRows.Any(r => r.IsFieldPresent(fieldNumber))); if (!hasFieldInForm) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); if (formObject.CurrentRow.IsFieldPresent(fieldNumber)) { @@ -760,7 +760,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && fieldsToSet.Any(f => formObject.OtherRows.Any(r => r.IsFieldPresent(f)))); if (!hasAnyField) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); var currentRowFieldNumbers = fieldsToSet.Where(formObject.CurrentRow.IsFieldPresent).ToList(); if (currentRowFieldNumbers.Count > 0) @@ -802,7 +802,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && formObject.OtherRows.Any(r => r.IsFieldPresent(fieldNumber))); if (!hasFieldInForm) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); if (formObject.CurrentRow.IsFieldPresent(fieldNumber)) { @@ -839,7 +839,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && fieldsToSet.Any(f => formObject.OtherRows.Any(r => r.IsFieldPresent(f)))); if (!hasAnyField) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); var currentRowFieldNumbers = fieldsToSet.Where(formObject.CurrentRow.IsFieldPresent).ToList(); if (currentRowFieldNumbers.Count > 0) @@ -881,7 +881,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && formObject.OtherRows.Any(r => r.IsFieldPresent(fieldNumber))); if (!hasFieldInForm) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); if (formObject.CurrentRow.IsFieldPresent(fieldNumber)) { @@ -918,7 +918,7 @@ public static bool IsRowPresent(this FormObject formObject, string rowId) || (formObject.MultipleIteration && formObject.HasOtherRows() && fieldsToSet.Any(f => formObject.OtherRows.Any(r => r.IsFieldPresent(f)))); if (!hasAnyField) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); var currentRowFieldNumbers = fieldsToSet.Where(formObject.CurrentRow.IsFieldPresent).ToList(); if (currentRowFieldNumbers.Count > 0) diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2015Helpers.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2015Helpers.cs index 7cef7340..4c5846fd 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2015Helpers.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2015Helpers.cs @@ -222,7 +222,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -267,7 +267,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -297,7 +297,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -342,7 +342,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -372,7 +372,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -417,7 +417,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -447,7 +447,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -492,7 +492,7 @@ public static OptionObject2015 DeleteRowObject(this OptionObject2015 optionObjec .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2Helpers.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2Helpers.cs index c7217ea7..4fe44418 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2Helpers.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObject2Helpers.cs @@ -202,7 +202,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -247,7 +247,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -277,7 +277,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -322,7 +322,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -352,7 +352,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -397,7 +397,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -427,7 +427,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -472,7 +472,7 @@ public static OptionObject2 DeleteRowObject(this OptionObject2 optionObject, str .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObjectHelpers.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObjectHelpers.cs index 7dd34893..44520c47 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObjectHelpers.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/OptionObjectHelpers.cs @@ -372,7 +372,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -417,7 +417,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -447,7 +447,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -492,7 +492,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -522,7 +522,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -567,7 +567,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -597,7 +597,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -642,7 +642,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -672,7 +672,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -720,7 +720,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { @@ -750,7 +750,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN return optionObject; if (!optionObject.Forms.Any(f => f.IsFieldPresent(fieldNumber))) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumber)); foreach (var form in optionObject.Forms) { @@ -798,7 +798,7 @@ public static bool IsFieldRequired(this OptionObject optionObject, string fieldN .ToList(); if (fieldsToSet.Count == 0) - throw new ArgumentException(ArgumentGuards.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); + throw new ArgumentException(StructuralMutationMessages.NoMatchingFieldObjectsMessage, nameof(fieldNumbers)); foreach (var form in optionObject.Forms) { diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/ScriptLinkParsing.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/ScriptLinkParsing.cs new file mode 100644 index 00000000..72de4610 --- /dev/null +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/ScriptLinkParsing.cs @@ -0,0 +1,86 @@ +using System; +using System.Globalization; + +namespace RarelySimple.AvatarScriptLink.Objects.Helpers +{ + /// + /// Provides safe parsing helpers for converting strings to common types with documented fallback behaviors. + /// + public static class ScriptLinkParsing + { + /// + /// Safely converts a string to an integer. + /// + /// The string to convert. + /// Returns the converted string as an int. Otherwise, returns 0 if string is not a valid integer. + /// + /// Behavior: + /// + /// Valid integers (including negative) are converted correctly. + /// Decimal strings (e.g., "10.5") return 0. + /// Alphabetic or special character strings return 0. + /// Null or empty strings return 0. + /// + /// + public static int SafeGetInt(string? intString) + { + if (int.TryParse(intString, out int convertedInt)) + return convertedInt; + return 0; + } + + /// + /// Safely converts a string to a boolean. + /// + /// The string to convert. + /// Returns true for "1", "T", "TRUE", "Y", "YES" (case-insensitive). Returns false for all other values. + /// + /// Behavior: + /// + /// "1", "T", "TRUE", "Y", "YES" (case-insensitive) convert to true. + /// "0", "F", "FALSE", "N", "NO" (case-insensitive) convert to false. + /// Any other string returns false. + /// Null or empty strings return false. + /// + /// + public static bool SafeGetBool(string? boolString) + { + if (boolString != null) + { + switch (boolString.ToUpperInvariant()) + { + case "1": + case "T": + case "TRUE": + case "Y": + case "YES": + return true; + default: + return false; + } + } + return false; + } + + /// + /// Safely converts a string to a DateTime. + /// + /// The string to convert. + /// Returns the converted string as a DateTime. Otherwise, returns new DateTime(0, DateTimeKind.Unspecified) if string is not a valid DateTime. + /// + /// Behavior: + /// + /// Supports common date formats: MM/DD/YYYY, MM-DD-YYYY, October 10, 2010, etc. + /// Uses CurrentCulture for parsing. + /// Invalid date strings return new DateTime(0) (January 1, 0001). + /// Null or empty strings return new DateTime(0). + /// + /// + public static DateTime SafeGetDateTime(string? dateTimeString) + { + if (DateTime.TryParse(dateTimeString, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime convertedDateTime)) + return convertedDateTime; + return new DateTime(0, DateTimeKind.Unspecified); + } + } +} diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/ScriptLinkValidation.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/ScriptLinkValidation.cs new file mode 100644 index 00000000..d7d45d6b --- /dev/null +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/ScriptLinkValidation.cs @@ -0,0 +1,100 @@ +using System; +using System.Text.RegularExpressions; + +namespace RarelySimple.AvatarScriptLink.Objects.Helpers +{ + /// + /// Provides validation helpers for ScriptLink-specific formats and values. + /// + public static class ScriptLinkValidation + { + /// + /// Returns whether a string is a valid URL for use with error code 5. + /// + /// The string to validate as a URL. + /// True if the string is a valid absolute URL; otherwise, false. + /// + /// Behavior: + /// + /// Null or empty strings return false. + /// Relative URLs (e.g., "everydaymatters.com") return false. + /// Only absolute URLs (with scheme like http:// or https://) return true. + /// Query strings and fragments are allowed. + /// + /// + public static bool IsValidUrl(string? strUrl) + { + return !string.IsNullOrWhiteSpace(strUrl) && Uri.IsWellFormedUriString(strUrl, UriKind.Absolute); + } + + /// + /// Returns whether a Uri is a valid URL for use with error code 5. + /// + /// The Uri to validate. + /// True if the Uri is an absolute URI; otherwise, false. + /// + /// Behavior: + /// + /// Null Uri returns false. + /// Relative URIs return false. + /// Only absolute URIs return true. + /// + /// + public static bool IsValidUrl(Uri? uri) + { + if (uri == null) + return false; + return uri.IsAbsoluteUri; + } + + /// + /// Returns whether a string is a valid OpenForm string for use with error code 6. + /// + /// The string to validate as an OpenForm string. + /// True if the string matches the OpenForm format; otherwise, false. + /// + /// OpenForm strings follow the pattern: [MODULE]FORM[|message][|patientId][|episodeNumber] + /// Valid examples: + /// + /// PR0001 + /// [PM]PR0001 + /// [CWS]RADplus_Imaging001 + /// PR0001|Error occurred|12345 + /// PR0001|Error occurred|12345|1 + /// + /// Behavior: + /// + /// Null or whitespace strings return false. + /// Strings with invalid characters or format return false. + /// + /// + public static bool IsValidOpenFormString(string? value) + { + if (string.IsNullOrWhiteSpace(value)) + return false; + + try + { + return Regex.IsMatch(value, RegexPatterns.FullPattern, RegexOptions.None, TimeSpan.FromMilliseconds(100)); + } + catch (RegexMatchTimeoutException) + { + return false; + } + } + + private static class RegexPatterns + { + private const string ModulePrefix = @"(\[(PM|CWS|MSO)\])?"; + private const string ModuleForm = @"[A-Z]+[0-9]+"; + private const string RadplusForm = @"RADplus_[A-Za-z]+[0-9]+"; + private const string Form = @"(?:" + ModuleForm + "|" + RadplusForm + ")"; + private const string PrefixedForm = @"(\s*)" + ModulePrefix + Form; + private const string FormList = PrefixedForm + @"((\s*)&(\s*)" + ModulePrefix + Form + ")*"; + private const string Message = @"((\s*)\|(\s*)([^\|\t\n\r])*)?"; + private const string PatientId = @"((\s*)\|(\s*)\d+)?"; + private const string EpisodeNumber = @"((\s*)\|(\s*)([1-9][0-9]*)+|(\s*)\|(\s*))?"; + public const string FullPattern = "^" + FormList + Message + PatientId + EpisodeNumber + "$"; + } + } +} diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/StructuralMutationCore.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/StructuralMutationCore.cs index 30e48669..e6ea76de 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/StructuralMutationCore.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/StructuralMutationCore.cs @@ -10,6 +10,9 @@ internal static class StructuralMutationMessages internal const string FormsCollectionCannotBeNull = "Forms collection cannot be null."; internal const string NoMatchingFormForFormId = "No matching form was found for the provided formId."; internal const string NoMatchingRowForRowId = "No matching row was found for the provided rowId."; + internal const string NoMatchingFieldObjectsMessage = "No matching field objects were found."; + internal const string CannotAddAnotherRowToNonMultipleIterationForm = "Cannot add another row to a non-multiple-iteration form."; + internal const string RowObjectAlreadyExists = "A row with the provided RowId already exists."; } internal static class StructuralMutationCore diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/Validators/ArgumentGuards.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/Validators/ArgumentGuards.cs index dd8f043f..5c4d2681 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/Validators/ArgumentGuards.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Helpers/Validators/ArgumentGuards.cs @@ -7,12 +7,12 @@ namespace RarelySimple.AvatarScriptLink.Objects.Helpers.Validators /// /// Shared guard helpers for argument validation and normalization used by helper extension methods. /// - public static class ArgumentGuards + internal static class ArgumentGuards { - internal const string NoMatchingFieldObjectsMessage = "No matching field objects were found."; - private const string NoFieldObjectsProvidedMessage = "No field objects were provided."; - private const string NoFieldNumbersProvidedMessage = "No field numbers were provided."; - private const string FieldNumberCannotBeEmptyMessage = "Field number cannot be empty."; + internal const string NoMatchingFieldObjectsMessage = StructuralMutationMessages.NoMatchingFieldObjectsMessage; + internal const string NoFieldObjectsProvidedMessage = "No field objects were provided."; + internal const string NoFieldNumbersProvidedMessage = "No field numbers were provided."; + internal const string FieldNumberCannotBeEmptyMessage = "Field number cannot be empty."; /// /// Validates a single field number argument. diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators.Tests/ResponseValidatorTests.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators.Tests/ResponseValidatorTests.cs index d702b287..7270ac8e 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators.Tests/ResponseValidatorTests.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators.Tests/ResponseValidatorTests.cs @@ -31,6 +31,34 @@ public void ValidateResponse_ReturnsError_WhenEntityIdIsMissing() CollectionAssert.Contains(result.Errors.ToList(), "EntityID is required."); } + [TestMethod] + public void ValidateResponse_ReturnsError_WhenErrorCodeIsNaN() + { + // Arrange + var optionObject = new OptionObject { EntityID = "PATID", ErrorCode = double.NaN, ErrorMesg = string.Empty }; + + // Act + ResponseValidationResult result = optionObject.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "ErrorCode must be a finite number between 0 and 6."); + } + + [TestMethod] + public void ValidateResponse_ReturnsError_WhenErrorCodeIsInfinity() + { + // Arrange + var optionObject = new OptionObject { EntityID = "PATID", ErrorCode = double.PositiveInfinity, ErrorMesg = string.Empty }; + + // Act + ResponseValidationResult result = optionObject.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "ErrorCode must be a finite number between 0 and 6."); + } + [TestMethod] public void ValidateResponse_ReturnsError_WhenErrorCodeIsInvalid() { @@ -161,6 +189,34 @@ public void ValidateResponsePayload_ReturnsErrors_ForInvalidForm() [TestClass] public class FormObjectResponseValidatorsTests { + [TestMethod] + public void ValidateResponse_ReturnsError_WhenFormObjectIsNull() + { + // Arrange + FormObject? form = null; + + // Act + ResponseValidationResult result = form.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "FormObject is null."); + } + + [TestMethod] + public void ValidateResponse_ReturnsError_WhenFormIdIsMissing() + { + // Arrange + var form = new FormObject { FormId = string.Empty }; + + // Act + ResponseValidationResult result = form.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "FormId is required."); + } + [TestMethod] public void ValidateResponse_ReturnsError_WhenMultipleIterationFalseAndOtherRowsPresent() { @@ -175,11 +231,65 @@ public void ValidateResponse_ReturnsError_WhenMultipleIterationFalseAndOtherRows Assert.IsFalse(result.IsValid); CollectionAssert.Contains(result.Errors.ToList(), "OtherRows must be empty when MultipleIteration is false."); } + + [TestMethod] + public void ValidateResponse_ReturnsSuccess_WhenFormIsValid() + { + // Arrange + var form = new FormObject { FormId = "FORM1", MultipleIteration = false }; + + // Act + ResponseValidationResult result = form.ValidateResponse(); + + // Assert + Assert.IsTrue(result.IsValid); + } + + [TestMethod] + public void ValidateResponse_ReturnsInvalid_WhenFormIdIsMissing() + { + // Arrange + var form = new FormObject { FormId = string.Empty }; + + // Act + bool result = form.ValidateResponse().IsValid; + + // Assert + Assert.IsFalse(result); + } } [TestClass] public class RowObjectResponseValidatorsTests { + [TestMethod] + public void ValidateResponse_ReturnsError_WhenRowObjectIsNull() + { + // Arrange + RowObject? row = null; + + // Act + ResponseValidationResult result = row.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "RowObject is null."); + } + + [TestMethod] + public void ValidateResponse_ReturnsError_WhenRowIdIsMissing() + { + // Arrange + var row = new RowObject { RowId = string.Empty }; + + // Act + ResponseValidationResult result = row.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "RowId is required."); + } + [TestMethod] public void ValidateResponse_ReturnsError_WhenRowActionMissingWithFields() { @@ -208,11 +318,51 @@ public void ValidateResponse_ReturnsError_WhenRowActionInvalid() Assert.IsFalse(result.IsValid); CollectionAssert.Contains(result.Errors.ToList(), "RowAction must be ADD, EDIT, DELETE, or empty."); } + + [TestMethod] + public void ValidateResponse_ReturnsSuccess_WhenRowIsValid() + { + // Arrange + var row = new RowObject { RowId = "1", RowAction = RowObject.RowActions.Edit }; + + // Act + ResponseValidationResult result = row.ValidateResponse(); + + // Assert + Assert.IsTrue(result.IsValid); + } + + [TestMethod] + public void ValidateResponse_ReturnsInvalid_WhenRowIdIsMissing() + { + // Arrange + var row = new RowObject { RowId = string.Empty }; + + // Act + bool result = row.ValidateResponse().IsValid; + + // Assert + Assert.IsFalse(result); + } } [TestClass] public class FieldObjectResponseValidatorsTests { + [TestMethod] + public void ValidateResponse_ReturnsError_WhenFieldObjectIsNull() + { + // Arrange + FieldObject? field = null; + + // Act + ResponseValidationResult result = field.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "FieldObject is null."); + } + [TestMethod] public void ValidateResponse_ReturnsError_WhenFieldNumberMissing() { @@ -240,5 +390,59 @@ public void ValidateResponse_ReturnsError_WhenFlagIsInvalid() Assert.IsFalse(result.IsValid); CollectionAssert.Contains(result.Errors.ToList(), "Enabled must be \"0\" or \"1\" when set."); } + + [TestMethod] + public void ValidateResponse_ReturnsError_WhenLockFlagIsInvalid() + { + // Arrange + var field = new FieldObject { FieldNumber = "100", Lock = "X" }; + + // Act + ResponseValidationResult result = field.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "Lock must be \"0\" or \"1\" when set."); + } + + [TestMethod] + public void ValidateResponse_ReturnsError_WhenRequiredFlagIsInvalid() + { + // Arrange + var field = new FieldObject { FieldNumber = "100", Required = "X" }; + + // Act + ResponseValidationResult result = field.ValidateResponse(); + + // Assert + Assert.IsFalse(result.IsValid); + CollectionAssert.Contains(result.Errors.ToList(), "Required must be \"0\" or \"1\" when set."); + } + + [TestMethod] + public void ValidateResponse_ReturnsSuccess_WhenFieldIsValid() + { + // Arrange + var field = new FieldObject { FieldNumber = "100", Enabled = "1", Lock = "0", Required = "0" }; + + // Act + ResponseValidationResult result = field.ValidateResponse(); + + // Assert + Assert.IsTrue(result.IsValid); + } + + [TestMethod] + public void ValidateResponse_ReturnsInvalid_WhenFieldNumberMissing() + { + // Arrange + var field = new FieldObject { FieldNumber = string.Empty }; + + // Act + bool result = field.ValidateResponse().IsValid; + + // Assert + Assert.IsFalse(result); + } } } diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FieldObjectResponseValidators.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FieldObjectResponseValidators.cs index 94035bfc..fbb41502 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FieldObjectResponseValidators.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FieldObjectResponseValidators.cs @@ -18,13 +18,13 @@ public static ResponseValidationResult ValidateResponse(this FieldObject? fieldO if (fieldObject == null) { - errors.Add("FieldObject is null."); + errors.Add(ResponseValidationMessages.FieldObjectIsNull); return new ResponseValidationResult(errors); } if (string.IsNullOrWhiteSpace(fieldObject.FieldNumber)) { - errors.Add("FieldNumber is required."); + errors.Add(ResponseValidationMessages.FieldNumberIsRequired); } ValidateFlag(fieldObject.Enabled, "Enabled", errors); @@ -38,7 +38,7 @@ private static void ValidateFlag(string value, string name, List errors) { if (!string.IsNullOrEmpty(value) && value != "0" && value != "1") { - errors.Add($"{name} must be \"0\" or \"1\" when set."); + errors.Add(string.Format(ResponseValidationMessages.FieldFlagMustBeZeroOrOne, name)); } } } diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FormObjectResponseValidators.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FormObjectResponseValidators.cs index 9a42ff8e..ccd1be20 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FormObjectResponseValidators.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/FormObjectResponseValidators.cs @@ -18,24 +18,24 @@ public static ResponseValidationResult ValidateResponse(this FormObject? formObj if (formObject == null) { - errors.Add("FormObject is null."); + errors.Add(ResponseValidationMessages.FormObjectIsNull); return new ResponseValidationResult(errors); } if (string.IsNullOrWhiteSpace(formObject.FormId)) { - errors.Add("FormId is required."); + errors.Add(ResponseValidationMessages.FormIdIsRequired); } if (formObject.OtherRows == null) { - errors.Add("OtherRows collection must not be null."); + errors.Add(ResponseValidationMessages.OtherRowsCollectionMustNotBeNull); return new ResponseValidationResult(errors); } if (!formObject.MultipleIteration && formObject.OtherRows.Count > 0) { - errors.Add("OtherRows must be empty when MultipleIteration is false."); + errors.Add(ResponseValidationMessages.OtherRowsMustBeEmptyWhenMultipleIterationIsFalse); } if (formObject.CurrentRow != null) diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/OptionObjectResponseValidators.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/OptionObjectResponseValidators.cs index a10766a3..ea6be2b6 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/OptionObjectResponseValidators.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/OptionObjectResponseValidators.cs @@ -21,13 +21,13 @@ public static ResponseValidationResult ValidateResponse(this IOptionObject? opti if (optionObject == null) { - errors.Add("OptionObject is null."); + errors.Add(ResponseValidationMessages.OptionObjectIsNull); return new ResponseValidationResult(errors); } if (string.IsNullOrWhiteSpace(optionObject.EntityID)) { - errors.Add("EntityID is required."); + errors.Add(ResponseValidationMessages.EntityIdIsRequired); } ValidateErrorCode(optionObject.ErrorCode, errors); @@ -101,20 +101,20 @@ private static void ValidateErrorCode(double errorCode, List errors) { if (double.IsNaN(errorCode) || double.IsInfinity(errorCode)) { - errors.Add("ErrorCode must be a finite number between 0 and 6."); + errors.Add(ResponseValidationMessages.ErrorCodeMustBeFiniteBetween0And6); return; } if (errorCode < 0 || errorCode > 6) { - errors.Add("ErrorCode must be between 0 and 6."); + errors.Add(ResponseValidationMessages.ErrorCodeMustBeBetween0And6); return; } double rounded = Math.Round(errorCode, 0, MidpointRounding.AwayFromZero); if (Math.Abs(errorCode - rounded) > 1e-9) { - errors.Add("ErrorCode must be an integer between 0 and 6."); + errors.Add(ResponseValidationMessages.ErrorCodeMustBeIntegerBetween0And6); } } @@ -128,25 +128,25 @@ private static void ValidateErrorMessage(double errorCode, string errorMessage, if (normalizedCode == 0 && !string.IsNullOrEmpty(errorMessage)) { - errors.Add("ErrorMesg must be empty when ErrorCode is 0."); + errors.Add(ResponseValidationMessages.ErrorMesgMustBeEmptyWhenErrorCodeIs0); return; } if (normalizedCode >= 1 && normalizedCode <= 4 && string.IsNullOrWhiteSpace(errorMessage)) { - errors.Add("ErrorMesg is required when ErrorCode is between 1 and 4."); + errors.Add(ResponseValidationMessages.ErrorMesgIsRequiredWhenErrorCodeIsBetween1And4); return; } if (normalizedCode == 5 && !IsValidUrl(errorMessage)) { - errors.Add("ErrorMesg must be a valid absolute URL when ErrorCode is 5."); + errors.Add(ResponseValidationMessages.ErrorMesgMustBeValidAbsoluteUrlWhenErrorCodeIs5); return; } if (normalizedCode == 6 && !IsValidOpenFormString(errorMessage)) { - errors.Add("ErrorMesg must be a valid OpenForm string when ErrorCode is 6."); + errors.Add(ResponseValidationMessages.ErrorMesgMustBeValidOpenFormStringWhenErrorCodeIs6); } } diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/ResponseValidationMessages.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/ResponseValidationMessages.cs new file mode 100644 index 00000000..3c410937 --- /dev/null +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/ResponseValidationMessages.cs @@ -0,0 +1,30 @@ +namespace RarelySimple.AvatarScriptLink.Objects.Validators +{ + internal static class ResponseValidationMessages + { + internal const string FieldObjectIsNull = "FieldObject is null."; + internal const string FieldNumberIsRequired = "FieldNumber is required."; + internal const string FieldFlagMustBeZeroOrOne = "{0} must be \"0\" or \"1\" when set."; + + internal const string FormObjectIsNull = "FormObject is null."; + internal const string FormIdIsRequired = "FormId is required."; + internal const string OtherRowsCollectionMustNotBeNull = "OtherRows collection must not be null."; + internal const string OtherRowsMustBeEmptyWhenMultipleIterationIsFalse = "OtherRows must be empty when MultipleIteration is false."; + + internal const string RowObjectIsNull = "RowObject is null."; + internal const string RowIdIsRequired = "RowId is required."; + internal const string RowActionMustBeAddEditDeleteOrEmpty = "RowAction must be ADD, EDIT, DELETE, or empty."; + internal const string FieldsCollectionMustNotBeNull = "Fields collection must not be null."; + internal const string RowActionIsRequiredWhenRowContainsFields = "RowAction is required when row contains fields."; + + internal const string OptionObjectIsNull = "OptionObject is null."; + internal const string EntityIdIsRequired = "EntityID is required."; + internal const string ErrorCodeMustBeFiniteBetween0And6 = "ErrorCode must be a finite number between 0 and 6."; + internal const string ErrorCodeMustBeBetween0And6 = "ErrorCode must be between 0 and 6."; + internal const string ErrorCodeMustBeIntegerBetween0And6 = "ErrorCode must be an integer between 0 and 6."; + internal const string ErrorMesgMustBeEmptyWhenErrorCodeIs0 = "ErrorMesg must be empty when ErrorCode is 0."; + internal const string ErrorMesgIsRequiredWhenErrorCodeIsBetween1And4 = "ErrorMesg is required when ErrorCode is between 1 and 4."; + internal const string ErrorMesgMustBeValidAbsoluteUrlWhenErrorCodeIs5 = "ErrorMesg must be a valid absolute URL when ErrorCode is 5."; + internal const string ErrorMesgMustBeValidOpenFormStringWhenErrorCodeIs6 = "ErrorMesg must be a valid OpenForm string when ErrorCode is 6."; + } +} diff --git a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/RowObjectResponseValidators.cs b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/RowObjectResponseValidators.cs index 7608b77b..c7817970 100644 --- a/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/RowObjectResponseValidators.cs +++ b/dotnet/RarelySimple.AvatarScriptLink.Objects.Validators/RowObjectResponseValidators.cs @@ -18,29 +18,29 @@ public static ResponseValidationResult ValidateResponse(this RowObject? rowObjec if (rowObject == null) { - errors.Add("RowObject is null."); + errors.Add(ResponseValidationMessages.RowObjectIsNull); return new ResponseValidationResult(errors); } if (string.IsNullOrWhiteSpace(rowObject.RowId)) { - errors.Add("RowId is required."); + errors.Add(ResponseValidationMessages.RowIdIsRequired); } if (!IsValidRowAction(rowObject.RowAction)) { - errors.Add("RowAction must be ADD, EDIT, DELETE, or empty."); + errors.Add(ResponseValidationMessages.RowActionMustBeAddEditDeleteOrEmpty); } if (rowObject.Fields == null) { - errors.Add("Fields collection must not be null."); + errors.Add(ResponseValidationMessages.FieldsCollectionMustNotBeNull); return new ResponseValidationResult(errors); } if (rowObject.Fields.Count > 0 && string.IsNullOrEmpty(rowObject.RowAction)) { - errors.Add("RowAction is required when row contains fields."); + errors.Add(ResponseValidationMessages.RowActionIsRequiredWhenRowContainsFields); } for (int i = 0; i < rowObject.Fields.Count; i++)