Skip to content

Commit 0c81be9

Browse files
authored
Merge pull request #2334 from Cratis:fix/fixes
Fix/fixes
2 parents 7c051ef + 5366733 commit 0c81be9

File tree

7 files changed

+138
-7
lines changed

7 files changed

+138
-7
lines changed

Source/Infrastructure.Specs/Changes/for_Changeset/when_adding_changes/and_properties_changed_to_different_empty_array_than_child_added.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ public class and_properties_changed_to_different_empty_array_than_child_added :
1515

1616
void Establish()
1717
{
18-
_itemsProperty = new PropertyPath("items");
19-
_tagsProperty = new PropertyPath("tags");
18+
// Create the array property paths using AddArrayIndex to match runtime behavior
19+
// At runtime, ChildrenPropertyPath is created with AddArrayIndex, resulting in format "[propertyName]"
20+
_itemsProperty = PropertyPath.Root.AddArrayIndex("items");
21+
_tagsProperty = PropertyPath.Root.AddArrayIndex("tags");
2022

2123
// Create a PropertiesChanged that sets tags to empty array
2224
var emptyArray = Array.Empty<object>();

Source/Infrastructure.Specs/Changes/for_Changeset/when_adding_changes/and_properties_changed_to_empty_array_then_child_added.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ public class and_properties_changed_to_empty_array_then_child_added : given.a_ch
1414

1515
void Establish()
1616
{
17-
_itemsProperty = new PropertyPath("items");
17+
// Create the array property path using AddArrayIndex to match runtime behavior
18+
// At runtime, ChildrenPropertyPath is created with AddArrayIndex, resulting in format "[items]"
19+
_itemsProperty = PropertyPath.Root.AddArrayIndex("items");
1820

1921
// Create a PropertiesChanged that sets items to empty array
2022
var emptyArray = Array.Empty<object>();

Source/Infrastructure.Specs/Changes/for_Changeset/when_adding_changes/and_properties_changed_to_empty_array_then_child_removed.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ public class and_properties_changed_to_empty_array_then_child_removed : given.a_
1414

1515
void Establish()
1616
{
17-
_itemsProperty = new PropertyPath("items");
17+
// Create the array property path using AddArrayIndex to match runtime behavior
18+
// At runtime, ChildrenPropertyPath is created with AddArrayIndex, resulting in format "[items]"
19+
_itemsProperty = PropertyPath.Root.AddArrayIndex("items");
1820

1921
// Create a PropertiesChanged that sets items to empty array
2022
var emptyArray = Array.Empty<object>();

Source/Infrastructure.Specs/Changes/for_Changeset/when_adding_changes/and_properties_changed_with_empty_array_and_other_properties_then_child_added.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ public class and_properties_changed_with_empty_array_and_other_properties_then_c
1515

1616
void Establish()
1717
{
18-
_itemsProperty = new PropertyPath("items");
18+
// Create the array property path using AddArrayIndex to match runtime behavior
19+
// At runtime, ChildrenPropertyPath is created with AddArrayIndex, resulting in format "[items]"
20+
_itemsProperty = PropertyPath.Root.AddArrayIndex("items");
1921
_nameProperty = new PropertyPath("name");
2022

2123
// Create a PropertiesChanged that sets items to empty array and changes name

Source/Infrastructure/Changes/Changeset.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ bool ShouldFilterPropertyDifference(PropertyDifference diff, HashSet<PropertyPat
459459
// Check if this array path matches any of the arrays with child operations
460460
foreach (var arrayPath in arraysWithChildOperations)
461461
{
462-
if (diff.PropertyPath.Path.Equals(arrayPath.Path, StringComparison.Ordinal))
462+
if (diff.PropertyPath.Path.Equals(arrayPath.LastSegment.Value, StringComparison.Ordinal))
463463
{
464464
return true;
465465
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright (c) Cratis. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Cratis.Chronicle.Concepts.Events;
5+
using Cratis.Chronicle.Concepts.Identities;
6+
using Cratis.Chronicle.Concepts.Keys;
7+
using Cratis.Chronicle.Concepts.Projections;
8+
using Cratis.Chronicle.Dynamic;
9+
using Cratis.Chronicle.Properties;
10+
using Cratis.Chronicle.Storage.EventSequences;
11+
using Cratis.Chronicle.Storage.Sinks;
12+
using Cratis.Monads;
13+
using Microsoft.Extensions.Logging.Abstractions;
14+
15+
namespace Cratis.Chronicle.Projections.for_KeyResolvers;
16+
17+
public class when_identifying_read_model_key_from_parent_hierarchy_using_sink_lookup : Specification
18+
{
19+
AppendedEvent _childEvent;
20+
Key _result;
21+
IProjection _parentProjection;
22+
IProjection _childProjection;
23+
IEventSequenceStorage _storage;
24+
ISink _sink;
25+
KeyResolvers _keyResolvers;
26+
27+
static EventType _parentEventType = new("5f4f4368-6989-4d9d-a84e-7393e0b41cfd", 1);
28+
static EventType _childEventType = new("02405794-91e7-4e4f-8ad1-f043070ca297", 1);
29+
const string RootKey = "root-key-123";
30+
const string ParentKey = "parent-key-456";
31+
const string ChildKey = "child-key-789";
32+
const string ChildEventSourceId = "different-event-source-id";
33+
34+
PropertyPath _queriedChildPropertyPath;
35+
36+
void Establish()
37+
{
38+
_keyResolvers = new KeyResolvers(NullLogger<KeyResolvers>.Instance);
39+
40+
// Child event appended to a DIFFERENT EventSourceId than the parent
41+
// This simulates the scenario where events are appended to a different stream
42+
// but contain the parent key in their content
43+
_childEvent = new(
44+
new(
45+
_childEventType,
46+
EventSourceType.Default,
47+
ChildEventSourceId, // Different from ParentKey
48+
EventStreamType.All,
49+
EventStreamId.Default,
50+
1,
51+
DateTimeOffset.UtcNow,
52+
"123b8935-a1a4-410d-aace-e340d48f0aa0",
53+
"41f18595-4748-4b01-88f7-4c0d0907aa90",
54+
CorrelationId.New(),
55+
[],
56+
Identity.System),
57+
new
58+
{
59+
parentId = ParentKey,
60+
childId = ChildKey,
61+
name = "Test Child"
62+
}.AsExpandoObject());
63+
64+
_parentProjection = Substitute.For<IProjection>();
65+
_parentProjection.EventTypes.Returns([_parentEventType]);
66+
_parentProjection.OwnEventTypes.Returns([_parentEventType]);
67+
_parentProjection.IdentifiedByProperty.Returns((PropertyPath)"id");
68+
_parentProjection.Path.Returns((ProjectionPath)"configurations");
69+
_parentProjection.ChildrenPropertyPath.Returns(PropertyPath.NotSet);
70+
_parentProjection.HasParent.Returns(false);
71+
_parentProjection.Parent.Returns((IProjection)null!);
72+
73+
_childProjection = Substitute.For<IProjection>();
74+
_childProjection.HasParent.Returns(true);
75+
_childProjection.Parent.Returns(_parentProjection);
76+
_childProjection.ChildrenPropertyPath.Returns((PropertyPath)"hubs");
77+
_childProjection.IdentifiedByProperty.Returns((PropertyPath)"hubId");
78+
_childProjection.Path.Returns((ProjectionPath)"configurations -> hubs");
79+
80+
_storage = Substitute.For<IEventSequenceStorage>();
81+
_sink = Substitute.For<ISink>();
82+
83+
// Parent event is NOT found by EventSourceId (returns empty)
84+
// This forces the code to use sink lookup
85+
_storage.TryGetLastInstanceOfAny(ParentKey, Arg.Any<IEnumerable<EventTypeId>>())
86+
.Returns(Option<AppendedEvent>.None());
87+
88+
// Sink lookup returns the root key
89+
// Capture the property path used in the query to verify it's correct
90+
_sink.When(x => x.TryFindRootKeyByChildValue(Arg.Any<PropertyPath>(), ParentKey))
91+
.Do(callInfo => _queriedChildPropertyPath = callInfo.ArgAt<PropertyPath>(0));
92+
93+
_sink.TryFindRootKeyByChildValue(Arg.Any<PropertyPath>(), ParentKey)
94+
.Returns(new Option<Key>(new Key(RootKey, ArrayIndexers.NoIndexers)));
95+
96+
// Mock recursive resolution to return a simple key
97+
_sink.TryFindRootKeyByChildValue(Arg.Any<PropertyPath>(), RootKey)
98+
.Returns(Option<Key>.None());
99+
}
100+
101+
async Task Because()
102+
{
103+
try
104+
{
105+
var keyResult = await _keyResolvers.FromParentHierarchy(
106+
_childProjection,
107+
_keyResolvers.FromEventValueProvider(EventValueProviders.EventContent("childId")),
108+
_keyResolvers.FromEventValueProvider(EventValueProviders.EventContent("parentId")),
109+
"hubId")(_storage, _sink, _childEvent);
110+
_result = (keyResult as ResolvedKey)?.Key;
111+
}
112+
catch
113+
{
114+
// Ignore exceptions - we're only testing that the sink was called with the correct path
115+
}
116+
}
117+
118+
[Fact] void should_query_sink_with_child_projection_children_property_path() =>
119+
_queriedChildPropertyPath.ShouldEqual((PropertyPath)"hubs.id");
120+
121+
[Fact] void should_not_query_sink_with_parent_projection_children_property_path() =>
122+
_queriedChildPropertyPath.ShouldNotEqual((PropertyPath)"configurations.id");
123+
}

Source/Kernel/Projections/KeyResolvers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public KeyResolver FromParentHierarchy(IProjection projection, KeyResolver keyRe
196196
return KeyResolverResult.Deferred(deferredFuture);
197197
}
198198

199-
var childPropertyPath = parentProjection.ChildrenPropertyPath + parentIdentifiedByProperty;
199+
var childPropertyPath = projection.ChildrenPropertyPath + parentIdentifiedByProperty;
200200

201201
logger.FromParentHierarchyLookupBySink(childPropertyPath.Path, parentKey.Value?.ToString() ?? "null");
202202

0 commit comments

Comments
 (0)