Files
MycLib/Src/Data/TestDataTypes.JSON.pas
T
Michael Schimmel 3268748c03 Types-JSON
2025-08-27 18:33:25 +02:00

412 lines
15 KiB
ObjectPascal

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;
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.