Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Partially incorrect reverse map when numerical values of source and destination enums differ #30

Closed
JeroMiya opened this issue Mar 1, 2025 · 4 comments

Comments

@JeroMiya
Copy link

JeroMiya commented Mar 1, 2025

Source and Destination Types

// Assume, as is the case for my use case, that both the source
// and destination enumerations are created via code generation
// from sources outside our control, and thus I cannot make updates
// or changes to them to work around the issue.

// This is idiomatic of how a Protobuf enum is generated, with the Unspecified
// value acting as a stand-in for when the field is not set by the client or server
public enum SourceEnum
{
    Unspecified = 0,
    Bar = 1,
    Baz = 2,
}

// In our case, this is generated from an OpenAPI spec via Kiota
public enum DestinationEnum
{
    BAR_ALT_NAME, // we can't map by name because the names don't match, even with case insensitivity turned on
    BAZ_ALT_NAME,
}

Mapping Configuration and Test Code

public class TestEnumProfile : Profile
{
    public TestEnumProfile()
    {
        CreateMap<SourceEnum, DestinationEnum>()
            .ConvertUsingEnumMapping(
                opts =>
                {
                    opts
                        .MapByValue()
                        .MapValue(SourceEnum.Bar, DestinationEnum.BAR_ALT_NAME)
                        .MapValue(SourceEnum.Baz, DestinationEnum.BAZ_ALT_NAME)
                        .MapException(SourceEnum.Unspecified, () => new InvalidOperationException($"Unspecified values are not supported"));
                })
            .ReverseMap();
    }
}

public class Tests
{
    IMapper _mapper = null!;

    [SetUp]
    public void Setup()
    {
        var config = new MapperConfiguration(configuration =>
        {
            configuration.EnableEnumMappingValidation();
            configuration.AddMaps(typeof(Tests).GetTypeInfo().Assembly);
        });

        config.AssertConfigurationIsValid();
        _mapper = config.CreateMapper();
    }

    [Test]
    public void TestBarMapping()
    {
        // Passes
        Assert.That(_mapper.Map<DestinationEnum>(SourceEnum.Bar), Is.EqualTo(DestinationEnum.BAR_ALT_NAME));
    }

    [Test]
    public void TestBazMapping()
    {
        // Passes
        Assert.That(_mapper.Map<DestinationEnum>(SourceEnum.Baz), Is.EqualTo(DestinationEnum.BAZ_ALT_NAME));
    }

    [Test]
    public void TestUnspecifiedMapping()
    {
        // Passes
        Assert.Throws<InvalidOperationException>(() =>
        {
            _mapper.Map<DestinationEnum>(SourceEnum.Unspecified);
        });
    }

    [Test]
    public void TestReverseBarMapping()
    {
        // Passes
        Assert.That(_mapper.Map<SourceEnum>(DestinationEnum.BAR_ALT_NAME), Is.EqualTo(SourceEnum.Bar));
    }

    [Test]
    public void TestReverseBazMapping()
    {
        // Failure: Expected: Baz  But was:  Bar
        Assert.That(_mapper.Map<SourceEnum>(DestinationEnum.BAZ_ALT_NAME), Is.EqualTo(SourceEnum.Baz));
    }
}

Version: Automapper.Extensions.EnumMapping version 4.0.0

Expected Behavior

All tests pass.

Actual Behavior

All tests pass except for TestReverseBazMapping. The reverse mapping from DestinationEnum.BAZ_ALT_NAME to SourceEnum.Baz is incorrect - expected SourceEnum.Baz, but the mapper gives me SourceEnum.Bar instead.

Note that although the integer values of DestinationEnum.BAZ_ALT_NAME and SourceEnum.Baz are different, the other mappings in the other three tests also involve source and destination values that differ in integer value. This specific reverse mapping value is the one that produces an incorrect value, but not the others. Note also that all the values mapped in the Source -> Destination direction are mapped correctly, only some of the values in the reverse map are incorrect. I'm not clear on what about this particular value is triggering the failed map.

@HenkKin
Copy link
Member

HenkKin commented Mar 1, 2025

I will have a look at it Monday

HenkKin pushed a commit that referenced this issue Mar 4, 2025
…estination enums differ #30

Setup testcase with error
@HenkKin
Copy link
Member

HenkKin commented Mar 4, 2025

I created a branch with same tests:

I will have a look at the logic.

@HenkKin
Copy link
Member

HenkKin commented Mar 4, 2025

I have added a MapByCustom option. MapByValue and MapByName both rely on or the values or the names. In case of no default on name or value, we can now use Custom. With Custom you have to specify all the mappings explicitly in the configuration.

I need to add more tests and more checks for AssertConfigurationIsValid.

HenkKin added a commit that referenced this issue Mar 6, 2025
* Partially incorrect reverse map when numerical values of source and destination enums differ #30
Setup testcase with error

* Added Mapping by custom mappings

* Added testcases

* Updated documentation

* Cleanup

---------

Co-authored-by: Henk Kin <[email protected]>
@HenkKin
Copy link
Member

HenkKin commented Mar 6, 2025

@JeroMiya I created a new release (v4.1.0) with MapByCustom #31

@HenkKin HenkKin closed this as completed Mar 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants