Skip to content

Commit 39cd52f

Browse files
committed
fix shallow copy, improve docs
1 parent a21a67d commit 39cd52f

File tree

2 files changed

+49
-2
lines changed

2 files changed

+49
-2
lines changed

Flagsmith.Engine/EvaluationContext/EvaluationContext.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,18 @@ public partial class EvaluationContext<SegmentMetadataT, FeatureMetadataT>
3737
[JsonProperty("segments", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
3838
public Dictionary<string, SegmentContext<SegmentMetadataT, FeatureMetadataT>> Segments { get; set; }
3939

40+
41+
/// <summary>
42+
/// Creates a copy of the EvaluationContext object
43+
/// for internal use in the engine.
44+
/// Optimised to avoid deep cloning where possible.
45+
/// </summary>
46+
/// <returns>EvaluationContext</returns>
4047
public EvaluationContext<SegmentMetadataT, FeatureMetadataT> Clone()
4148
{
42-
return (EvaluationContext<SegmentMetadataT, FeatureMetadataT>)MemberwiseClone();
49+
var clone = (EvaluationContext<SegmentMetadataT, FeatureMetadataT>)MemberwiseClone();
50+
clone.Identity = clone.Identity?.Clone();
51+
return clone;
4352
}
4453
}
4554

@@ -166,6 +175,16 @@ public partial class IdentityContext
166175
/// </summary>
167176
[JsonProperty("traits", Required = Required.DisallowNull, NullValueHandling = NullValueHandling.Ignore)]
168177
public Dictionary<string, object> Traits { get; set; }
178+
179+
/// <summary>
180+
/// Creates a copy of the IdentityContext object
181+
/// for internal use in the engine.
182+
/// </summary>
183+
/// <returns>IdentityContext</returns>
184+
public IdentityContext Clone()
185+
{
186+
return (IdentityContext)MemberwiseClone();
187+
}
169188
}
170189

171190
/// <summary>

Flagsmith.EngineTest/EngineTestV2.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,33 @@ public static IEnumerable<object[]> ExtractTestCaseFilenames()
6262
}
6363
return testCases;
6464
}
65+
66+
[Fact]
67+
public void TestGetEvaluationResult_ShouldNotMutateOriginalContextIdentity()
68+
{
69+
// Arrange
70+
var engine = new Engine();
71+
var context = new EvaluationContext<object, object>
72+
{
73+
Environment = new EnvironmentContext
74+
{
75+
Key = "test-env",
76+
Name = "Test Environment"
77+
},
78+
Identity = new IdentityContext
79+
{
80+
Identifier = "user-123",
81+
Key = null // Empty Key triggers the clone+mutate logic in GetEnrichedEvaluationContext
82+
},
83+
Features = new Dictionary<string, FeatureContext<object>>(),
84+
Segments = new Dictionary<string, SegmentContext<object, object>>()
85+
};
86+
87+
// Act
88+
var result = engine.GetEvaluationResult(context);
89+
90+
// Assert: The original context's Identity.Key should still be null
91+
Assert.Null(context.Identity.Key);
92+
}
6593
}
66-
}
94+
}

0 commit comments

Comments
 (0)