use pasta_lua::loader::PersistenceConfig;
use pasta_lua::runtime::persistence;
use tempfile::TempDir;
use crate::common;
#[test]
fn test_persistence_json_roundtrip() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("save.json");
let data = serde_json::json!({
"player_name": "Alice",
"score": 1000,
"flags": {
"tutorial_complete": true
}
});
persistence::save_to_file(&data, &file_path, false).unwrap();
let loaded = persistence::load_from_file(&file_path).unwrap();
assert_eq!(loaded["player_name"], "Alice");
assert_eq!(loaded["score"], 1000);
assert_eq!(loaded["flags"]["tutorial_complete"], true);
}
#[test]
fn test_persistence_obfuscated_roundtrip() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("save.dat");
let data = serde_json::json!({
"secret": "hidden_value",
"inventory": ["sword", "shield", "potion"]
});
persistence::save_to_file(&data, &file_path, true).unwrap();
let raw = std::fs::read(&file_path).unwrap();
assert!(raw.len() >= 2);
assert_eq!(raw[0], 0x1f);
assert_eq!(raw[1], 0x8b);
let loaded = persistence::load_from_file(&file_path).unwrap();
assert_eq!(loaded["secret"], "hidden_value");
assert_eq!(loaded["inventory"][0], "sword");
}
#[test]
fn test_persistence_config_effective_path() {
let config = PersistenceConfig::default();
assert_eq!(config.effective_file_path(), "profile/pasta/save/save.json");
let config = PersistenceConfig {
obfuscate: true,
file_path: "profile/pasta/save/save.json".to_string(),
debug_mode: false,
};
assert_eq!(config.effective_file_path(), "profile/pasta/save/save.dat");
let config = PersistenceConfig {
obfuscate: true,
file_path: "profile/pasta/save/save.dat".to_string(),
debug_mode: false,
};
assert_eq!(config.effective_file_path(), "profile/pasta/save/save.dat");
}
#[test]
fn test_persistence_creates_directories() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir
.path()
.join("nested")
.join("deep")
.join("save.json");
let data = serde_json::json!({"test": true});
persistence::save_to_file(&data, &file_path, false).unwrap();
assert!(file_path.exists());
let loaded = persistence::load_from_file(&file_path).unwrap();
assert_eq!(loaded["test"], true);
}
#[test]
fn test_lua_persistence_module() {
let lua = common::e2e_helpers::create_runtime_with_finalize().unwrap();
let result: mlua::Table = lua
.load(r#"return require("@pasta_persistence").load()"#)
.eval()
.unwrap();
assert_eq!(result.len().unwrap(), 0);
lua.load(
r#"
local p = require("@pasta_persistence")
local data = { name = "Test", count = 42 }
local ok, err = p.save(data)
assert(ok, err)
"#,
)
.exec()
.unwrap();
let result: mlua::Table = lua
.load(r#"return require("@pasta_persistence").load()"#)
.eval()
.unwrap();
let name: String = result.get("name").unwrap();
let count: i32 = result.get("count").unwrap();
assert_eq!(name, "Test");
assert_eq!(count, 42);
}
#[test]
fn test_pasta_save_ctx_integration() {
let lua = common::e2e_helpers::create_runtime_with_finalize().unwrap();
let save_type: String = lua
.load(r#"return type(require("pasta.save"))"#)
.eval()
.unwrap();
assert_eq!(save_type, "table");
lua.load(
r#"
local SAVE = require("pasta.save")
local ACT = require("pasta.act")
local act = ACT.new({})
-- They should be the same reference
assert(act.save == SAVE, "act.save should reference pasta.save")
-- Changes in one should reflect in the other
act.save.test_key = "test_value"
assert(SAVE.test_key == "test_value", "Changes should reflect")
"#,
)
.exec()
.unwrap();
}
#[test]
fn test_store_save_deprecated() {
let lua = common::e2e_helpers::create_runtime_with_finalize().unwrap();
let save_value: mlua::Value = lua
.load(r#"return require("pasta.store").save"#)
.eval()
.unwrap();
assert!(save_value.is_nil(), "STORE.save should be nil (deprecated)");
}
fn register_module_in_temp(
temp_dir: &TempDir,
file_path: &str,
obfuscate: bool,
) -> (mlua::Lua, mlua::Table) {
let config = PersistenceConfig {
obfuscate,
file_path: file_path.to_string(),
debug_mode: false,
};
let lua = mlua::Lua::new();
let module = persistence::register(&lua, &config, temp_dir.path()).unwrap();
(lua, module)
}
#[test]
fn test_persistence_load_corrupted_json_returns_empty_table() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("save.json"), "{ not valid json !!").unwrap();
let (_lua, module) = register_module_in_temp(&temp_dir, "save.json", false);
let load_fn: mlua::Function = module.get("load").unwrap();
let result: mlua::Table = load_fn.call(()).unwrap();
assert_eq!(result.len().unwrap(), 0, "corrupted file should degrade to empty table");
}
#[test]
fn test_persistence_load_non_object_json_returns_empty_table() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(temp_dir.path().join("save.json"), r#""just a string""#).unwrap();
let (_lua, module) = register_module_in_temp(&temp_dir, "save.json", false);
let load_fn: mlua::Function = module.get("load").unwrap();
let result: mlua::Table = load_fn.call(()).unwrap();
assert_eq!(
result.len().unwrap(),
0,
"non-object JSON should degrade to empty table"
);
}
#[test]
fn test_persistence_load_corrupted_gzip_returns_empty_table() {
let temp_dir = TempDir::new().unwrap();
std::fs::write(
temp_dir.path().join("save.dat"),
[0x1f, 0x8b, 0xff, 0xff, 0x00, 0x12, 0x34],
)
.unwrap();
let (_lua, module) = register_module_in_temp(&temp_dir, "save.dat", true);
let load_fn: mlua::Function = module.get("load").unwrap();
let result: mlua::Table = load_fn.call(()).unwrap();
assert_eq!(
result.len().unwrap(),
0,
"corrupted gzip should degrade to empty table"
);
}
#[test]
fn test_persistence_module_obfuscated_roundtrip() {
let temp_dir = TempDir::new().unwrap();
let (lua, module) = register_module_in_temp(&temp_dir, "save.dat", true);
let data: mlua::Table = lua.create_table().unwrap();
data.set("secret", "hidden").unwrap();
data.set("count", 3).unwrap();
let save_fn: mlua::Function = module.get("save").unwrap();
let (ok, err): (Option<bool>, Option<String>) = save_fn.call(data).unwrap();
assert_eq!(ok, Some(true), "save should succeed: {err:?}");
let raw = std::fs::read(temp_dir.path().join("save.dat")).unwrap();
assert!(raw.len() >= 2);
assert_eq!(&raw[0..2], &[0x1f, 0x8b], "file should be gzip compressed");
let load_fn: mlua::Function = module.get("load").unwrap();
let result: mlua::Table = load_fn.call(()).unwrap();
let secret: String = result.get("secret").unwrap();
let count: i64 = result.get("count").unwrap();
assert_eq!(secret, "hidden");
assert_eq!(count, 3);
}
#[test]
fn test_persistence_save_failure_returns_nil_and_message() {
let temp_dir = TempDir::new().unwrap();
std::fs::create_dir(temp_dir.path().join("save.json")).unwrap();
let (lua, module) = register_module_in_temp(&temp_dir, "save.json", false);
let data: mlua::Table = lua.create_table().unwrap();
data.set("k", "v").unwrap();
let save_fn: mlua::Function = module.get("save").unwrap();
let (ok, err): (Option<bool>, Option<String>) = save_fn.call(data).unwrap();
assert_eq!(ok, None, "save should report failure");
let err = err.expect("error message should be present");
assert!(
err.contains("Failed to save"),
"error message should describe the failure: {err}"
);
}