blob: 753441dcc0c5e3bb921a5f82c231936c32d6912e [file]
#region License
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#endregion
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Gremlin.Net.Structure;
using Gremlin.Net.Structure.IO.GraphBinary4;
using Xunit;
namespace Gremlin.Net.UnitTest.Structure
{
public class ProviderDefinedAttributeTests
{
[Fact]
public void ShouldHydrateNestedAnnotatedPdt()
{
ProviderDefinedAttribute.RegisteredTypes["test:Address"] = typeof(TestAddress);
ProviderDefinedAttribute.RegisteredTypes["test:Person"] = typeof(TestPerson);
try
{
var addressPdt = new CompositePDT("test:Address",
new Dictionary<string, object?> { ["Street"] = "123 Main", ["City"] = "Springfield" });
var personPdt = new CompositePDT("test:Person",
new Dictionary<string, object?> { ["Name"] = "Alice", ["Address"] = addressPdt });
var result = ProviderDefinedAttribute.HydrateIfRegistered(personPdt);
var person = Assert.IsType<TestPerson>(result);
Assert.Equal("Alice", person.Name);
Assert.NotNull(person.Address);
Assert.Equal("123 Main", person.Address!.Street);
Assert.Equal("Springfield", person.Address.City);
}
finally
{
ProviderDefinedAttribute.RegisteredTypes.TryRemove("test:Address", out _);
ProviderDefinedAttribute.RegisteredTypes.TryRemove("test:Person", out _);
}
}
[Fact]
public void ShouldReturnRawPdtWhenFieldConversionFails()
{
ProviderDefinedAttribute.RegisteredTypes["test:Bad"] = typeof(TestBadTarget);
try
{
var pdt = new CompositePDT("test:Bad",
new Dictionary<string, object?> { ["Count"] = "not-an-int" });
var result = ProviderDefinedAttribute.HydrateIfRegistered(pdt);
Assert.Same(pdt, result);
}
finally
{
ProviderDefinedAttribute.RegisteredTypes.TryRemove("test:Bad", out _);
}
}
[Fact]
public void ShouldReturnRawPdtWhenNestedTypeNotRegistered()
{
ProviderDefinedAttribute.RegisteredTypes["test:Outer"] = typeof(TestOuter);
try
{
var nestedPdt = new CompositePDT("test:Unknown",
new Dictionary<string, object?> { ["X"] = 1 });
var outerPdt = new CompositePDT("test:Outer",
new Dictionary<string, object?> { ["Inner"] = nestedPdt });
// Inner is typed as TestInner but nested PDT can't hydrate — Convert.ChangeType
// would throw, but the try/catch returns raw pdt gracefully.
var result = ProviderDefinedAttribute.HydrateIfRegistered(outerPdt);
Assert.Same(outerPdt, result);
}
finally
{
ProviderDefinedAttribute.RegisteredTypes.TryRemove("test:Outer", out _);
}
}
[Fact]
public async Task ShouldNotThrowFromReaderWhenHydrationFails()
{
ProviderDefinedAttribute.RegisteredTypes["test:Bad"] = typeof(TestBadTarget);
try
{
var pdt = new CompositePDT("test:Bad",
new Dictionary<string, object?> { ["Count"] = "not-an-int" });
var writer = new GraphBinaryWriter();
var reader = new GraphBinaryReader();
using var stream = new MemoryStream();
await writer.WriteAsync(pdt, stream);
stream.Position = 0;
var result = await reader.ReadAsync(stream);
// Should return raw PDT, not throw
Assert.IsType<CompositePDT>(result);
}
finally
{
ProviderDefinedAttribute.RegisteredTypes.TryRemove("test:Bad", out _);
}
}
[Fact]
public void ShouldReturnRawPdtWhenTypeNotRegistered()
{
var pdt = new CompositePDT("test:Unregistered",
new Dictionary<string, object?> { ["X"] = 1 });
var result = ProviderDefinedAttribute.HydrateIfRegistered(pdt);
Assert.Same(pdt, result);
}
[Fact]
public void ShouldHydrateRegisteredInnerInsideUnregisteredOuter()
{
// Register ONLY the inner type, NOT the outer
ProviderDefinedAttribute.RegisteredTypes["test:InnerWidget"] = typeof(TestInnerWidget);
try
{
var innerPdt = new CompositePDT("test:InnerWidget",
new Dictionary<string, object?> { ["Label"] = "hello" });
var outerPdt = new CompositePDT("test:UnregisteredOuter",
new Dictionary<string, object?> { ["Widget"] = innerPdt, ["Count"] = 5 });
// Act
var result = ProviderDefinedAttribute.HydrateIfRegistered(outerPdt);
// Assert: outer is unregistered so stays raw PDT
var rawOuter = Assert.IsType<CompositePDT>(result);
Assert.Equal("test:UnregisteredOuter", rawOuter.Name);
// The inner field SHOULD be hydrated because it IS registered
var widgetValue = rawOuter.Fields["Widget"];
var hydratedInner = Assert.IsType<TestInnerWidget>(widgetValue);
Assert.Equal("hello", hydratedInner.Label);
// Non-PDT fields unchanged
Assert.Equal(5, rawOuter.Fields["Count"]);
}
finally
{
ProviderDefinedAttribute.RegisteredTypes.TryRemove("test:InnerWidget", out _);
}
}
#region Test helpers
[ProviderDefined(Name = "test:Address")]
public class TestAddress
{
public string Street { get; set; } = "";
public string City { get; set; } = "";
}
[ProviderDefined(Name = "test:Person")]
public class TestPerson
{
public string Name { get; set; } = "";
public TestAddress? Address { get; set; }
}
[ProviderDefined(Name = "test:Bad")]
public class TestBadTarget
{
public int Count { get; set; }
}
[ProviderDefined(Name = "test:Outer")]
public class TestOuter
{
public TestInner? Inner { get; set; }
}
[ProviderDefined(Name = "test:Inner")]
public class TestInner
{
public int X { get; set; }
}
[ProviderDefined(Name = "test:InnerWidget")]
public class TestInnerWidget
{
public string Label { get; set; } = "";
}
#endregion
}
}