rojo/tests/tests/syncback_format_transitions.rs
Joe 411f79ee81 Enhance syncback functionality and improve code readability
- Refactored the syncback process to streamline directory management and improve handling of class transitions in clean mode.
- Updated the `clone_tree_filtered` function to accept a tuple of compiled Globs and original pattern strings, enhancing flexibility in ignore pattern handling.
- Simplified logic for checking if paths are inside removed directories, improving code clarity.
- Added detailed comments to clarify the purpose of new logic for handling script directory transitions, ensuring better maintainability.
- Improved logging for better traceability during syncback operations.
2026-02-01 20:10:11 -06:00

2259 lines
79 KiB
Rust

//! Comprehensive tests for plugin syncback format transitions.
//!
//! These tests verify that the plugin API syncback correctly handles:
//! 1. Directory format → Standalone format (removing children)
//! 2. Standalone format → Directory format (adding children)
//! 3. Various script types (ModuleScript, Script, LocalScript)
//! 4. Non-script types (model.json5, txt, etc.)
//! 5. scriptsOnlyMode variations
//!
//! The key invariant: existing file formats should be preserved to prevent
//! duplicate file creation.
use std::collections::HashMap;
use std::path::Path;
use librojo::web_api::{AddedInstance, WriteRequest};
use rbx_dom_weak::types::{Ref, Variant};
use crate::rojo_test::serve_util::run_serve_test;
/// Helper to create a basic AddedInstance
fn make_added_instance(
parent: Ref,
name: &str,
class_name: &str,
source: Option<&str>,
children: Vec<AddedInstance>,
) -> AddedInstance {
let mut properties = HashMap::new();
if let Some(src) = source {
properties.insert("Source".to_string(), Variant::String(src.to_string()));
}
AddedInstance {
parent: Some(parent),
name: name.to_string(),
class_name: class_name.to_string(),
properties,
children,
}
}
/// Helper to create a child AddedInstance (no parent needed)
fn make_child_instance(
name: &str,
class_name: &str,
source: Option<&str>,
children: Vec<AddedInstance>,
) -> AddedInstance {
let mut properties = HashMap::new();
if let Some(src) = source {
properties.insert("Source".to_string(), Variant::String(src.to_string()));
}
AddedInstance {
parent: None,
name: name.to_string(),
class_name: class_name.to_string(),
properties,
children,
}
}
/// Helper to send a write request and wait for processing
fn send_write_request(
session: &crate::rojo_test::serve_util::TestServeSession,
session_id: &librojo::SessionId,
_parent_ref: Ref,
added: AddedInstance,
) {
let instance_ref = Ref::new();
let mut added_map = HashMap::new();
added_map.insert(instance_ref, added);
let write_request = WriteRequest {
session_id: *session_id,
removed: vec![],
added: added_map,
updated: vec![],
};
session
.post_api_write(&write_request)
.expect("Write request should succeed");
// Give the server time to process
std::thread::sleep(std::time::Duration::from_millis(200));
}
/// Helper to verify a directory exists with specific contents
fn assert_directory_exists(path: &Path, message: &str) {
assert!(path.exists(), "{}: path should exist", message);
assert!(path.is_dir(), "{}: should be a directory", message);
}
/// Helper to verify a file exists
fn assert_file_exists(path: &Path, message: &str) {
assert!(path.exists(), "{}: path should exist", message);
assert!(path.is_file(), "{}: should be a file", message);
}
/// Helper to verify a path does NOT exist
fn assert_not_exists(path: &Path, message: &str) {
assert!(!path.exists(), "{}: path should NOT exist", message);
}
// =============================================================================
// DIRECTORY → STANDALONE TRANSITIONS (Removing Children)
// =============================================================================
/// Test: Directory ModuleScript (init.luau + children) receives sync with no children
/// Expected: Directory format preserved (no standalone .luau created)
#[test]
fn dir_module_sync_removing_children_preserves_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
// Verify initial state
let dir_path = src_path.join("DirModuleWithChildren");
let init_file = dir_path.join("init.luau");
let standalone_file = src_path.join("DirModuleWithChildren.luau");
assert_directory_exists(&dir_path, "DirModuleWithChildren directory");
assert_file_exists(&init_file, "init.luau");
assert_not_exists(
&standalone_file,
"standalone file should not exist initially",
);
// Send sync with NO children (simulating children deletion in Studio)
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- Updated, no children\nreturn {}"),
vec![], // No children!
);
send_write_request(&session, &info.session_id, rs_id, added);
// CRITICAL: No standalone file should be created
assert_not_exists(
&standalone_file,
"Standalone .luau should NOT be created when directory exists",
);
// Directory should still exist
assert_directory_exists(&dir_path, "Directory should be preserved");
});
}
/// Test: Directory Script (init.server.luau + children) receives sync with no children
#[test]
fn dir_script_sync_removing_children_preserves_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirScriptWithChildren");
let init_file = dir_path.join("init.server.luau");
let standalone_file = src_path.join("DirScriptWithChildren.server.luau");
let standalone_legacy = src_path.join("DirScriptWithChildren.legacy.luau");
assert_directory_exists(&dir_path, "DirScriptWithChildren directory");
assert_file_exists(&init_file, "init.server.luau");
// Send sync with NO children
let added = make_added_instance(
rs_id,
"DirScriptWithChildren",
"Script",
Some("-- Updated Script, no children\nprint('updated')"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
// No standalone file should be created
assert_not_exists(
&standalone_file,
"Standalone .server.luau should NOT be created",
);
assert_not_exists(
&standalone_legacy,
"Standalone .legacy.luau should NOT be created",
);
assert_directory_exists(&dir_path, "Directory should be preserved");
});
}
/// Test: Directory LocalScript (init.local.luau + children) receives sync with no children
/// Note: init.local.luau produces LocalScript, NOT init.client.luau (which produces Script with Client RunContext)
#[test]
fn dir_localscript_sync_removing_children_preserves_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirLocalScriptWithChildren");
let init_file = dir_path.join("init.local.luau");
let standalone_file = src_path.join("DirLocalScriptWithChildren.local.luau");
assert_directory_exists(&dir_path, "DirLocalScriptWithChildren directory");
assert_file_exists(&init_file, "init.local.luau");
// Send sync with NO children
let added = make_added_instance(
rs_id,
"DirLocalScriptWithChildren",
"LocalScript",
Some("-- Updated LocalScript, no children\nprint('updated')"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
// No standalone file should be created
assert_not_exists(
&standalone_file,
"Standalone .local.luau should NOT be created",
);
assert_directory_exists(&dir_path, "Directory should be preserved");
});
}
/// Test: Directory model (init.meta.json5 + children) receives sync with no children
#[test]
fn dir_model_sync_removing_children_preserves_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModelWithChildren");
let meta_file = dir_path.join("init.meta.json5");
let standalone_file = src_path.join("DirModelWithChildren.model.json5");
assert_directory_exists(&dir_path, "DirModelWithChildren directory");
assert_file_exists(&meta_file, "init.meta.json5");
// Send sync with NO children
let added =
make_added_instance(rs_id, "DirModelWithChildren", "Configuration", None, vec![]);
send_write_request(&session, &info.session_id, rs_id, added);
// No standalone model file should be created
assert_not_exists(
&standalone_file,
"Standalone .model.json5 should NOT be created",
);
assert_directory_exists(&dir_path, "Directory should be preserved");
});
}
// =============================================================================
// STANDALONE → DIRECTORY TRANSITIONS (Adding Children)
// =============================================================================
/// Test: Standalone ModuleScript receives sync WITH children
/// Expected: Standalone converted to directory format when children are added
/// Standalone scripts cannot have children in Rojo's file format.
#[test]
fn standalone_module_sync_adding_children_converts_to_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let standalone_file = src_path.join("StandaloneModule.luau");
let dir_path = src_path.join("StandaloneModule");
assert_file_exists(&standalone_file, "StandaloneModule.luau");
assert_not_exists(&dir_path, "Directory should not exist initially");
// Send sync WITH children (simulating children added in Studio)
let added = make_added_instance(
rs_id,
"StandaloneModule",
"ModuleScript",
Some("-- Now has children\nreturn {}"),
vec![make_child_instance(
"NewChild",
"ModuleScript",
Some("return 'child'"),
vec![],
)],
);
send_write_request(&session, &info.session_id, rs_id, added);
// Standalone file should be removed (converted to directory)
assert_not_exists(
&standalone_file,
"Standalone file should be removed after conversion",
);
// Directory should be created with init file
assert_directory_exists(
&dir_path,
"Directory should be created for script with children",
);
let init_file = dir_path.join("init.luau");
assert_file_exists(&init_file, "init.luau should exist");
// Child should exist in the directory
let child_file = dir_path.join("NewChild.luau");
assert_file_exists(&child_file, "Child module should be created");
});
}
/// Test: Standalone Script receives sync WITH children - converts to directory format
/// Standalone scripts cannot have children in Rojo's file format.
#[test]
fn standalone_script_sync_adding_children_converts_to_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let standalone_file = src_path.join("StandaloneScript.server.luau");
let dir_path = src_path.join("StandaloneScript");
assert_file_exists(&standalone_file, "StandaloneScript.server.luau");
assert_not_exists(&dir_path, "Directory should not exist initially");
// Send sync WITH children
let added = make_added_instance(
rs_id,
"StandaloneScript",
"Script",
Some("-- Now has children\nprint('updated')"),
vec![make_child_instance(
"Handler",
"ModuleScript",
Some("return function() end"),
vec![],
)],
);
send_write_request(&session, &info.session_id, rs_id, added);
// Standalone file should be removed (converted to directory)
assert_not_exists(
&standalone_file,
"Standalone file should be removed after conversion",
);
// Directory should be created with init file
assert_directory_exists(
&dir_path,
"Directory should be created for script with children",
);
let init_file = dir_path.join("init.server.luau");
assert_file_exists(&init_file, "init.server.luau should exist");
// Child should exist in the directory
let child_file = dir_path.join("Handler.luau");
assert_file_exists(&child_file, "Child module should be created");
});
}
/// Test: Standalone LocalScript receives sync WITH children - converts to directory format
/// When a standalone script receives children, it must be converted to directory format
/// because standalone scripts cannot have children in Rojo's file format.
#[test]
fn standalone_localscript_sync_adding_children_converts_to_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let standalone_file = src_path.join("StandaloneLocalScript.client.luau");
let dir_path = src_path.join("StandaloneLocalScript");
assert_file_exists(&standalone_file, "StandaloneLocalScript.client.luau");
assert_not_exists(&dir_path, "Directory should not exist initially");
// Send sync WITH children
let added = make_added_instance(
rs_id,
"StandaloneLocalScript",
"LocalScript",
Some("-- Now has children\nprint('updated')"),
vec![make_child_instance(
"UIComponent",
"ModuleScript",
Some("return {}"),
vec![],
)],
);
send_write_request(&session, &info.session_id, rs_id, added);
// Standalone file should be removed (converted to directory)
assert_not_exists(
&standalone_file,
"Standalone file should be removed after conversion",
);
// Directory should be created with init file
assert_directory_exists(
&dir_path,
"Directory should be created for script with children",
);
let init_file = dir_path.join("init.local.luau");
assert_file_exists(&init_file, "init.local.luau should exist");
// Child should exist in the directory
let child_file = dir_path.join("UIComponent.luau");
assert_file_exists(&child_file, "Child module should be created");
});
}
/// Test: Standalone model.json5 receives sync WITH children
#[test]
fn standalone_model_sync_adding_children_preserves_standalone() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let standalone_file = src_path.join("StandaloneModel.model.json5");
let dir_path = src_path.join("StandaloneModel");
assert_file_exists(&standalone_file, "StandaloneModel.model.json5");
assert_not_exists(&dir_path, "Directory should not exist initially");
// Send sync WITH children
let child = make_child_instance("ChildValue", "StringValue", None, vec![]);
let added =
make_added_instance(rs_id, "StandaloneModel", "Configuration", None, vec![child]);
send_write_request(&session, &info.session_id, rs_id, added);
// Standalone file should still exist
assert_file_exists(&standalone_file, "Standalone file should be preserved");
// No directory should be created
assert_not_exists(
&dir_path,
"Directory should NOT be created for existing standalone file",
);
});
}
// =============================================================================
// MULTIPLE CHILDREN / NESTED SCENARIOS
// =============================================================================
/// Test: Sync with multiple levels of nested children
#[test]
fn sync_with_deeply_nested_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Send sync with multiple levels of nesting
let level3 = make_child_instance("Level3", "ModuleScript", Some("return 3"), vec![]);
let level2 = make_child_instance("Level2", "ModuleScript", Some("return 2"), vec![level3]);
let level1 = make_child_instance("Level1", "ModuleScript", Some("return 1"), vec![level2]);
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- Has deeply nested children\nreturn {}"),
vec![level1],
);
send_write_request(&session, &info.session_id, rs_id, added);
// Directory should still exist
assert_directory_exists(&dir_path, "Directory should be preserved");
// No standalone file
let standalone = src_path.join("DirModuleWithChildren.luau");
assert_not_exists(&standalone, "No standalone file should be created");
});
}
/// Test: Sync with mixed script and non-script children
#[test]
fn sync_with_mixed_children_types() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Send sync with various child types
let children = vec![
make_child_instance(
"ChildModule",
"ModuleScript",
Some("return 'module'"),
vec![],
),
make_child_instance("ChildScript", "Script", Some("print('script')"), vec![]),
make_child_instance("ChildFolder", "Folder", None, vec![]),
make_child_instance("ChildValue", "StringValue", None, vec![]),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- Has mixed children\nreturn {}"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
// Directory should still exist
assert_directory_exists(&dir_path, "Directory should be preserved");
});
}
// =============================================================================
// EDGE CASES
// =============================================================================
/// Test: Syncing the same instance twice preserves format
#[test]
fn sync_same_instance_twice_preserves_format() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let standalone = src_path.join("DirModuleWithChildren.luau");
// First sync
let added1 = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- First sync\nreturn 1"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added1);
assert_directory_exists(&dir_path, "Directory after first sync");
assert_not_exists(&standalone, "No standalone after first sync");
// Second sync
let added2 = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- Second sync\nreturn 2"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added2);
assert_directory_exists(&dir_path, "Directory after second sync");
assert_not_exists(&standalone, "No standalone after second sync");
});
}
/// Test: Sync with empty name child (edge case)
#[test]
fn sync_handles_children_with_special_names() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Send sync with children that have special names
let children = vec![
make_child_instance(
"Child With Spaces",
"ModuleScript",
Some("return 1"),
vec![],
),
make_child_instance(
"Child-With-Dashes",
"ModuleScript",
Some("return 2"),
vec![],
),
make_child_instance(
"Child_With_Underscores",
"ModuleScript",
Some("return 3"),
vec![],
),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- Has special name children\nreturn {}"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
// Directory should still exist
assert_directory_exists(&dir_path, "Directory should be preserved");
});
}
/// Test: StringValue (.txt) format preservation
#[test]
fn txt_file_format_preserved() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let txt_file = src_path.join("StandaloneValue.txt");
let model_file = src_path.join("StandaloneValue.model.json5");
assert_file_exists(&txt_file, "StandaloneValue.txt");
assert_not_exists(&model_file, ".model.json5 should not exist initially");
// Send sync for StringValue
let mut properties = HashMap::new();
properties.insert(
"Value".to_string(),
Variant::String("Updated value".to_string()),
);
let added = AddedInstance {
parent: Some(rs_id),
name: "StandaloneValue".to_string(),
class_name: "StringValue".to_string(),
properties,
children: vec![],
};
send_write_request(&session, &info.session_id, rs_id, added);
// .txt file should still exist
assert_file_exists(&txt_file, ".txt file should be preserved");
// No .model.json5 should be created
assert_not_exists(&model_file, ".model.json5 should NOT be created");
});
}
// =============================================================================
// TRULY NEW INSTANCES (No existing file)
// =============================================================================
/// Test: Truly new ModuleScript with children creates directory
#[test]
fn new_module_with_children_creates_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
// This instance doesn't exist on disk
let new_dir = src_path.join("BrandNewModule");
let new_standalone = src_path.join("BrandNewModule.luau");
assert_not_exists(&new_dir, "New directory should not exist initially");
assert_not_exists(&new_standalone, "New standalone should not exist initially");
// Send sync for truly new instance with children
let added = make_added_instance(
rs_id,
"BrandNewModule",
"ModuleScript",
Some("-- Brand new with children\nreturn {}"),
vec![make_child_instance(
"Child",
"ModuleScript",
Some("return 'child'"),
vec![],
)],
);
send_write_request(&session, &info.session_id, rs_id, added);
// For truly new instances with children, directory format should be used
// Note: This depends on implementation - check what actually happens
// The key is that only ONE format should be created, not both
let dir_exists = new_dir.exists();
let standalone_exists = new_standalone.exists();
// At most one should exist (no duplicates)
assert!(
!(dir_exists && standalone_exists),
"Both directory and standalone should NOT exist (no duplicates)"
);
});
}
/// Test: Truly new ModuleScript without children creates standalone
#[test]
fn new_module_without_children_creates_standalone() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
// This instance doesn't exist on disk
let new_dir = src_path.join("AnotherNewModule");
let new_standalone = src_path.join("AnotherNewModule.luau");
assert_not_exists(&new_dir, "New directory should not exist initially");
assert_not_exists(&new_standalone, "New standalone should not exist initially");
// Send sync for truly new instance WITHOUT children
let added = make_added_instance(
rs_id,
"AnotherNewModule",
"ModuleScript",
Some("-- Brand new, no children\nreturn {}"),
vec![], // No children
);
send_write_request(&session, &info.session_id, rs_id, added);
// For truly new instances without children, standalone should be used
// Note: This depends on implementation
let dir_exists = new_dir.exists();
let standalone_exists = new_standalone.exists();
// At most one should exist (no duplicates)
assert!(
!(dir_exists && standalone_exists),
"Both directory and standalone should NOT exist (no duplicates)"
);
});
}
// =============================================================================
// RAPID SUCCESSIVE SYNCS
// =============================================================================
/// Test: Multiple rapid syncs don't create duplicates
#[test]
fn rapid_syncs_dont_create_duplicates() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let standalone = src_path.join("DirModuleWithChildren.luau");
// Rapid fire 5 syncs
for i in 0..5 {
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(&format!("-- Sync {}\nreturn {}", i, i)),
vec![],
);
let instance_ref = Ref::new();
let mut added_map = HashMap::new();
added_map.insert(instance_ref, added);
let write_request = WriteRequest {
session_id: info.session_id,
removed: vec![],
added: added_map,
updated: vec![],
};
session
.post_api_write(&write_request)
.expect("Write request should succeed");
// Minimal delay between syncs
std::thread::sleep(std::time::Duration::from_millis(10));
}
// Wait for processing
std::thread::sleep(std::time::Duration::from_millis(300));
// Should never have duplicates
assert_directory_exists(&dir_path, "Directory should exist");
assert_not_exists(&standalone, "No standalone should exist");
});
}
// =============================================================================
// FOLDER SCENARIOS
// =============================================================================
/// Test: Folder with children synced without children preserves directory
#[test]
fn folder_sync_removing_children_preserves_directory() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
// DirModelWithChildren is essentially a folder-like structure
let dir_path = src_path.join("DirModelWithChildren");
assert_directory_exists(&dir_path, "Directory should exist initially");
// Send sync as Folder with no children
let added = make_added_instance(rs_id, "DirModelWithChildren", "Folder", None, vec![]);
send_write_request(&session, &info.session_id, rs_id, added);
// Directory should still exist
assert_directory_exists(&dir_path, "Directory should be preserved for Folder");
});
}
// =============================================================================
// SCRIPT TYPE EDGE CASES
// =============================================================================
/// Test: Script sent as LocalScript preserves existing Script format
#[test]
fn class_change_preserves_existing_format() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let server_file = src_path
.join("DirScriptWithChildren")
.join("init.server.luau");
assert_file_exists(&server_file, "init.server.luau should exist");
// Send as LocalScript instead of Script
let added = make_added_instance(
rs_id,
"DirScriptWithChildren",
"LocalScript", // Different class!
Some("-- Sent as LocalScript\nprint('test')"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
// Should still have the .server.luau file (format preserved)
// Note: Behavior depends on implementation - this tests format preservation
let dir_path = src_path.join("DirScriptWithChildren");
assert_directory_exists(&dir_path, "Directory should still exist");
});
}
// =============================================================================
// scriptsOnlyMode TESTS
// =============================================================================
/// Test: scriptsOnlyMode - Script sync preserves directory format
#[test]
fn scripts_only_mode_script_preserves_directory() {
run_serve_test("syncback_scripts_only", |session, _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();
let sss_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ServerScriptService")
.map(|(id, _)| *id)
.expect("ServerScriptService should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("ScriptWithChildren");
let init_file = dir_path.join("init.server.luau");
let standalone = src_path.join("ScriptWithChildren.server.luau");
let standalone_legacy = src_path.join("ScriptWithChildren.legacy.luau");
assert_directory_exists(&dir_path, "ScriptWithChildren directory");
assert_file_exists(&init_file, "init.server.luau");
// In scriptsOnlyMode, sync a Script without children
let added = make_added_instance(
sss_id,
"ScriptWithChildren",
"Script",
Some("-- Updated in scriptsOnlyMode\nprint('updated')"),
vec![],
);
send_write_request(&session, &info.session_id, sss_id, added);
// No standalone file should be created
assert_not_exists(&standalone, "No standalone .server.luau");
assert_not_exists(&standalone_legacy, "No standalone .legacy.luau");
// Directory should be preserved
assert_directory_exists(&dir_path, "Directory preserved in scriptsOnlyMode");
});
}
/// Test: scriptsOnlyMode - Standalone module converts to directory when children added
/// Even in scriptsOnlyMode, standalone scripts must convert to directory format
/// when children are added, because standalone scripts cannot have children.
#[test]
fn scripts_only_mode_standalone_module_converts_to_directory() {
run_serve_test("syncback_scripts_only", |session, _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();
let sss_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ServerScriptService")
.map(|(id, _)| *id)
.expect("ServerScriptService should exist");
let src_path = session.path().join("src");
let standalone = src_path.join("StandaloneModule.luau");
let dir_path = src_path.join("StandaloneModule");
assert_file_exists(&standalone, "StandaloneModule.luau");
assert_not_exists(&dir_path, "Directory should not exist");
// In scriptsOnlyMode, sync with children
let added = make_added_instance(
sss_id,
"StandaloneModule",
"ModuleScript",
Some("-- Updated with children in scriptsOnlyMode\nreturn {}"),
vec![make_child_instance(
"ChildModule",
"ModuleScript",
Some("return 'child'"),
vec![],
)],
);
send_write_request(&session, &info.session_id, sss_id, added);
// Standalone should be removed (converted to directory)
assert_not_exists(&standalone, "Standalone removed after children sync");
// Directory should be created
assert_directory_exists(&dir_path, "Directory created for script with children");
let init_file = dir_path.join("init.luau");
assert_file_exists(&init_file, "init.luau should exist");
});
}
/// Test: scriptsOnlyMode - Module with deeply nested children
#[test]
fn scripts_only_mode_nested_children() {
run_serve_test("syncback_scripts_only", |session, _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();
let sss_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ServerScriptService")
.map(|(id, _)| *id)
.expect("ServerScriptService should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("ScriptWithChildren");
// Send sync with nested children
let level2 = make_child_instance("Level2", "ModuleScript", Some("return 2"), vec![]);
let level1 = make_child_instance("Level1", "ModuleScript", Some("return 1"), vec![level2]);
let added = make_added_instance(
sss_id,
"ScriptWithChildren",
"Script",
Some("-- Nested children in scriptsOnlyMode\nprint('nested')"),
vec![level1],
);
send_write_request(&session, &info.session_id, sss_id, added);
// Directory should be preserved
assert_directory_exists(&dir_path, "Directory preserved with nested children");
});
}
/// Test: scriptsOnlyMode - rapid syncs don't create duplicates
#[test]
fn scripts_only_mode_rapid_syncs() {
run_serve_test("syncback_scripts_only", |session, _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();
let sss_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ServerScriptService")
.map(|(id, _)| *id)
.expect("ServerScriptService should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("ScriptWithChildren");
let standalone = src_path.join("ScriptWithChildren.server.luau");
// Rapid fire syncs in scriptsOnlyMode
for i in 0..5 {
let added = make_added_instance(
sss_id,
"ScriptWithChildren",
"Script",
Some(&format!(
"-- Rapid sync {} in scriptsOnlyMode\nprint({})",
i, i
)),
vec![],
);
let instance_ref = Ref::new();
let mut added_map = HashMap::new();
added_map.insert(instance_ref, added);
let write_request = WriteRequest {
session_id: info.session_id,
removed: vec![],
added: added_map,
updated: vec![],
};
session
.post_api_write(&write_request)
.expect("Write request should succeed");
std::thread::sleep(std::time::Duration::from_millis(10));
}
std::thread::sleep(std::time::Duration::from_millis(300));
assert_directory_exists(&dir_path, "Directory should exist");
assert_not_exists(&standalone, "No standalone should exist");
});
}
// =============================================================================
// ADDITIONAL EDGE CASES
// =============================================================================
/// Test: Sync ModuleScript with Script children
#[test]
fn module_with_script_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Send sync with Script as child (unusual but valid)
let children = vec![
make_child_instance(
"ChildScript",
"Script",
Some("print('server child')"),
vec![],
),
make_child_instance(
"ChildLocalScript",
"LocalScript",
Some("print('client child')"),
vec![],
),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- Module with script children\nreturn {}"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved");
});
}
/// Test: Sync Script with ModuleScript children
#[test]
fn script_with_module_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirScriptWithChildren");
// Send sync with multiple ModuleScript children
let children = vec![
make_child_instance(
"Config",
"ModuleScript",
Some("return { config = true }"),
vec![],
),
make_child_instance(
"Utils",
"ModuleScript",
Some("return { utils = true }"),
vec![],
),
make_child_instance("Types", "ModuleScript", Some("return {}"), vec![]),
];
let added = make_added_instance(
rs_id,
"DirScriptWithChildren",
"Script",
Some("-- Script with many module children\nprint('service')"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved");
});
}
/// Test: Alternating syncs with and without children
#[test]
fn alternating_children_syncs() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let standalone = src_path.join("DirModuleWithChildren.luau");
// Alternate between syncs with and without children
for i in 0..6 {
let children = if i % 2 == 0 {
vec![make_child_instance(
"TempChild",
"ModuleScript",
Some("return 'temp'"),
vec![],
)]
} else {
vec![]
};
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(&format!("-- Alternating sync {}\nreturn {}", i, i)),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
}
// Directory should always be preserved
assert_directory_exists(&dir_path, "Directory preserved through alternating syncs");
assert_not_exists(&standalone, "No standalone created");
});
}
/// Test: LocalScript directory with Module children
#[test]
fn localscript_with_module_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirLocalScriptWithChildren");
let children = vec![
make_child_instance("UIModule", "ModuleScript", Some("return {}"), vec![]),
make_child_instance("EventHandler", "ModuleScript", Some("return {}"), vec![]),
];
let added = make_added_instance(
rs_id,
"DirLocalScriptWithChildren",
"LocalScript",
Some("-- LocalScript with UI modules\nprint('ui')"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved");
});
}
/// Test: Large number of children
#[test]
fn sync_with_many_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Create many children
let children: Vec<AddedInstance> = (0..20)
.map(|i| {
make_child_instance(
&format!("Child{}", i),
"ModuleScript",
Some(&format!("return {}", i)),
vec![],
)
})
.collect();
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- Module with many children\nreturn {}"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with many children");
});
}
// =============================================================================
// ABSOLUTELY VAPID EDGE CASES
// =============================================================================
/// Test: Empty source string
#[test]
fn sync_with_empty_source() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Empty source string
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(""),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with empty source");
});
}
/// Test: Source with only whitespace
#[test]
fn sync_with_whitespace_only_source() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(" \n\t\n "),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with whitespace source");
});
}
/// Test: Source with unicode characters
#[test]
fn sync_with_unicode_source() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- 你好世界 🎮 émojis everywhere 🚀\nreturn { message = '日本語' }"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with unicode source");
});
}
/// Test: Very long source code
#[test]
fn sync_with_very_long_source() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Generate a very long source (10KB of comments)
let long_source: String = (0..500)
.map(|i| {
format!(
"-- Line {} of extremely verbose and pointless commentary\n",
i
)
})
.collect::<String>()
+ "return {}";
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(&long_source),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with long source");
});
}
/// Test: Child with same name as parent
#[test]
fn sync_child_with_parent_name() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Child has same name as parent (weird but valid)
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 'parent'"),
vec![make_child_instance(
"DirModuleWithChildren", // Same name!
"ModuleScript",
Some("return 'child'"),
vec![],
)],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with same-name child");
});
}
/// Test: Deeply recursive children (10 levels)
#[test]
fn sync_extremely_deep_nesting() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Build 10 levels deep
let mut current = make_child_instance("Level10", "ModuleScript", Some("return 10"), vec![]);
for i in (1..10).rev() {
current = make_child_instance(
&format!("Level{}", i),
"ModuleScript",
Some(&format!("return {}", i)),
vec![current],
);
}
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 'root'"),
vec![current],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with 10-level nesting");
});
}
/// Test: All script types as siblings
#[test]
fn sync_all_script_types_as_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let children = vec![
make_child_instance(
"ChildModule",
"ModuleScript",
Some("return 'module'"),
vec![],
),
make_child_instance("ChildServer", "Script", Some("print('server')"), vec![]),
make_child_instance(
"ChildClient",
"LocalScript",
Some("print('client')"),
vec![],
),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 'parent with all script types'"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with all script types");
});
}
/// Test: Script with only Folder children (no scripts)
#[test]
fn sync_script_with_only_folder_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let children = vec![
make_child_instance("FolderA", "Folder", None, vec![]),
make_child_instance("FolderB", "Folder", None, vec![]),
make_child_instance("FolderC", "Folder", None, vec![]),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 'module with only folders'"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with folder children");
});
}
/// Test: Script with only Value children
#[test]
fn sync_script_with_only_value_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let children = vec![
make_child_instance("StringVal", "StringValue", None, vec![]),
make_child_instance("IntVal", "IntValue", None, vec![]),
make_child_instance("BoolVal", "BoolValue", None, vec![]),
make_child_instance("NumberVal", "NumberValue", None, vec![]),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 'module with only values'"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with value children");
});
}
/// Test: Sync then immediately sync again with different content
#[test]
fn sync_immediate_double_sync() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let standalone = src_path.join("DirModuleWithChildren.luau");
// First sync - don't wait
let added1 = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 1"),
vec![],
);
let instance_ref = Ref::new();
let mut added_map = HashMap::new();
added_map.insert(instance_ref, added1);
let write_request = WriteRequest {
session_id: info.session_id,
removed: vec![],
added: added_map,
updated: vec![],
};
session.post_api_write(&write_request).unwrap();
// Immediately send second sync - no delay!
let added2 = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 2"),
vec![make_child_instance(
"QuickChild",
"ModuleScript",
Some("return 'quick'"),
vec![],
)],
);
let instance_ref2 = Ref::new();
let mut added_map2 = HashMap::new();
added_map2.insert(instance_ref2, added2);
let write_request2 = WriteRequest {
session_id: info.session_id,
removed: vec![],
added: added_map2,
updated: vec![],
};
session.post_api_write(&write_request2).unwrap();
std::thread::sleep(std::time::Duration::from_millis(300));
assert_directory_exists(&dir_path, "Directory should exist after double sync");
assert_not_exists(&standalone, "No standalone after double sync");
});
}
/// Test: 50 rapid-fire syncs
#[test]
fn stress_test_50_rapid_syncs() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let standalone = src_path.join("DirModuleWithChildren.luau");
for i in 0..50 {
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(&format!("return {}", i)),
vec![],
);
let instance_ref = Ref::new();
let mut added_map = HashMap::new();
added_map.insert(instance_ref, added);
let write_request = WriteRequest {
session_id: info.session_id,
removed: vec![],
added: added_map,
updated: vec![],
};
session.post_api_write(&write_request).unwrap();
// No delay between syncs!
}
std::thread::sleep(std::time::Duration::from_millis(500));
assert_directory_exists(&dir_path, "Directory survived 50 rapid syncs");
assert_not_exists(&standalone, "No standalone after 50 rapid syncs");
});
}
/// Test: Sync with newlines in content that look like multiple files
#[test]
fn sync_with_tricky_newlines() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let tricky_source = r#"
-- This looks like it could be multiple files
-- File: SomeOtherThing.luau
return {
-- But it's not!
anotherFile = [[
-- File: YetAnotherThing.luau
return "nested string that looks like a file"
]]
}
"#;
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(tricky_source),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with tricky newlines");
});
}
/// Test: Child has no source property (non-script child)
#[test]
fn sync_with_sourceless_children() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let children = vec![
make_child_instance("RemoteEvent", "RemoteEvent", None, vec![]),
make_child_instance("RemoteFunction", "RemoteFunction", None, vec![]),
make_child_instance("BindableEvent", "BindableEvent", None, vec![]),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 'module with remote children'"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with sourceless children");
});
}
/// Test: Very long instance name
#[test]
fn sync_with_very_long_name() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
// 100 character name
let long_name = "A".repeat(100);
let dir_path = src_path.join(&long_name);
let standalone = src_path.join(format!("{}.luau", long_name));
let added = make_added_instance(
rs_id,
&long_name,
"ModuleScript",
Some("return 'very long name'"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
// Should create one or the other, not both
let dir_exists = dir_path.exists();
let standalone_exists = standalone.exists();
assert!(
!(dir_exists && standalone_exists),
"Should not create both directory and standalone for long name"
);
});
}
/// Test: Instance name with dots (looks like file extension)
#[test]
fn sync_name_with_dots() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Child with dots in name
let children = vec![
make_child_instance(
"config.dev.local",
"ModuleScript",
Some("return 'dev'"),
vec![],
),
make_child_instance(
"data.v2.backup",
"ModuleScript",
Some("return 'backup'"),
vec![],
),
];
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return 'parent'"),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with dotted child names");
});
}
/// Test: Sync standalone then immediately sync directory version
/// When children are added, standalone must convert to directory format.
#[test]
fn sync_standalone_then_directory_format() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let standalone = src_path.join("StandaloneModule.luau");
let dir_path = src_path.join("StandaloneModule");
assert_file_exists(&standalone, "Standalone exists initially");
// First sync - no children (standalone format preserved)
let added1 = make_added_instance(
rs_id,
"StandaloneModule",
"ModuleScript",
Some("return 'no children'"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added1);
// Standalone should still exist after sync without children
assert_file_exists(
&standalone,
"Standalone preserved after sync without children",
);
// Second sync - with children (converts to directory)
let added2 = make_added_instance(
rs_id,
"StandaloneModule",
"ModuleScript",
Some("return 'with children'"),
vec![make_child_instance(
"Child",
"ModuleScript",
Some("return 'child'"),
vec![],
)],
);
send_write_request(&session, &info.session_id, rs_id, added2);
// Standalone should be removed (converted to directory)
assert_not_exists(&standalone, "Standalone removed after children sync");
// Directory should now exist
assert_directory_exists(&dir_path, "Directory created after children sync");
let init_file = dir_path.join("init.luau");
assert_file_exists(&init_file, "init.luau should exist");
});
}
/// Test: Configuration instance sync
#[test]
fn sync_configuration_instance() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModelWithChildren");
let added = make_added_instance(
rs_id,
"DirModelWithChildren",
"Configuration",
None,
vec![make_child_instance("Setting", "BoolValue", None, vec![])],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Configuration directory preserved");
});
}
/// Test: Script that returns nothing (no return statement)
#[test]
fn sync_script_no_return() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// ModuleScript with no return (bad practice but valid)
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("-- This module doesn't return anything\nlocal x = 1\nlocal y = 2"),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved with no-return module");
});
}
/// Test: Sync with syntax errors in source
#[test]
fn sync_with_syntax_errors() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
// Lua with syntax error
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some("return { unclosed = "),
vec![],
);
send_write_request(&session, &info.session_id, rs_id, added);
assert_directory_exists(&dir_path, "Directory preserved despite syntax error");
});
}
/// Test: Multiple syncs with varying children counts
#[test]
fn sync_varying_children_counts() {
run_serve_test("syncback_format_transitions", |session, _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();
let rs_id = read_response
.instances
.iter()
.find(|(_, inst)| inst.class_name == "ReplicatedStorage")
.map(|(id, _)| *id)
.expect("ReplicatedStorage should exist");
let src_path = session.path().join("src");
let dir_path = src_path.join("DirModuleWithChildren");
let standalone = src_path.join("DirModuleWithChildren.luau");
// Sync with 0, 5, 0, 10, 0, 1 children
let child_counts = [0, 5, 0, 10, 0, 1];
for count in child_counts {
let children: Vec<AddedInstance> = (0..count)
.map(|i| {
make_child_instance(
&format!("Child{}", i),
"ModuleScript",
Some("return 1"),
vec![],
)
})
.collect();
let added = make_added_instance(
rs_id,
"DirModuleWithChildren",
"ModuleScript",
Some(&format!("return {{ children = {} }}", count)),
children,
);
send_write_request(&session, &info.session_id, rs_id, added);
}
assert_directory_exists(&dir_path, "Directory after varying children counts");
assert_not_exists(&standalone, "No standalone after varying children counts");
});
}