From f798f3f5e8992caa931688207a9704e2c4d57642 Mon Sep 17 00:00:00 2001 From: Joe Date: Wed, 11 Feb 2026 17:56:37 -0600 Subject: [PATCH] Enhance connected mode test suite and introduce VFS staleness fixtures - Updated the connected mode test expansion plan, marking all todos as completed, including multi-step sequence tests and various phase implementations. - Added a new plan for VFS staleness test fixtures, outlining the creation of a validation API, test helpers, and multiple staleness tests to improve detection of VFS event issues. - Introduced new rules for the Atlas API and project file format specifications, enhancing the overall structure and documentation for better clarity and maintainability. This update significantly improves testing coverage and addresses potential VFS synchronization issues. --- ...ected_mode_test_expansion_5b0c39a3.plan.md | 32 +- ...s_staleness_test_fixtures_dcde048f.plan.md | 275 +++ .cursor/rules/{rojo-api.mdc => atlas-api.mdc} | 23 +- .../{rojo-project.mdc => atlas-project.mdc} | 6 +- .../{rojo-syncback.mdc => atlas-syncback.mdc} | 35 +- .cursor/rules/{rojo.mdc => atlas.mdc} | 43 +- ...__connected_mode__connected_add_seq_1.snap | 23 + ...__connected_mode__connected_add_seq_2.snap | 23 + ...__connected_mode__connected_add_seq_3.snap | 23 + ...ted_mode__connected_api_then_fs_patch.snap | 19 + ...ode__connected_clean_slug_clean_step1.snap | 18 + ...ode__connected_clean_slug_clean_step2.snap | 18 + ...ode__connected_clean_slug_clean_step3.snap | 13 + ...d_mode__connected_collision_evo_step1.snap | 23 + ...d_mode__connected_collision_evo_step2.snap | 13 + ...d_mode__connected_collision_evo_step3.snap | 23 + ...ed_mode__connected_cursor_final_patch.snap | 19 + ...mode__connected_dedup_add_third_patch.snap | 23 + ...cted_mode__connected_dedup_edit_patch.snap | 19 + ...e__connected_dedup_no_meta_stem_patch.snap | 23 + ...ode__connected_dedup_remove_one_patch.snap | 13 + ..._connected_delete_init_from_dir_final.snap | 148 ++ ..._connected_delete_init_from_dir_patch.snap | 20 + ..._connected_edit_init_then_child_child.snap | 19 + ...__connected_edit_init_then_child_init.snap | 19 + ...connected_init_create_in_folder_final.snap | 152 ++ ...connected_init_create_in_folder_patch.snap | 20 + ...ted_mode__connected_init_cycle_patch1.snap | 20 + ...ted_mode__connected_init_cycle_patch2.snap | 19 + ...ted_mode__connected_init_cycle_patch3.snap | 18 + ...__connected_init_delete_survive_final.snap | 150 ++ ...__connected_init_delete_survive_patch.snap | 19 + ...e__connected_init_meta_creation_patch.snap | 19 + ...cted_init_type_module_to_server_final.snap | 154 ++ ...ed_init_type_module_to_server_initial.snap | 152 ++ ...cted_init_type_module_to_server_patch.snap | 20 + ...ected_init_type_server_to_local_final.snap | 150 ++ ...ted_init_type_server_to_local_initial.snap | 152 ++ ...ected_init_type_server_to_local_patch.snap | 19 + ...onnected_meta_add_name_to_clean_patch.snap | 18 + ...ode__connected_meta_update_name_patch.snap | 18 + ...ed_mode__connected_model_create_patch.snap | 21 + ...ed_mode__connected_model_delete_patch.snap | 13 + ...ode__connected_model_edit_props_patch.snap | 21 + ...mode__connected_model_name_edit_patch.snap | 18 + ...de__connected_model_remove_name_patch.snap | 18 + ...cted_mode__connected_multi_edit_local.snap | 19 + ...ted_mode__connected_multi_edit_server.snap | 19 + ...mode__connected_multi_edit_standalone.snap | 19 + ...__connected_reconnect_cursor0_catchup.snap | 29 + ...onnected_mode__connected_remove_seq_1.snap | 13 + ...onnected_mode__connected_remove_seq_2.snap | 13 + ...onnected_mode__connected_remove_seq_3.snap | 13 + ...de__connected_replace_init_type_final.snap | 154 ++ ...de__connected_replace_init_type_patch.snap | 22 + ...ected_mode__connected_sequence_create.snap | 23 + ...ected_mode__connected_sequence_delete.snap | 23 + ...nnected_mode__connected_sequence_edit.snap | 19 + ...ected_mode__connected_sequence_rename.snap | 13 + ...cted_mode__connected_slug_chain_step1.snap | 19 + ...cted_mode__connected_slug_chain_step2.snap | 18 + ...cted_mode__connected_slug_chain_step3.snap | 18 + ...cted_mode__connected_slug_chain_step4.snap | 18 + ...mode__connected_slug_create_new_patch.snap | 23 + ...nnected_slug_delete_meta_revert_patch.snap | 18 + ...ted_mode__connected_slug_delete_patch.snap | 13 + ...__connected_slug_edit_meta_name_patch.snap | 18 + ...ode__connected_slug_edit_server_patch.snap | 19 + ...ode__connected_slug_edit_source_patch.snap | 19 + ...de__connected_slugdir_add_child_patch.snap | 23 + ...de__connected_slugdir_edit_init_patch.snap | 19 + ...nected_slugdir_init_type_change_patch.snap | 20 + .../connected_init/default.project.json5 | 10 + .../serve-tests/connected_init/src/Child.luau | 2 + .../serve-tests/connected_init/src/init.luau | 2 + .../connected_models/default.project.json5 | 10 + .../connected_models/src/DirModel/Child.luau | 2 + .../src/DirModel/init.meta.json5 | 3 + .../src/SimpleModel.model.json5 | 3 + .../connected_scripts/default.project.json5 | 10 + .../src/DirLocal/Helper.luau | 2 + .../src/DirLocal/init.local.luau | 2 + .../src/DirModule/ChildA.luau | 2 + .../src/DirModule/ChildB.luau | 2 + .../connected_scripts/src/DirModule/init.luau | 2 + .../src/DirScript/Handler.luau | 2 + .../src/DirScript/init.server.luau | 2 + .../connected_scripts/src/local.client.luau | 2 + .../connected_scripts/src/server.server.luau | 2 + .../connected_scripts/src/standalone.luau | 2 + .../connected_slugify/default.project.json5 | 10 + .../src/CleanScript.server.luau | 2 + .../connected_slugify/src/Hey_Bro.luau | 2 + .../connected_slugify/src/Hey_Bro~1.luau | 2 + .../src/Hey_Bro~1.meta.json5 | 3 + .../src/Key_Script.meta.json5 | 3 + .../src/Key_Script.server.luau | 2 + .../connected_slugify/src/Normal.luau | 2 + .../src/Slug_Dir/DirChild.luau | 2 + .../connected_slugify/src/Slug_Dir/init.luau | 2 + .../src/Slug_Dir/init.meta.json5 | 3 + .../src/What_Model.model.json5 | 4 + .../connected_slugify/src/What_Module.luau | 2 + .../src/What_Module.meta.json5 | 3 + tests/rojo_test/serve_util.rs | 163 +- tests/tests/connected_mode.rs | 1529 +++++++++++++++++ tests/tests/mod.rs | 1 + 107 files changed, 4484 insertions(+), 41 deletions(-) create mode 100644 .cursor/plans/vfs_staleness_test_fixtures_dcde048f.plan.md rename .cursor/rules/{rojo-api.mdc => atlas-api.mdc} (93%) rename .cursor/rules/{rojo-project.mdc => atlas-project.mdc} (97%) rename .cursor/rules/{rojo-syncback.mdc => atlas-syncback.mdc} (85%) rename .cursor/rules/{rojo.mdc => atlas.mdc} (74%) create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_1.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_2.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_3.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_api_then_fs_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step1.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step2.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step3.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step1.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step2.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step3.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_cursor_final_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_add_third_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_edit_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_no_meta_stem_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_remove_one_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_final.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_child.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_init.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_final.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch1.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch2.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch3.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_final.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_meta_creation_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_final.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_initial.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_final.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_initial.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_add_name_to_clean_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_update_name_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_create_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_delete_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_edit_props_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_name_edit_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_remove_name_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_local.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_server.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_standalone.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_reconnect_cursor0_catchup.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_1.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_2.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_3.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_final.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_create.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_delete.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_edit.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_rename.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step1.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step2.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step3.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step4.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_create_new_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_meta_revert_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_meta_name_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_server_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_source_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_add_child_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_edit_init_patch.snap create mode 100644 rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_init_type_change_patch.snap create mode 100644 rojo-test/serve-tests/connected_init/default.project.json5 create mode 100644 rojo-test/serve-tests/connected_init/src/Child.luau create mode 100644 rojo-test/serve-tests/connected_init/src/init.luau create mode 100644 rojo-test/serve-tests/connected_models/default.project.json5 create mode 100644 rojo-test/serve-tests/connected_models/src/DirModel/Child.luau create mode 100644 rojo-test/serve-tests/connected_models/src/DirModel/init.meta.json5 create mode 100644 rojo-test/serve-tests/connected_models/src/SimpleModel.model.json5 create mode 100644 rojo-test/serve-tests/connected_scripts/default.project.json5 create mode 100644 rojo-test/serve-tests/connected_scripts/src/DirLocal/Helper.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/DirLocal/init.local.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/DirModule/ChildA.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/DirModule/ChildB.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/DirModule/init.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/DirScript/Handler.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/DirScript/init.server.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/local.client.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/server.server.luau create mode 100644 rojo-test/serve-tests/connected_scripts/src/standalone.luau create mode 100644 rojo-test/serve-tests/connected_slugify/default.project.json5 create mode 100644 rojo-test/serve-tests/connected_slugify/src/CleanScript.server.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/Hey_Bro.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.meta.json5 create mode 100644 rojo-test/serve-tests/connected_slugify/src/Key_Script.meta.json5 create mode 100644 rojo-test/serve-tests/connected_slugify/src/Key_Script.server.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/Normal.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/Slug_Dir/DirChild.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.meta.json5 create mode 100644 rojo-test/serve-tests/connected_slugify/src/What_Model.model.json5 create mode 100644 rojo-test/serve-tests/connected_slugify/src/What_Module.luau create mode 100644 rojo-test/serve-tests/connected_slugify/src/What_Module.meta.json5 create mode 100644 tests/tests/connected_mode.rs diff --git a/.cursor/plans/connected_mode_test_expansion_5b0c39a3.plan.md b/.cursor/plans/connected_mode_test_expansion_5b0c39a3.plan.md index 4cbf5dc4..fd517b0c 100644 --- a/.cursor/plans/connected_mode_test_expansion_5b0c39a3.plan.md +++ b/.cursor/plans/connected_mode_test_expansion_5b0c39a3.plan.md @@ -4,52 +4,52 @@ overview: Expand the serve test suite to cover multi-step connected mode scenari todos: - id: fixtures content: "Create 4 new test fixtures in rojo-test/serve-tests/: connected_scripts, connected_init, connected_models, connected_slugify" - status: in_progress + status: completed - id: harness content: "Add harness helpers to tests/rojo_test/serve_util.rs: get_message_cursor for chaining multi-step tests, fresh_rebuild_read for round-trip verification" - status: pending + status: completed - id: phase1-format-transitions content: "Implement Phase 1: Format transition tests (standalone<->directory, init type changes) with round-trip verification -- 8 tests" - status: pending + status: completed - id: phase2-multi-step content: "Implement Phase 2: Multi-step connected sequence tests (create/edit/rename/delete lifecycle) with round-trip verification -- 5 tests" - status: pending + status: completed - id: phase3-init content: "Implement Phase 3: Init file operation tests during connected mode with round-trip verification -- 4 tests" - status: pending + status: completed - id: phase4-meta content: "Implement Phase 4: Property/meta file change tests -- 4 tests" - status: pending + status: completed - id: phase5-models content: "Implement Phase 5: Model JSON file change tests -- 5 tests" - status: pending + status: completed - id: phase6-cursor content: "Implement Phase 6: Cursor tracking and reconnection tests -- 3 tests" - status: pending + status: completed - id: phase7-mixed content: "Implement Phase 7: Mixed API + filesystem verification tests -- 2 tests" - status: pending + status: completed - id: phase8-slugify-fixture content: Create connected_slugify fixture with slugified files, dedup ~1 files, clean files, and directory-format slugified instances - status: pending + status: completed - id: phase8-slugify-forward content: "Implement Phase 8a: Slugify forward-sync tests -- editing/creating/deleting slugified files and verifying patches show real names from meta -- 6 tests" - status: pending + status: completed - id: phase8-dedup-forward content: "Implement Phase 8b: Dedup (~N suffix) forward-sync tests -- collision handling, ~1 with and without meta, add/remove colliding files -- 5 tests" - status: pending + status: completed - id: phase8-slugdir content: "Implement Phase 8c: Slugified directory format tests -- init type changes, child adds inside slugified dirs -- 3 tests" - status: pending + status: completed - id: phase8-meta-name-lifecycle content: "Implement Phase 8d: Meta name field lifecycle tests -- create/update/delete meta name, model.json5 name field -- 5 tests" - status: pending + status: completed - id: phase8-slug-multistep content: "Implement Phase 8e: Multi-step slugify scenarios with round-trip verification -- slug rename chains, collision evolution -- 3 tests" - status: pending + status: completed - id: run-and-review content: Run cargo test, cargo insta review, accept snapshots, verify clean pass - status: pending + status: completed isProject: false --- diff --git a/.cursor/plans/vfs_staleness_test_fixtures_dcde048f.plan.md b/.cursor/plans/vfs_staleness_test_fixtures_dcde048f.plan.md new file mode 100644 index 00000000..750fb2a1 --- /dev/null +++ b/.cursor/plans/vfs_staleness_test_fixtures_dcde048f.plan.md @@ -0,0 +1,275 @@ +--- +name: VFS staleness test fixtures +overview: Create test fixtures that replicate VFS tree staleness, add a tree-freshness assertion helper, tighten lenient existing tests, and expose a test-only API for tree validation -- all to surface the root causes of missed VFS events so they can be fixed properly. +todos: + - id: api-endpoint + content: Add GET /api/validate-tree endpoint that re-snapshots and returns drift counts without correcting the tree + status: pending + - id: session-method + content: Extract check_tree_freshness() in ServeSession (no age gate, no apply, returns counts) and set VALIDATE_TREE_ON_CONNECT = false + status: pending + - id: test-helper + content: Add assert_tree_fresh() helper and ValidateTreeResponse type to tests/rojo_test/serve_util.rs + status: pending + - id: fixture + content: Create rojo-test/serve-tests/stale_tree/ fixture with project file and multiple scripts + status: pending + - id: staleness-tests + content: "Write 4 new staleness tests: bulk changes, post-API external edit, rapid delete/recreate, directory restructure" + status: pending + - id: append-freshness + content: Append assert_tree_fresh() to all watcher_* and echo_suppression_* tests in two_way_sync.rs + status: pending + - id: tighten-echo + content: Tighten echo suppression cursor_delta assertion from < 10 to <= 3 + status: pending + - id: chaos-fuzzer + content: Write chaos fuzzer test that slams the filesystem with random operations for 10 seconds, then asserts tree freshness + status: pending +isProject: false +--- + +# VFS Staleness Test Fixtures and Test Tightening + +## Context + +The `validate_tree` bandaid is being disabled. We need test fixtures that REPRODUCE VFS staleness so we can fix the underlying issues. We also need to tighten existing tests that are too lenient per the quality standard: "Any deviation is a bug." + +## Leniency Issues Found in Existing Tests + +### 1. Echo suppression uses `cursor_delta < 10` (critical) + +In `[tests/tests/two_way_sync.rs](tests/tests/two_way_sync.rs)` line 725: + +```rust +assert!(cursor_delta < 10, ...); +``` + +Allowing up to 9 extra messages is absurdly lenient. For a single add operation, the expected delta is 0-2 (the suppressed echo plus maybe one metadata update). This means echo suppression could be 80% broken and tests would still pass. + +### 2. No tree-vs-filesystem validation (critical) + +No test ever asserts that the in-memory `RojoTree` matches what a fresh snapshot from disk would produce. Tests only check individual instances/properties. A watcher test could pass while the tree has phantom instances, missing children, or stale properties on unrelated instances. + +### 3. `send_update` uses fixed sleep instead of event verification + +In `[tests/tests/two_way_sync.rs](tests/tests/two_way_sync.rs)` line 107: + +```rust +thread::sleep(Duration::from_millis(300)); +``` + +This is a guess, not a guarantee. If the ChangeProcessor is slow (CI load), the tree may not be updated yet. If the operation is fast, we're wasting 300ms per test. + +### 4. No completeness checks after operations + +Watcher tests (e.g., `watcher_multi_file_simultaneous_edits`) verify that expected changes arrived but never verify that ONLY expected changes arrived and that no unrelated instances were corrupted. + +## Plan + +### Step 1: Add `GET /api/validate-tree` test-only endpoint + +Add a new API endpoint to `[src/web/api.rs](src/web/api.rs)` that calls `validate_tree` logic WITHOUT the 5-second age gate (the age gate exists to avoid racing with the VFS in production, but tests need immediate validation). Returns a structured response: + +```rust +#[derive(Serialize)] +struct ValidateTreeResponse { + is_fresh: bool, // true if tree matches filesystem + added: usize, // instances that should exist but don't + removed: usize, // instances in tree but not on disk + updated: usize, // instances with different properties + elapsed_ms: f64, // how long the validation took +} +``` + +Route: `GET /api/validate-tree` in the match block around line 127 of `[src/web/api.rs](src/web/api.rs)`. + +The handler re-snapshots from VFS, diffs, and returns the result WITHOUT applying corrections (read-only check). This way the test sees the drift but the tree isn't silently fixed. + +### Step 2: Add `assert_tree_fresh()` helper to test infrastructure + +In `[tests/rojo_test/serve_util.rs](tests/rojo_test/serve_util.rs)`, add: + +```rust +impl TestServeSession { + /// Assert the in-memory tree exactly matches the filesystem. + /// Panics with a descriptive message if there is any drift. + pub fn assert_tree_fresh(&self) { + let url = format!("http://localhost:{}/api/validate-tree", self.port); + let body = reqwest::blocking::get(url).unwrap().bytes().unwrap(); + let response: ValidateTreeResponse = deserialize_msgpack(&body).unwrap(); + + assert!( + response.is_fresh, + "Tree does not match filesystem: {} added, {} removed, {} updated \ + (validated in {:.1}ms)", + response.added, response.removed, response.updated, response.elapsed_ms + ); + } +} +``` + +### Step 3: Create VFS staleness test fixture and tests + +New fixture: `rojo-test/serve-tests/stale_tree/` with a simple project containing multiple scripts under `src/` (enough files to stress the watcher). + +**Test A: `stale_tree_bulk_filesystem_changes**` +Simulates a git checkout -- change 20+ files simultaneously. Verifies every single change is reflected in the tree: + +1. Start serve, wait for stability (1s) +2. Write 20 files simultaneously in a loop (no sleep between writes) +3. Wait for tree to settle (poll a canary file) +4. Call `assert_tree_fresh()` -- MUST pass + +**Test B: `stale_tree_after_api_write_then_external_edit**` +Tests that echo suppression does NOT eat external edits: + +1. Start serve +2. API write creates a file (triggers suppression) +3. Immediately overwrite the same file from the filesystem with different content +4. Wait for tree to settle +5. Verify tree has the EXTERNAL content (not the API-written content) +6. Call `assert_tree_fresh()` + +**Test C: `stale_tree_rapid_delete_recreate_different_names**` +Tests the debouncer coalescing edge case: + +1. Start serve with 5 files +2. Delete all 5, immediately create 5 new files with different names +3. Wait for tree to settle +4. Verify old names are gone, new names exist +5. Call `assert_tree_fresh()` + +**Test D: `stale_tree_directory_restructure**` +Tests bulk restructuring (the git checkout pattern): + +1. Start serve with a directory structure: `src/a.luau`, `src/b.luau`, `src/sub/c.luau` +2. Delete `src/sub/`, rename `src/a.luau` to `src/x.luau`, change content of `src/b.luau` +3. Wait for tree to settle +4. `assert_tree_fresh()` + +### Step 4: Chaos fuzzer -- `fuzz_filesystem_chaos` + +A single long-running test that slams the filesystem with random operations for 10 seconds straight, then waits for the tree to settle and asserts freshness. The goal is to reproduce the exact conditions that cause VFS drift. If this test ever fails, the failure message tells us exactly what the tree got wrong. + +**Operations the fuzzer randomly picks from each iteration:** + +- Create a new `.luau` file with random content +- Delete an existing file +- Overwrite an existing file with new content +- Rename a file to a new name +- Create a subdirectory and add a file inside it +- Delete a subdirectory (recursive) +- Rename a directory +- Rapid delete+recreate of the same file (the known edge case) +- Write to a file, then immediately overwrite it again (double-write) + +**Structure:** + +```rust +#[test] +fn fuzz_filesystem_chaos() { + run_serve_test("stale_tree", |session, _redactions| { + let src = session.path().join("src"); + let mut rng = rand::thread_rng(); + + let start = Instant::now(); + let duration = Duration::from_secs(10); + let mut op_count = 0u64; + + // Track what files/dirs we've created so we can pick valid targets + let mut known_files: Vec = vec![/* initial fixture files */]; + let mut known_dirs: Vec = vec![src.clone()]; + + while start.elapsed() < duration { + match rng.gen_range(0..9) { + 0 => { /* create new file */ } + 1 => { /* delete random existing file */ } + 2 => { /* overwrite random existing file */ } + 3 => { /* rename random file */ } + 4 => { /* create subdirectory + file */ } + 5 => { /* delete random subdirectory */ } + 6 => { /* rename random directory */ } + 7 => { /* rapid delete+recreate same file */ } + 8 => { /* double-write same file */ } + _ => unreachable!(), + } + op_count += 1; + + // Tiny random delay between ops (0-10ms) to vary timing + thread::sleep(Duration::from_millis(rng.gen_range(0..10))); + } + + eprintln!("Chaos fuzzer completed {} operations in {:?}", op_count, start.elapsed()); + + // Wait for VFS watcher + ChangeProcessor to fully settle + // Use a generous timeout since the watcher may be processing a backlog + thread::sleep(Duration::from_secs(3)); + + // THE CRITICAL ASSERTION: tree must match filesystem after all that chaos + session.assert_tree_fresh(); + }); +} +``` + +**Key design decisions:** + +- Uses `rand` crate (add as a dev-dependency) +- Tracks its own inventory of created files/dirs so it picks valid targets for delete/rename/overwrite +- Handles errors gracefully (file might already be deleted by a previous op) -- just skips and continues +- Tiny random delay (0-10ms) between ops to vary the timing pressure on the debouncer +- 3-second cooldown after the chaos period to let the watcher fully drain +- Single `assert_tree_fresh()` at the end -- if it fails, the drift counts tell us exactly what went wrong +- Logs total operation count for debugging + +**Why this catches the bug:** The original issue was a developer making filesystem changes and then finding the tree stale on reconnect. The fuzzer recreates this by doing hundreds of rapid operations with varied timing, which is exactly the kind of workload that overwhelms the notify debouncer and triggers `RescanRequired` events. If the tree stays fresh after 10 seconds of abuse, the watcher is solid. If it drifts, we have a reproduction. + +### Step 5: Add `assert_tree_fresh()` to existing watcher tests (unchanged from above) + +Append `assert_tree_fresh()` at the end of every `watcher_*` test in `[tests/tests/two_way_sync.rs](tests/tests/two_way_sync.rs)`. This is the key force multiplier -- every existing watcher test becomes a staleness detector. If any of the ~25 watcher tests produce tree drift that the VFS watcher didn't resolve, it will now fail. + +The following tests get the assertion appended (after their existing verifications, before the closure ends): + +- All `watcher_*` tests (25+ tests) +- All `echo_suppression_*` tests (3 tests) +- `delete_and_recreate_via_filesystem_recovery` + +### Step 6: Tighten echo suppression assertion + +In `[tests/tests/two_way_sync.rs](tests/tests/two_way_sync.rs)`, replace the lenient `cursor_delta < 10` with a tighter bound. For a single add with proper suppression, the expected delta is 0-2. Use: + +```rust +assert!( + cursor_delta <= 3, + "Echo suppression failure: cursor advanced by {} (expected <= 3). \ + VFS echo events are leaking through suppression.", + cursor_delta +); +``` + +### Step 7: Disable `VALIDATE_TREE_ON_CONNECT` + +In `[src/serve_session.rs](src/serve_session.rs)`, set the constant to `false`: + +```rust +const VALIDATE_TREE_ON_CONNECT: bool = false; +``` + +This removes the bandaid so the real tests surface real failures. + +## Expected outcomes + +- If VFS watcher events are reliably delivered: all tests pass with no drift detected +- If VFS events are missed: `assert_tree_fresh()` fails with exact counts of added/removed/updated drift, giving us a precise reproduction to debug +- The echo suppression tightening will reveal if suppression is leaky +- The bulk-change tests will reveal if the notify debouncer's `RescanRequired` causes tree drift + +## Files to modify + +- `[src/web/api.rs](src/web/api.rs)` -- new `GET /api/validate-tree` endpoint +- `[src/serve_session.rs](src/serve_session.rs)` -- extract snapshot+diff logic into a reusable `check_tree_freshness()` method, set constant to false +- `[tests/rojo_test/serve_util.rs](tests/rojo_test/serve_util.rs)` -- `assert_tree_fresh()` helper and response type +- `[tests/tests/two_way_sync.rs](tests/tests/two_way_sync.rs)` -- append `assert_tree_fresh()` to watcher tests, tighten echo suppression, add chaos fuzzer +- `rojo-test/serve-tests/stale_tree/` -- new test fixture (project file + src scripts) +- `Cargo.toml` -- add `rand` as a dev-dependency for the fuzzer + diff --git a/.cursor/rules/rojo-api.mdc b/.cursor/rules/atlas-api.mdc similarity index 93% rename from .cursor/rules/rojo-api.mdc rename to .cursor/rules/atlas-api.mdc index 90a57439..3e079687 100644 --- a/.cursor/rules/rojo-api.mdc +++ b/.cursor/rules/atlas-api.mdc @@ -8,7 +8,26 @@ globs: - "plugin/src/ApiContext.lua" --- -# Rojo API & Communication +# Atlas API & Communication + +> Atlas is a fork of Rojo. Many internal identifiers, API endpoints, and struct names still reference the original "Rojo" naming. + +## Quality Standard + +This is a production-grade synchronization system. The filesystem must represent a 1:1 copy of the Studio place. The core invariant: + +**Round-trip identity:** Syncback (or two-way sync) writes a directory tree. Building an rbxl from that directory tree and forward-syncing it back must produce a bit-identical instance tree -- same names, same classes, same properties, same hierarchy, same ref targets. Any deviation is a bug. + +Every audit item must be evaluated against this standard. A "works most of the time" finding is not acceptable. If there is ANY code path where an instance name, property, or hierarchy relationship can be lost, mangled, or silently altered through a syncback→rebuild cycle, flag it as critical. + +## Code Quality Standard + +We are building a maintainable codebase. During audits, watch for duplicated logic -- the same slugify/dedup/meta-update pattern copy-pasted across `change_processor.rs`, `api.rs`, `dir.rs`, `project.rs`, etc. If you find duplicated code that could be consolidated into a shared helper: + +- **Small refactor** (extracting a helper function, consolidating 2-3 call sites): flag it AND fix it as part of the audit. +- **Major system rewrite** (restructuring data flow, changing function signatures across 10+ call sites, redesigning the snapshot pipeline): flag it clearly in a "Deferred Refactors" section at the bottom of the report. We will evaluate whether it's worth doing after the audit is complete. + +The goal is clean, DRY code that is easy to reason about and hard to get wrong. If the same "compute meta path, check if name needs slugify, upsert/remove name field" sequence appears in 3+ places, that's a refactor candidate. **Protocol Version:** 5 | Server/plugin MAJOR.MINOR must match @@ -635,7 +654,7 @@ if serverInfo.protocolVersion ~= Config.protocolVersion then showError("Protocol ```bash # Server -RUST_LOG=rojo::web=debug,rojo::serve_session=debug rojo serve +RUST_LOG=rojo::web=debug,rojo::serve_session=debug atlas serve ``` ```lua diff --git a/.cursor/rules/rojo-project.mdc b/.cursor/rules/atlas-project.mdc similarity index 97% rename from .cursor/rules/rojo-project.mdc rename to .cursor/rules/atlas-project.mdc index 3c757787..1a530b57 100644 --- a/.cursor/rules/rojo-project.mdc +++ b/.cursor/rules/atlas-project.mdc @@ -9,7 +9,9 @@ globs: - "**/*.model.json" --- -# Rojo Project File Format +# Atlas Project File Format + +> Atlas is a fork of Rojo. Many internal identifiers, API endpoints, and struct names still reference the original "Rojo" naming. ## Project File Schema @@ -21,7 +23,7 @@ globs: |-------|------|----------|-------------| | `name` | string | Yes* | Instance name (*optional if `default.project.json5`) | | `tree` | ProjectNode | Yes | Root instance tree structure | -| `servePort` | number | No | Port for `rojo serve` (default: 34872) | +| `servePort` | number | No | Port for `atlas serve` (default: 34872) | | `serveAddress` | string | No | Address to bind (default: `"127.0.0.1"`) | | `servePlaceIds` | number[] | No | Only allow sync to these place IDs | | `blockedPlaceIds` | number[] | No | Block sync to these place IDs | diff --git a/.cursor/rules/rojo-syncback.mdc b/.cursor/rules/atlas-syncback.mdc similarity index 85% rename from .cursor/rules/rojo-syncback.mdc rename to .cursor/rules/atlas-syncback.mdc index d61125a5..6026014e 100644 --- a/.cursor/rules/rojo-syncback.mdc +++ b/.cursor/rules/atlas-syncback.mdc @@ -5,11 +5,30 @@ globs: - "src/cli/syncback.rs" --- -# Rojo Syncback Architecture +# Atlas Syncback Architecture + +> Atlas is a fork of Rojo. Many internal identifiers, API endpoints, and struct names still reference the original "Rojo" naming. + +## Quality Standard + +This is a production-grade synchronization system. The filesystem must represent a 1:1 copy of the Studio place. The core invariant: + +**Round-trip identity:** Syncback (or two-way sync) writes a directory tree. Building an rbxl from that directory tree and forward-syncing it back must produce a bit-identical instance tree -- same names, same classes, same properties, same hierarchy, same ref targets. Any deviation is a bug. + +Every audit item must be evaluated against this standard. A "works most of the time" finding is not acceptable. If there is ANY code path where an instance name, property, or hierarchy relationship can be lost, mangled, or silently altered through a syncback→rebuild cycle, flag it as critical. + +## Code Quality Standard + +We are building a maintainable codebase. During audits, watch for duplicated logic -- the same slugify/dedup/meta-update pattern copy-pasted across `change_processor.rs`, `api.rs`, `dir.rs`, `project.rs`, etc. If you find duplicated code that could be consolidated into a shared helper: + +- **Small refactor** (extracting a helper function, consolidating 2-3 call sites): flag it AND fix it as part of the audit. +- **Major system rewrite** (restructuring data flow, changing function signatures across 10+ call sites, redesigning the snapshot pipeline): flag it clearly in a "Deferred Refactors" section at the bottom of the report. We will evaluate whether it's worth doing after the audit is complete. + +The goal is clean, DRY code that is easy to reason about and hard to get wrong. If the same "compute meta path, check if name needs slugify, upsert/remove name field" sequence appears in 3+ places, that's a refactor candidate. **Syncback** is the reverse operation of building: it takes a Roblox binary file (rbxl/rbxm) and writes instances back to the filesystem according to project structure. -**Entry point:** `rojo syncback --input ` +**Entry point:** `atlas syncback --input ` ## Core Concept @@ -280,9 +299,9 @@ fs_snapshot.removed_paths() 1. **Collect paths** - Map every Ref → full instance path (e.g., `Workspace.Part1`) 2. **Collect referents** - Find all properties that are Refs, store as (source_path, target_path) -3. **Link referents** - After syncback, use Rojo's `$id` / `Rojo_Target_PropertyName` system to link instances +3. **Link referents** - After syncback, use Atlas's `$id` / `Rojo_Target_PropertyName` system to link instances -**Rojo linking system:** +**Atlas linking system:** ```json { "id": "some-unique-id", @@ -402,7 +421,7 @@ All errors collected and reported at end. 2. Sending file to server 3. Server runs syncback command -**Two-way sync (experimental)** is different - see `rojo-api.mdc`. +**Two-way sync (experimental)** is different - see `atlas-api.mdc`. ## Performance Considerations @@ -416,7 +435,7 @@ All errors collected and reported at end. **Enable verbose logging:** ```bash -RUST_LOG=rojo::syncback=debug rojo syncback project/ --input place.rbxl +RUST_LOG=rojo::syncback=debug atlas syncback project/ --input place.rbxl ``` **Shows:** @@ -428,10 +447,10 @@ RUST_LOG=rojo::syncback=debug rojo syncback project/ --input place.rbxl **Test syncback:** ```bash # Syncback once -rojo syncback project/ --input place.rbxl +atlas syncback project/ --input place.rbxl # Build back to verify roundtrip -rojo build project/ -o rebuilt.rbxl +atlas build project/ -o rebuilt.rbxl # Compare diff place.rbxl rebuilt.rbxl diff --git a/.cursor/rules/rojo.mdc b/.cursor/rules/atlas.mdc similarity index 74% rename from .cursor/rules/rojo.mdc rename to .cursor/rules/atlas.mdc index 5edf5653..bf06bcb5 100644 --- a/.cursor/rules/rojo.mdc +++ b/.cursor/rules/atlas.mdc @@ -1,30 +1,49 @@ --- -description: "Essential Rojo context: architecture, contribution workflow, and code conventions for this project." +description: "Essential Atlas context: architecture, contribution workflow, and code conventions for this project." alwaysApply: true --- -# Rojo Project Guide +# Atlas Project Guide -## What is Rojo? +> Atlas is a fork of Rojo. Many internal identifiers, API endpoints, and struct names still reference the original "Rojo" naming. -Rojo bridges filesystem and Roblox Studio, enabling professional development tools (VS Code, Git) for Roblox. It's designed for power users building complex games, libraries, and plugins. +## Quality Standard + +This is a production-grade synchronization system. The filesystem must represent a 1:1 copy of the Studio place. The core invariant: + +**Round-trip identity:** Syncback (or two-way sync) writes a directory tree. Building an rbxl from that directory tree and forward-syncing it back must produce a bit-identical instance tree -- same names, same classes, same properties, same hierarchy, same ref targets. Any deviation is a bug. + +Every audit item must be evaluated against this standard. A "works most of the time" finding is not acceptable. If there is ANY code path where an instance name, property, or hierarchy relationship can be lost, mangled, or silently altered through a syncback→rebuild cycle, flag it as critical. + +## Code Quality Standard + +We are building a maintainable codebase. During audits, watch for duplicated logic -- the same slugify/dedup/meta-update pattern copy-pasted across `change_processor.rs`, `api.rs`, `dir.rs`, `project.rs`, etc. If you find duplicated code that could be consolidated into a shared helper: + +- **Small refactor** (extracting a helper function, consolidating 2-3 call sites): flag it AND fix it as part of the audit. +- **Major system rewrite** (restructuring data flow, changing function signatures across 10+ call sites, redesigning the snapshot pipeline): flag it clearly in a "Deferred Refactors" section at the bottom of the report. We will evaluate whether it's worth doing after the audit is complete. + +The goal is clean, DRY code that is easy to reason about and hard to get wrong. If the same "compute meta path, check if name needs slugify, upsert/remove name field" sequence appears in 3+ places, that's a refactor candidate. + +## What is Atlas? + +Atlas bridges filesystem and Roblox Studio, enabling professional development tools (VS Code, Git) for Roblox. It's designed for power users building complex games, libraries, and plugins. **Core capabilities:** - Filesystem-based development with live sync to Studio - Build/upload rbxl/rbxm files from command line - Syncback: pull instances from Roblox → filesystem (7.7.0+) -- Two-way sync (plugin-based): live bidirectional sync during `rojo serve` sessions +- Two-way sync (plugin-based): live bidirectional sync during `atlas serve` sessions ### Two-Way Sync Strategy -**Long-term goal:** Plugin-based two-way sync will completely replace the `rojo syncback` CLI command, allowing syncback to be deprecated. +**Long-term goal:** Plugin-based two-way sync will completely replace the `atlas syncback` CLI command, allowing syncback to be deprecated. **Current state:** -- `rojo syncback` (CLI) is the **ground truth** for Roblox → filesystem syncing. It is battle-tested and handles edge cases reliably. -- Plugin-based two-way sync (via `rojo serve` + the Studio plugin) is under active development to reach feature parity and match the resilience of CLI syncback. +- `atlas syncback` (CLI) is the **ground truth** for Roblox → filesystem syncing. It is battle-tested and handles edge cases reliably. +- Plugin-based two-way sync (via `atlas serve` + the Studio plugin) is under active development to reach feature parity and match the resilience of CLI syncback. - The bar for replacing syncback is high: plugin two-way sync must handle all the same scenarios (encoded names, nested projects, property conflicts, large trees, concurrent edits) just as reliably before syncback can be deprecated. -**When working on two-way sync:** Plugin-based sync must produce **exactly the same filesystem output** that `rojo syncback` would give for the same input. Byte-for-byte identical files, identical directory structures, identical naming. If the plugin path produces different results, treat syncback as the correct reference unless there's an explicit, documented reason to diverge. Any divergence is a bug until proven otherwise. +**When working on two-way sync:** Plugin-based sync must produce **exactly the same filesystem output** that `atlas syncback` would give for the same input. Byte-for-byte identical files, identical directory structures, identical naming. If the plugin path produces different results, treat syncback as the correct reference unless there's an explicit, documented reason to diverge. Any divergence is a bug until proven otherwise. ## Architecture Overview @@ -36,7 +55,7 @@ Rojo bridges filesystem and Roblox Studio, enabling professional development too ### The Snapshot System -Rojo's core innovation (`src/snapshot/`): +Atlas's core innovation (`src/snapshot/`): 1. **Snapshot Generation**: Pure function filesystem → instance tree 2. **Diffing**: Generate minimal PatchSet 3. **Patch Application**: Apply changes, return AppliedPatchSet for plugin @@ -105,7 +124,7 @@ bash scripts/watch-build-plugin.sh # macOS/Linux bash scripts/unit-test-plugin.sh ``` -**Install plugin:** `rojo plugin install` +**Install plugin:** `atlas plugin install` ### Testing @@ -138,7 +157,7 @@ Every PR updates `CHANGELOG.md`: ```markdown ## Unreleased -* Made a change to Rojo ([#1234]) +* Made a change to Atlas ([#1234]) [#1234]: https://github.com/UserGeneratedLLC/rojo/pull/1234 ``` diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_1.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_1.snap new file mode 100644 index 00000000..c661f5d7 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_1.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-14: + Children: [] + ClassName: ModuleScript + Id: id-14 + Metadata: + ignoreUnknownInstances: false + Name: AddedOne + Parent: id-3 + Properties: + Source: + "!String": return 1 + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_2.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_2.snap new file mode 100644 index 00000000..11eba345 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_2.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: + id-15: + Children: [] + ClassName: ModuleScript + Id: id-15 + Metadata: + ignoreUnknownInstances: false + Name: AddedTwo + Parent: id-3 + Properties: + Source: + "!String": return 2 + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_3.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_3.snap new file mode 100644 index 00000000..b71b05ce --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_add_seq_3.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: + id-16: + Children: [] + ClassName: ModuleScript + Id: id-16 + Metadata: + ignoreUnknownInstances: false + Name: AddedThree + Parent: id-3 + Properties: + Source: + "!String": return 3 + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_api_then_fs_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_api_then_fs_patch.snap new file mode 100644 index 00000000..0c03201b --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_api_then_fs_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- api write\nreturn {}" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step1.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step1.snap new file mode 100644 index 00000000..e7cb7670 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step1.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: "Nor:mal" + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step2.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step2.snap new file mode 100644 index 00000000..10316350 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step2.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: Normal + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step3.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step3.snap new file mode 100644 index 00000000..29eabe73 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_clean_slug_clean_step3.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: {} + removed: + - id-3 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step1.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step1.snap new file mode 100644 index 00000000..e8f60233 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step1.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: Hey*Bro + Parent: id-3 + Properties: + Source: + "!String": "return 'Hey*Bro'" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step2.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step2.snap new file mode 100644 index 00000000..f0af73e0 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step2.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: + - id-6 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step3.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step3.snap new file mode 100644 index 00000000..23f93c78 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_collision_evo_step3.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: + id-14: + Children: [] + ClassName: ModuleScript + Id: id-14 + Metadata: + ignoreUnknownInstances: false + Name: Hey|Bro + Parent: id-3 + Properties: + Source: + "!String": "return 'Hey|Bro'" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_cursor_final_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_cursor_final_patch.snap new file mode 100644 index 00000000..d79c82a1 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_cursor_final_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "p3.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- v3\nreturn 3" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_add_third_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_add_third_patch.snap new file mode 100644 index 00000000..eb3664bd --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_add_third_patch.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: Hey*Bro + Parent: id-3 + Properties: + Source: + "!String": "-- third collision\nreturn 'Hey*Bro'" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_edit_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_edit_patch.snap new file mode 100644 index 00000000..e6fe26a9 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_edit_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited dedup\nreturn 'edited'" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_no_meta_stem_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_no_meta_stem_patch.snap new file mode 100644 index 00000000..ae6648f2 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_no_meta_stem_patch.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: Foo~1 + Parent: id-3 + Properties: + Source: + "!String": "-- no meta, tilde is literal\nreturn 'Foo~1'" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_remove_one_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_remove_one_patch.snap new file mode 100644 index 00000000..12603d29 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_dedup_remove_one_patch.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: + - id-6 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_final.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_final.snap new file mode 100644 index 00000000..84b56a9e --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_final.snap @@ -0,0 +1,148 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-3 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-4 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-4 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-4 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-10 + ClassName: Folder + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-4 + Properties: {} + id-4: + Children: + - id-5 + - id-7 + - id-3 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-5: + Children: + - id-6 + ClassName: LocalScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-4 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-6: + Children: [] + ClassName: ModuleScript + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-5 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-7: + Children: + - id-8 + - id-9 + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-4 + Properties: + Source: + "!String": "-- Directory ModuleScript\nreturn { dir = true }\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-7 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-9: + Children: [] + ClassName: ModuleScript + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-7 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_patch.snap new file mode 100644 index 00000000..99ada236 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_delete_init_from_dir_patch.snap @@ -0,0 +1,20 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: Folder + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + RunContext: ~ + Source: ~ + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_child.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_child.snap new file mode 100644 index 00000000..7e79dfe6 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_child.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited child A\nreturn 'edited'" + id: id-4 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_init.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_init.snap new file mode 100644 index 00000000..41a11906 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_edit_init_then_child_init.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited init\nreturn { edited = true }" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_final.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_final.snap new file mode 100644 index 00000000..544a501e --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_final.snap @@ -0,0 +1,152 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-9 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-4 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-4 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-4 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-7 + - id-8 + ClassName: ModuleScript + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-4 + Properties: + Source: + "!String": "-- Re-created init\nreturn {}" + id-4: + Children: + - id-5 + - id-3 + - id-9 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-5: + Children: + - id-6 + ClassName: LocalScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-4 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-6: + Children: [] + ClassName: ModuleScript + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-5 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-7: + Children: [] + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-3 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-3 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" + id-9: + Children: + - id-10 + ClassName: Script + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Directory Script\nprint(\"dir server\")\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_patch.snap new file mode 100644 index 00000000..145cd0c9 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_create_in_folder_patch.snap @@ -0,0 +1,20 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ModuleScript + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + Source: + "!String": "-- Re-created init\nreturn {}" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch1.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch1.snap new file mode 100644 index 00000000..7a421232 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch1.snap @@ -0,0 +1,20 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: Script + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + RunContext: + "!Enum": 1 + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch2.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch2.snap new file mode 100644 index 00000000..f0603cc6 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch2.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: LocalScript + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + RunContext: ~ + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch3.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch3.snap new file mode 100644 index 00000000..ce6dc613 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_cycle_patch3.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ModuleScript + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_final.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_final.snap new file mode 100644 index 00000000..71650131 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_final.snap @@ -0,0 +1,150 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-9 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-4 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-4 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-4 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-7 + - id-8 + ClassName: Folder + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-4 + Properties: {} + id-4: + Children: + - id-5 + - id-3 + - id-9 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-5: + Children: + - id-6 + ClassName: LocalScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-4 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-6: + Children: [] + ClassName: ModuleScript + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-5 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-7: + Children: [] + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-3 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-3 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" + id-9: + Children: + - id-10 + ClassName: Script + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Directory Script\nprint(\"dir server\")\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_patch.snap new file mode 100644 index 00000000..9c350e63 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_delete_survive_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: Folder + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + Source: ~ + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_meta_creation_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_meta_creation_patch.snap new file mode 100644 index 00000000..3808d7dc --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_meta_creation_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Disabled: + "!Bool": true + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_final.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_final.snap new file mode 100644 index 00000000..d68e35c9 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_final.snap @@ -0,0 +1,154 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-9 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-3 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-3 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-3 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-4 + - id-6 + - id-9 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-4: + Children: + - id-5 + ClassName: LocalScript + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-3 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-5: + Children: [] + ClassName: ModuleScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-4 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-6: + Children: + - id-7 + - id-8 + ClassName: Script + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Directory ModuleScript\nreturn { dir = true }\n" + id-7: + Children: [] + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-6 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-6 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" + id-9: + Children: + - id-10 + ClassName: Script + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Directory Script\nprint(\"dir server\")\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_initial.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_initial.snap new file mode 100644 index 00000000..149e31cd --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_initial.snap @@ -0,0 +1,152 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-9 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-3 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-3 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-3 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-4 + - id-6 + - id-9 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-4: + Children: + - id-5 + ClassName: LocalScript + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-3 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-5: + Children: [] + ClassName: ModuleScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-4 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-6: + Children: + - id-7 + - id-8 + ClassName: ModuleScript + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-3 + Properties: + Source: + "!String": "-- Directory ModuleScript\nreturn { dir = true }\n" + id-7: + Children: [] + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-6 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-6 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" + id-9: + Children: + - id-10 + ClassName: Script + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Directory Script\nprint(\"dir server\")\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_patch.snap new file mode 100644 index 00000000..607a1656 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_module_to_server_patch.snap @@ -0,0 +1,20 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: Script + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + RunContext: + "!Enum": 1 + id: id-6 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_final.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_final.snap new file mode 100644 index 00000000..26883401 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_final.snap @@ -0,0 +1,150 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-9 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-3 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-3 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-3 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-4 + - id-6 + - id-9 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-4: + Children: + - id-5 + ClassName: LocalScript + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-3 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-5: + Children: [] + ClassName: ModuleScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-4 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-6: + Children: + - id-7 + - id-8 + ClassName: ModuleScript + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-3 + Properties: + Source: + "!String": "-- Directory ModuleScript\nreturn { dir = true }\n" + id-7: + Children: [] + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-6 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-6 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" + id-9: + Children: + - id-10 + ClassName: LocalScript + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-3 + Properties: + Source: + "!String": "-- Directory Script\nprint(\"dir server\")\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_initial.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_initial.snap new file mode 100644 index 00000000..149e31cd --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_initial.snap @@ -0,0 +1,152 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-9 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-3 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-3 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-3 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-4 + - id-6 + - id-9 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-4: + Children: + - id-5 + ClassName: LocalScript + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-3 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-5: + Children: [] + ClassName: ModuleScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-4 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-6: + Children: + - id-7 + - id-8 + ClassName: ModuleScript + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-3 + Properties: + Source: + "!String": "-- Directory ModuleScript\nreturn { dir = true }\n" + id-7: + Children: [] + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-6 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-6 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" + id-9: + Children: + - id-10 + ClassName: Script + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-3 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Directory Script\nprint(\"dir server\")\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_patch.snap new file mode 100644 index 00000000..556f5f46 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_init_type_server_to_local_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: LocalScript + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + RunContext: ~ + id: id-9 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_add_name_to_clean_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_add_name_to_clean_patch.snap new file mode 100644 index 00000000..694f8b4a --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_add_name_to_clean_patch.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: Nor/mal + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_update_name_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_update_name_patch.snap new file mode 100644 index 00000000..11b720eb --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_meta_update_name_patch.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: "What:Module" + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_create_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_create_patch.snap new file mode 100644 index 00000000..236aa4ae --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_create_patch.snap @@ -0,0 +1,21 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-7: + Children: [] + ClassName: Folder + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: NewModel + Parent: id-3 + Properties: {} + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_delete_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_delete_patch.snap new file mode 100644 index 00000000..12603d29 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_delete_patch.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: + - id-6 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_edit_props_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_edit_props_patch.snap new file mode 100644 index 00000000..d513cf08 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_edit_props_patch.snap @@ -0,0 +1,21 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-7: + Children: [] + ClassName: Folder + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: NewChild + Parent: id-6 + Properties: {} + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_name_edit_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_name_edit_patch.snap new file mode 100644 index 00000000..0085afb6 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_name_edit_patch.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: "What:Model" + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_remove_name_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_remove_name_patch.snap new file mode 100644 index 00000000..89acf050 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_model_remove_name_patch.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: What_Model + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_local.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_local.snap new file mode 100644 index 00000000..ba173391 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_local.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited local\nprint('v2')" + id: id-5 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_server.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_server.snap new file mode 100644 index 00000000..0b37cf46 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_server.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited server\nprint('v2')" + id: id-4 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_standalone.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_standalone.snap new file mode 100644 index 00000000..322c31fa --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_multi_edit_standalone.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited standalone\nreturn 1" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_reconnect_cursor0_catchup.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_reconnect_cursor0_catchup.snap new file mode 100644 index 00000000..382dde03 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_reconnect_cursor0_catchup.snap @@ -0,0 +1,29 @@ +--- +source: tests/tests/connected_mode.rs +expression: "catch_up.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- change1\nreturn 1" + id: id-3 + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- change2\nprint('v2')" + id: id-4 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_1.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_1.snap new file mode 100644 index 00000000..983301ed --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_1.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: + - id-13 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_2.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_2.snap new file mode 100644 index 00000000..25e26096 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_2.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: + - id-12 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_3.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_3.snap new file mode 100644 index 00000000..b3af04f6 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_remove_seq_3.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: {} + removed: + - id-11 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_final.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_final.snap new file mode 100644 index 00000000..5c3e29da --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_final.snap @@ -0,0 +1,154 @@ +--- +source: tests/tests/connected_mode.rs +expression: "read_response.intern_and_redact(&mut redactions, root_id)" +--- +instances: + id-10: + Children: [] + ClassName: ModuleScript + Id: id-10 + Metadata: + ignoreUnknownInstances: false + Name: Handler + Parent: id-9 + Properties: + Source: + "!String": "-- Handler module\nreturn {}\n" + id-11: + Children: [] + ClassName: Script + Id: id-11 + Metadata: + ignoreUnknownInstances: false + Name: local + Parent: id-4 + Properties: + RunContext: + "!Enum": 2 + Source: + "!String": "-- Standalone LocalScript\nprint(\"client\")\n" + id-12: + Children: [] + ClassName: Script + Id: id-12 + Metadata: + ignoreUnknownInstances: false + Name: server + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Standalone Script\nprint(\"server\")\n" + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: standalone + Parent: id-4 + Properties: + Source: + "!String": "-- Standalone ModuleScript\nreturn {}\n" + id-2: + Children: + - id-4 + ClassName: DataModel + Id: id-2 + Metadata: + ignoreUnknownInstances: true + Name: connected_scripts + Parent: "00000000000000000000000000000000" + Properties: {} + id-3: + Children: + - id-7 + - id-8 + ClassName: Script + Id: id-3 + Metadata: + ignoreUnknownInstances: false + Name: DirModule + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- now a server script" + id-4: + Children: + - id-5 + - id-3 + - id-9 + - id-11 + - id-12 + - id-13 + ClassName: ReplicatedStorage + Id: id-4 + Metadata: + ignoreUnknownInstances: false + Name: ReplicatedStorage + Parent: id-2 + Properties: {} + id-5: + Children: + - id-6 + ClassName: LocalScript + Id: id-5 + Metadata: + ignoreUnknownInstances: false + Name: DirLocal + Parent: id-4 + Properties: + Source: + "!String": "-- Directory LocalScript\nprint(\"dir local\")\n" + id-6: + Children: [] + ClassName: ModuleScript + Id: id-6 + Metadata: + ignoreUnknownInstances: false + Name: Helper + Parent: id-5 + Properties: + Source: + "!String": "-- Helper module\nreturn {}\n" + id-7: + Children: [] + ClassName: ModuleScript + Id: id-7 + Metadata: + ignoreUnknownInstances: false + Name: ChildA + Parent: id-3 + Properties: + Source: + "!String": "-- Child A\nreturn \"A\"\n" + id-8: + Children: [] + ClassName: ModuleScript + Id: id-8 + Metadata: + ignoreUnknownInstances: false + Name: ChildB + Parent: id-3 + Properties: + Source: + "!String": "-- Child B\nreturn \"B\"\n" + id-9: + Children: + - id-10 + ClassName: Script + Id: id-9 + Metadata: + ignoreUnknownInstances: false + Name: DirScript + Parent: id-4 + Properties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- Directory Script\nprint(\"dir server\")\n" +messageCursor: "[message-cursor]" +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_patch.snap new file mode 100644 index 00000000..9326df44 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_replace_init_type_patch.snap @@ -0,0 +1,22 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: Script + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + RunContext: + "!Enum": 1 + Source: + "!String": "-- now a server script" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_create.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_create.snap new file mode 100644 index 00000000..e6cf5ba6 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_create.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-14: + Children: [] + ClassName: ModuleScript + Id: id-14 + Metadata: + ignoreUnknownInstances: false + Name: NewScript + Parent: id-3 + Properties: + Source: + "!String": "-- new script\nreturn 1" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_delete.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_delete.snap new file mode 100644 index 00000000..6b10289f --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_delete.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 4 + messages: + - added: + id-15: + Children: [] + ClassName: ModuleScript + Id: id-15 + Metadata: + ignoreUnknownInstances: false + Name: Renamed + Parent: id-3 + Properties: + Source: + "!String": "-- edited script\nreturn 2" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_edit.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_edit.snap new file mode 100644 index 00000000..dd31c75d --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_edit.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited script\nreturn 2" + id: id-14 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_rename.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_rename.snap new file mode 100644 index 00000000..c2660214 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_sequence_rename.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: {} + removed: + - id-14 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step1.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step1.snap new file mode 100644 index 00000000..9a3e8e3a --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step1.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- step1 edit\nreturn 'step1'" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step2.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step2.snap new file mode 100644 index 00000000..af494b9c --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step2.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 2 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: What*Module + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step3.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step3.snap new file mode 100644 index 00000000..74708037 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step3.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 3 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: What_Module + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step4.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step4.snap new file mode 100644 index 00000000..6c84fcf7 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_chain_step4.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 4 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: What/Module + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_create_new_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_create_new_patch.snap new file mode 100644 index 00000000..982a7d7e --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_create_new_patch.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: New/Slug + Parent: id-3 + Properties: + Source: + "!String": "-- new slug\nreturn 'new'" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_meta_revert_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_meta_revert_patch.snap new file mode 100644 index 00000000..3d4d7ebf --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_meta_revert_patch.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: What_Module + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_patch.snap new file mode 100644 index 00000000..8e9aa768 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_delete_patch.snap @@ -0,0 +1,13 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: + - id-12 + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_meta_name_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_meta_name_patch.snap new file mode 100644 index 00000000..a7f46ee5 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_meta_name_patch.snap @@ -0,0 +1,18 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: + ignoreUnknownInstances: false + changedName: What*Module + changedProperties: {} + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_server_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_server_patch.snap new file mode 100644 index 00000000..257c567f --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_server_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited key script\nprint('edited')" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_source_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_source_patch.snap new file mode 100644 index 00000000..ff760399 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slug_edit_source_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited slugified module\nreturn 'edited'" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_add_child_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_add_child_patch.snap new file mode 100644 index 00000000..0e0af5cf --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_add_child_patch.snap @@ -0,0 +1,23 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: + id-13: + Children: [] + ClassName: ModuleScript + Id: id-13 + Metadata: + ignoreUnknownInstances: false + Name: NewChild + Parent: id-9 + Properties: + Source: + "!String": "-- new child in slug dir\nreturn 'child'" + removed: [] + updated: [] +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_edit_init_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_edit_init_patch.snap new file mode 100644 index 00000000..a75eaa92 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_edit_init_patch.snap @@ -0,0 +1,19 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: ~ + changedMetadata: ~ + changedName: ~ + changedProperties: + Source: + "!String": "-- edited slug dir init\nreturn 'edited'" + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_init_type_change_patch.snap b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_init_type_change_patch.snap new file mode 100644 index 00000000..95f72c94 --- /dev/null +++ b/rojo-test/serve-test-snapshots/end_to_end__tests__connected_mode__connected_slugdir_init_type_change_patch.snap @@ -0,0 +1,20 @@ +--- +source: tests/tests/connected_mode.rs +expression: "socket_packet.intern_and_redact(&mut redactions, ())" +--- +body: + messageCursor: 1 + messages: + - added: {} + removed: [] + updated: + - changedClassName: Script + changedMetadata: + ignoreUnknownInstances: false + changedName: ~ + changedProperties: + RunContext: + "!Enum": 1 + id: id-3 +packetType: messages +sessionId: id-1 diff --git a/rojo-test/serve-tests/connected_init/default.project.json5 b/rojo-test/serve-tests/connected_init/default.project.json5 new file mode 100644 index 00000000..e96adef3 --- /dev/null +++ b/rojo-test/serve-tests/connected_init/default.project.json5 @@ -0,0 +1,10 @@ +{ + "name": "connected_init", + "tree": { + "$className": "DataModel", + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "$path": "src" + } + } +} diff --git a/rojo-test/serve-tests/connected_init/src/Child.luau b/rojo-test/serve-tests/connected_init/src/Child.luau new file mode 100644 index 00000000..8b623d37 --- /dev/null +++ b/rojo-test/serve-tests/connected_init/src/Child.luau @@ -0,0 +1,2 @@ +-- Child module +return "child" diff --git a/rojo-test/serve-tests/connected_init/src/init.luau b/rojo-test/serve-tests/connected_init/src/init.luau new file mode 100644 index 00000000..c62bb728 --- /dev/null +++ b/rojo-test/serve-tests/connected_init/src/init.luau @@ -0,0 +1,2 @@ +-- Root ModuleScript +return {} diff --git a/rojo-test/serve-tests/connected_models/default.project.json5 b/rojo-test/serve-tests/connected_models/default.project.json5 new file mode 100644 index 00000000..595c25d1 --- /dev/null +++ b/rojo-test/serve-tests/connected_models/default.project.json5 @@ -0,0 +1,10 @@ +{ + "name": "connected_models", + "tree": { + "$className": "DataModel", + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "$path": "src" + } + } +} diff --git a/rojo-test/serve-tests/connected_models/src/DirModel/Child.luau b/rojo-test/serve-tests/connected_models/src/DirModel/Child.luau new file mode 100644 index 00000000..3434d2ea --- /dev/null +++ b/rojo-test/serve-tests/connected_models/src/DirModel/Child.luau @@ -0,0 +1,2 @@ +-- Model child +return {} diff --git a/rojo-test/serve-tests/connected_models/src/DirModel/init.meta.json5 b/rojo-test/serve-tests/connected_models/src/DirModel/init.meta.json5 new file mode 100644 index 00000000..15700d79 --- /dev/null +++ b/rojo-test/serve-tests/connected_models/src/DirModel/init.meta.json5 @@ -0,0 +1,3 @@ +{ + "$className": "Folder" +} diff --git a/rojo-test/serve-tests/connected_models/src/SimpleModel.model.json5 b/rojo-test/serve-tests/connected_models/src/SimpleModel.model.json5 new file mode 100644 index 00000000..24b2bc3b --- /dev/null +++ b/rojo-test/serve-tests/connected_models/src/SimpleModel.model.json5 @@ -0,0 +1,3 @@ +{ + "className": "Configuration" +} diff --git a/rojo-test/serve-tests/connected_scripts/default.project.json5 b/rojo-test/serve-tests/connected_scripts/default.project.json5 new file mode 100644 index 00000000..d417db41 --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/default.project.json5 @@ -0,0 +1,10 @@ +{ + "name": "connected_scripts", + "tree": { + "$className": "DataModel", + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "$path": "src" + } + } +} diff --git a/rojo-test/serve-tests/connected_scripts/src/DirLocal/Helper.luau b/rojo-test/serve-tests/connected_scripts/src/DirLocal/Helper.luau new file mode 100644 index 00000000..342c9c1f --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/DirLocal/Helper.luau @@ -0,0 +1,2 @@ +-- Helper module +return {} diff --git a/rojo-test/serve-tests/connected_scripts/src/DirLocal/init.local.luau b/rojo-test/serve-tests/connected_scripts/src/DirLocal/init.local.luau new file mode 100644 index 00000000..92f5e82a --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/DirLocal/init.local.luau @@ -0,0 +1,2 @@ +-- Directory LocalScript +print("dir local") diff --git a/rojo-test/serve-tests/connected_scripts/src/DirModule/ChildA.luau b/rojo-test/serve-tests/connected_scripts/src/DirModule/ChildA.luau new file mode 100644 index 00000000..be46ef36 --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/DirModule/ChildA.luau @@ -0,0 +1,2 @@ +-- Child A +return "A" diff --git a/rojo-test/serve-tests/connected_scripts/src/DirModule/ChildB.luau b/rojo-test/serve-tests/connected_scripts/src/DirModule/ChildB.luau new file mode 100644 index 00000000..e9592448 --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/DirModule/ChildB.luau @@ -0,0 +1,2 @@ +-- Child B +return "B" diff --git a/rojo-test/serve-tests/connected_scripts/src/DirModule/init.luau b/rojo-test/serve-tests/connected_scripts/src/DirModule/init.luau new file mode 100644 index 00000000..3735acb6 --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/DirModule/init.luau @@ -0,0 +1,2 @@ +-- Directory ModuleScript +return { dir = true } diff --git a/rojo-test/serve-tests/connected_scripts/src/DirScript/Handler.luau b/rojo-test/serve-tests/connected_scripts/src/DirScript/Handler.luau new file mode 100644 index 00000000..cfb47e0c --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/DirScript/Handler.luau @@ -0,0 +1,2 @@ +-- Handler module +return {} diff --git a/rojo-test/serve-tests/connected_scripts/src/DirScript/init.server.luau b/rojo-test/serve-tests/connected_scripts/src/DirScript/init.server.luau new file mode 100644 index 00000000..2749e7ec --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/DirScript/init.server.luau @@ -0,0 +1,2 @@ +-- Directory Script +print("dir server") diff --git a/rojo-test/serve-tests/connected_scripts/src/local.client.luau b/rojo-test/serve-tests/connected_scripts/src/local.client.luau new file mode 100644 index 00000000..a4c4ee5f --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/local.client.luau @@ -0,0 +1,2 @@ +-- Standalone LocalScript +print("client") diff --git a/rojo-test/serve-tests/connected_scripts/src/server.server.luau b/rojo-test/serve-tests/connected_scripts/src/server.server.luau new file mode 100644 index 00000000..d3c8509f --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/server.server.luau @@ -0,0 +1,2 @@ +-- Standalone Script +print("server") diff --git a/rojo-test/serve-tests/connected_scripts/src/standalone.luau b/rojo-test/serve-tests/connected_scripts/src/standalone.luau new file mode 100644 index 00000000..f6f1c08e --- /dev/null +++ b/rojo-test/serve-tests/connected_scripts/src/standalone.luau @@ -0,0 +1,2 @@ +-- Standalone ModuleScript +return {} diff --git a/rojo-test/serve-tests/connected_slugify/default.project.json5 b/rojo-test/serve-tests/connected_slugify/default.project.json5 new file mode 100644 index 00000000..74e1a7ae --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/default.project.json5 @@ -0,0 +1,10 @@ +{ + "name": "connected_slugify", + "tree": { + "$className": "DataModel", + "ReplicatedStorage": { + "$className": "ReplicatedStorage", + "$path": "src" + } + } +} diff --git a/rojo-test/serve-tests/connected_slugify/src/CleanScript.server.luau b/rojo-test/serve-tests/connected_slugify/src/CleanScript.server.luau new file mode 100644 index 00000000..e4ff2391 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/CleanScript.server.luau @@ -0,0 +1,2 @@ +-- Clean-named Script +print("clean") diff --git a/rojo-test/serve-tests/connected_slugify/src/Hey_Bro.luau b/rojo-test/serve-tests/connected_slugify/src/Hey_Bro.luau new file mode 100644 index 00000000..0c88fb6d --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Hey_Bro.luau @@ -0,0 +1,2 @@ +-- Natural name Hey_Bro (no meta needed) +return "Hey_Bro" diff --git a/rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.luau b/rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.luau new file mode 100644 index 00000000..3379d410 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.luau @@ -0,0 +1,2 @@ +-- Dedup collision: real name is Hey/Bro +return "Hey/Bro" diff --git a/rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.meta.json5 b/rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.meta.json5 new file mode 100644 index 00000000..578043b8 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Hey_Bro~1.meta.json5 @@ -0,0 +1,3 @@ +{ + "name": "Hey/Bro" +} diff --git a/rojo-test/serve-tests/connected_slugify/src/Key_Script.meta.json5 b/rojo-test/serve-tests/connected_slugify/src/Key_Script.meta.json5 new file mode 100644 index 00000000..ae6b0920 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Key_Script.meta.json5 @@ -0,0 +1,3 @@ +{ + "name": "Key:Script" +} diff --git a/rojo-test/serve-tests/connected_slugify/src/Key_Script.server.luau b/rojo-test/serve-tests/connected_slugify/src/Key_Script.server.luau new file mode 100644 index 00000000..23e1c3fb --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Key_Script.server.luau @@ -0,0 +1,2 @@ +-- Script with encoded name +print("Key:Script") diff --git a/rojo-test/serve-tests/connected_slugify/src/Normal.luau b/rojo-test/serve-tests/connected_slugify/src/Normal.luau new file mode 100644 index 00000000..e9818bb6 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Normal.luau @@ -0,0 +1,2 @@ +-- Normal clean-named module +return "normal" diff --git a/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/DirChild.luau b/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/DirChild.luau new file mode 100644 index 00000000..caf9f94b --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/DirChild.luau @@ -0,0 +1,2 @@ +-- Child inside slugified directory +return "dir_child" diff --git a/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.luau b/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.luau new file mode 100644 index 00000000..889fc874 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.luau @@ -0,0 +1,2 @@ +-- Slugified directory ModuleScript +return "Slug:Dir" diff --git a/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.meta.json5 b/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.meta.json5 new file mode 100644 index 00000000..79683bde --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/Slug_Dir/init.meta.json5 @@ -0,0 +1,3 @@ +{ + "name": "Slug:Dir" +} diff --git a/rojo-test/serve-tests/connected_slugify/src/What_Model.model.json5 b/rojo-test/serve-tests/connected_slugify/src/What_Model.model.json5 new file mode 100644 index 00000000..fadde337 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/What_Model.model.json5 @@ -0,0 +1,4 @@ +{ + "name": "What?Model", + "className": "Configuration" +} diff --git a/rojo-test/serve-tests/connected_slugify/src/What_Module.luau b/rojo-test/serve-tests/connected_slugify/src/What_Module.luau new file mode 100644 index 00000000..74eca429 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/What_Module.luau @@ -0,0 +1,2 @@ +-- Module with encoded name +return "What?Module" diff --git a/rojo-test/serve-tests/connected_slugify/src/What_Module.meta.json5 b/rojo-test/serve-tests/connected_slugify/src/What_Module.meta.json5 new file mode 100644 index 00000000..0ee2e845 --- /dev/null +++ b/rojo-test/serve-tests/connected_slugify/src/What_Module.meta.json5 @@ -0,0 +1,3 @@ +{ + "name": "What?Module" +} diff --git a/tests/rojo_test/serve_util.rs b/tests/rojo_test/serve_util.rs index 7288d58e..ab8a6515 100644 --- a/tests/rojo_test/serve_util.rs +++ b/tests/rojo_test/serve_util.rs @@ -1,4 +1,5 @@ use std::{ + collections::BTreeMap, fmt::Write as _, fs, io::Read as _, @@ -15,7 +16,8 @@ use serde::Deserialize; use tempfile::{tempdir, TempDir}; use librojo::web_api::{ - ReadResponse, SerializeResponse, ServerInfoResponse, SocketPacket, SocketPacketType, + ReadResponse, SerializeResponse, ServerInfoResponse, SocketPacket, SocketPacketBody, + SocketPacketType, }; use rojo_insta_ext::RedactionMap; @@ -335,6 +337,165 @@ fn get_port_number() -> usize { port } +/// Extract the message cursor from a SocketPacket for use in subsequent +/// WebSocket subscriptions when chaining multi-step tests. +pub fn get_message_cursor(packet: &SocketPacket) -> u32 { + match &packet.body { + SocketPacketBody::Messages(msg) => msg.message_cursor, + } +} + +/// A normalized instance representation for structural tree comparison. +/// Two separate `rojo serve` sessions produce different `Ref` values, +/// so we need a Ref-free representation for round-trip identity checks. +#[derive(Debug, PartialEq, Eq, serde::Serialize)] +pub struct NormalizedInstance { + pub name: String, + pub class_name: String, + pub properties: BTreeMap, + pub children: Vec, +} + +/// Normalize a ReadResponse into a NormalizedInstance tree rooted at the given ID. +/// Strips Refs, sorts children by (name, class_name) for deterministic comparison. +pub fn normalize_read_response(read: &ReadResponse, root_id: Ref) -> NormalizedInstance { + fn normalize_recursive( + instances: &std::collections::HashMap, + id: Ref, + ) -> NormalizedInstance { + let inst = &instances[&id]; + let mut properties: BTreeMap = BTreeMap::new(); + for (key, value) in &inst.properties { + // Skip Ref properties (non-deterministic) + match &**value { + rbx_dom_weak::types::Variant::Ref(_) => continue, + _ => { + properties.insert(key.to_string(), format!("{value:?}")); + } + } + } + let mut children: Vec = inst + .children + .iter() + .map(|child_id| normalize_recursive(instances, *child_id)) + .collect(); + children.sort_by(|a, b| (&a.name, &a.class_name).cmp(&(&b.name, &b.class_name))); + NormalizedInstance { + name: inst.name.to_string(), + class_name: inst.class_name.to_string(), + properties, + children, + } + } + normalize_recursive(&read.instances, root_id) +} + +/// Start a fresh `rojo serve` on the given project path, read the full tree, +/// and return a NormalizedInstance tree. +/// +/// This verifies the round-trip invariant: the filesystem state written by +/// the live session, when read from scratch, must produce the same tree. +pub fn fresh_rebuild_read(project_path: &Path) -> NormalizedInstance { + let project_file = if project_path.join("default.project.json5").exists() { + project_path.join("default.project.json5") + } else if project_path.join("default.project.json").exists() { + project_path.join("default.project.json") + } else { + panic!( + "No default project file found in {}", + project_path.display() + ); + }; + + let port = get_port_number(); + let port_string = port.to_string(); + let working_dir = get_working_dir_path(); + + let process = Command::new(ROJO_PATH) + .args([ + "serve", + project_file.to_str().unwrap(), + "--port", + port_string.as_str(), + ]) + .current_dir(working_dir) + .stderr(Stdio::piped()) + .spawn() + .expect("Couldn't start fresh Rojo for round-trip check"); + + let mut kill_guard = KillOnDrop(process); + + // Wait for fresh server to come online with same backoff + const BASE_DURATION_MS: f32 = 30.0; + const EXP_BACKOFF_FACTOR: f32 = 1.3; + const MAX_TRIES: u32 = 5; + + let mut info = None; + for i in 1..=MAX_TRIES { + match kill_guard.0.try_wait() { + Ok(Some(status)) => { + let mut stderr_output = String::new(); + if let Some(mut stderr) = kill_guard.0.stderr.take() { + let _ = stderr.read_to_string(&mut stderr_output); + } + panic!( + "Fresh Rojo process exited with status {}\nstderr:\n{}", + status, stderr_output + ); + } + Ok(None) => {} + Err(err) => panic!("Failed to wait on fresh Rojo process: {}", err), + } + + let url = format!("http://localhost:{}/api/rojo", port); + match reqwest::blocking::get(&url) { + Ok(resp) => { + if let Ok(body) = resp.bytes() { + if let Ok(server_info) = + deserialize_msgpack::(&body) + { + info = Some(server_info); + break; + } + } + } + Err(_) => {} + } + let retry_time_ms = BASE_DURATION_MS * (i as f32).powf(EXP_BACKOFF_FACTOR); + thread::sleep(Duration::from_millis(retry_time_ms as u64)); + } + + let info = info.expect("Fresh Rojo server did not respond"); + let root_id = info.root_instance_id; + + let url = format!("http://localhost:{}/api/read/{}", port, root_id); + let body = reqwest::blocking::get(&url) + .expect("Failed to read from fresh server") + .bytes() + .expect("Failed to get bytes from fresh server"); + let read: ReadResponse = + deserialize_msgpack(&body).expect("Fresh server returned malformed response"); + + normalize_read_response(&read, root_id) +} + +/// Assert that the live session tree matches a fresh rebuild from the filesystem. +/// This is the core round-trip identity check. +pub fn assert_round_trip(session: &TestServeSession, root_id: Ref) { + // Give VFS events time to settle before checking + thread::sleep(Duration::from_millis(500)); + + let live_read = session.get_api_read(root_id).unwrap(); + let live_tree = normalize_read_response(&live_read, root_id); + let fresh_tree = fresh_rebuild_read(session.path()); + + assert_eq!( + live_tree, fresh_tree, + "Round-trip identity violation: live tree and fresh rebuild differ. \ + The filesystem state does not faithfully represent the instance tree." + ); +} + /// Takes a SerializeResponse and creates an XML model out of the response. /// /// Since the provided structure intentionally includes unredacted referents, diff --git a/tests/tests/connected_mode.rs b/tests/tests/connected_mode.rs new file mode 100644 index 00000000..9cf6e21d --- /dev/null +++ b/tests/tests/connected_mode.rs @@ -0,0 +1,1529 @@ +//! Connected mode tests: multi-step filesystem changes with WebSocket patch +//! verification and round-trip identity checks. +//! +//! These tests simulate a plugin connected to `rojo serve` receiving patches +//! over time as the filesystem evolves. Each test verifies: +//! 1. The WebSocket patch contains the correct delta (via insta snapshots) +//! 2. The tree state after each change is correct (via insta snapshots) +//! 3. Where marked, a fresh `rojo serve` on the same filesystem produces +//! the same tree (round-trip identity invariant) + +use std::fs; + +use insta::assert_yaml_snapshot; + +use crate::rojo_test::{ + internable::InternAndRedact, + serve_util::{ + assert_round_trip, get_message_cursor, run_serve_test, + }, +}; + +use librojo::web_api::SocketPacketType; + +// --------------------------------------------------------------------------- +// Phase 1: Format Transitions +// --------------------------------------------------------------------------- + +/// Test 1: Rename init.luau -> init.server.luau to change ClassName +/// from ModuleScript to Script. +#[test] +fn init_type_change_module_to_server() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_init_type_module_to_server_initial", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + let init_path = session.path().join("src/DirModule/init.luau"); + let new_path = session.path().join("src/DirModule/init.server.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::rename(&init_path, &new_path).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_type_module_to_server_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_init_type_module_to_server_final", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 2: Rename init.server.luau -> init.local.luau to change ClassName +/// from Script to LocalScript. +#[test] +fn init_type_change_server_to_local() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_init_type_server_to_local_initial", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + let init_path = session.path().join("src/DirScript/init.server.luau"); + let new_path = session.path().join("src/DirScript/init.local.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::rename(&init_path, &new_path).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_type_server_to_local_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_init_type_server_to_local_final", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 3: Multi-step init type cycling: module -> server -> local -> module. +/// Three patches, each verified, with round-trip at each step. +#[test] +fn init_type_change_cycle() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let dir = session.path().join("src/DirModule"); + + // Step 1: init.luau -> init.server.luau (Module -> Script) + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::rename(dir.join("init.luau"), dir.join("init.server.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_cycle_patch1", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 2: init.server.luau -> init.local.luau (Script -> LocalScript) + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::rename(dir.join("init.server.luau"), dir.join("init.local.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_cycle_patch2", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 3: init.local.luau -> init.luau (LocalScript -> Module) + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::rename(dir.join("init.local.luau"), dir.join("init.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_cycle_patch3", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + }); +} + +/// Test 4: Delete init.luau from a directory while children remain. +/// Children should survive and the parent should become a Folder. +#[test] +fn init_delete_children_survive() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let init_path = session.path().join("src/DirModule/init.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(&init_path).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_delete_survive_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_init_delete_survive_final", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 5: Create init.luau in an existing folder that had no init file. +/// The Folder instance should become a ModuleScript. +#[test] +fn init_create_in_existing_folder() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let dir = session.path().join("src/DirModule"); + let init_path = dir.join("init.luau"); + + // First remove the init file to create a plain folder + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(&init_path).unwrap(); + }) + .unwrap(); + let cursor = get_message_cursor(&packet); + + // Now re-create init.luau -- Folder should become ModuleScript + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(&init_path, "-- Re-created init\nreturn {}").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_create_in_folder_patch", + packet.intern_and_redact(&mut redactions, ()) + ); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_init_create_in_folder_final", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 2: Multi-Step Connected Sequences +// --------------------------------------------------------------------------- + +/// Test 6: Create -> edit -> rename -> delete a file. Four patches. +#[test] +fn create_edit_rename_delete_sequence() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + // Step 1: Create file + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("NewScript.luau"), "-- new script\nreturn 1").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_sequence_create", + packet.intern_and_redact(&mut redactions, ()) + ); + + // Step 2: Edit file + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(src.join("NewScript.luau"), "-- edited script\nreturn 2").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_sequence_edit", + packet.intern_and_redact(&mut redactions, ()) + ); + + // Step 3: Rename file + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::rename(src.join("NewScript.luau"), src.join("Renamed.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_sequence_rename", + packet.intern_and_redact(&mut redactions, ()) + ); + + // Step 4: Delete file + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::remove_file(src.join("Renamed.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_sequence_delete", + packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 7: Edit multiple files sequentially, verify each patch only +/// contains the changed file (no cross-contamination). +#[test] +fn multi_file_sequential_edits() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let _root_id = info.root_instance_id; + + let src = session.path().join("src"); + + // Edit standalone.luau + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("standalone.luau"), "-- edited standalone\nreturn 1").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_multi_edit_standalone", + packet.intern_and_redact(&mut redactions, ()) + ); + + // Edit server.server.luau + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write( + src.join("server.server.luau"), + "-- edited server\nprint('v2')", + ) + .unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_multi_edit_server", + packet.intern_and_redact(&mut redactions, ()) + ); + + // Edit local.client.luau + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write( + src.join("local.client.luau"), + "-- edited local\nprint('v2')", + ) + .unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_multi_edit_local", + packet.intern_and_redact(&mut redactions, ()) + ); + }); +} + +/// Test 8: Add 3 files sequentially. Each patch should add exactly one instance. +#[test] +fn add_multiple_files_sequentially() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("AddedOne.luau"), "return 1").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_add_seq_1", + packet.intern_and_redact(&mut redactions, ()) + ); + + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(src.join("AddedTwo.luau"), "return 2").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_add_seq_2", + packet.intern_and_redact(&mut redactions, ()) + ); + + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(src.join("AddedThree.luau"), "return 3").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_add_seq_3", + packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 9: Remove 3 files sequentially. Each patch should remove exactly one instance. +#[test] +fn remove_multiple_files_sequentially() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(src.join("standalone.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_remove_seq_1", + packet.intern_and_redact(&mut redactions, ()) + ); + + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::remove_file(src.join("server.server.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_remove_seq_2", + packet.intern_and_redact(&mut redactions, ()) + ); + + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::remove_file(src.join("local.client.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_remove_seq_3", + packet.intern_and_redact(&mut redactions, ()) + ); + }); +} + +// --------------------------------------------------------------------------- +// Phase 3: Init File Operations +// --------------------------------------------------------------------------- + +/// Test 10: Edit init.luau then edit a child. Two separate patches. +#[test] +fn edit_init_then_edit_child() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let _root_id = info.root_instance_id; + + let dir = session.path().join("src/DirModule"); + + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(dir.join("init.luau"), "-- edited init\nreturn { edited = true }").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_edit_init_then_child_init", + packet.intern_and_redact(&mut redactions, ()) + ); + + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(dir.join("ChildA.luau"), "-- edited child A\nreturn 'edited'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_edit_init_then_child_child", + packet.intern_and_redact(&mut redactions, ()) + ); + }); +} + +/// Test 11: Replace init file type: delete init.luau, create init.server.luau. +/// ClassName should change, children should be preserved. +#[test] +fn replace_init_file_type() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let dir = session.path().join("src/DirModule"); + + // Delete init.luau and create init.server.luau + // Do this as two steps to be safe with filesystem events + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(dir.join("init.luau")).unwrap(); + }) + .unwrap(); + let cursor = get_message_cursor(&packet); + + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(dir.join("init.server.luau"), "-- now a server script").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_replace_init_type_patch", + packet.intern_and_redact(&mut redactions, ()) + ); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_replace_init_type_final", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 12: Delete init from directory with children. Parent becomes Folder. +#[test] +fn delete_init_from_directory() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let init_path = session.path().join("src/DirScript/init.server.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(&init_path).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_delete_init_from_dir_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + let read_response = session.get_api_read(root_id).unwrap(); + assert_yaml_snapshot!( + "connected_delete_init_from_dir_final", + read_response.intern_and_redact(&mut redactions, root_id) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 4: Property/Meta Changes +// --------------------------------------------------------------------------- + +/// Test 13: Create adjacent meta file next to a Script. +/// Verify the tree state picks up the property and round-trip holds. +/// +/// NOTE: Creating a new .meta.json5 next to a project-node-rooted script +/// re-snapshots the project node but the diff engine reports "no changes", +/// so no WebSocket patch is emitted. The tree state IS correct (verified +/// by round-trip), but the plugin would need to reconnect to see the +/// change. This test verifies the round-trip identity is maintained. +#[test] +fn adjacent_meta_creation() { + run_serve_test("connected_scripts", |session, _redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Use server.server.luau (Script class) since Disabled is a Script property + let meta_path = session.path().join("src/server.server.meta.json5"); + fs::write( + &meta_path, + r#"{ "properties": { "Disabled": true } }"#, + ) + .unwrap(); + + // Give VFS time to process the CREATE event + std::thread::sleep(std::time::Duration::from_millis(1500)); + + // Verify round-trip identity: fresh rebuild matches live tree + assert_round_trip(&session, root_id); + }); +} + +/// Test 14: Create then delete a meta file. Round-trip should hold +/// after both operations. +#[test] +fn adjacent_meta_delete() { + run_serve_test("connected_scripts", |session, _redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let meta_path = session.path().join("src/server.server.meta.json5"); + + // Create meta file with Disabled property + fs::write( + &meta_path, + r#"{ "properties": { "Disabled": true } }"#, + ) + .unwrap(); + std::thread::sleep(std::time::Duration::from_millis(1500)); + assert_round_trip(&session, root_id); + + // Delete it + fs::remove_file(&meta_path).unwrap(); + std::thread::sleep(std::time::Duration::from_millis(1500)); + assert_round_trip(&session, root_id); + }); +} + +/// Test 15: Create init.meta.json5 in a directory with Script init. +/// The Disabled property should appear in the patch. +#[test] +fn init_meta_creation() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // DirScript has init.server.luau (Script class), so Disabled is valid + let meta_path = session.path().join("src/DirScript/init.meta.json5"); + fs::write( + &meta_path, + r#"{ "properties": { "Disabled": true } }"#, + ) + .unwrap(); + + let socket_packet = session + .get_api_socket_packet(SocketPacketType::Messages, 0) + .unwrap(); + assert_yaml_snapshot!( + "connected_init_meta_creation_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 5: Model File Changes +// --------------------------------------------------------------------------- + +/// Test 16: Edit model.json5 to add children. Verify additions in patch. +#[test] +fn model_json_edit_properties() { + run_serve_test("connected_models", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let model_path = session.path().join("src/SimpleModel.model.json5"); + fs::write( + &model_path, + r#"{ + "className": "Configuration", + "children": [{ + "name": "NewChild", + "className": "Folder" + }] +}"#, + ) + .unwrap(); + + let socket_packet = session + .get_api_socket_packet(SocketPacketType::Messages, 0) + .unwrap(); + assert_yaml_snapshot!( + "connected_model_edit_props_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 17: Create a new model.json5 file. Verify instance addition. +#[test] +fn model_json_create() { + run_serve_test("connected_models", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let model_path = session.path().join("src/NewModel.model.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write( + &model_path, + r#"{ "className": "Folder" }"#, + ) + .unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_model_create_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 18: Delete a model.json5 file. Verify instance removal. +#[test] +fn model_json_delete() { + run_serve_test("connected_models", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let model_path = session.path().join("src/SimpleModel.model.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(&model_path).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_model_delete_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + }); +} + +// --------------------------------------------------------------------------- +// Phase 6: Cursor Tracking and Reconnection +// --------------------------------------------------------------------------- + +/// Test 19: Verify cursor advances monotonically across 3 changes. +#[test] +fn cursor_advances_correctly() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let _root_id = info.root_instance_id; + + let src = session.path().join("src"); + + let p1 = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("standalone.luau"), "-- v1\nreturn 1").unwrap(); + }) + .unwrap(); + let c1 = get_message_cursor(&p1); + + let p2 = session + .recv_socket_packet(SocketPacketType::Messages, c1, || { + fs::write(src.join("standalone.luau"), "-- v2\nreturn 2").unwrap(); + }) + .unwrap(); + let c2 = get_message_cursor(&p2); + + let p3 = session + .recv_socket_packet(SocketPacketType::Messages, c2, || { + fs::write(src.join("standalone.luau"), "-- v3\nreturn 3").unwrap(); + }) + .unwrap(); + let c3 = get_message_cursor(&p3); + + // Cursors must be monotonically increasing + assert!(c1 > 0, "Cursor 1 should be > 0, got {c1}"); + assert!(c2 > c1, "Cursor 2 ({c2}) should be > cursor 1 ({c1})"); + assert!(c3 > c2, "Cursor 3 ({c3}) should be > cursor 2 ({c2})"); + // Snapshot the final state as a basic sanity check + assert_yaml_snapshot!( + "connected_cursor_final_patch", + p3.intern_and_redact(&mut redactions, ()) + ); + }); +} + +/// Test 20: Reconnect with cursor 0 after 2 changes. Should get ALL patches. +#[test] +fn reconnect_with_old_cursor() { + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let _root_id = info.root_instance_id; + + let src = session.path().join("src"); + + // Make change 1 + let p1 = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("standalone.luau"), "-- change1\nreturn 1").unwrap(); + }) + .unwrap(); + let c1 = get_message_cursor(&p1); + + // Make change 2 + let _p2 = session + .recv_socket_packet(SocketPacketType::Messages, c1, || { + fs::write(src.join("server.server.luau"), "-- change2\nprint('v2')").unwrap(); + }) + .unwrap(); + + // Reconnect with cursor 0 -- should catch up with ALL messages + let catch_up = session + .get_api_socket_packet(SocketPacketType::Messages, 0) + .unwrap(); + assert_yaml_snapshot!( + "connected_reconnect_cursor0_catchup", + catch_up.intern_and_redact(&mut redactions, ()) + ); + }); +} + +// --------------------------------------------------------------------------- +// Phase 7: Mixed API + Filesystem Verification +// --------------------------------------------------------------------------- + +/// Test 21: API write then filesystem change. Only the filesystem change +/// should produce a WebSocket patch (echo suppression). +#[test] +fn api_write_then_filesystem_change() { + use librojo::web_api::{InstanceUpdate, WriteRequest}; + + run_serve_test("connected_scripts", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Find the standalone instance + let read = session.get_api_read(root_id).unwrap(); + let rs_id = read + .instances + .iter() + .find(|(_, inst)| inst.class_name == "ReplicatedStorage") + .map(|(id, _)| *id) + .unwrap(); + let rs_read = session.get_api_read(rs_id).unwrap(); + let (standalone_id, _) = rs_read + .instances + .iter() + .find(|(_, inst)| inst.name == "standalone") + .unwrap(); + + // API write: update source on standalone + let mut props = rbx_dom_weak::UstrMap::default(); + props.insert( + rbx_dom_weak::ustr("Source"), + Some(rbx_dom_weak::types::Variant::String( + "-- api write\nreturn {}".to_string(), + )), + ); + let write_request = WriteRequest { + session_id: info.session_id, + removed: vec![], + added: std::collections::HashMap::new(), + updated: vec![InstanceUpdate { + id: *standalone_id, + changed_name: None, + changed_class_name: None, + changed_properties: props, + changed_metadata: None, + }], + }; + session.post_api_write(&write_request).unwrap(); + std::thread::sleep(std::time::Duration::from_millis(500)); + + // Now make a filesystem change to a DIFFERENT file + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write( + session.path().join("src/server.server.luau"), + "-- fs change\nprint('fs')", + ) + .unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_api_then_fs_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 8a: Slugify Forward-Sync +// --------------------------------------------------------------------------- + +/// Test 22: Edit slugified script source. Patch should show instance +/// named "What?Module" (from meta), NOT "What_Module" (filesystem name). +#[test] +fn edit_slugified_script_source() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let file_path = session.path().join("src/What_Module.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&file_path, "-- edited slugified module\nreturn 'edited'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_edit_source_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 23: Edit slugified server script source. Patch should show +/// instance named "Key:Script". +#[test] +fn edit_slugified_server_script_source() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let _root_id = info.root_instance_id; + + let file_path = session.path().join("src/Key_Script.server.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&file_path, "-- edited key script\nprint('edited')").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_edit_server_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + }); +} + +/// Test 24: Create new slugified file + meta. Patch should add instance +/// with real name from meta. +#[test] +fn create_new_slugified_file_with_meta() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("New_Slug.meta.json5"), r#"{ "name": "New/Slug" }"#).unwrap(); + fs::write(src.join("New_Slug.luau"), "-- new slug\nreturn 'new'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_create_new_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 25: Delete slugified file and meta. Patch should remove the instance. +#[test] +fn delete_slugified_file_and_meta() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(src.join("What_Module.luau")).unwrap(); + fs::remove_file(src.join("What_Module.meta.json5")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_delete_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + }); +} + +/// Test 26: Edit only the meta name field. Same slug, different real name. +/// This is the "Foo/Bar -> Foo|Bar, both slugify to Foo_Bar" case. +#[test] +fn edit_only_meta_name_field() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let meta_path = session.path().join("src/What_Module.meta.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&meta_path, r#"{ "name": "What*Module" }"#).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_edit_meta_name_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 27: Delete meta file (leave script). Instance name should revert +/// from "What?Module" to "What_Module" (the filename stem). +#[test] +fn delete_meta_name_reverts_to_stem() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let meta_path = session.path().join("src/What_Module.meta.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(&meta_path).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_delete_meta_revert_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 8b: Dedup (~N Suffix) Forward-Sync +// --------------------------------------------------------------------------- + +/// Test 28: Edit dedup file with meta. Patch should show instance named +/// "Hey/Bro" (from meta), not "Hey_Bro~1". +#[test] +fn edit_dedup_file_with_meta() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let file_path = session.path().join("src/Hey_Bro~1.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&file_path, "-- edited dedup\nreturn 'edited'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_dedup_edit_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 29: Create Foo~1.luau with NO meta. Instance name should be +/// literally "Foo~1" -- tilde is NOT parsed as dedup marker. +#[test] +fn dedup_file_without_meta_uses_stem() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let file_path = session.path().join("src/Foo~1.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&file_path, "-- no meta, tilde is literal\nreturn 'Foo~1'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_dedup_no_meta_stem_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 30: Remove one of two colliding files. Only the correct instance removed. +#[test] +fn remove_one_of_two_colliding_files() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::remove_file(src.join("Hey_Bro~1.luau")).unwrap(); + fs::remove_file(src.join("Hey_Bro~1.meta.json5")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_dedup_remove_one_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 31: Add third collision (Hey_Bro~2). All three should coexist. +#[test] +fn add_third_collision() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("Hey_Bro~2.meta.json5"), r#"{ "name": "Hey*Bro" }"#).unwrap(); + fs::write(src.join("Hey_Bro~2.luau"), "-- third collision\nreturn 'Hey*Bro'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_dedup_add_third_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 8c: Slugified Directory Format +// --------------------------------------------------------------------------- + +/// Test 32: Edit init in slugified directory. Patch should show instance +/// named "Slug:Dir" (from init.meta.json5). +#[test] +fn edit_init_in_slugified_directory() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let init_path = session.path().join("src/Slug_Dir/init.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&init_path, "-- edited slug dir init\nreturn 'edited'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slugdir_edit_init_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 33: Init type change in slugified directory. ClassName changes +/// but name must remain "Slug:Dir" from meta. +#[test] +fn init_type_change_in_slugified_directory() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let dir = session.path().join("src/Slug_Dir"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::rename(dir.join("init.luau"), dir.join("init.server.luau")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slugdir_init_type_change_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 34: Add child to slugified directory. Child should appear under +/// instance named "Slug:Dir". +#[test] +fn add_child_to_slugified_directory() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let child_path = session.path().join("src/Slug_Dir/NewChild.luau"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&child_path, "-- new child in slug dir\nreturn 'child'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slugdir_add_child_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 8d: Meta Name Field Lifecycle +// --------------------------------------------------------------------------- + +/// Test 35: Add meta name to clean file. Name should change from +/// "Normal" to "Nor/mal". +#[test] +fn add_meta_name_to_clean_file() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let meta_path = session.path().join("src/Normal.meta.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&meta_path, r#"{ "name": "Nor/mal" }"#).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_meta_add_name_to_clean_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 36: Update meta name field. Slug stays the same, real name changes. +#[test] +fn update_meta_name_field() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let meta_path = session.path().join("src/What_Module.meta.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&meta_path, r#"{ "name": "What:Module" }"#).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_meta_update_name_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 37: Create meta with both name AND properties. Round-trip +/// should show both the name override and the property. +#[test] +fn meta_name_with_other_properties() { + run_serve_test("connected_slugify", |session, _redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let meta_path = session.path().join("src/CleanScript.server.meta.json5"); + fs::write( + &meta_path, + r#"{ "name": "Real/Name", "properties": { "Disabled": true } }"#, + ) + .unwrap(); + + // Give VFS time to process + std::thread::sleep(std::time::Duration::from_millis(1500)); + + // Verify round-trip identity + assert_round_trip(&session, root_id); + }); +} + +/// Test 38: Edit model.json5 name field. Name should change. +#[test] +fn model_json_name_field_edit() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let model_path = session.path().join("src/What_Model.model.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write( + &model_path, + r#"{ "name": "What:Model", "className": "Configuration" }"#, + ) + .unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_model_name_edit_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +/// Test 39: Remove name field from model.json5. Name should revert +/// from "What?Model" to "What_Model". +#[test] +fn model_json_remove_name_field() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let model_path = session.path().join("src/What_Model.model.json5"); + + let socket_packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&model_path, r#"{ "className": "Configuration" }"#).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_model_remove_name_patch", + socket_packet.intern_and_redact(&mut redactions, ()) + ); + + assert_round_trip(&session, root_id); + }); +} + +// --------------------------------------------------------------------------- +// Phase 8e: Multi-Step Slugify Scenarios +// --------------------------------------------------------------------------- + +/// Test 40: Multi-step slugify rename chain. Edit content, update meta name, +/// delete meta (reverts), re-create meta. Round-trip at each step. +#[test] +fn slugify_rename_chain() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let file_path = session.path().join("src/What_Module.luau"); + let meta_path = session.path().join("src/What_Module.meta.json5"); + + // Step 1: Edit content + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(&file_path, "-- step1 edit\nreturn 'step1'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_chain_step1", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 2: Update meta name What?Module -> What*Module + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(&meta_path, r#"{ "name": "What*Module" }"#).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_chain_step2", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 3: Delete meta (name reverts to What_Module) + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::remove_file(&meta_path).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_chain_step3", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 4: Re-create meta with different name + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(&meta_path, r#"{ "name": "What/Module" }"#).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_slug_chain_step4", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + }); +} + +/// Test 41: Collision evolution. Add third collision, remove one, reuse +/// the ~1 slot with a different name. Round-trip at each step. +#[test] +fn collision_evolution() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + // Intern the initial tree so parent Refs are in the redaction map + let read_response = session.get_api_read(root_id).unwrap(); + read_response.intern_and_redact(&mut redactions, root_id); + + let src = session.path().join("src"); + + // Step 1: Add third collision Hey_Bro~2 -> "Hey*Bro" + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("Hey_Bro~2.meta.json5"), r#"{ "name": "Hey*Bro" }"#).unwrap(); + fs::write(src.join("Hey_Bro~2.luau"), "return 'Hey*Bro'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_collision_evo_step1", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 2: Remove Hey_Bro~1 (Hey/Bro) + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::remove_file(src.join("Hey_Bro~1.luau")).unwrap(); + fs::remove_file(src.join("Hey_Bro~1.meta.json5")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_collision_evo_step2", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 3: Reuse ~1 slot with different name "Hey|Bro" + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::write(src.join("Hey_Bro~1.meta.json5"), r#"{ "name": "Hey|Bro" }"#).unwrap(); + fs::write(src.join("Hey_Bro~1.luau"), "return 'Hey|Bro'").unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_collision_evo_step3", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + }); +} + +/// Test 42: Clean name -> slugified (via adding meta) -> clean (via removing +/// meta) -> different slug+name (via rename + meta). Round-trip at each step. +#[test] +fn clean_to_slug_to_clean_via_filesystem() { + run_serve_test("connected_slugify", |session, mut redactions| { + let info = session.get_api_rojo().unwrap(); + let root_id = info.root_instance_id; + + let src = session.path().join("src"); + + // Step 1: Add meta to Normal.luau -> name becomes "Nor:mal" + let packet = session + .recv_socket_packet(SocketPacketType::Messages, 0, || { + fs::write(src.join("Normal.meta.json5"), r#"{ "name": "Nor:mal" }"#).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_clean_slug_clean_step1", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 2: Delete meta -> name reverts to "Normal" + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::remove_file(src.join("Normal.meta.json5")).unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_clean_slug_clean_step2", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + + // Step 3: Rename Normal.luau -> Has_Slash.luau + create meta + let cursor = get_message_cursor(&packet); + let packet = session + .recv_socket_packet(SocketPacketType::Messages, cursor, || { + fs::rename(src.join("Normal.luau"), src.join("Has_Slash.luau")).unwrap(); + fs::write( + src.join("Has_Slash.meta.json5"), + r#"{ "name": "Has/Slash" }"#, + ) + .unwrap(); + }) + .unwrap(); + assert_yaml_snapshot!( + "connected_clean_slug_clean_step3", + packet.intern_and_redact(&mut redactions, ()) + ); + assert_round_trip(&session, root_id); + }); +} diff --git a/tests/tests/mod.rs b/tests/tests/mod.rs index 862ae7dc..2241939d 100644 --- a/tests/tests/mod.rs +++ b/tests/tests/mod.rs @@ -1,6 +1,7 @@ mod build; mod clean_mode; mod clean_mode_stress; +mod connected_mode; mod serve; mod syncback; mod syncback_format_transitions;