unit TestDataTypes.JSON; interface uses System.JSON, DUnitX.TestFramework; type [TestFixture] [IgnoreMemoryLeaks] TTestDataTypesJSON = class private FJson: TJSONValue; public [Setup] procedure SetUp; [TearDown] procedure TearDown; [Test] procedure TestVoid; [Test] [TestCase('Positive', '12345')] [TestCase('Negative', '-54321')] [TestCase('Zero', '0')] procedure TestOrdinal(const AValue: Int64); [Test] [TestCase('Positive', '123.456')] [TestCase('Negative', '-543.21')] [TestCase('Zero', '0.0')] procedure TestFloat(const AValue: Double); [Test] [TestCase('Simple', 'Hello World')] [TestCase('Empty', '')] [TestCase('SpecialChars', 'äöüß#+*?%&/()=')] procedure TestText(const AValue: string); [Test] procedure TestTimestamp; [Test] [TestCase('Scale4_Positive', '1234567,4,123.4567')] [TestCase('Scale2_Negative', '-987,2,-9.87')] [TestCase('Scale5_Small', '123,5,0.00123')] [TestCase('Scale0_Int', '123,0,123')] [TestCase('Scale2_Zero', '0,2,0.00')] [TestCase('Scale0_Zero', '0,0,0')] procedure TestDecimal(const AValue: Int64; AScale: Integer; const AExpectedJsonString: string); [Test] procedure TestEnum; [Test] procedure TestRecord; [Test] procedure TestTuple; [Test] procedure TestArray; [Test] procedure TestVector; [Test] procedure TestMethodFails; [Test] procedure TestNullValue; end; implementation uses System.SysUtils, System.DateUtils, System.Math, Myc.Data.Types, Myc.Data.Types.JSON; { TTestDataTypesJSON } procedure TTestDataTypesJSON.SetUp; begin FJson := nil; end; procedure TTestDataTypesJSON.TearDown; begin FJson.Free; end; procedure TTestDataTypesJSON.TestVoid; var voidType: TDataType.TVoid; voidValue: TDataType.TVoid.TValue; deserializedValue: TDataType.TValue; begin // Setup voidType := TDataType.Void; voidValue := voidType.Value; // Serialize FJson := TDataJsonConverter.ValueToJson(voidValue); Assert.IsNotNull(FJson, 'JSON value should not be nil'); Assert.IsTrue(FJson is TJSONNull, 'Void should serialize to JSON Null'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, voidType); Assert.IsNotNull(deserializedValue.DataValue, 'Deserialized value should not be nil'); Assert.AreEqual(dkVoid, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkVoid'); end; procedure TTestDataTypesJSON.TestOrdinal(const AValue: Int64); var ordType: TDataType.TOrdinal; ordValue: TDataType.TOrdinal.TValue; deserializedValue: TDataType.TValue; begin // Setup ordType := TDataType.Ordinal; ordValue := ordType.CreateValue(AValue); // Serialize FJson := TDataJsonConverter.ValueToJson(ordValue); Assert.IsTrue(FJson is TJSONNumber, 'Ordinal should serialize to JSON Number'); Assert.AreEqual(AValue, (FJson as TJSONNumber).AsInt64, 'Serialized value mismatch'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, ordType); Assert.AreEqual(dkOrdinal, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkOrdinal'); Assert.AreEqual(AValue, deserializedValue.AsOrdinal.Value, 'Deserialized value mismatch'); end; procedure TTestDataTypesJSON.TestFloat(const AValue: Double); var floatType: TDataType.TFloat; floatValue: TDataType.TFloat.TValue; deserializedValue: TDataType.TValue; begin // Setup floatType := TDataType.Float; floatValue := floatType.CreateValue(AValue); // Serialize FJson := TDataJsonConverter.ValueToJson(floatValue); Assert.IsTrue(FJson is TJSONNumber, 'Float should serialize to JSON Number'); Assert.AreEqual(AValue, (FJson as TJSONNumber).AsDouble, 1E-9, 'Serialized value mismatch'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, floatType); Assert.AreEqual(dkFloat, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkFloat'); Assert.AreEqual(AValue, deserializedValue.AsFloat.Value, 1E-9, 'Deserialized value mismatch'); end; procedure TTestDataTypesJSON.TestText(const AValue: string); var textType: TDataType.TText; textValue: TDataType.TText.TValue; deserializedValue: TDataType.TValue; begin // Setup textType := TDataType.Text; textValue := textType.CreateValue(AValue); // Serialize FJson := TDataJsonConverter.ValueToJson(textValue); Assert.IsTrue(FJson is TJSONString, 'Text should serialize to JSON String'); Assert.AreEqual(AValue, (FJson as TJSONString).Value, 'Serialized value mismatch'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, textType); Assert.AreEqual(dkText, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkText'); Assert.AreEqual(AValue, deserializedValue.AsText.Value, 'Deserialized value mismatch'); end; procedure TTestDataTypesJSON.TestTimestamp; var tsType: TDataType.TTimestamp; tsValue: TDataType.TTimestamp.TValue; deserializedValue: TDataType.TValue; testVal: TDateTime; begin // Setup testVal := EncodeDateTime(2025, 8, 27, 17, 30, 15, 500); tsType := TDataType.Timestamp; tsValue := tsType.CreateValue(testVal); // Serialize FJson := TDataJsonConverter.ValueToJson(tsValue); Assert.IsTrue(FJson is TJSONString, 'Timestamp should serialize to JSON String'); Assert.AreEqual(DateToISO8601(testVal, true), (FJson as TJSONString).Value, 'Serialized value mismatch'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, tsType); Assert.AreEqual(dkTimestamp, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkTimestamp'); Assert.IsTrue(CompareDateTime(testVal, deserializedValue.AsTimestamp.Value) = 0, 'Deserialized value mismatch'); end; procedure TTestDataTypesJSON.TestDecimal(const AValue: Int64; AScale: Integer; const AExpectedJsonString: string); var decType: TDataType.TDecimal; decValue: TDataType.TDecimal.TValue; deserializedValue: TDataType.TValue; begin // Setup decType := TDataType.DecimalOf(AScale); decValue := decType.CreateValue(AValue); // Serialize FJson := TDataJsonConverter.ValueToJson(decValue); Assert.IsTrue(FJson is TJSONString, 'Decimal should serialize to JSON String'); Assert.AreEqual(AExpectedJsonString, (FJson as TJSONString).Value, 'Serialized string value mismatch'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, decType); Assert.AreEqual(dkDecimal, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkDecimal'); Assert.AreEqual(AValue, deserializedValue.AsDecimal.Value, 'Deserialized value mismatch'); Assert.AreEqual(AScale, deserializedValue.AsDecimal.DataType.Scale, 'Deserialized scale mismatch'); // Test Deserialization from Number (compatibility) FJson.Free; // Free previous value before creating new one FJson := TJSONNumber.Create(AValue / Power(10, AScale)); deserializedValue := TDataJsonConverter.JsonToValue(FJson, decType); Assert.AreEqual(AValue, deserializedValue.AsDecimal.Value, 'Deserialized value from number mismatch'); end; procedure TTestDataTypesJSON.TestEnum; var enumType: TDataType.TEnum; enumValue: TDataType.TEnum.TValue; deserializedValue: TDataType.TValue; begin // Setup enumType := TDataType.EnumOf('MyEnum', ['Red', 'Green', 'Blue']); enumValue := enumType.CreateValue(1); // Green // Serialize FJson := TDataJsonConverter.ValueToJson(enumValue); Assert.IsTrue(FJson is TJSONString, 'Enum should serialize to JSON String'); Assert.AreEqual('Green', (FJson as TJSONString).Value, 'Serialized value mismatch'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, enumType); Assert.AreEqual(dkEnum, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkEnum'); Assert.AreEqual(1, deserializedValue.AsEnum.Value, 'Deserialized value mismatch'); end; procedure TTestDataTypesJSON.TestRecord; var recordType: TDataType.TRecord; idVal: TDataType.TOrdinal.TValue; nameVal: TDataType.TText.TValue; recordValue: TDataType.TRecord.TValue; deserializedValue: TDataType.TValue; begin // Setup recordType := TDataType.RecordOf([TDataRecordField.Create('ID', TDataType.Ordinal), TDataRecordField.Create('Name', TDataType.Text)]); idVal := TDataType.Ordinal.CreateValue(99); nameVal := TDataType.Text.CreateValue('TestRecord'); recordValue := recordType.CreateValue([idVal, nameVal]); // Serialize FJson := TDataJsonConverter.ValueToJson(recordValue); Assert.IsTrue(FJson is TJSONObject, 'Record should serialize to JSON Object'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, recordType); Assert.AreEqual(dkRecord, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkRecord'); var desRecord := deserializedValue.AsRecord; Assert.AreEqual(Int64(99), desRecord.Items[0].AsOrdinal.Value, 'Deserialized record field ID mismatch'); Assert.AreEqual('TestRecord', desRecord.Items[1].AsText.Value, 'Deserialized record field Name mismatch'); end; procedure TTestDataTypesJSON.TestTuple; var tupleType: TDataType.TTuple; idVal: TDataType.TOrdinal.TValue; nameVal: TDataType.TText.TValue; tupleValue: TDataType.TTuple.TValue; deserializedValue: TDataType.TValue; begin // Setup tupleType := TDataType.TupleOf([TDataType.Ordinal, TDataType.Text]); idVal := TDataType.Ordinal.CreateValue(101); nameVal := TDataType.Text.CreateValue('TestTuple'); tupleValue := tupleType.CreateValue([idVal, nameVal]); // Serialize FJson := TDataJsonConverter.ValueToJson(tupleValue); Assert.IsTrue(FJson is TJSONArray, 'Tuple should serialize to JSON Array'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, tupleType); Assert.AreEqual(dkTuple, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkTuple'); var desTuple := deserializedValue.AsTuple; Assert.AreEqual(Int64(101), desTuple.Items[0].AsOrdinal.Value, 'Deserialized tuple item 0 mismatch'); Assert.AreEqual('TestTuple', desTuple.Items[1].AsText.Value, 'Deserialized tuple item 1 mismatch'); end; procedure TTestDataTypesJSON.TestArray; var arrayType: TDataType.TArray; val1, val2: TDataType.TText.TValue; arrayValue: TDataType.TArray.TValue; deserializedValue: TDataType.TValue; begin // Setup arrayType := TDataType.ArrayOf(TDataType.Text); val1 := TDataType.Text.CreateValue('A'); val2 := TDataType.Text.CreateValue('B'); arrayValue := arrayType.CreateValue([val1, val2]); // Serialize FJson := TDataJsonConverter.ValueToJson(arrayValue); Assert.IsTrue(FJson is TJSONArray, 'Array should serialize to JSON Array'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, arrayType); Assert.AreEqual(dkArray, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkArray'); var desArray := deserializedValue.AsArray; Assert.AreEqual(2, desArray.ElementCount, 'Deserialized array element count mismatch'); Assert.AreEqual('A', desArray.Items[0].AsText.Value, 'Deserialized array item 0 mismatch'); Assert.AreEqual('B', desArray.Items[1].AsText.Value, 'Deserialized array item 1 mismatch'); end; procedure TTestDataTypesJSON.TestVector; var vectorType: TDataType.TVector; val1, val2: TDataType.TOrdinal.TValue; vectorValue: TDataType.TVector.TValue; deserializedValue: TDataType.TValue; jsonArray: TJSONArray; begin // Setup vectorType := TDataType.VectorOf(TDataType.Ordinal, 2); val1 := TDataType.Ordinal.CreateValue(10); val2 := TDataType.Ordinal.CreateValue(20); vectorValue := vectorType.CreateValue([val1, val2]); // Serialize FJson := TDataJsonConverter.ValueToJson(vectorValue); Assert.IsTrue(FJson is TJSONArray, 'Vector should serialize to JSON Array'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, vectorType); Assert.AreEqual(dkVector, deserializedValue.DataType.Kind, 'Deserialized type kind should be dkVector'); var desVector := deserializedValue.AsVector; Assert.AreEqual(2, desVector.ElementCount, 'Deserialized vector element count mismatch'); Assert.AreEqual(Int64(10), desVector.Items[0].AsOrdinal.Value, 'Deserialized vector item 0 mismatch'); Assert.AreEqual(Int64(20), desVector.Items[1].AsOrdinal.Value, 'Deserialized vector item 1 mismatch'); // Test wrong size deserialization jsonArray := nil; try jsonArray := TJSONArray.Create; jsonArray.Add(1); Assert.WillRaise( procedure begin TDataJsonConverter.JsonToValue(jsonArray, vectorType); end, EConvertError, 'Deserializing a JSON array with wrong size for a vector should raise an exception' ); finally jsonArray.Free; end; end; procedure TTestDataTypesJSON.TestMethodFails; var methodType: TDataType.TMethod; methodValue: TDataType.TMethod.TValue; begin // Setup methodType := TDataType.MethodOf(TDataType.Ordinal, TDataType.Ordinal); methodValue := methodType.CreateValue(function(const AValue: TDataType.TValue): TDataType.TValue begin Result := AValue; end); // Test Serialization Assert.WillRaise( procedure begin TDataJsonConverter.ValueToJson(methodValue).Free; end, ENotSupportedException, 'Serializing a method should raise an exception' ); // Test Deserialization FJson := TJSONNull.Create; //TODO Fails with "Method did not throw any exceptions. Deserializing a method should raise an exception" Assert.WillRaise( procedure begin TDataJsonConverter.JsonToValue(FJson, methodType); end, ENotSupportedException, 'Deserializing a method should raise an exception' ); end; procedure TTestDataTypesJSON.TestNullValue; var value: TDataType.TValue; deserializedValue: TDataType.TValue; begin // Setup value := TDataType.Void.Value; // Serialize FJson := TDataJsonConverter.ValueToJson(value); Assert.IsTrue(FJson is TJSONNull, 'nil IDataValue should serialize to JSON Null'); // Deserialize deserializedValue := TDataJsonConverter.JsonToValue(FJson, TDataType.Void); Assert.IsTrue(deserializedValue.DataType.Kind = dkVoid, 'JSON Null should deserialize to a TValue with a nil DataValue'); end; initialization TDUnitX.RegisterTestFixture(TTestDataTypesJSON); end.