FSKit简化版数据验证指南:结构与意义详解
核心内容: 1. 数据结构说明(file_nodes表) 2. 字段意义详解(node_id/label/parent_id/aliases_json/file_size) 3. 4种验证方法(query_node/query_children/read_file/statfs) 4. 验证步骤流程(6步完整流程) 5. 数据意义解析(技术+业务层面) 6. 创建验证测试代码(5个warren_tests) 关键发现: - node_id:32字符UUID,确定性生成 - parent_id:NULL为根节点,有值为子节点 - aliases_json.path:文件实际路径(重要!) - 数据规模:12659 nodes(801 folders + 11857 files) 下一步: cargo test --lib fskit::warren_tests
This commit is contained in:
917
docs/FSKIT_SIMPLE_DATA_VALIDATION_GUIDE.md
Normal file
917
docs/FSKIT_SIMPLE_DATA_VALIDATION_GUIDE.md
Normal file
@@ -0,0 +1,917 @@
|
||||
# FSKit 简化版数据验证指南
|
||||
|
||||
**日期**: 2026-05-18
|
||||
**目标**: 验证 SQLite backend 数据正确性与完整性
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据结构说明
|
||||
|
||||
### file_nodes 表结构
|
||||
|
||||
```sql
|
||||
CREATE TABLE file_nodes (
|
||||
node_id TEXT PRIMARY KEY, -- 节点唯一标识(UUID)
|
||||
label TEXT NOT NULL, -- 文件/文件夹名称
|
||||
aliases_json TEXT, -- JSON格式的扩展信息
|
||||
sha256 TEXT, -- SHA256 hash值
|
||||
file_uuid TEXT, -- 文件UUID(可选)
|
||||
file_size INTEGER, -- 文件大小(字节)
|
||||
registered_at INTEGER, -- 注册时间(timestamp)
|
||||
parent_id TEXT, -- 父节点ID(NULL为根节点)
|
||||
children_json TEXT, -- 子节点列表(JSON)
|
||||
node_type TEXT NOT NULL, -- 节点类型:folder/file
|
||||
icon TEXT, -- 图标名称(可选)
|
||||
color TEXT, -- 颜色(可选)
|
||||
bg_color TEXT, -- 背景颜色(可选)
|
||||
created_at INTEGER, -- 创建时间
|
||||
updated_at INTEGER, -- 更新时间
|
||||
sort_order INTEGER DEFAULT 0 -- 排序顺序
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 数据字段含义详解
|
||||
|
||||
### node_id(节点唯一标识)
|
||||
|
||||
**格式**: 32字符UUID
|
||||
|
||||
**示例**: `8b1ede3cd6970f02fa85b8e34b682caf`
|
||||
|
||||
**生成方式**:
|
||||
```rust
|
||||
// src/scan.rs 中的生成逻辑
|
||||
SHA256(path | filename | mac_address | mtime)
|
||||
.chars()
|
||||
.take(32)
|
||||
```
|
||||
|
||||
**特性**:
|
||||
- ✅ 确定性(同一文件 = 同一UUID)
|
||||
- ✅ 唯一性(不同文件 = 不同UUID)
|
||||
- ✅ 支持增量导入(无需外部API)
|
||||
|
||||
**用途**:
|
||||
- SQLite primary key
|
||||
- FSVolume query_node(node_id)
|
||||
- Finder 文件识别
|
||||
|
||||
---
|
||||
|
||||
### label(文件/文件夹名称)
|
||||
|
||||
**格式**: 文本字符串
|
||||
|
||||
**示例**:
|
||||
```
|
||||
"Test_Plan_ME5.docx"
|
||||
"Marketing"
|
||||
"Videos"
|
||||
"demo.mp4"
|
||||
```
|
||||
|
||||
**特性**:
|
||||
- ✅ 用户可见名称
|
||||
- ✅ 支持中文/英文/数字
|
||||
- ✅ 包含文件扩展名
|
||||
|
||||
**用途**:
|
||||
- Finder 显示名称
|
||||
- FSVolume enumerate_directory 输出
|
||||
- 搜索与排序依据
|
||||
|
||||
---
|
||||
|
||||
### node_type(节点类型)
|
||||
|
||||
**格式**: 枚举值
|
||||
|
||||
**取值**:
|
||||
- `folder` - 资料夹节点
|
||||
- `file` - 文件节点
|
||||
|
||||
**统计**(warren.sqlite):
|
||||
```
|
||||
folder: 801 个
|
||||
file: 11857 个
|
||||
total: 12659 个
|
||||
```
|
||||
|
||||
**用途**:
|
||||
- 决定 FSItem 类型(FSItemType::Directory / FSItemType::File)
|
||||
- 决定是否可枚举子节点
|
||||
- 决定是否可读取文件内容
|
||||
|
||||
---
|
||||
|
||||
### parent_id(父节点ID)
|
||||
|
||||
**格式**: 32字符UUID 或 NULL
|
||||
|
||||
**特性**:
|
||||
- NULL = 根节点(root folder)
|
||||
- 有值 = 子节点
|
||||
|
||||
**层级关系**:
|
||||
```
|
||||
root_id (NULL)
|
||||
├── node_id_1 (parent_id = root_id)
|
||||
│ ├── node_id_2 (parent_id = node_id_1)
|
||||
│ └── node_id_3 (parent_id = node_id_1)
|
||||
└── node_id_4 (parent_id = root_id)
|
||||
├── node_id_5 (parent_id = node_id_4)
|
||||
└── node_id_6 (parent_id = node_id_4)
|
||||
```
|
||||
|
||||
**用途**:
|
||||
- 构建文件树结构
|
||||
- query_children(parent_id) 查询
|
||||
- 目录枚举逻辑
|
||||
|
||||
---
|
||||
|
||||
### aliases_json(扩展信息)
|
||||
|
||||
**格式**: JSON字符串
|
||||
|
||||
**结构**:
|
||||
```json
|
||||
{
|
||||
"path": "/Users/accusys/momentry/var/sftpgo/data/warren/Test_Plan_ME5.docx",
|
||||
"alias_zh_tw": "测试计划",
|
||||
"alias_en": "Test Plan",
|
||||
"alias_ja": "テスト計画"
|
||||
}
|
||||
```
|
||||
|
||||
**关键字段**:
|
||||
- `path` - 文件在磁盘上的实际路径(重要!)
|
||||
|
||||
**用途**:
|
||||
- read_file(node_id) 读取文件内容
|
||||
- 文件路径解析
|
||||
- 多语言别名支持
|
||||
|
||||
---
|
||||
|
||||
### file_size(文件大小)
|
||||
|
||||
**格式**: 整数(字节)
|
||||
|
||||
**示例**:
|
||||
```
|
||||
1024 - 1KB
|
||||
1048576 - 1MB
|
||||
26214400 - 25MB
|
||||
```
|
||||
|
||||
**特性**:
|
||||
- folder节点: NULL
|
||||
- file节点: 实际文件大小
|
||||
|
||||
**用途**:
|
||||
- FSVolume get_attributes 输出
|
||||
- Finder 文件大小显示
|
||||
- statfs 总大小统计
|
||||
|
||||
---
|
||||
|
||||
### sha256(文件哈希)
|
||||
|
||||
**格式**: 64字符十六进制字符串
|
||||
|
||||
**示例**: `355a063b697a812742fae2a021cdda5c355a063b697a812742fae2a021cdda5c`
|
||||
|
||||
**生成方式**:
|
||||
```rust
|
||||
// src/scan.rs hash 计算
|
||||
use sha2::{Sha256, Digest};
|
||||
|
||||
let mut hasher = Sha256::new();
|
||||
hasher.update(file_content);
|
||||
let hash = hasher.finalize();
|
||||
```
|
||||
|
||||
**用途**:
|
||||
- 文件完整性验证
|
||||
- 重复文件检测
|
||||
- 版本控制依据
|
||||
|
||||
---
|
||||
|
||||
### created_at / updated_at(时间戳)
|
||||
|
||||
**格式**: Unix timestamp(秒)
|
||||
|
||||
**示例**:
|
||||
```
|
||||
1715788800 - 2024-05-15 08:00:00 UTC
|
||||
1744876800 - 2025-05-18 12:00:00 UTC
|
||||
```
|
||||
|
||||
**用途**:
|
||||
- FSVolume get_attributes 输出
|
||||
- Finder 创建/修改时间显示
|
||||
- 文件排序依据
|
||||
|
||||
---
|
||||
|
||||
## 3. 数据验证方法
|
||||
|
||||
### 方法1:query_node(节点查询)
|
||||
|
||||
**验证目标**: 确认节点存在且数据正确
|
||||
|
||||
**测试代码**:
|
||||
```rust
|
||||
#[test]
|
||||
fn test_query_warren_root() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 查询根节点
|
||||
let root_id = "8b1ede3cd6970f02fa85b8e34b682caf";
|
||||
let root = fs.query_node(root_id);
|
||||
|
||||
assert!(root.is_some());
|
||||
|
||||
let root_node = root.unwrap();
|
||||
assert_eq!(root_node.node_type, "folder");
|
||||
assert!(root_node.label.contains("Home"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_query_warren_file() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 查询文件节点
|
||||
let file_id = "test_file_node_id";
|
||||
let file = fs.query_node(file_id);
|
||||
|
||||
assert!(file.is_some());
|
||||
|
||||
let file_node = file.unwrap();
|
||||
assert_eq!(file_node.node_type, "file");
|
||||
assert!(file_node.file_size.is_some());
|
||||
assert!(file_node.file_size.unwrap() > 0);
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 根节点存在
|
||||
- ✅ node_type正确
|
||||
- ✅ label包含用户ID
|
||||
|
||||
---
|
||||
|
||||
### 方法2:query_children(子节点查询)
|
||||
|
||||
**验证目标**: 确认层级关系正确
|
||||
|
||||
**测试代码**:
|
||||
```rust
|
||||
#[test]
|
||||
fn test_query_warren_children() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 查询根节点的子节点
|
||||
let root_id = "root_node_id";
|
||||
let children = fs.query_children(root_id);
|
||||
|
||||
// 预期:801 folders + 11857 files
|
||||
assert!(children.len() > 1000);
|
||||
|
||||
// 检查子节点类型
|
||||
let folders = children.iter().filter(|c| c.node_type == "folder").count();
|
||||
let files = children.iter().filter(|c| c.node_type == "file").count();
|
||||
|
||||
println!("Folders: {}, Files: {}", folders, files);
|
||||
|
||||
assert!(folders > 0);
|
||||
assert!(files > folders);
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 子节点数量正确(>1000)
|
||||
- ✅ folders + files = 总节点数
|
||||
- ✅ parent_id正确关联
|
||||
|
||||
---
|
||||
|
||||
### 方法3:read_file(文件读取)
|
||||
|
||||
**验证目标**: 确认 aliases.json.path 可读取
|
||||
|
||||
**测试代码**:
|
||||
```rust
|
||||
#[test]
|
||||
fn test_read_warren_file() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 选择一个小文件测试
|
||||
let file_id = "small_text_file_node_id";
|
||||
let content = fs.read_file(file_id);
|
||||
|
||||
assert!(content.is_some());
|
||||
|
||||
let data = content.unwrap();
|
||||
assert!(data.len() > 0);
|
||||
|
||||
// 检查文件内容
|
||||
let text = String::from_utf8(data).unwrap();
|
||||
println!("File content: {}", text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_warren_large_file() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 测试大文件(视频/图片)
|
||||
let video_id = "video_file_node_id";
|
||||
let content = fs.read_file(video_id);
|
||||
|
||||
assert!(content.is_some());
|
||||
|
||||
let data = content.unwrap();
|
||||
assert!(data.len() > 1_000_000); // > 1MB
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
- ✅ 文件内容可读取
|
||||
- ✅ 文件大小正确
|
||||
- ✅ 文件路径解析成功
|
||||
|
||||
---
|
||||
|
||||
### 方法4:statfs(统计验证)
|
||||
|
||||
**验证目标**: 确认总体统计正确
|
||||
|
||||
**测试代码**:
|
||||
```rust
|
||||
#[test]
|
||||
fn test_warren_statfs() {
|
||||
let conn = Connection::open("data/users/warren.sqlite").unwrap();
|
||||
let volume = MarkBaseVolume::new(conn, "warren".to_string());
|
||||
|
||||
let (total_nodes, total_size) = volume.statfs();
|
||||
|
||||
println!("Total nodes: {}", total_nodes);
|
||||
println!("Total size: {} bytes ({:.2} GB)",
|
||||
total_size,
|
||||
total_size as f64 / 1_073_741_824.0
|
||||
);
|
||||
|
||||
assert_eq!(total_nodes, 12659);
|
||||
assert!(total_size > 0);
|
||||
}
|
||||
```
|
||||
|
||||
**预期结果**:
|
||||
- ✅ total_nodes = 12659
|
||||
- ✅ total_size > 0
|
||||
- ✅ 统计数据准确
|
||||
|
||||
---
|
||||
|
||||
## 4. 数据验证步骤
|
||||
|
||||
### Step 1:准备测试环境
|
||||
|
||||
**前提条件**:
|
||||
- ✅ warren.sqlite 存在(12MB)
|
||||
- ✅ MarkBaseFS struct 编译成功
|
||||
- ✅ SQLite connection 可用
|
||||
|
||||
**检查数据库**:
|
||||
```bash
|
||||
# 确认数据库存在
|
||||
ls -lh data/users/warren.sqlite
|
||||
|
||||
# 检查节点总数
|
||||
sqlite3 data/users/warren.sqlite "SELECT COUNT(*) FROM file_nodes"
|
||||
|
||||
# 检查节点类型分布
|
||||
sqlite3 data/users/warren.sqlite "
|
||||
SELECT node_type, COUNT(*)
|
||||
FROM file_nodes
|
||||
GROUP BY node_type
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2:执行基础验证
|
||||
|
||||
**测试命令**:
|
||||
```bash
|
||||
cargo test --lib fskit::filesystem::test_query_warren_root
|
||||
cargo test --lib fskit::filesystem::test_query_warren_children
|
||||
cargo test --lib fskit::filesystem::test_read_warren_file
|
||||
cargo test --lib fskit::volume::test_warren_statfs
|
||||
```
|
||||
|
||||
**验证点**:
|
||||
1. ✅ query_node 返回正确节点
|
||||
2. ✅ query_children 返回正确数量
|
||||
3. ✅ read_file 返回正确内容
|
||||
4. ✅ statfs 返回正确统计
|
||||
|
||||
---
|
||||
|
||||
### Step 3:深度验证
|
||||
|
||||
**检查数据完整性**:
|
||||
```bash
|
||||
# 检查所有节点都有 parent_id 关联
|
||||
sqlite3 data/users/warren.sqlite "
|
||||
SELECT COUNT(*) FROM file_nodes
|
||||
WHERE parent_id IS NULL
|
||||
AND node_type = 'folder'
|
||||
"
|
||||
|
||||
# 预期:至少有1个根节点(root folder)
|
||||
```
|
||||
|
||||
**检查 aliases.json 完整性**:
|
||||
```bash
|
||||
# 检查所有文件节点都有 path
|
||||
sqlite3 data/users/warren.sqlite "
|
||||
SELECT COUNT(*) FROM file_nodes
|
||||
WHERE node_type = 'file'
|
||||
AND aliases_json IS NOT NULL
|
||||
AND aliases_json LIKE '%path%'
|
||||
"
|
||||
|
||||
# 预期:接近 11857(所有文件节点)
|
||||
```
|
||||
|
||||
**检查文件大小一致性**:
|
||||
```bash
|
||||
# 检查 file_size 与实际文件大小匹配
|
||||
sqlite3 data/users/warren.sqlite "
|
||||
SELECT node_id, label, file_size,
|
||||
aliases_json
|
||||
FROM file_nodes
|
||||
WHERE node_type = 'file'
|
||||
AND file_size IS NOT NULL
|
||||
LIMIT 5
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据意义详解
|
||||
|
||||
### 节点ID(node_id)的意义
|
||||
|
||||
**技术意义**:
|
||||
- SQLite primary key
|
||||
- 文件系统唯一标识
|
||||
- 支持快速查询(索引)
|
||||
|
||||
**业务意义**:
|
||||
- 用户文件追踪
|
||||
- 版本控制依据
|
||||
- 增量导入识别
|
||||
|
||||
**示例数据**:
|
||||
```
|
||||
node_id: 8b1ede3cd6970f02fa85b8e34b682caf
|
||||
含义: Home文件夹的唯一标识
|
||||
用途:
|
||||
- query_node(node_id) → 查询节点详情
|
||||
- query_children(node_id) → 查询子节点
|
||||
- Finder 显示文件树
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 标签(label)的意义
|
||||
|
||||
**技术意义**:
|
||||
- 用户可见名称
|
||||
- 文件系统显示名称
|
||||
- 搜索索引依据
|
||||
|
||||
**业务意义**:
|
||||
- 用户文件命名
|
||||
- 多语言支持(aliases)
|
||||
- 业务分类依据
|
||||
|
||||
**示例数据**:
|
||||
```
|
||||
label: Test_Plan_ME5.docx
|
||||
含义: 文件名称 + 扩展名
|
||||
用途:
|
||||
- Finder 显示名称
|
||||
- 文件搜索
|
||||
- 业务文档识别
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 父节点ID(parent_id)的意义
|
||||
|
||||
**技术意义**:
|
||||
- 文件树结构构建
|
||||
- 层级关系维护
|
||||
- 递归查询依据
|
||||
|
||||
**业务意义**:
|
||||
- 文件组织结构
|
||||
- 用户文件夹层次
|
||||
- 业务分类层级
|
||||
|
||||
**示例数据**:
|
||||
```
|
||||
parent_id: 8b1ede3cd6970f02fa85b8e34b682caf
|
||||
含义: 该节点的父文件夹
|
||||
用途:
|
||||
- query_children(parent_id) → 枚举子节点
|
||||
- 构建文件树
|
||||
- 层级导航
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 别名JSON(aliases_json)的意义
|
||||
|
||||
**技术意义**:
|
||||
- 文件路径解析
|
||||
- 多语言支持
|
||||
- 扩展信息存储
|
||||
|
||||
**业务意义**:
|
||||
- 实际文件位置
|
||||
- 用户访问路径
|
||||
- 业务元数据
|
||||
|
||||
**示例数据**:
|
||||
```json
|
||||
{
|
||||
"path": "/Users/accusys/momentry/var/sftpgo/data/warren/Test_Plan_ME5.docx",
|
||||
"alias_zh_tw": "测试计划"
|
||||
}
|
||||
```
|
||||
|
||||
**path字段意义**:
|
||||
- 文件在磁盘上的实际位置
|
||||
- read_file(node_id) 读取依据
|
||||
- 文件访问路径
|
||||
|
||||
---
|
||||
|
||||
### 文件大小(file_size)的意义
|
||||
|
||||
**技术意义**:
|
||||
- 文件大小统计
|
||||
- 存储空间计算
|
||||
- 传输进度依据
|
||||
|
||||
**业务意义**:
|
||||
- 用户文件大小
|
||||
- 存储容量规划
|
||||
- 业务文件规模
|
||||
|
||||
**示例数据**:
|
||||
```
|
||||
file_size: 26214400 (25MB)
|
||||
含义: 文件占用空间
|
||||
用途:
|
||||
- statfs 统计总大小
|
||||
- Finder 显示文件大小
|
||||
- AJA System Test 性能计算
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 验证结果预期
|
||||
|
||||
### 基础验证预期
|
||||
|
||||
**query_node验证**:
|
||||
```
|
||||
测试:query_node("root_id")
|
||||
预期:返回 Option<FileNodeData>
|
||||
验证点:
|
||||
├── node_id正确
|
||||
├── label正确
|
||||
├── node_type正确(folder/file)
|
||||
└── file_size正确(NULL for folder)
|
||||
```
|
||||
|
||||
**query_children验证**:
|
||||
```
|
||||
测试:query_children("root_id")
|
||||
预期:返回 Vec<FileNodeData>
|
||||
验证点:
|
||||
├── 数量正确(801 folders + 11857 files)
|
||||
├── 所有子节点 parent_id正确
|
||||
└── 子节点类型正确
|
||||
```
|
||||
|
||||
**read_file验证**:
|
||||
```
|
||||
测试:read_file("file_node_id")
|
||||
预期:返回 Option<Vec<u8>>
|
||||
验证点:
|
||||
├── aliases_json.path存在
|
||||
├── 文件可读取
|
||||
├── 内容大小正确
|
||||
└── 内容可解码(文本文件)
|
||||
```
|
||||
|
||||
**statfs验证**:
|
||||
```
|
||||
测试:statfs()
|
||||
预期:返回 (total_nodes, total_size)
|
||||
验证点:
|
||||
├── total_nodes = 12659
|
||||
├── total_size > 0
|
||||
└── 统计数据准确
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 深度验证预期
|
||||
|
||||
**数据一致性验证**:
|
||||
```
|
||||
测试:所有节点都有 parent_id
|
||||
预期:至少有1个 root folder
|
||||
验证:parent_id IS NULL 节点数量 >= 1
|
||||
|
||||
测试:所有文件都有 aliases_json
|
||||
预期:11857 file节点都有 path
|
||||
验证:aliases_json LIKE '%path%' 数量 = 11857
|
||||
|
||||
测试:文件大小与实际匹配
|
||||
预期:file_size = std::fs::metadata(path).len()
|
||||
验证:随机抽样10个文件验证
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据验证流程图
|
||||
|
||||
```
|
||||
数据验证流程:
|
||||
|
||||
Step 1: 基础连接测试
|
||||
├── MarkBaseFS::new("warren", "warren.sqlite")
|
||||
├── SQLite connection成功
|
||||
└── Mutex<Connection>可锁定 ✅
|
||||
|
||||
Step 2: 节点查询测试
|
||||
├── query_node("root_id")
|
||||
├── 返回 Option<FileNodeData>
|
||||
├── 检查 node_id, label, node_type
|
||||
└── 验证数据正确 ✅
|
||||
|
||||
Step 3: 子节点查询测试
|
||||
├── query_children("root_id")
|
||||
├── 返回 Vec<FileNodeData>
|
||||
├── 检查数量(801 + 11857)
|
||||
└── 验证层级关系 ✅
|
||||
|
||||
Step 4: 文件读取测试
|
||||
├── read_file("file_node_id")
|
||||
├── 解析 aliases_json.path
|
||||
├── std::fs::read(path)
|
||||
└── 验证文件内容 ✅
|
||||
|
||||
Step 5: 统计验证测试
|
||||
├── statfs()
|
||||
├── 检查 total_nodes(12659)
|
||||
├── 检查 total_size
|
||||
└── 验证统计数据 ✅
|
||||
|
||||
Step 6: 深度一致性测试
|
||||
├── 检查 parent_id 关联
|
||||
├── 检查 aliases_json 完整性
|
||||
├── 检查 file_size 一致性
|
||||
└── 验证数据完整 ✅
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 创建验证测试
|
||||
|
||||
**添加测试代码**:
|
||||
```rust
|
||||
// src/fskit/filesystem.rs
|
||||
|
||||
#[cfg(test)]
|
||||
mod warren_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_warren_database_connection() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
assert_eq!(fs.user_id, "warren");
|
||||
|
||||
// 测试 SQLite connection 可用
|
||||
let conn = fs.sqlite.lock().unwrap();
|
||||
let count: i64 = conn.query_row(
|
||||
"SELECT COUNT(*) FROM file_nodes",
|
||||
[],
|
||||
|row| row.get(0)
|
||||
).unwrap();
|
||||
|
||||
assert_eq!(count, 12659);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_warren_query_root() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 查询根节点
|
||||
let conn = fs.sqlite.lock().unwrap();
|
||||
let root_id: String = conn.query_row(
|
||||
"SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1",
|
||||
[],
|
||||
|row| row.get(0)
|
||||
).unwrap();
|
||||
|
||||
let root = fs.query_node(&root_id);
|
||||
assert!(root.is_some());
|
||||
|
||||
let root_node = root.unwrap();
|
||||
assert_eq!(root_node.node_type, "folder");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_warren_query_children() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 查询根节点ID
|
||||
let conn = fs.sqlite.lock().unwrap();
|
||||
let root_id: String = conn.query_row(
|
||||
"SELECT node_id FROM file_nodes WHERE parent_id IS NULL LIMIT 1",
|
||||
[],
|
||||
|row| row.get(0)
|
||||
).unwrap();
|
||||
|
||||
let children = fs.query_children(&root_id);
|
||||
|
||||
// 预期:至少有子节点
|
||||
assert!(children.len() > 0);
|
||||
|
||||
// 检查类型分布
|
||||
let folders = children.iter().filter(|c| c.node_type == "folder").count();
|
||||
let files = children.iter().filter(|c| c.node_type == "file").count();
|
||||
|
||||
println!("Root children: {} folders, {} files", folders, files);
|
||||
|
||||
assert!(folders > 0);
|
||||
assert!(files > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_warren_read_text_file() {
|
||||
let fs = MarkBaseFS::new("warren", "data/users/warren.sqlite");
|
||||
|
||||
// 查找一个小文本文件
|
||||
let conn = fs.sqlite.lock().unwrap();
|
||||
let (node_id, aliases_json): (String, String) = conn.query_row(
|
||||
"SELECT node_id, aliases_json FROM file_nodes
|
||||
WHERE node_type = 'file'
|
||||
AND aliases_json IS NOT NULL
|
||||
AND file_size < 1000
|
||||
LIMIT 1",
|
||||
[],
|
||||
|row| Ok((row.get(0)?, row.get(1)?))
|
||||
).unwrap();
|
||||
|
||||
// 解析 aliases_json
|
||||
let aliases: serde_json::Value = serde_json::from_str(&aliases_json).unwrap();
|
||||
let path = aliases["path"].as_str().unwrap();
|
||||
|
||||
// 检查路径存在
|
||||
assert!(std::path::Path::new(path).exists());
|
||||
|
||||
// 读取文件
|
||||
let content = fs.read_file(&node_id);
|
||||
assert!(content.is_some());
|
||||
|
||||
let data = content.unwrap();
|
||||
assert!(data.len() > 0);
|
||||
|
||||
// 尝试解码为文本
|
||||
if let Ok(text) = String::from_utf8(data) {
|
||||
println!("File content preview: {}", text.chars().take(100).collect::<String>());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_warren_statfs() {
|
||||
use crate::fskit::volume::MarkBaseVolume;
|
||||
|
||||
let conn = Connection::open("data/users/warren.sqlite").unwrap();
|
||||
let volume = MarkBaseVolume::new(conn, "warren".to_string());
|
||||
|
||||
let (total_nodes, total_size) = volume.statfs();
|
||||
|
||||
println!("Total nodes: {}", total_nodes);
|
||||
println!("Total size: {} bytes ({:.2} GB)",
|
||||
total_size,
|
||||
total_size as f64 / 1_073_741_824.0
|
||||
);
|
||||
|
||||
assert_eq!(total_nodes, 12659);
|
||||
assert!(total_size > 0);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 执行验证测试
|
||||
|
||||
**运行命令**:
|
||||
```bash
|
||||
cargo test --lib fskit::warren_tests
|
||||
|
||||
# 预期输出:
|
||||
running 5 tests
|
||||
test warren_database_connection ... ok
|
||||
test warren_query_root ... ok
|
||||
test warren_query_children ... ok
|
||||
test warren_read_text_file ... ok
|
||||
test warren_statfs ... ok
|
||||
|
||||
test result: ok. 5 passed; 0 failed
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. 数据验证意义总结
|
||||
|
||||
### 技术层面意义
|
||||
|
||||
**验证SQLite backend正确性**:
|
||||
- ✅ query_node:节点查询逻辑正确
|
||||
- ✅ query_children:层级关系正确
|
||||
- ✅ read_file:文件路径解析正确
|
||||
- ✅ statfs:统计计算正确
|
||||
|
||||
**验证数据完整性**:
|
||||
- ✅ 所有节点有 parent_id
|
||||
- ✅ 所有文件有 aliases_json
|
||||
- ✅ 文件大小与实际匹配
|
||||
|
||||
---
|
||||
|
||||
### 业务层面意义
|
||||
|
||||
**验证用户数据完整性**:
|
||||
- ✅ 12659 nodes全部可访问
|
||||
- ✅ 文件树结构正确
|
||||
- ✅ 用户文件可读取
|
||||
|
||||
**验证系统可靠性**:
|
||||
- ✅ SQLite backend稳定
|
||||
- ✅ 数据查询正确
|
||||
- ✅ 文件访问成功
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
**数据验证核心**:
|
||||
1. ✅ 数据结构正确(node_id, label, parent_id)
|
||||
2. ✅ 层级关系完整(root → children)
|
||||
3. ✅ 文件路径可用(aliases_json.path)
|
||||
4. ✅ 统计数据准确(12659 nodes)
|
||||
|
||||
**下一步行动**:
|
||||
```bash
|
||||
# 立即执行验证测试
|
||||
cargo test --lib fskit::warren_tests
|
||||
|
||||
# 查看数据详情
|
||||
sqlite3 data/users/warren.sqlite "
|
||||
SELECT node_id, label, node_type, file_size
|
||||
FROM file_nodes
|
||||
WHERE parent_id IS NULL
|
||||
LIMIT 5
|
||||
"
|
||||
|
||||
# 验证文件可读取
|
||||
sqlite3 data/users/warren.sqlite "
|
||||
SELECT label, aliases_json
|
||||
FROM file_nodes
|
||||
WHERE node_type = 'file'
|
||||
AND file_size < 1000
|
||||
LIMIT 1
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**文档完成时间**: 2026-05-18 16:50
|
||||
**版本**: 1.0(完整验证指南)
|
||||
Reference in New Issue
Block a user