Files
microsoft--semantic-kernel/dotnet/test/VectorData/Redis.UnitTests/RedisJsonDynamicMapperTests.cs
T
wehub-resource-sync b957a53def
CodeQL / Analyze (csharp) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:21:23 +08:00

182 lines
6.7 KiB
C#

// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using System.Text.Json.Nodes;
using Microsoft.Extensions.VectorData;
using Microsoft.Extensions.VectorData.ProviderServices;
using Xunit;
namespace Microsoft.SemanticKernel.Connectors.Redis.UnitTests;
/// <summary>
/// Contains tests for the <see cref="RedisJsonDynamicMapper"/> class.
/// </summary>
public class RedisJsonDynamicMapperTests
{
private static readonly float[] s_floatVector = new float[] { 1.0f, 2.0f, 3.0f, 4.0f };
private static readonly CollectionModel s_model
= new RedisJsonModelBuilder(RedisJsonCollection<object, Dictionary<string, object?>>.ModelBuildingOptions)
.BuildDynamic(
new()
{
Properties =
[
new VectorStoreKeyProperty("Key", typeof(string)),
new VectorStoreDataProperty("StringData", typeof(string)),
new VectorStoreDataProperty("IntData", typeof(int)),
new VectorStoreDataProperty("NullableIntData", typeof(int?)),
new VectorStoreDataProperty("ComplexObjectData", typeof(ComplexObject)),
new VectorStoreVectorProperty("FloatVector", typeof(ReadOnlyMemory<float>), 10),
]
},
defaultEmbeddingGenerator: null);
[Fact]
public void MapFromDataToStorageModelMapsAllSupportedTypes()
{
// Arrange.
var sut = new RedisJsonDynamicMapper(s_model, JsonSerializerOptions.Default);
var dataModel = new Dictionary<string, object?>
{
["Key"] = "key",
["StringData"] = "data 1",
["IntData"] = 1,
["NullableIntData"] = 2,
["ComplexObjectData"] = new ComplexObject { Prop1 = "prop 1", Prop2 = "prop 2" },
["FloatVector"] = new ReadOnlyMemory<float>(s_floatVector)
};
// Act.
var storageModel = sut.MapFromDataToStorageModel(dataModel, recordIndex: 0, generatedEmbeddings: null);
// Assert
Assert.Equal("key", storageModel.Key);
Assert.Equal("data 1", (string)storageModel.Node["StringData"]!);
Assert.Equal(1, (int)storageModel.Node["IntData"]!);
Assert.Equal(2, (int?)storageModel.Node["NullableIntData"]!);
Assert.Equal("prop 1", (string)storageModel.Node["ComplexObjectData"]!.AsObject()["Prop1"]!);
Assert.Equal(new float[] { 1, 2, 3, 4 }, storageModel.Node["FloatVector"]?.AsArray().GetValues<float>().ToArray());
}
[Fact]
public void MapFromDataToStorageModelMapsNullValues()
{
// Arrange.
var sut = new RedisJsonDynamicMapper(s_model, JsonSerializerOptions.Default);
var dataModel = new Dictionary<string, object?>
{
["Key"] = "key",
["StringData"] = null,
["IntData"] = null,
["NullableIntData"] = null,
["ComplexObjectData"] = null,
["FloatVector"] = null,
};
// Act.
var storageModel = sut.MapFromDataToStorageModel(dataModel, recordIndex: 0, generatedEmbeddings: null);
// Assert
Assert.Equal("key", storageModel.Key);
Assert.Null(storageModel.Node["storage_string_data"]);
Assert.Null(storageModel.Node["IntData"]);
Assert.Null(storageModel.Node["NullableIntData"]);
Assert.Null(storageModel.Node["ComplexObjectData"]);
Assert.Null(storageModel.Node["FloatVector"]);
}
[Fact]
public void MapFromStorageToDataModelMapsAllSupportedTypes()
{
// Arrange.
var sut = new RedisJsonDynamicMapper(s_model, JsonSerializerOptions.Default);
var storageModel = new JsonObject
{
{ "StringData", "data 1" },
{ "IntData", 1 },
{ "NullableIntData", 2 },
{ "ComplexObjectData", new JsonObject(new KeyValuePair<string, JsonNode?>[] { new("Prop1", JsonValue.Create("prop 1")), new("Prop2", JsonValue.Create("prop 2")) }) },
{ "FloatVector", new JsonArray(new float[] { 1, 2, 3, 4 }.Select(x => JsonValue.Create(x)).ToArray()) }
};
// Act.
var dataModel = sut.MapFromStorageToDataModel(("key", storageModel), includeVectors: true);
// Assert.
Assert.Equal("key", dataModel["Key"]);
Assert.Equal("data 1", dataModel["StringData"]);
Assert.Equal(1, dataModel["IntData"]);
Assert.Equal(2, dataModel["NullableIntData"]);
Assert.Equal("prop 1", ((ComplexObject)dataModel["ComplexObjectData"]!).Prop1);
Assert.Equal(new float[] { 1, 2, 3, 4 }, ((ReadOnlyMemory<float>)dataModel["FloatVector"]!).ToArray());
}
[Fact]
public void MapFromStorageToDataModelMapsNullValues()
{
// Arrange.
var sut = new RedisJsonDynamicMapper(s_model, JsonSerializerOptions.Default);
var storageModel = new JsonObject
{
{ "StringData", null },
{ "IntData", null },
{ "NullableIntData", null },
{ "ComplexObjectData", null },
{ "FloatVector", null }
};
// Act.
var dataModel = sut.MapFromStorageToDataModel(("key", storageModel), includeVectors: true);
// Assert.
Assert.Equal("key", dataModel["Key"]);
Assert.Null(dataModel["StringData"]);
Assert.Null(dataModel["IntData"]);
Assert.Null(dataModel["NullableIntData"]);
Assert.Null(dataModel["ComplexObjectData"]);
Assert.Null(dataModel["FloatVector"]);
}
[Fact]
public void MapFromDataToStorageModelSkipsMissingProperties()
{
// Arrange.
var sut = new RedisJsonDynamicMapper(s_model, JsonSerializerOptions.Default);
var dataModel = new Dictionary<string, object?> { ["Key"] = "key" };
// Act.
var storageModel = sut.MapFromDataToStorageModel(dataModel, recordIndex: 0, generatedEmbeddings: null);
// Assert
Assert.Equal("key", storageModel.Key);
Assert.Empty(storageModel.Node.AsObject());
}
[Fact]
public void MapFromStorageToDataModelSkipsMissingProperties()
{
// Arrange.
var storageModel = new JsonObject();
var sut = new RedisJsonDynamicMapper(s_model, JsonSerializerOptions.Default);
// Act.
var dataModel = sut.MapFromStorageToDataModel(("key", storageModel), includeVectors: true);
// Assert.
Assert.Equal("key", dataModel["Key"]);
Assert.Single(dataModel);
}
private sealed class ComplexObject
{
public string Prop1 { get; set; } = string.Empty;
public string Prop2 { get; set; } = string.Empty;
}
}