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
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using System;
using EventStore.Core.Services.Storage.Indexing;
using Xunit;

namespace EventStore.Core.XUnit.Tests.Services.Storage.Indexing;

public class IndexDefinitionTests
{
[Fact]
public void constructor_rejects_missing_fields()
{
var exception = Assert.Throws<ArgumentNullException>(() => new IndexDefinition(new IndexEventFilter("event.type == 'order'"), null!));

Assert.Equal("fields", exception.ParamName);
}

[Fact]
public void constructor_rejects_null_field()
{
var exception = Assert.Throws<ArgumentException>(() => new IndexDefinition(new IndexEventFilter("event.type == 'order'"), [null!]));

Assert.Equal("fields", exception.ParamName);
}

[Fact]
public void constructor_requires_filter_or_field()
{
var exception = Assert.Throws<ArgumentException>(() => new IndexDefinition(null, []));

Assert.Equal("fields", exception.ParamName);
}

[Fact]
public void constructor_accepts_filter_without_fields()
{
var filter = new IndexEventFilter("event.type == 'order'");
var definition = new IndexDefinition(filter, []);

Assert.Same(filter, definition.Filter);
Assert.Empty(definition.Fields);
}

[Fact]
public void constructor_accepts_field_without_filter()
{
var field = new IndexFieldDefinition("customerId", new IndexFieldSelector("event.body.customerId"));
var definition = new IndexDefinition(null, [field]);

Assert.Null(definition.Filter);
Assert.Equal([field], definition.Fields);
}

[Fact]
public void constructor_copies_fields()
{
var field = new IndexFieldDefinition("customerId", new IndexFieldSelector("event.body.customerId"));
var fields = new[] { field };
var definition = new IndexDefinition(null, fields);

fields[0] = new IndexFieldDefinition("tenantId", new IndexFieldSelector("event.body.tenantId"));

Assert.Equal([field], definition.Fields);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void filter_rejects_empty_value(string value)
{
var exception = Assert.Throws<ArgumentException>(() => new IndexEventFilter(value));

Assert.Equal("value", exception.ParamName);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void field_rejects_empty_name(string name)
{
var exception = Assert.Throws<ArgumentException>(() => new IndexFieldDefinition(name, new IndexFieldSelector("event.body.customerId")));

Assert.Equal("name", exception.ParamName);
}

[Fact]
public void field_rejects_missing_selector()
{
var exception = Assert.Throws<ArgumentNullException>(() => new IndexFieldDefinition("customerId", null!));

Assert.Equal("selector", exception.ParamName);
}

[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
public void field_selector_rejects_empty_value(string value)
{
var exception = Assert.Throws<ArgumentException>(() => new IndexFieldSelector(value));

Assert.Equal("value", exception.ParamName);
}
}
84 changes: 84 additions & 0 deletions src/EventStore.Core/Services/Storage/Indexing/IndexDefinition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace EventStore.Core.Services.Storage.Indexing;

public sealed record IndexDefinition
{
public IndexEventFilter Filter { get; }

public IReadOnlyList<IndexFieldDefinition> Fields { get; }

public IndexDefinition(IndexEventFilter filter, IReadOnlyList<IndexFieldDefinition> fields)
{
ArgumentNullException.ThrowIfNull(fields);

if (fields.Any(static field => field is null))
{
throw new ArgumentException("Index fields cannot contain null.", nameof(fields));
}

if (filter is null && fields.Count == 0)
{
throw new ArgumentException("Index definition must specify at least one filter or field.", nameof(fields));
}

Filter = filter;
Fields = fields.ToArray();
}
}

public sealed record IndexEventFilter
{
public string Value { get; }

public IndexEventFilter(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Index filter cannot be empty.", nameof(value));
}

Value = value;
}

public override string ToString() => Value;
}

public sealed record IndexFieldDefinition
{
public string Name { get; }

public IndexFieldSelector Selector { get; }

public IndexFieldDefinition(string name, IndexFieldSelector selector)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("Index field name cannot be empty.", nameof(name));
}

ArgumentNullException.ThrowIfNull(selector);

Name = name;
Selector = selector;
}
}

public sealed record IndexFieldSelector
{
public string Value { get; }

public IndexFieldSelector(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Index field selector cannot be empty.", nameof(value));
}

Value = value;
}

public override string ToString() => Value;
}
Loading