413 lines
15 KiB
ObjectPascal
413 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;
|
|
//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.
|