From 31f837402a2aa811ed8b47d3e725d8144af18a71 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 27 Jul 2024 12:22:29 +0800 Subject: [PATCH] add unit tests for json (#2993) * add unit tests for json * fix ut * add ut cases for JString. * add invalid jpath tests * behavior of jboolean is kinda weird. i have: Assert.AreEqual failed. Expected:. Actual:. * fix test error * add more test --------- Co-authored-by: Shargon --- tests/Neo.Json.UnitTests/UT_JArray.cs | 66 +++- tests/Neo.Json.UnitTests/UT_JBoolean.cs | 45 +++ tests/Neo.Json.UnitTests/UT_JPath.cs | 69 +++++ tests/Neo.Json.UnitTests/UT_JString.cs | 394 ++++++++++++++++++++++-- 4 files changed, 550 insertions(+), 24 deletions(-) diff --git a/tests/Neo.Json.UnitTests/UT_JArray.cs b/tests/Neo.Json.UnitTests/UT_JArray.cs index b6c603ca2a..1538dfe558 100644 --- a/tests/Neo.Json.UnitTests/UT_JArray.cs +++ b/tests/Neo.Json.UnitTests/UT_JArray.cs @@ -255,17 +255,75 @@ public void TestAsString() Assert.AreEqual(s, "{\"name\":\"alice\",\"age\":30,\"score\":100.001,\"gender\":\"female\",\"isMarried\":true,\"pet\":{\"name\":\"Tom\",\"type\":\"cat\"}},{\"name\":\"bob\",\"age\":100000,\"score\":0.001,\"gender\":\"male\",\"isMarried\":false,\"pet\":{\"name\":\"Paul\",\"type\":\"dog\"}}"); } + [TestMethod] + public void TestCount() + { + var jArray = new JArray { alice, bob }; + jArray.Count.Should().Be(2); + } + + [TestMethod] + public void TestInvalidIndexAccess() + { + var jArray = new JArray { alice }; + Action action = () => { var item = jArray[1]; }; + action.Should().Throw(); + } + + [TestMethod] + public void TestEmptyEnumeration() + { + var jArray = new JArray(); + foreach (var item in jArray) + { + Assert.Fail("Enumeration should not occur on an empty JArray"); + } + } + + [TestMethod] + public void TestImplicitConversionFromJTokenArray() + { + JToken[] jTokens = { alice, bob }; + JArray jArray = jTokens; + + jArray.Count.Should().Be(2); + jArray[0].Should().Be(alice); + jArray[1].Should().Be(bob); + } + + [TestMethod] + public void TestAddNullValues() + { + var jArray = new JArray(); + jArray.Add(null); + jArray.Count.Should().Be(1); + jArray[0].Should().BeNull(); + } + [TestMethod] public void TestClone() { - var jArray = new JArray + var jArray = new JArray { alice, bob }; + var clone = (JArray)jArray.Clone(); + + clone.Should().NotBeSameAs(jArray); + clone.Count.Should().Be(jArray.Count); + + for (int i = 0; i < jArray.Count; i++) { - alice, - bob, - }; + clone[i]?.AsString().Should().Be(jArray[i]?.AsString()); + } + var a = jArray.AsString(); var b = jArray.Clone().AsString(); a.Should().Be(b); } + + [TestMethod] + public void TestReadOnlyBehavior() + { + var jArray = new JArray(); + jArray.IsReadOnly.Should().BeFalse(); + } } } diff --git a/tests/Neo.Json.UnitTests/UT_JBoolean.cs b/tests/Neo.Json.UnitTests/UT_JBoolean.cs index 0e81e99c35..4c5ed6f263 100644 --- a/tests/Neo.Json.UnitTests/UT_JBoolean.cs +++ b/tests/Neo.Json.UnitTests/UT_JBoolean.cs @@ -9,6 +9,8 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using Newtonsoft.Json; + namespace Neo.Json.UnitTests { [TestClass] @@ -31,6 +33,49 @@ public void TestAsNumber() jTrue.AsNumber().Should().Be(1); } + [TestMethod] + public void TestDefaultConstructor() + { + var defaultJBoolean = new JBoolean(); + defaultJBoolean.AsNumber().Should().Be(0); + } + + [TestMethod] + public void TestExplicitFalse() + { + var explicitFalse = new JBoolean(false); + explicitFalse.AsNumber().Should().Be(0); + } + + [TestMethod] + public void TestNullJBoolean() + { + JBoolean nullJBoolean = null; + Assert.ThrowsException(() => nullJBoolean.AsNumber()); + } + + [TestMethod] + public void TestConversionToOtherTypes() + { + Assert.AreEqual("true", jTrue.ToString()); + Assert.AreEqual("false", jFalse.ToString()); + } + + [TestMethod] + public void TestComparisonsWithOtherBooleans() + { + Assert.IsTrue(jTrue.Equals(new JBoolean(true))); + Assert.IsTrue(jFalse.Equals(new JBoolean())); + } + + [TestMethod] + public void TestSerializationAndDeserialization() + { + string serialized = JsonConvert.SerializeObject(jTrue); + var deserialized = JsonConvert.DeserializeObject(serialized); + Assert.AreEqual(jTrue, deserialized); + } + [TestMethod] public void TestEqual() { diff --git a/tests/Neo.Json.UnitTests/UT_JPath.cs b/tests/Neo.Json.UnitTests/UT_JPath.cs index 9d958b1cde..68e204a030 100644 --- a/tests/Neo.Json.UnitTests/UT_JPath.cs +++ b/tests/Neo.Json.UnitTests/UT_JPath.cs @@ -99,6 +99,75 @@ public void TestInvalidFormat() Assert.ThrowsException(() => json.JsonPath("$..*")); Assert.ThrowsException(() => json.JsonPath("..book")); Assert.ThrowsException(() => json.JsonPath("$..")); + + // Test with an empty JSON Path + // Assert.ThrowsException(() => json.JsonPath("")); + + // Test with only special characters + Assert.ThrowsException(() => json.JsonPath("@#$%^&*()")); + + // Test with unmatched brackets + Assert.ThrowsException(() => json.JsonPath("$.store.book[")); + Assert.ThrowsException(() => json.JsonPath("$.store.book)]")); + + // Test with invalid operators + Assert.ThrowsException(() => json.JsonPath("$.store.book=>2")); + + // Test with incorrect field syntax + Assert.ThrowsException(() => json.JsonPath("$.store.'book'")); + Assert.ThrowsException(() => json.JsonPath("$.store.[book]")); + + // Test with unexpected end of expression + Assert.ThrowsException(() => json.JsonPath("$.store.book[?(@.price<")); + + // Test with invalid array indexing + // Assert.ThrowsException(() => json.JsonPath("$.store.book['one']")); + // Assert.ThrowsException(() => json.JsonPath("$.store.book[999]")); + + // Test with invalid recursive descent + Assert.ThrowsException(() => json.JsonPath("$..*..author")); + + // Test with nonexistent functions + Assert.ThrowsException(() => json.JsonPath("$.store.book.length()")); + + // Test with incorrect use of wildcards + // Assert.ThrowsException(() => json.JsonPath("$.*.store")); + + // Test with improper use of filters + Assert.ThrowsException(() => json.JsonPath("$.store.book[?(@.price)]")); + + // Test with mixing of valid and invalid syntax + Assert.ThrowsException(() => json.JsonPath("$.store.book[*],$.invalid")); + + // Test with invalid escape sequences + Assert.ThrowsException(() => json.JsonPath("$.store.book[\\]")); + + // Test with incorrect property access + Assert.ThrowsException(() => json.JsonPath("$.store.'b?ook'")); + + // Test with invalid use of wildcard in array index + // Assert.ThrowsException(() => json.JsonPath("$.store.book[*]")); + + // Test with missing operators in filter expressions + Assert.ThrowsException(() => json.JsonPath("$.store.book[?(@.price)]")); + + // Test with incorrect boolean logic in filters + Assert.ThrowsException(() => json.JsonPath("$.store.book[?(@.price AND @.title)]")); + + // Test with nested filters without proper closure + Assert.ThrowsException(() => json.JsonPath("$.store.book[?(@.price[?(@ < 10)])]")); + + // Test with misplaced recursive descent operator + // Assert.ThrowsException(() => json.JsonPath("$..store..book")); + + // Test with using JSONPath reserved keywords incorrectly + Assert.ThrowsException(() => json.JsonPath("$..@.book")); + + // Test with incorrect combinations of valid operators + Assert.ThrowsException(() => json.JsonPath("$.store.book..[0]")); + + // Test with invalid script expressions (if supported) + Assert.ThrowsException(() => json.JsonPath("$.store.book[(@.length-1)]")); } } } diff --git a/tests/Neo.Json.UnitTests/UT_JString.cs b/tests/Neo.Json.UnitTests/UT_JString.cs index 3f0ca159ab..a80920ee51 100644 --- a/tests/Neo.Json.UnitTests/UT_JString.cs +++ b/tests/Neo.Json.UnitTests/UT_JString.cs @@ -9,11 +9,50 @@ // Redistribution and use in source and binary forms with or without // modifications are permitted. +using System.Text; +using System.Text.Json; + namespace Neo.Json.UnitTests { [TestClass] public class UT_JString { + private static readonly JString AsicString = "hello world"; + private static readonly JString EscapeString = "\n\t\'\""; + private static readonly JString BadChar = ((char)0xff).ToString(); + private static readonly JString IntegerString = "123"; + private static readonly JString EmptyString = ""; + private static readonly JString SpaceString = " "; + private static readonly JString DoubleString = "123.456"; + private static readonly JString UnicodeString = "\ud83d\ude03\ud83d\ude01"; + private static readonly JString EmojString = "ã🦆"; + private static readonly JString MixedString = "abc123!@# "; + private static readonly JString LongString = new String('x', 5000); // 5000 + private static readonly JString MultiLangString = "Hello 你好 مرحبا"; + private static readonly JString JsonString = "{\"key\": \"value\"}"; + private static readonly JString HtmlEntityString = "& < >"; + private static readonly JString ControlCharString = "\t\n\r"; + private static readonly JString SingleCharString = "a"; + private static readonly JString LongWordString = "Supercalifragilisticexpialidocious"; + private static readonly JString ConcatenatedString = new JString("Hello" + "123" + "!@#"); + private static readonly JString WhiteSpaceString = new JString(" leading and trailing spaces "); + private static readonly JString FilePathString = new JString(@"C:\Users\Example\file.txt"); + private static readonly JString LargeNumberString = new JString("12345678901234567890"); + private static readonly JString HexadecimalString = new JString("0x1A3F"); + private static readonly JString PalindromeString = new JString("racecar"); + private static readonly JString SqlInjectionString = new JString("SELECT * FROM users WHERE name = 'a'; DROP TABLE users;"); + private static readonly JString RegexString = new JString(@"^\d{3}-\d{2}-\d{4}$"); + private static readonly JString DateTimeString = new JString("2023-01-01T00:00:00"); + private static readonly JString SpecialCharString = new JString("!?@#$%^&*()"); + private static readonly JString SubstringString = new JString("Hello world".Substring(0, 5)); + private static readonly JString CaseSensitiveString1 = new JString("TestString"); + private static readonly JString CaseSensitiveString2 = new JString("teststring"); + private static readonly JString BooleanString = new JString("true"); + private static readonly JString FormatSpecifierString = new JString("{0:C}"); + private static readonly JString EmojiSequenceString = new JString("👨‍👩‍👦"); + private static readonly JString NullCharString = new JString("Hello\0World"); + private static readonly JString RepeatingPatternString = new JString("abcabcabc"); + [TestMethod] public void TestConstructor() { @@ -23,42 +62,284 @@ public void TestConstructor() Assert.ThrowsException(() => new JString(null)); } + [TestMethod] + [ExpectedException(typeof(ArgumentNullException))] + public void TestConstructorNull() + { + string s = null; + JString jstring = new JString(s); + Assert.AreEqual(s, jstring.Value); + Assert.ThrowsException(() => new JString(null!)); + } + + [TestMethod] + public void TestConstructorEmpty() + { + string s = ""; + JString jstring = new JString(s); + Assert.AreEqual(s, jstring.Value); + } + + [TestMethod] + public void TestConstructorSpace() + { + string s = " "; + JString jstring = new JString(s); + Assert.AreEqual(s, jstring.Value); + Assert.ThrowsException(() => new JString(null)); + } + [TestMethod] public void TestAsBoolean() { - string s1 = "hello world"; - string s2 = ""; - JString jstring1 = new JString(s1); - JString jstring2 = new JString(s2); - Assert.AreEqual(true, jstring1.AsBoolean()); - Assert.AreEqual(false, jstring2.AsBoolean()); + Assert.AreEqual(true, AsicString.AsBoolean()); + Assert.AreEqual(true, EscapeString.AsBoolean()); + Assert.AreEqual(true, BadChar.AsBoolean()); + Assert.AreEqual(true, IntegerString.AsBoolean()); + Assert.AreEqual(false, EmptyString.AsBoolean()); + Assert.AreEqual(true, SpaceString.AsBoolean()); + Assert.AreEqual(true, DoubleString.AsBoolean()); + Assert.AreEqual(true, UnicodeString.AsBoolean()); + Assert.AreEqual(true, EmojString.AsBoolean()); + Assert.AreEqual(true, MixedString.AsBoolean()); + Assert.AreEqual(true, LongString.AsBoolean()); + Assert.AreEqual(true, MultiLangString.AsBoolean()); + Assert.AreEqual(true, JsonString.AsBoolean()); + Assert.AreEqual(true, HtmlEntityString.AsBoolean()); + Assert.AreEqual(true, ControlCharString.AsBoolean()); + Assert.AreEqual(true, SingleCharString.AsBoolean()); + Assert.AreEqual(true, LongWordString.AsBoolean()); + Assert.AreEqual(true, ConcatenatedString.AsBoolean()); + Assert.AreEqual(true, WhiteSpaceString.AsBoolean()); + Assert.AreEqual(true, FilePathString.AsBoolean()); + Assert.AreEqual(true, LargeNumberString.AsBoolean()); + Assert.AreEqual(true, HexadecimalString.AsBoolean()); + Assert.AreEqual(true, PalindromeString.AsBoolean()); + Assert.AreEqual(true, SqlInjectionString.AsBoolean()); + Assert.AreEqual(true, RegexString.AsBoolean()); + Assert.AreEqual(true, DateTimeString.AsBoolean()); + Assert.AreEqual(true, SpecialCharString.AsBoolean()); + Assert.AreEqual(true, SubstringString.AsBoolean()); + Assert.AreEqual(true, CaseSensitiveString1.AsBoolean()); + Assert.AreEqual(true, CaseSensitiveString2.AsBoolean()); + Assert.AreEqual(true, BooleanString.AsBoolean()); + Assert.AreEqual(true, FormatSpecifierString.AsBoolean()); + Assert.AreEqual(true, EmojiSequenceString.AsBoolean()); + Assert.AreEqual(true, NullCharString.AsBoolean()); + Assert.AreEqual(true, RepeatingPatternString.AsBoolean()); } [TestMethod] public void TestAsNumber() { - string s1 = "hello world"; - string s2 = "123"; - string s3 = ""; - JString jstring1 = new JString(s1); - JString jstring2 = new JString(s2); - JString jstring3 = new JString(s3); - Assert.AreEqual(double.NaN, jstring1.AsNumber()); - Assert.AreEqual(123, jstring2.AsNumber()); - Assert.AreEqual(0, jstring3.AsNumber()); + Assert.AreEqual(double.NaN, AsicString.AsNumber()); + Assert.AreEqual(double.NaN, EscapeString.AsNumber()); + Assert.AreEqual(double.NaN, BadChar.AsNumber()); + Assert.AreEqual(123, IntegerString.AsNumber()); + Assert.AreEqual(0, EmptyString.AsNumber()); + Assert.AreEqual(double.NaN, SpaceString.AsNumber()); + Assert.AreEqual(123.456, DoubleString.AsNumber()); + Assert.AreEqual(double.NaN, UnicodeString.AsNumber()); + Assert.AreEqual(double.NaN, EmojString.AsNumber()); + Assert.AreEqual(double.NaN, MixedString.AsNumber()); + Assert.AreEqual(double.NaN, LongString.AsNumber()); + Assert.AreEqual(double.NaN, MultiLangString.AsNumber()); + Assert.AreEqual(double.NaN, JsonString.AsNumber()); + Assert.AreEqual(double.NaN, HtmlEntityString.AsNumber()); + Assert.AreEqual(double.NaN, ControlCharString.AsNumber()); + Assert.AreEqual(double.NaN, SingleCharString.AsNumber()); + Assert.AreEqual(double.NaN, LongWordString.AsNumber()); + Assert.AreEqual(double.NaN, ConcatenatedString.AsNumber()); + Assert.AreEqual(double.NaN, WhiteSpaceString.AsNumber()); + Assert.AreEqual(double.NaN, FilePathString.AsNumber()); + Assert.AreEqual(12345678901234567890d, LargeNumberString.AsNumber()); + Assert.AreEqual(double.NaN, HexadecimalString.AsNumber()); // Depending on how hexadecimal strings are handled + Assert.AreEqual(double.NaN, PalindromeString.AsNumber()); + Assert.AreEqual(double.NaN, SqlInjectionString.AsNumber()); + Assert.AreEqual(double.NaN, RegexString.AsNumber()); + Assert.AreEqual(double.NaN, DateTimeString.AsNumber()); + Assert.AreEqual(double.NaN, SpecialCharString.AsNumber()); + Assert.AreEqual(double.NaN, SubstringString.AsNumber()); + Assert.AreEqual(double.NaN, CaseSensitiveString1.AsNumber()); + Assert.AreEqual(double.NaN, CaseSensitiveString2.AsNumber()); + Assert.AreEqual(double.NaN, BooleanString.AsNumber()); + Assert.AreEqual(double.NaN, FormatSpecifierString.AsNumber()); + Assert.AreEqual(double.NaN, EmojiSequenceString.AsNumber()); + Assert.AreEqual(double.NaN, NullCharString.AsNumber()); + Assert.AreEqual(double.NaN, RepeatingPatternString.AsNumber()); } [TestMethod] - public void TestGetEnum() + public void TestValidGetEnum() { - JString s = "James"; - Woo woo = s.GetEnum(); + JString validEnum = "James"; + + Woo woo = validEnum.GetEnum(); Assert.AreEqual(Woo.James, woo); - s = ""; - woo = s.AsEnum(Woo.Jerry, false); + validEnum = ""; + woo = validEnum.AsEnum(Woo.Jerry, false); Assert.AreEqual(Woo.Jerry, woo); } + + [TestMethod] + [ExpectedException(typeof(ArgumentException))] + public void TestInValidGetEnum() + { + JString validEnum = "_James"; + Woo woo = validEnum.GetEnum(); + } + + [TestMethod] + public void TestMixedString() + { + Assert.AreEqual("abc123!@# ", MixedString.Value); + } + + [TestMethod] + public void TestLongString() + { + Assert.AreEqual(new String('x', 5000), LongString.Value); + } + + [TestMethod] + public void TestMultiLangString() + { + Assert.AreEqual("Hello 你好 مرحبا", MultiLangString.Value); + } + + [TestMethod] + public void TestJsonString() + { + Assert.AreEqual("{\"key\": \"value\"}", JsonString.Value); + } + + [TestMethod] + public void TestHtmlEntityString() + { + Assert.AreEqual("& < >", HtmlEntityString.Value); + } + + [TestMethod] + public void TestControlCharString() + { + Assert.AreEqual("\t\n\r", ControlCharString.Value); + } + + [TestMethod] + public void TestSingleCharString() + { + Assert.AreEqual("a", SingleCharString.Value); + } + + [TestMethod] + public void TestLongWordString() + { + Assert.AreEqual("Supercalifragilisticexpialidocious", LongWordString.Value); + } + + [TestMethod] + public void TestConcatenatedString() + { + Assert.AreEqual("Hello123!@#", ConcatenatedString.Value); + } + + [TestMethod] + public void TestWhiteSpaceString() + { + Assert.AreEqual(" leading and trailing spaces ", WhiteSpaceString.Value); + } + + [TestMethod] + public void TestFilePathString() + { + Assert.AreEqual(@"C:\Users\Example\file.txt", FilePathString.Value); + } + + [TestMethod] + public void TestLargeNumberString() + { + Assert.AreEqual("12345678901234567890", LargeNumberString.Value); + } + + [TestMethod] + public void TestHexadecimalString() + { + Assert.AreEqual("0x1A3F", HexadecimalString.Value); + } + + [TestMethod] + public void TestPalindromeString() + { + Assert.AreEqual("racecar", PalindromeString.Value); + } + + [TestMethod] + public void TestSqlInjectionString() + { + Assert.AreEqual("SELECT * FROM users WHERE name = 'a'; DROP TABLE users;", SqlInjectionString.Value); + } + + [TestMethod] + public void TestRegexString() + { + Assert.AreEqual(@"^\d{3}-\d{2}-\d{4}$", RegexString.Value); + } + + [TestMethod] + public void TestDateTimeString() + { + Assert.AreEqual("2023-01-01T00:00:00", DateTimeString.Value); + } + + [TestMethod] + public void TestSpecialCharString() + { + Assert.AreEqual("!?@#$%^&*()", SpecialCharString.Value); + } + + [TestMethod] + public void TestSubstringString() + { + Assert.AreEqual("Hello", SubstringString.Value); + } + + [TestMethod] + public void TestCaseSensitiveStrings() + { + Assert.AreNotEqual(CaseSensitiveString1.Value, CaseSensitiveString2.Value); + } + + [TestMethod] + public void TestBooleanString() + { + Assert.AreEqual("true", BooleanString.Value); + } + + [TestMethod] + public void TestFormatSpecifierString() + { + Assert.AreEqual("{0:C}", FormatSpecifierString.Value); + } + + [TestMethod] + public void TestEmojiSequenceString() + { + Assert.AreEqual("👨‍👩‍👦", EmojiSequenceString.Value); + } + + [TestMethod] + public void TestNullCharString() + { + Assert.AreEqual("Hello\0World", NullCharString.Value); + } + + [TestMethod] + public void TestRepeatingPatternString() + { + Assert.AreEqual("abcabcabc", RepeatingPatternString.Value); + } + [TestMethod] public void TestEqual() { @@ -80,5 +361,78 @@ public void TestEqual() var reference = jString; Assert.IsTrue(jString.Equals(reference)); } + + [TestMethod] + public void TestWrite() + { + var jString = new JString("hello world"); + using (var stream = new MemoryStream()) + using (var writer = new Utf8JsonWriter(stream)) + { + jString.Write(writer); + writer.Flush(); + var json = Encoding.UTF8.GetString(stream.ToArray()); + Assert.AreEqual("\"hello world\"", json); + } + } + + [TestMethod] + public void TestClone() + { + var jString = new JString("hello world"); + var clone = jString.Clone(); + Assert.AreEqual(jString, clone); + Assert.AreSame(jString, clone); // Cloning should return the same instance for immutable objects + } + + [TestMethod] + public void TestEqualityWithDifferentTypes() + { + var jString = new JString("hello world"); + Assert.IsFalse(jString.Equals(123)); + Assert.IsFalse(jString.Equals(new object())); + Assert.IsFalse(jString.Equals(new JBoolean())); + } + + [TestMethod] + public void TestImplicitOperators() + { + JString fromEnum = EnumExample.Value; + Assert.AreEqual("Value", fromEnum.Value); + + JString fromString = "test string"; + Assert.AreEqual("test string", fromString.Value); + + JString nullString = (string)null; + Assert.IsNull(nullString); + } + + [TestMethod] + public void TestBoundaryAndSpecialCases() + { + JString largeString = new string('a', ushort.MaxValue); + Assert.AreEqual(ushort.MaxValue, largeString.Value.Length); + + JString specialUnicode = "\uD83D\uDE00"; // 😀 emoji + Assert.AreEqual("\uD83D\uDE00", specialUnicode.Value); + + JString complexJson = "{\"nested\":{\"key\":\"value\"}}"; + Assert.AreEqual("{\"nested\":{\"key\":\"value\"}}", complexJson.Value); + } + + [TestMethod] + public void TestExceptionHandling() + { + JString invalidEnum = "invalid_value"; + + var result = invalidEnum.AsEnum(Woo.Jerry); + Assert.AreEqual(Woo.Jerry, result); + + Assert.ThrowsException(() => invalidEnum.GetEnum()); + } + } + public enum EnumExample + { + Value } }