Skip to content

Commit f69f4db

Browse files
Add C# + Unreal tests for nullable filters
1 parent 5f2a3f8 commit f69f4db

160 files changed

Lines changed: 22164 additions & 4669 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

crates/bindings-csharp/Codegen.Tests/Tests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,33 @@ public static void TestNullableBTreeIndex(ReducerContext ctx)
379379
_ = ctx.Db.NullableBTreeIndex.AccountId.Filter(new Bound<uint?>(null, 99));
380380
}
381381
}
382+
383+
[SpacetimeDB.Table]
384+
public partial struct NullableUniqueIndex
385+
{
386+
[SpacetimeDB.PrimaryKey]
387+
public uint Id;
388+
389+
[SpacetimeDB.Unique]
390+
public uint? AccountId;
391+
392+
[SpacetimeDB.Unique]
393+
public string? Name;
394+
395+
[SpacetimeDB.Unique]
396+
public SpacetimeDB.Uuid? ExternalId;
397+
398+
[SpacetimeDB.Reducer]
399+
public static void TestNullableUniqueIndex(ReducerContext ctx)
400+
{
401+
_ = ctx.Db.NullableUniqueIndex.AccountId.Find((uint?)null);
402+
_ = ctx.Db.NullableUniqueIndex.AccountId.Find((uint?)55);
403+
_ = ctx.Db.NullableUniqueIndex.Name.Find((string?)null);
404+
_ = ctx.Db.NullableUniqueIndex.Name.Find("name");
405+
_ = ctx.Db.NullableUniqueIndex.ExternalId.Find((SpacetimeDB.Uuid?)null);
406+
_ = ctx.Db.NullableUniqueIndex.ExternalId.Find(SpacetimeDB.Uuid.NIL);
407+
}
408+
}
382409
""";
383410

384411
var parseOptions = new CSharpParseOptions(fixture.SampleCompilation.LanguageVersion);

crates/codegen/src/csharp.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -561,10 +561,12 @@ impl Lang for Csharp<'_> {
561561
(csharp_field_name_pascal, csharp_field_type)
562562
};
563563

564-
let (row_to_key, key_type) = match columns.as_singleton() {
564+
let (row_to_key, key_type, nullable_single_column) = match columns.as_singleton() {
565565
Some(col_pos) => {
566566
let (field_name, field_type) = get_csharp_field_name_and_type(col_pos);
567-
(format!("row.{field_name}"), field_type.to_string())
567+
let field_type = field_type.to_string();
568+
let nullable_single_column = field_type.ends_with('?');
569+
(format!("row.{field_name}"), field_type, nullable_single_column)
568570
}
569571
None => {
570572
let mut key_accessors = Vec::new();
@@ -576,6 +578,7 @@ impl Lang for Csharp<'_> {
576578
(
577579
format!("({})", key_accessors.join(", ")),
578580
format!("({})", key_type_elems.join(", ")),
581+
false,
579582
)
580583
}
581584
};
@@ -585,10 +588,18 @@ impl Lang for Csharp<'_> {
585588
let mut csharp_index_class_name = csharp_index_name.clone();
586589
let csharp_index_base_class_name = if schema.is_unique(&columns) {
587590
csharp_index_class_name += "UniqueIndex";
588-
"UniqueIndexBase"
591+
if nullable_single_column {
592+
"NullableUniqueIndexBase"
593+
} else {
594+
"UniqueIndexBase"
595+
}
589596
} else {
590597
csharp_index_class_name += "Index";
591-
"BTreeIndexBase"
598+
if nullable_single_column {
599+
"NullableBTreeIndexBase"
600+
} else {
601+
"BTreeIndexBase"
602+
}
592603
};
593604

594605
writeln!(

sdks/csharp/examples~/regression-tests/client/Program.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ void OnConnected(DbConnection conn, Identity identity, string authToken)
5050
.AddQuery(qb => qb.From.MyTable())
5151
.AddQuery(qb => qb.From.NullStringNonnullable())
5252
.AddQuery(qb => qb.From.NullStringNullable())
53+
.AddQuery(qb => qb.From.NullableUniqueLookup())
5354
.AddQuery(qb => qb.From.MyLog())
5455
.AddQuery(qb => qb.From.TestEvent())
5556
.AddQuery(qb => qb.From.Admins())
@@ -195,6 +196,83 @@ int y
195196
);
196197
};
197198

199+
conn.Reducers.OnInsertNullableUniqueLookupRows += (ReducerEventContext ctx) =>
200+
{
201+
Log.Info("Got InsertNullableUniqueLookupRows callback");
202+
waiting--;
203+
Debug.Assert(
204+
ctx.Event.Status is Status.Committed,
205+
$"InsertNullableUniqueLookupRows should commit, got {ctx.Event.Status}"
206+
);
207+
ValidateNullableUniqueLookupRows(ctx);
208+
209+
Log.Debug("Calling DeleteNullableUniqueLookupByNumber(null)");
210+
waiting++;
211+
conn.Reducers.DeleteNullableUniqueLookupByNumber(null);
212+
213+
Log.Debug("Calling DeleteNullableUniqueLookupByString(null)");
214+
waiting++;
215+
conn.Reducers.DeleteNullableUniqueLookupByString(null);
216+
217+
Log.Debug("Calling DeleteNullableUniqueLookupByUuid(null)");
218+
waiting++;
219+
conn.Reducers.DeleteNullableUniqueLookupByUuid(null);
220+
};
221+
222+
conn.Reducers.OnDeleteNullableUniqueLookupByNumber += (
223+
ReducerEventContext ctx,
224+
uint? optionalNumber
225+
) =>
226+
{
227+
Log.Info("Got DeleteNullableUniqueLookupByNumber callback");
228+
waiting--;
229+
Debug.Assert(
230+
ctx.Event.Status is Status.Committed,
231+
$"DeleteNullableUniqueLookupByNumber should commit, got {ctx.Event.Status}"
232+
);
233+
Debug.Assert(optionalNumber is null, $"Expected null optionalNumber, got {optionalNumber}");
234+
Debug.Assert(
235+
ctx.Db.NullableUniqueLookup.OptionalNumber.Find((uint?)null) is null,
236+
"Expected OptionalNumber.Find(null) to be absent after delete"
237+
);
238+
};
239+
240+
conn.Reducers.OnDeleteNullableUniqueLookupByString += (
241+
ReducerEventContext ctx,
242+
string? optionalString
243+
) =>
244+
{
245+
Log.Info("Got DeleteNullableUniqueLookupByString callback");
246+
waiting--;
247+
Debug.Assert(
248+
ctx.Event.Status is Status.Committed,
249+
$"DeleteNullableUniqueLookupByString should commit, got {ctx.Event.Status}"
250+
);
251+
Debug.Assert(optionalString is null, $"Expected null optionalString, got {optionalString}");
252+
Debug.Assert(
253+
ctx.Db.NullableUniqueLookup.OptionalString.Find(null) is null,
254+
"Expected OptionalString.Find(null) to be absent after delete"
255+
);
256+
};
257+
258+
conn.Reducers.OnDeleteNullableUniqueLookupByUuid += (
259+
ReducerEventContext ctx,
260+
Uuid? optionalUuid
261+
) =>
262+
{
263+
Log.Info("Got DeleteNullableUniqueLookupByUuid callback");
264+
waiting--;
265+
Debug.Assert(
266+
ctx.Event.Status is Status.Committed,
267+
$"DeleteNullableUniqueLookupByUuid should commit, got {ctx.Event.Status}"
268+
);
269+
Debug.Assert(optionalUuid is null, $"Expected null optionalUuid, got {optionalUuid}");
270+
Debug.Assert(
271+
ctx.Db.NullableUniqueLookup.OptionalUuid.Find(null) is null,
272+
"Expected OptionalUuid.Find(null) to be absent after delete"
273+
);
274+
};
275+
198276
conn.Reducers.OnUpdateWhereTest += (
199277
ReducerEventContext ctx,
200278
uint id,
@@ -463,6 +541,35 @@ void ValidateNullableVecView(
463541
}
464542
}
465543

544+
void ValidateNullableUniqueLookupRows(IRemoteDbContext conn)
545+
{
546+
Log.Debug("Checking nullable unique lookup indexes...");
547+
548+
var numberNull = conn.Db.NullableUniqueLookup.OptionalNumber.Find((uint?)null);
549+
Debug.Assert(numberNull is not null, "Expected OptionalNumber.Find(null) to find row 1");
550+
Debug.Assert(numberNull.Id == 1, $"Expected OptionalNumber null row id 1, got {numberNull.Id}");
551+
552+
var numberValue = conn.Db.NullableUniqueLookup.OptionalNumber.Find(7);
553+
Debug.Assert(numberValue is not null, "Expected OptionalNumber.Find(7) to find row 2");
554+
Debug.Assert(numberValue.Id == 2, $"Expected OptionalNumber 7 row id 2, got {numberValue.Id}");
555+
556+
var stringNull = conn.Db.NullableUniqueLookup.OptionalString.Find(null);
557+
Debug.Assert(stringNull is not null, "Expected OptionalString.Find(null) to find row 2");
558+
Debug.Assert(stringNull.Id == 2, $"Expected OptionalString null row id 2, got {stringNull.Id}");
559+
560+
var stringValue = conn.Db.NullableUniqueLookup.OptionalString.Find("alpha");
561+
Debug.Assert(stringValue is not null, "Expected OptionalString.Find(\"alpha\") to find row 1");
562+
Debug.Assert(stringValue.Id == 1, $"Expected OptionalString alpha row id 1, got {stringValue.Id}");
563+
564+
var uuidNull = conn.Db.NullableUniqueLookup.OptionalUuid.Find(null);
565+
Debug.Assert(uuidNull is not null, "Expected OptionalUuid.Find(null) to find row 3");
566+
Debug.Assert(uuidNull.Id == 3, $"Expected OptionalUuid null row id 3, got {uuidNull.Id}");
567+
568+
var uuidValue = conn.Db.NullableUniqueLookup.OptionalUuid.Find(Uuid.NIL);
569+
Debug.Assert(uuidValue is not null, "Expected OptionalUuid.Find(Uuid.NIL) to find row 1");
570+
Debug.Assert(uuidValue.Id == 1, $"Expected OptionalUuid NIL row id 1, got {uuidValue.Id}");
571+
}
572+
466573
void ValidateReducerErrorDoesNotContainStackTrace(Exception exception)
467574
{
468575
Debug.Assert(
@@ -1303,6 +1410,10 @@ void OnSubscriptionApplied(SubscriptionEventContext context)
13031410
waiting++;
13041411
context.Reducers.InsertNullStringIntoNullable();
13051412

1413+
Log.Debug("Calling InsertNullableUniqueLookupRows");
1414+
waiting++;
1415+
context.Reducers.InsertNullableUniqueLookupRows();
1416+
13061417
Log.Debug("Calling EmitTestEvent");
13071418
waiting++;
13081419
context.Reducers.EmitTestEvent(EXPECTED_TEST_EVENT_NAME, EXPECTED_TEST_EVENT_VALUE);

sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/DeleteNullableUniqueLookupByNumber.g.cs

Lines changed: 66 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/DeleteNullableUniqueLookupByString.g.cs

Lines changed: 66 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)