Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 106 additions & 0 deletions src/Testing/CoreTests/Transports/MapIncomingPropertyTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Shouldly;
using Wolverine.Configuration;
using Wolverine.Runtime;
using Wolverine.Transports;
Expand Down Expand Up @@ -26,6 +27,111 @@ public void uses_the_custom_incoming_mapping()

envelope.ReplyUri.ShouldBe(expectedReplyUri);
}

// Regression coverage for https://github.com/JasperFx/wolverine/issues/2551.
//
// MapIncomingProperty / MapOutgoingProperty customize a single direction.
// They must NOT disturb the opposite direction's default header mapping
// (registered via MapPropertyToHeader during the mapper's constructor).
//
// The bug was that compileIncoming/compileOutgoing had their filter
// predicates swapped — so MapIncomingProperty silently deleted the
// outgoing header mapping and vice versa.

[Fact]
public void custom_incoming_property_preserves_default_outgoing_header()
{
var mapper = new StubEnvelopeMapper(new StubEndpoint());

// Customize only the incoming direction for Id — read it from a
// transport-specific location. This must not stop Id from being
// written as the "id" header on outgoing messages.
mapper.MapIncomingProperty(x => x.Id, (envelope, incoming) =>
{
if (incoming.Headers.TryGetValue("custom-id", out var raw) && Guid.TryParse(raw, out var parsed))
{
envelope.Id = parsed;
}
});

var envelope = new Envelope { Id = Guid.NewGuid() };
var outgoing = new StubTransportMessage();

mapper.MapEnvelopeToOutgoing(envelope, outgoing);

outgoing.Headers.ContainsKey(EnvelopeConstants.IdKey).ShouldBeTrue(
"MapIncomingProperty(x => x.Id, ...) must not delete the default outgoing 'id' header mapping");
outgoing.Headers[EnvelopeConstants.IdKey].ShouldBe(envelope.Id.ToString());
}

[Fact]
public void custom_outgoing_property_preserves_default_incoming_header_read()
{
var mapper = new StubEnvelopeMapper(new StubEndpoint());

// Customize only the outgoing direction for Id — write it to a
// transport-specific location. This must not stop Id from being
// read from the default "id" header on incoming messages.
mapper.MapOutgoingProperty(x => x.Id, (envelope, outgoing) =>
{
outgoing.Headers["custom-id"] = envelope.Id.ToString();
});

var expected = Guid.NewGuid();
var envelope = new Envelope();
mapper.MapIncomingToEnvelope(envelope, new StubTransportMessage
{
Headers = { [EnvelopeConstants.IdKey] = expected.ToString() }
});

envelope.Id.ShouldBe(expected);
}

[Fact]
public void custom_incoming_property_still_overrides_its_own_direction()
{
// Sanity check: the customization itself still works on the direction
// it actually targets. This complements
// custom_incoming_property_preserves_default_outgoing_header so a future
// regression that over-corrects can't slip past.
var mapper = new StubEnvelopeMapper(new StubEndpoint());

mapper.MapIncomingProperty(x => x.Id, (envelope, incoming) =>
{
if (incoming.Headers.TryGetValue("custom-id", out var raw) && Guid.TryParse(raw, out var parsed))
{
envelope.Id = parsed;
}
});

var expected = Guid.NewGuid();
var envelope = new Envelope();
mapper.MapIncomingToEnvelope(envelope, new StubTransportMessage
{
Headers = { ["custom-id"] = expected.ToString() }
});

envelope.Id.ShouldBe(expected);
}

[Fact]
public void custom_outgoing_property_still_overrides_its_own_direction()
{
var mapper = new StubEnvelopeMapper(new StubEndpoint());

mapper.MapOutgoingProperty(x => x.Id, (envelope, outgoing) =>
{
outgoing.Headers["custom-id"] = envelope.Id.ToString();
});

var envelope = new Envelope { Id = Guid.NewGuid() };
var outgoing = new StubTransportMessage();

mapper.MapEnvelopeToOutgoing(envelope, outgoing);

outgoing.Headers.ContainsKey("custom-id").ShouldBeTrue();
outgoing.Headers["custom-id"].ShouldBe(envelope.Id.ToString());
}
}

internal class StubTransportMessage
Expand Down
14 changes: 12 additions & 2 deletions src/Wolverine/Transports/EnvelopeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,12 @@ private Action<Envelope, TIncoming> compileIncoming()
writeHeaders
};

foreach (var pair in _envelopeToHeader.Where(x => !_envelopeToOutgoing.ContainsKey(x.Key)))
// Use the default header read for a property unless the caller has
// supplied a custom incoming mapping for it. Previously this predicate
// was accidentally checking _envelopeToOutgoing, which caused
// MapOutgoingProperty to silently delete the incoming header read for
// the same property. See https://github.com/JasperFx/wolverine/issues/2551.
foreach (var pair in _envelopeToHeader.Where(x => !_incomingToEnvelope.ContainsKey(x.Key)))
{
var getMethod = getString!;
if (pair.Key.PropertyType == typeof(Uri))
Expand Down Expand Up @@ -278,7 +283,12 @@ private Action<Envelope, TOutgoing> compileOutgoing()
writeHeaders
};

var headers = _envelopeToHeader.Where(x => !_incomingToEnvelope.ContainsKey(x.Key));
// Use the default header write for a property unless the caller has
// supplied a custom outgoing mapping for it. Previously this predicate
// was accidentally checking _incomingToEnvelope, which caused
// MapIncomingProperty to silently delete the outgoing header write for
// the same property. See https://github.com/JasperFx/wolverine/issues/2551.
var headers = _envelopeToHeader.Where(x => !_envelopeToOutgoing.ContainsKey(x.Key));
foreach (var pair in headers)
{
var setMethod = setString!;
Expand Down
Loading