v26.10.2 修复bug,简化项目

This commit is contained in:
LIm1tZz7 committed 2026-10-09 09:27:44 +08:00
1 parent ff3dbddd13
commit fc9bbc20e4
26 files changed
+557 -299

No files matched your search

+59
View File
@@ -1,9 +1,45 @@
use chusql_core_storage::btree::{fits_in_page, DiskBTree, MAX_KEY_BYTES};
use chusql_core_storage::config::{DEFAULT_BTREE_ORDER, DEFAULT_PAGE_SIZE};
use chusql_core_storage::page::DEFAULT_POOL_SIZE;
use std::io::{ErrorKind, Seek, SeekFrom, Write};
// B+ 树测试:字节键、重复键、范围扫描、重开与配置校验。
/// 损坏节点返回数据错误且重复读取不 panic
#[test]
fn malformed_node_fields_return_invalid_data() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("corrupt.idx");
{
let mut tree = open(dir.path(), "corrupt.idx");
tree.insert(b"x", 1).unwrap();
tree.insert(b"y", 2).unwrap();
}
let original = std::fs::read(&path).unwrap();
let cell = u16::from_le_bytes([original[DEFAULT_PAGE_SIZE + 11], original[DEFAULT_PAGE_SIZE + 12]]) as usize;
for (name, offset, bytes) in [
("slot count", 1, vec![255, 255]),
("node kind", 0, vec![2]),
("cell overlaps slots", 11, vec![0, 0]),
("cell beyond page", 11, vec![255, 15]),
("overlapping cells", 13, (cell as u16).to_le_bytes().to_vec()),
("key length", cell, vec![255, 255]),
("key order", cell + 2, vec![b'z']),
("zero child", 0, vec![1]),
] {
std::fs::write(&path, &original).unwrap();
let mut file = std::fs::OpenOptions::new().write(true).open(&path).unwrap();
file.seek(SeekFrom::Start((DEFAULT_PAGE_SIZE + offset) as u64)).unwrap();
file.write_all(&bytes).unwrap();
drop(file);
let mut tree = open(dir.path(), "corrupt.idx");
for _ in 0..2 {
let error = tree.get(b"x").unwrap_err();
assert_eq!(error.kind(), ErrorKind::InvalidData, "{name}: {error}");
}
}
}
/// 按默认参数打开指定索引文件
fn open(dir: &std::path::Path, name: &str) -> DiskBTree {
DiskBTree::open(dir.join(name), DEFAULT_PAGE_SIZE, DEFAULT_BTREE_ORDER, DEFAULT_POOL_SIZE).unwrap()
@@ -14,6 +50,29 @@ fn key(n: i64) -> [u8; 8] {
(n as u64 ^ (1u64 << 63)).to_be_bytes()
}
/// 重复键跨页后仍能完整查找和删除
#[test]
fn duplicate_keys_across_splits() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("split-duplicates.idx");
let mut tree = DiskBTree::open(&path, DEFAULT_PAGE_SIZE, 4, DEFAULT_POOL_SIZE).unwrap();
for value in (0..100).rev() {
tree.insert(b"same", value).unwrap();
}
drop(tree);
let mut tree = DiskBTree::open(&path, DEFAULT_PAGE_SIZE, 4, DEFAULT_POOL_SIZE).unwrap();
assert_eq!(tree.get(b"same").unwrap(), Some(0));
assert_eq!(tree.get_all(b"same").unwrap(), (0..100).collect::<Vec<_>>());
assert_eq!(tree.scan(Some(b"same"), Some(b"same")).unwrap().len(), 100);
for value in 0..100 {
assert!(tree.delete(b"same", value).unwrap());
}
assert!(!tree.delete(b"same", 0).unwrap());
drop(tree);
let mut tree = DiskBTree::open(&path, DEFAULT_PAGE_SIZE, 4, DEFAULT_POOL_SIZE).unwrap();
assert!(tree.get_all(b"same").unwrap().is_empty());
}
/// 空树查不到,遍历为空
#[test]
fn empty_tree_get() {
+4 -3
View File
@@ -187,13 +187,14 @@ fn load_without_explicit_path_uses_builtin_defaults() {
}
}
/// [server] 分区归别的层管;本层拼写错误要报错
/// 忽略其他层分区并拒绝存储配置拼写错误
#[test]
fn server_section_belongs_to_another_layer() {
let _guard = env_guard();
let db_server = "[server]\nlisten_host = \"0.0.0.0\"\nport = 7778\nmax_rows = 10\n";
let db_server = "[page]\nsize = 8192\n[storage]\ndata_dir = \"../data\"\n[server]\nlisten_host = \"0.0.0.0\"\nport = 7778\nmax_rows = 10\n[web]\nport = 7778\n";
let loaded = config::resolve(Some(db_server), None).unwrap();
assert_eq!(loaded.config.data_dir, config::default_data_dir());
assert_eq!(loaded.config.page_size, 8192);
assert_eq!(loaded.config.data_dir, PathBuf::from("../data"));
let typo = "[storage]\ndata_dirs = \"oops\"\n";
assert!(config::resolve(Some(typo), None).is_err());
+32
View File
@@ -5,6 +5,38 @@ use chusql_core_storage::runtime::Storage;
// 进程内存储集成测试:协议请求、索引、账号与配置。
/// 检查点损坏不冒充零 LSN且可恢复读取
#[test]
fn system_status_reports_checkpoint_read_errors() -> Result<(), Box<dyn std::error::Error>> {
let (_server, data) = start_server();
let mut connection = connect()?;
let checkpoint = data.path().join("system/wal.checkpoint");
let initial: serde_json::Value = serde_json::from_str(&send_raw(&mut connection, r#"{"method":"system_status"}"#))?;
assert_eq!(initial["last_lsn"], 0);
request_ok(&mut connection, serde_json::json!({"method":"object_acl_replace","expected":null,"payload":"{}"}))?;
let original = std::fs::read(&checkpoint)?;
let before: serde_json::Value = serde_json::from_str(&send_raw(&mut connection, r#"{"method":"system_status"}"#))?;
assert_eq!(before["status"], "system");
assert!(before["last_lsn"].as_u64().unwrap() > 0);
std::fs::write(&checkpoint, b"bad")?;
let broken: serde_json::Value = serde_json::from_str(&send_raw(&mut connection, r#"{"method":"system_status"}"#))?;
assert_eq!(broken["status"], "error");
assert!(broken["message"].as_str().unwrap().contains("invalid WAL checkpoint length"));
assert!(broken.get("last_lsn").is_none());
assert!(send_raw(&mut connection, r#"{"method":"ping"}"#).contains("pong"));
std::fs::write(&checkpoint, &original)?;
let restored: serde_json::Value = serde_json::from_str(&send_raw(&mut connection, r#"{"method":"system_status"}"#))?;
assert_eq!(restored, before);
std::fs::remove_file(&checkpoint)?;
std::fs::create_dir(&checkpoint)?;
let unreadable: serde_json::Value = serde_json::from_str(&send_raw(&mut connection, r#"{"method":"system_status"}"#))?;
assert_eq!(unreadable["status"], "error");
assert!(unreadable.get("last_lsn").is_none());
std::fs::remove_dir(&checkpoint)?;
std::fs::write(&checkpoint, original)?;
Ok(())
}
/// 权限快照支持跨页文本并拒绝旧版本覆盖
#[test]
fn object_acl_snapshots_are_private_and_compare_versions() -> Result<(), Box<dyn std::error::Error>> {