Files
Mario Montoya 8fb0bcf922 Add UUID built-in convenience type to SpacetimeDB (#3538)
# Description of Changes

Closes
[#3290](https://github.com/clockworklabs/SpacetimeDB/issues/3290).

Adds a new "special" type to SATS, `UUID`, which is represented as the
product `{ __uuid__: u128 }`. Adds versions of this type to all of our
various languages' module bindings libraries and client SDKs, and
updates codegen to recognize it and output references to those named
library types. Adds methods for creating new UUIDs according to the V4
(all random) and V7 (timestamp, monotonic counter and random)
specifications.

# API and ABI breaking changes

We add a new type 

# Expected complexity level and risk

2

it impacts all over the code

# Testing

- [x] Extends the Rust and Unreal SDK tests, and the associated
`module-test` modules in Rust, C# and TypeScript, with uses of UUIDs.
- [x] Extends the C# SDK regression tests with uses of UUIDs.
- [x] Extends the TypeScript test suite with tests with uses of UUIDs.

---------

Signed-off-by: Mario Montoya <mamcx@elmalabarista.com>
Co-authored-by: Phoebe Goldman <phoebe@clockworklabs.io>
Co-authored-by: Jason Larabie <jason@clockworklabs.io>
Co-authored-by: John Detter <4099508+jdetter@users.noreply.github.com>
2026-01-02 17:17:24 +00:00

401 lines
11 KiB
Rust

macro_rules! declare_tests_with_suffix {
($lang:ident, $suffix:literal) => {
mod $lang {
use spacetimedb_testing::sdk::Test;
const MODULE: &str = concat!("sdk-test", $suffix);
const CLIENT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/test-client");
fn make_test(subcommand: &str) -> Test {
Test::builder()
.with_name(subcommand)
.with_module(MODULE)
.with_client(CLIENT)
.with_language("rust")
.with_bindings_dir("src/module_bindings")
.with_compile_command("cargo build")
.with_run_command(format!("cargo run -- {}", subcommand))
.build()
}
#[test]
fn insert_primitive() {
make_test("insert-primitive").run();
}
#[test]
fn subscribe_and_cancel() {
make_test("subscribe-and-cancel").run();
}
#[test]
fn subscribe_and_unsubscribe() {
make_test("subscribe-and-unsubscribe").run();
}
#[test]
fn subscription_error_smoke_test() {
make_test("subscription-error-smoke-test").run();
}
#[test]
fn delete_primitive() {
make_test("delete-primitive").run();
}
#[test]
fn update_primitive() {
make_test("update-primitive").run();
}
#[test]
fn insert_identity() {
make_test("insert-identity").run();
}
#[test]
fn insert_caller_identity() {
make_test("insert-caller-identity").run();
}
#[test]
fn delete_identity() {
make_test("delete-identity").run();
}
#[test]
fn update_identity() {
make_test("delete-identity").run();
}
#[test]
fn insert_connection_id() {
make_test("insert-connection-id").run();
}
#[test]
fn insert_caller_connection_id() {
make_test("insert-caller-connection-id").run();
}
#[test]
fn delete_connection_id() {
make_test("delete-connection-id").run();
}
#[test]
fn update_connection_id() {
make_test("delete-connection-id").run();
}
#[test]
fn insert_timestamp() {
make_test("insert-timestamp").run();
}
#[test]
fn insert_call_uuid_v4() {
make_test("insert-call-uuid-v4").run();
}
#[test]
fn insert_call_uuid_v7() {
make_test("insert-call-uuid-v7").run();
}
#[test]
fn insert_uuid() {
make_test("insert-uuid").run();
}
#[test]
fn delete_uuid() {
make_test("delete-uuid").run();
}
#[test]
fn update_uuid() {
make_test("delete-uuid").run();
}
#[test]
fn on_reducer() {
make_test("on-reducer").run();
}
#[test]
fn fail_reducer() {
make_test("fail-reducer").run();
}
#[test]
fn insert_vec() {
make_test("insert-vec").run();
}
#[test]
fn insert_option_some() {
make_test("insert-option-some").run();
}
#[test]
fn insert_option_none() {
make_test("insert-option-none").run();
}
#[test]
fn insert_struct() {
make_test("insert-struct").run();
}
#[test]
fn insert_simple_enum() {
make_test("insert-simple-enum").run();
}
#[test]
fn insert_enum_with_payload() {
make_test("insert-enum-with-payload").run();
}
#[test]
fn insert_delete_large_table() {
make_test("insert-delete-large-table").run();
}
#[test]
fn insert_primitives_as_strings() {
make_test("insert-primitives-as-strings").run();
}
// #[test]
// fn resubscribe() {
// make_test("resubscribe").run();
// }
#[test]
#[should_panic]
fn should_fail() {
make_test("should-fail").run();
}
#[test]
fn reauth() {
make_test("reauth-part-1").run();
make_test("reauth-part-2").run();
}
#[test]
fn reconnect_different_connection_id() {
make_test("reconnect-different-connection-id").run();
}
#[test]
fn connect_disconnect_callbacks() {
Test::builder()
.with_name(concat!("connect-disconnect-callback-", stringify!($lang)))
.with_module(concat!("sdk-test-connect-disconnect", $suffix))
.with_client(concat!(
env!("CARGO_MANIFEST_DIR"),
"/tests/connect_disconnect_client"
))
.with_language("rust")
.with_bindings_dir("src/module_bindings")
.with_compile_command("cargo build")
.with_run_command("cargo run")
.build()
.run();
}
#[test]
fn caller_always_notified() {
make_test("caller-always-notified").run();
}
#[test]
fn subscribe_all_select_star() {
make_test("subscribe-all-select-star").run();
}
#[test]
fn caller_alice_receives_reducer_callback_but_not_bob() {
make_test("caller-alice-receives-reducer-callback-but-not-bob").run();
}
#[test]
fn row_deduplication() {
make_test("row-deduplication").run();
}
#[test]
fn row_deduplication_join_r_and_s() {
make_test("row-deduplication-join-r-and-s").run();
}
#[test]
fn row_deduplication_r_join_s_and_r_join_t8() {
make_test("row-deduplication-r-join-s-and-r-joint").run();
}
#[test]
fn test_lhs_join_update() {
make_test("test-lhs-join-update").run()
}
#[test]
fn test_lhs_join_update_disjoint_queries() {
make_test("test-lhs-join-update-disjoint-queries").run()
}
#[test]
fn test_intra_query_bag_semantics_for_join() {
make_test("test-intra-query-bag-semantics-for-join").run()
}
#[test]
fn two_different_compression_algos() {
make_test("two-different-compression-algos").run();
}
#[test]
fn test_parameterized_subscription() {
make_test("test-parameterized-subscription").run();
}
#[test]
fn test_rls_subscription() {
make_test("test-rls-subscription").run()
}
#[test]
fn pk_simple_enum() {
make_test("pk-simple-enum").run();
}
#[test]
fn indexed_simple_enum() {
make_test("indexed-simple-enum").run();
}
#[test]
fn overlapping_subscriptions() {
make_test("overlapping-subscriptions").run();
}
#[test]
fn sorted_uuids_insert() {
make_test("sorted-uuids-insert").run();
}
}
};
}
declare_tests_with_suffix!(rust, "");
declare_tests_with_suffix!(typescript, "-ts");
// TODO: migrate csharp to snake_case table names
declare_tests_with_suffix!(csharp, "-cs");
macro_rules! procedure_tests {
($mod_name:ident, $suffix:literal) => {
mod $mod_name {
//! Tests of procedure functionality, using <./procedure_client> and <../../../modules/sdk-test-procedure>.
//!
//! These are separate from the existing client and module because as of writing (pgoldman 2025-10-30),
//! we do not have procedure support in all of the module languages we have tested.
use spacetimedb_testing::sdk::Test;
const MODULE: &str = concat!("sdk-test-procedure", $suffix);
const CLIENT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/procedure-client");
fn make_test(subcommand: &str) -> Test {
Test::builder()
.with_name(subcommand)
.with_module(MODULE)
.with_client(CLIENT)
.with_language("rust")
.with_bindings_dir("src/module_bindings")
.with_compile_command("cargo build")
.with_run_command(format!("cargo run -- {}", subcommand))
.build()
}
#[test]
fn return_values() {
make_test("procedure-return-values").run()
}
#[test]
fn observe_panic() {
make_test("procedure-observe-panic").run()
}
#[test]
fn with_tx_commit() {
make_test("insert-with-tx-commit").run()
}
#[test]
fn with_tx_rollback() {
make_test("insert-with-tx-rollback").run()
}
#[test]
fn http_ok() {
make_test("procedure-http-ok").run()
}
#[test]
fn http_err() {
make_test("procedure-http-err").run()
}
#[test]
fn schedule_procedure() {
make_test("schedule-procedure").run()
}
}
};
}
procedure_tests!(rust_procedures, "");
procedure_tests!(typescript_procedures, "-ts");
mod view {
use spacetimedb_testing::sdk::Test;
const MODULE: &str = "sdk-test-view";
const CLIENT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/view-client");
fn make_test(subcommand: &str) -> Test {
Test::builder()
.with_name(subcommand)
.with_module(MODULE)
.with_client(CLIENT)
.with_language("rust")
.with_bindings_dir("src/module_bindings")
.with_compile_command("cargo build")
.with_run_command(format!("cargo run -- {}", subcommand))
.build()
}
#[test]
fn subscribe_anonymous_view() {
make_test("view-anonymous-subscribe").run()
}
#[test]
fn subscribe_non_anonymous_view() {
make_test("view-non-anonymous-subscribe").run()
}
#[test]
fn subscribe_view_non_table_return() {
make_test("view-non-table-return").run()
}
#[test]
fn subscription_updates_for_view() {
make_test("view-subscription-update").run()
}
}