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
84 changes: 84 additions & 0 deletions .github/durable-test-seconds.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"account_ownership": 96,
"auction_buyout": 69,
"auction_cancel": 101,
"auction_expiry": 5,
"aura_tick": 7,
"away_and_whisper": 17,
"binding_quests": 16,
"cast_global_cooldown": 28,
"chat_channel_moderation": 46,
"chat_channels": 28,
"corpse_loot_residue": 6,
"creature_advance_persist": 14,
"creature_death_stop": 7,
"creature_path_renewal": 7,
"deadmines_package": 0,
"diminishing_returns": 4,
"distract": 43,
"encounter_move": 25,
"eventai_quest_credit": 4,
"eventai_revision": 8,
"eventai_runtime_boundaries": 15,
"fixture_publish": 13,
"friends": 8,
"go_collision": 7,
"goober_lock": 5,
"guild_chat": 24,
"guild_cleanup": 11,
"guild_core": 4,
"guild_fee": 17,
"guild_membership": 13,
"guild_petition": 9,
"guild_recruitment": 6,
"guild_settings": 17,
"hunter_pet": 6,
"instance_party": 6,
"instance_removal": 29,
"inventory_protocol": 12,
"item_guids": 83,
"lethal_damage_floor": 0,
"loot_roll_promotion": 24,
"loot_tag": 6,
"mail_delivery": 12,
"mail_expiry": 18,
"mail_letter_copy": 60,
"meeting_stone_matching": 123,
"meeting_stone_queue": 27,
"mount": 17,
"operator_gates": 17,
"package_account": 10,
"package_api_lint": 0,
"package_config": 21,
"package_delta_references": 32,
"package_runtime_scripts": 26,
"package_teardown": 11,
"pending_cast_replacement": 4,
"proximity_emotes": 10,
"quest_choice_reward": 7,
"quest_transfer": 9,
"queued_strike_fizzle": 8,
"raid_broadcasts": 33,
"raid_chat": 28,
"raid_convert": 40,
"raid_leadership": 38,
"raid_quest_credit": 4,
"raid_subgroups": 16,
"random_properties": 64,
"random_property_stats": 23,
"realm_chat": 10,
"realm_transfer_locator": 21,
"region_assignment": 4,
"reward_letter": 16,
"scripted_spell_effect": 21,
"self_resurrection": 17,
"session_expiry": 10,
"sessionless_consent": 57,
"teleport_motion": 4,
"transfer_fence": 41,
"typed_taxi_contracts": 8,
"watched_faction": 20,
"weather": 11,
"xp_cap": 7,
"zone_transition": 4
}
39 changes: 21 additions & 18 deletions .github/workflows/module-durable.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
name: Module durable tests and Gateway bindings

# The durable integration rung: `module/tests/`, marked `#[ignore]` because each target
# starts its own SpacetimeDB standalone, publishes the module to it, and drives real reducers.
# `rust.yml`'s `--lib` run cannot reach any of it.
#
# There is no shared node to start here: `lyracore-test-support` starts one standalone per
# test on a free loopback port with `--in-memory`, publishes with `--features=debug_reducers` (the
# feature set `lyracore publish` bakes in), and kills it on drop. This job installs the pinned CLI
# and gets out of the way.
# Durable tests start private SpacetimeDB instances and publish the Module with test fixtures.
# The Rust workflow's --lib run does not execute these ignored integration tests.

on:
pull_request:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

Expand All @@ -24,9 +22,7 @@ env:
SPACETIME_ARCHIVE_SHA256: 9b58192135733083479f30aba47dbbbc79b51db368c46cc221baa663e9c3a2c1

jobs:
# The suite is mostly wall-clock waiting on standalones, so three shards run in parallel. Each
# shard takes every third test target by position in the sorted file list, so a new target lands
# in a shard without a workflow edit. The binding checks and the Gateway regressions run once.
# Balance measured target durations, reserving time for each job's builds and Gateway checks.
durable:
runs-on: ubuntu-latest
timeout-minutes: 30
Expand All @@ -42,6 +38,7 @@ jobs:

# Each test uses its own temporary CLI configuration.
- name: Install SpacetimeDB ${{ env.SPACETIME_VERSION }}
timeout-minutes: 3
run: |
archive_path="$RUNNER_TEMP/spacetime.tar.gz"
cli_dir="$RUNNER_TEMP/spacetime-$SPACETIME_VERSION"
Expand All @@ -61,9 +58,9 @@ jobs:
echo "spacetime $found"
[ "$found" = "$SPACETIME_VERSION" ]

- name: Test the Gateway binding check
- name: Test the CI scripts
if: matrix.shard == 0
run: python3 -m unittest scripts/tests/test_check_gateway_bindings.py
run: python3 -m unittest discover -s scripts/tests

# This regenerates the complete private schema into a temporary directory. It does not need a
# running Realm and never publishes the Module.
Expand All @@ -74,7 +71,7 @@ jobs:
# Build failures report before any fixture starts. Each test process asks Cargo for the
# current artifact, caches its bytes, and publishes private copies through --bin-path.
- name: Build the module wasm
run: cargo build --release --target wasm32-unknown-unknown -p lyracore-module --features=debug_reducers
run: cargo build --release --target wasm32-unknown-unknown -p lyracore-module --features=debug_reducers,package_test_fixture

# Each test owns a whole database process, so the thread cap is a memory bound, not a CPU one.
#
Expand All @@ -83,13 +80,19 @@ jobs:
# is flaky on `main` with or without this change.
- name: Run the durable suite, shard ${{ matrix.shard }} of 3
run: |
targets=$(ls module/tests/*.rs | xargs -n1 basename -s .rs | sort | awk -v shard="${{ matrix.shard }}" 'NR % 3 == shard { printf "--test %s ", $0 }')
echo "targets: $targets"
cargo test -p lyracore-module $targets -- --ignored --test-threads=4 --skip deadmines --skip lethal_floor
mapfile -t targets < <(python3 scripts/durable-test-targets.py "${{ matrix.shard }}")
[ "${#targets[@]}" -gt 0 ]
args=()
for target in "${targets[@]}"; do args+=(--test "$target"); done
echo "targets: ${targets[*]}"
cargo test -p lyracore-module "${args[@]}" -- --ignored --test-threads=4 --skip deadmines --skip lethal_floor

- name: Run the single-shard auction Refusal regression
if: matrix.shard == 0
run: cargo test -p lyracore-gateway --bin lyracore-gateway stdb::reducers::auction_reducer_tests::a_local_bid_interaction_refusal_stays_a_client_outcome -- --ignored --exact
run: |
target_test=stdb::store::auction::auction_reducer_tests::a_local_bid_interaction_refusal_stays_a_client_outcome
cargo test -p lyracore-gateway --bin lyracore-gateway "$target_test" -- --ignored --exact --list | grep -Fx "$target_test: test"
cargo test -p lyracore-gateway --bin lyracore-gateway "$target_test" -- --ignored --exact

- name: Run the Gateway ownership and durable projection regressions
if: matrix.shard == 0
Expand Down
6 changes: 6 additions & 0 deletions .github/workflows/runtime-scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@ on:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 5
defaults:
run:
working-directory: datascripts
Expand All @@ -20,5 +25,6 @@ jobs:
with:
bun-version: 1.3.7
- run: bun install --frozen-lockfile
timeout-minutes: 2
- name: Test Datascripts and compile Runtime Scripts
run: bun test
10 changes: 10 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ on:
push:
branches: [main]

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

permissions:
contents: read

jobs:
fmt:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Check formatting
Expand All @@ -36,6 +41,7 @@ jobs:

clippy:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
Expand All @@ -44,6 +50,7 @@ jobs:

test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
Expand All @@ -58,6 +65,7 @@ jobs:

wasm:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
Expand All @@ -67,10 +75,12 @@ jobs:
# A Core change that breaks an official Package fails here, before the Package's next publish.
official-packages:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# A paired packages PR uses the same branch name; build against it when it exists.
- id: packages-ref
timeout-minutes: 2
env:
BRANCH: ${{ github.head_ref || github.ref_name }}
run: |
Expand Down
24 changes: 19 additions & 5 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,20 +46,34 @@ fails before the Package's next publish. To run the same check, link each Packag

From `.github/workflows/module-durable.yml`. Each test in `module/tests/` is `#[ignore]`d because it
starts its own SpacetimeDB standalone on a free loopback port, publishes the Module with
`--features=debug_reducers`, calls reducers and reads the result. The `lyracore-test-support` crate
owns that fixture. Standalone logs go to `lyracore-standalone-logs` under the system temp directory.
`--features=debug_reducers,package_test_fixture`, calls reducers and reads the result. The
`lyracore-test-support` crate owns that fixture. Standalone logs go to `lyracore-standalone-logs`
under the system temp directory.

Build the Module first, so a build failure shows before any fixture starts. Then run one target:

```bash
cargo build --release --target wasm32-unknown-unknown -p lyracore-module --features=debug_reducers
cargo build --release --target wasm32-unknown-unknown -p lyracore-module --features=debug_reducers,package_test_fixture
cargo test -p lyracore-module --test weather -- --ignored
```

CI runs every target in three parallel shards with `--test-threads=4 --skip deadmines --skip
CI runs every target in three parallel jobs with `--test-threads=4 --skip deadmines --skip
lethal_floor`. The Deadmines targets need the separately installed dungeons Package, and the
`lethal_floor` ranged phase is flaky.

`scripts/durable-test-targets.py` assigns targets by the durations in
`.github/durable-test-seconds.json`. It includes every current target once and estimates 20 seconds
for a new target. `JOB_SECONDS` reserves time for each job's builds, setup and Gateway checks.
To refresh the estimates, use a successful main run: record each Module target's reported test
duration, rounded up to whole seconds, in the JSON file. Set each job's `JOB_SECONDS` entry to its
total duration minus those target durations. Keep the run reference in the script current.
Run `python3 -m unittest discover -s scripts/tests` to check the assignment.

The Session expiry test checks the five-minute production interval before it shortens the schedule
on its private fixture. It then checks two real scheduled cleanups without a five-minute wait.
The Operator-only `debug_accelerate_session_reaper` control is for disposable test instances;
its shorter interval persists if used on an existing Shard.

CI also runs Gateway tests that need a node. The workflow names individual tests with
`--ignored --exact` and runs the party command test group with:

Expand All @@ -74,7 +88,7 @@ directory and compares it with `gateway/src/stdb/bindings`. It needs the Spaceti
publishes nothing.

```bash
python3 -m unittest scripts/tests/test_check_gateway_bindings.py
python3 -m unittest discover -s scripts/tests
scripts/check-gateway-bindings.py
```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.

#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};

#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub(super) struct DebugAccelerateSessionReaperArgs {}

impl From<DebugAccelerateSessionReaperArgs> for super::Reducer {
fn from(args: DebugAccelerateSessionReaperArgs) -> Self {
Self::DebugAccelerateSessionReaper
}
}

impl __sdk::InModule for DebugAccelerateSessionReaperArgs {
type Module = super::RemoteModule;
}

#[allow(non_camel_case_types)]
/// Extension trait for access to the reducer `debug_accelerate_session_reaper`.
///
/// Implemented for [`super::RemoteReducers`].
pub trait debug_accelerate_session_reaper {
/// Request that the remote module invoke the reducer `debug_accelerate_session_reaper` to run as soon as possible.
///
/// This method returns immediately, and errors only if we are unable to send the request.
/// The reducer will run asynchronously in the future,
/// and this method provides no way to listen for its completion status.
/// /// Use [`debug_accelerate_session_reaper:debug_accelerate_session_reaper_then`] to run a callback after the reducer completes.
fn debug_accelerate_session_reaper(&self) -> __sdk::Result<()> {
self.debug_accelerate_session_reaper_then(|_, _| {})
}

/// Request that the remote module invoke the reducer `debug_accelerate_session_reaper` to run as soon as possible,
/// registering `callback` to run when we are notified that the reducer completed.
///
/// This method returns immediately, and errors only if we are unable to send the request.
/// The reducer will run asynchronously in the future,
/// and its status can be observed with the `callback`.
fn debug_accelerate_session_reaper_then(
&self,

callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
+ Send
+ 'static,
) -> __sdk::Result<()>;
}

impl debug_accelerate_session_reaper for super::RemoteReducers {
fn debug_accelerate_session_reaper_then(
&self,

callback: impl FnOnce(&super::ReducerEventContext, Result<Result<(), String>, __sdk::InternalError>)
+ Send
+ 'static,
) -> __sdk::Result<()> {
self.imp
.invoke_reducer_with_callback(DebugAccelerateSessionReaperArgs {}, callback)
}
}
Loading
Loading