Skip to content
Open
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
136 changes: 69 additions & 67 deletions src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,7 @@

namespace KurrentDB.Projections.Core.Javascript.Tests;

public class SpecRunner {
private readonly ITestOutputHelper _output;

public SpecRunner(ITestOutputHelper output) {
_output = output;
}

public class SpecRunner(ITestOutputHelper output) {
private static readonly Encoding Utf8NoBom = new UTF8Encoding(false, true);

public static IEnumerable<object[]> GetTestCases() {
Expand Down Expand Up @@ -71,10 +65,9 @@

var initializedPartitions = new List<string>();
if (e.TryGetProperty("initializedPartitions", out var partitions)) {
foreach (var element in partitions.EnumerateArray()) {
initializedPartitions.Add(element.GetString()!);
}
initializedPartitions.AddRange(partitions.EnumerateArray().Select(element => element.GetString()!));
}

var expectedStates = new Dictionary<string, string?>();
var expectedResults = new Dictionary<string, string?>();
int stateCount = 0;
Expand All @@ -84,9 +77,10 @@
var partition = state.GetProperty("partition").GetString()!;
var expectedStateNode = state.GetProperty("state");
var expectedState = expectedStateNode.ValueKind == JsonValueKind.Null ? null : expectedStateNode.GetRawText();
if (!expectedStates.TryAdd(partition, expectedState)) throw new InvalidOperationException("Duplicate expected state");
if (!expectedStates.TryAdd(partition, expectedState))
throw new InvalidOperationException("Duplicate expected state");

if (state.TryGetProperty("result", out var expectedResultNode )) {
if (state.TryGetProperty("result", out var expectedResultNode)) {
var expectedResult = expectedResultNode.ValueKind == JsonValueKind.Null
? null
: expectedResultNode.GetRawText();
Expand All @@ -96,19 +90,18 @@
}
}


if (stateCount > 2)
throw new InvalidOperationException("Cannot specify more than 2 states");

sequence.Events.Add(new InputEvent(et!
, e.GetProperty("data").GetRawText()
, e.TryGetProperty("metadata", out var metadata) ? metadata.GetRawText() : null
, initializedPartitions
, expectedStates
, expectedResults
, skip
, e.TryGetProperty("eventId", out var idElement) && idElement.TryGetGuid(out var id) ? id : Guid.NewGuid()
));
, e.GetProperty("data").GetRawText()
, e.TryGetProperty("metadata", out var metadata) ? metadata.GetRawText() : null
, initializedPartitions
, expectedStates
, expectedResults
, skip
, e.TryGetProperty("eventId", out var idElement) && idElement.TryGetGuid(out var id) ? id : Guid.NewGuid()
));
}
}

Expand Down Expand Up @@ -154,6 +147,7 @@
foreach (var c in item.Value.EnumerateArray()) {
sdb.FromCategory(c.GetString());
}

break;
case "partitioned":
if (item.Value.GetBoolean())
Expand All @@ -163,18 +157,21 @@
foreach (var e in item.Value.EnumerateArray()) {
sdb.IncludeEvent(e.GetString());
}

break;
case "allEvents":
if (item.Value.GetBoolean()) {
sdb.AllEvents();
} else {
sdb.NotAllEvents();
}

break;
case "allStreams":
if (item.Value.GetBoolean()) {
sdb.FromAll();
}

break;
default:
throw new Exception($"unexpected property in expected config {item.Name}");
Expand All @@ -186,14 +183,12 @@
if (output.TryGetProperty("emitted", out var expectedEmittedElement)) {
foreach (var element in expectedEmittedElement.EnumerateObject()) {
var stream = element.Name;
foreach (var eventElement in element.Value.EnumerateArray()) {
if (eventElement.ValueKind == JsonValueKind.String) {
expectedEmittedEvents.Add(
new OutputEvent(stream, "$>", eventElement.GetString(), null));
}
}
expectedEmittedEvents.AddRange(from eventElement in element.Value.EnumerateArray()
where eventElement.ValueKind == JsonValueKind.String
select new OutputEvent(stream, "$>", eventElement.GetString(), null));
}
}

JintProjectionStateHandler runner = null;
IQuerySources definition = null;

Expand All @@ -213,18 +208,26 @@
yield return For($"{projection} qs.AllEvents", () => Assert.Equal(expectedDefinition.AllEvents, definition.AllEvents));
yield return For($"{projection} qs.Events", () => Assert.Equal(expectedDefinition.Events, definition.Events));
yield return For($"{projection} qs.ByStreams", () => Assert.Equal(expectedDefinition.ByStreams, definition.ByStreams));
yield return For($"{projection} qs.ByCustomPartitions", () => Assert.Equal(expectedDefinition.ByCustomPartitions, definition.ByCustomPartitions));
yield return For($"{projection} qs.ByCustomPartitions",
() => Assert.Equal(expectedDefinition.ByCustomPartitions, definition.ByCustomPartitions));
yield return For($"{projection} qs.DefinesStateTransform", () => Assert.Equal(expectedDefinition.DefinesStateTransform, definition.DefinesStateTransform));
yield return For($"{projection} qs.DefinesFold", () => Assert.Equal(expectedDefinition.DefinesFold, definition.DefinesFold));
yield return For($"{projection} qs.HandlesDeletedNotifications", () => Assert.Equal(expectedDefinition.HandlesDeletedNotifications, definition.HandlesDeletedNotifications));
yield return For($"{projection} qs.ProducesResults", () => Assert.Equal(expectedDefinition.ProducesResults, definition.ProducesResults));
yield return For($"{projection} qs.HandlesDeletedNotifications",
() => Assert.Equal(expectedDefinition.HandlesDeletedNotifications, definition.HandlesDeletedNotifications));
yield return For($"{projection} qs.ProducesResults",
() => Assert.Equal(expectedDefinition.ProducesResults, definition.ProducesResults));
yield return For($"{projection} qs.IsBiState", () => Assert.Equal(expectedDefinition.IsBiState, definition.IsBiState));
yield return For($"{projection} qs.IncludeLinksOption", () => Assert.Equal(expectedDefinition.IncludeLinksOption, definition.IncludeLinksOption));
yield return For($"{projection} qs.ResultStreamNameOption", () => Assert.Equal(expectedDefinition.ResultStreamNameOption, definition.ResultStreamNameOption));
yield return For($"{projection} qs.PartitionResultStreamNamePatternOption", () => Assert.Equal(expectedDefinition.PartitionResultStreamNamePatternOption,
definition.PartitionResultStreamNamePatternOption));
yield return For($"{projection} qs.ReorderEventsOption", () => Assert.Equal(expectedDefinition.ReorderEventsOption, definition.ReorderEventsOption));
yield return For($"{projection} qs.ProcessingLagOption", () => Assert.Equal(expectedDefinition.ProcessingLagOption, definition.ProcessingLagOption));
yield return For($"{projection} qs.IncludeLinksOption",
() => Assert.Equal(expectedDefinition.IncludeLinksOption, definition.IncludeLinksOption));
yield return For($"{projection} qs.ResultStreamNameOption",
() => Assert.Equal(expectedDefinition.ResultStreamNameOption, definition.ResultStreamNameOption));
yield return For($"{projection} qs.PartitionResultStreamNamePatternOption", ()
=> Assert.Equal(expectedDefinition.PartitionResultStreamNamePatternOption,
definition.PartitionResultStreamNamePatternOption));
yield return For($"{projection} qs.ReorderEventsOption",
() => Assert.Equal(expectedDefinition.ReorderEventsOption, definition.ReorderEventsOption));
yield return For($"{projection} qs.ProcessingLagOption",
() => Assert.Equal(expectedDefinition.ProcessingLagOption, definition.ProcessingLagOption));
var partitionedState = new Dictionary<string, string>();
var partitionedResult = new Dictionary<string, string>();
var sharedStateInitialized = false;
Expand All @@ -236,6 +239,7 @@
if (!revision.TryGetValue(sequence.Stream, out _)) {
revision[sequence.Stream] = 0;
}

for (int j = 0; j < sequences[i].Events.Count; j++) {
var logPosition = i * 100 + j;
var flags = PrepareFlags.IsJson | PrepareFlags.Data;
Expand All @@ -262,6 +266,7 @@
if (@event.Metadata != null) {
metadata = Utf8NoBom.GetBytes(JObject.Parse(@event.Metadata).ToString(Formatting.Indented));
}

var er = new EventRecord(
revision[sequence.Stream], logPosition, Guid.NewGuid(), @event.EventId, i, j,
sequence.Stream, i, DateTime.Now, flags, @event.EventType,
Expand All @@ -274,13 +279,11 @@
var expectedPartition = "";
if (expectedDefinition.DefinesFold) {
if (@event.ExpectedStates.Any()) {
expectedPartition = @event.ExpectedStates
.SingleOrDefault(x => string.Empty != x.Key).Key ?? string.Empty;
expectedPartition = @event.ExpectedStates.SingleOrDefault(x => string.Empty != x.Key).Key ?? string.Empty;
if (expectedPartition != string.Empty) {
yield return For(
$"{projection} {er.EventNumber}@{sequence.Stream} returns expected partition",
() => Assert.Equal(expectedPartition,
runner.GetStatePartition(CheckpointTag.Empty, "", e)));
() => Assert.Equal(expectedPartition, runner.GetStatePartition(CheckpointTag.Empty, "", e)));
foreach (var initializedState in @event.InitializedPartitions) {
yield return For(
$"should not have already initialized \"{initializedState}\" at {er.EventNumber}@{sequence.Stream}",
Expand Down Expand Up @@ -331,7 +334,8 @@
$"process event {@event.EventType} at {er.EventNumber}@{sequence.Stream}",
() => {
Assert.True(runner.ProcessEvent(expectedPartition, CheckpointTag.Empty, "", e,
out var newState, out var newSharedState, out var emittedEvents), "Process event should always return true");
out var newState, out var newSharedState, out var emittedEvents),
"Process event should always return true");
var result = runner.TransformStateToResult();
if (newSharedState != null) {
partitionedState[""] = newSharedState;
Expand All @@ -340,11 +344,9 @@
partitionedState[expectedPartition] = newState;
partitionedResult[expectedPartition] = result;

if (emittedEvents != null && emittedEvents.Length > 0) {
if (emittedEvents is { Length: > 0 }) {
actualEmittedEvents.AddRange(emittedEvents);
}


});
if (@event.ExpectedStates.Any()) {
foreach (var expectedState in @event.ExpectedStates) {
Expand All @@ -353,9 +355,10 @@

yield return For($@"state ""{expectedState.Key}"" is correct at {er.EventNumber}@{sequence.Stream}",
() => {
if (string.IsNullOrEmpty(expectedState.Value)) Assert.Null(partitionedState[expectedState.Key]);
if (string.IsNullOrEmpty(expectedState.Value)) Assert.Null(partitionedState[expectedState.Key]);
else {
Assert.True(partitionedState.ContainsKey(expectedState.Key), $"partition does not contain key {expectedState.Key}");
Assert.True(partitionedState.ContainsKey(expectedState.Key),
$"partition does not contain key {expectedState.Key}");
Assert.NotNull(partitionedState[expectedState.Key]);
Assert.NotEmpty(partitionedState[expectedState.Key]);
var expected = Sort(expectedState.Value, "expected");
Expand All @@ -365,17 +368,19 @@
});
}
}

if (@event.ExpectedResult.Any()) {
foreach (var expectedResult in @event.ExpectedResult) {

yield return For($@"result ""{expectedResult.Key}"" exists at {er.EventNumber}@{sequence.Stream}",
() => Assert.Contains(expectedResult.Key, (IReadOnlyDictionary<string, string>)partitionedResult));

yield return For($@"result ""{expectedResult.Key}"" is correct at {er.EventNumber}@{sequence.Stream}",
() => {
if (string.IsNullOrEmpty(expectedResult.Value)) Assert.Null(partitionedResult[expectedResult.Key]);
if (string.IsNullOrEmpty(expectedResult.Value))
Assert.Null(partitionedResult[expectedResult.Key]);
else {
Assert.True(partitionedResult.ContainsKey(expectedResult.Key), $"partition does not contain key {expectedResult.Key}");
Assert.True(partitionedResult.ContainsKey(expectedResult.Key),
$"partition does not contain key {expectedResult.Key}");
if (expectedResult.Value is null) {
Assert.Null(partitionedResult[expectedResult.Key]);
} else {
Expand All @@ -389,17 +394,11 @@
});
}
}




}
}

revision[sequence.Stream] = revision[sequence.Stream] + 1;
revision[sequence.Stream] += 1;
}


}

if (expectedEmittedEvents.Count == 0) {
Expand All @@ -417,17 +416,21 @@
}

object[] For(string name, Action a) {
return new object[]{ new TestDefinition(Name(name),_ => {
a();
return default;
})};
return new object[] {
new TestDefinition(Name(name), _ => {
a();
return default;
})
};
}

object[] WithOutput(string name, Action<ITestOutputHelper> a) {
return new object[]{ new TestDefinition(Name(name),o => {
a(o);
return default;
})};
return new object[] {
new TestDefinition(Name(name), o => {
a(o);
return default;
})
};
}

string Name(string name) {
Expand All @@ -444,14 +447,15 @@
} catch {
throw;
}

return new(root.Properties().OrderBy(x => x.Name));
}
}

[Theory]
[MemberData(nameof(GetTestCases))]
public Task Test(TestDefinition def) {
return def.Execute(_output).AsTask();
return def.Execute(output).AsTask();

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-windows-2022

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []

Check failure on line 458 in src/KurrentDB.Projections.Core.Javascript.Tests/SpecRunner.cs

View workflow job for this annotation

GitHub Actions / build / ci/github/build-ubuntu-22.04

KurrentDB.Projections.Core.Javascript.Tests.SpecRunner.Test

Assert.Contains() Failure: Filter not matched in collection Collection: []
}

public class TestDefinition {
Expand Down Expand Up @@ -492,7 +496,6 @@
IReadOnlyDictionary<string, string> expectedResults,
bool skip,
Guid eventId) {

public string EventType { get; } = eventType;
public string Body { get; } = body;
public string Metadata { get; } = metadata;
Expand All @@ -501,7 +504,6 @@
public IReadOnlyDictionary<string, string> ExpectedResult { get; } = expectedResults;
public bool Skip { get; } = skip;
public Guid EventId { get; } = eventId;

}

class OutputEvent {
Expand Down
1 change: 0 additions & 1 deletion src/KurrentDB.Projections.Core.Tests/AssertEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public static void AreEqual(IQuerySources expected, IQuerySources actual) {
Assert.AreEqual(expected.ByStreams, actual.ByStreams, $"Expected {nameof(expected.ByStreams)} to be {expected.ByStreams} but was {actual.ByStreams}");
Assert.AreEqual(expected.DefinesFold, actual.DefinesFold, $"Expected {nameof(expected.DefinesFold)} to be {expected.DefinesFold} but was {actual.DefinesFold}");
Assert.AreEqual(expected.DefinesStateTransform, actual.DefinesStateTransform, $"Expected {nameof(expected.DefinesStateTransform)} to be {expected.DefinesStateTransform} but was {actual.DefinesStateTransform}");

Assert.AreEqual(expected.IncludeLinksOption, actual.IncludeLinksOption, $"Expected {nameof(expected.IncludeLinksOption)} to be {expected.IncludeLinksOption} but was {actual.IncludeLinksOption}");
Assert.AreEqual(expected.IsBiState, actual.IsBiState, $"Expected {nameof(expected.IsBiState)} to be {expected.IsBiState} but was {actual.IsBiState}");
Assert.AreEqual(expected.PartitionResultStreamNamePatternOption, actual.PartitionResultStreamNamePatternOption, $"Expected {nameof(expected.PartitionResultStreamNamePatternOption)} to be {expected.PartitionResultStreamNamePatternOption} but was {actual.PartitionResultStreamNamePatternOption}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ public class claims_serialization {
[Test]
public void should_serialize_principal_name() {
var principalName = "foo-name";
var claimsIdentity = new ClaimsIdentity(new Claim[] {
var claimsIdentity = new ClaimsIdentity([
new(ClaimTypes.Name, principalName),
new(ClaimTypes.Role, "$admins"),
});
new(ClaimTypes.Role, "$admins")
]);
var runas = new ProjectionManagementMessage.RunAs(new ClaimsPrincipal(claimsIdentity));
var sra = SerializedRunAs.SerializePrincipal(runas);
Assert.AreEqual(principalName, sra.Name);
Expand All @@ -26,12 +26,12 @@ public void should_serialize_principal_name() {
[Test]
public void should_only_serialize_role() {
var roleClaim = new Claim(ClaimTypes.Role, "$admins");
var claimsIdentity = new ClaimsIdentity(new[] {
var claimsIdentity = new ClaimsIdentity([
new(ClaimTypes.Name, "foo-name"),
roleClaim,
new("uid", "foo-uid"),
new("pwd", "foo-pwd")
});
]);
var runas = new ProjectionManagementMessage.RunAs(new ClaimsPrincipal(claimsIdentity));
var sra = SerializedRunAs.SerializePrincipal(runas);
Assert.AreEqual(1, sra.Roles.Length);
Expand Down
Loading
Loading