use std::fs;
use tempfile::TempDir;
use writestead::config::{AppConfig, McpConfig, RawConfig, SearchConfig, SyncBackend, SyncConfig};
use writestead::raw::RawOps;
use writestead::vault;
fn test_config(vault_path: &str) -> AppConfig {
AppConfig {
name: "test".to_string(),
vault_path: vault_path.to_string(),
host: "127.0.0.1".to_string(),
port: 0,
sync: SyncConfig {
backend: SyncBackend::None,
},
mcp: McpConfig::default(),
search: SearchConfig::default(),
raw: RawConfig::default(),
}
}
fn setup_raw() -> (TempDir, AppConfig, RawOps) {
let dir = TempDir::new().expect("tempdir");
let cfg = test_config(dir.path().to_str().expect("path str"));
vault::init_vault(&cfg, true).expect("init vault");
let raw = RawOps::new(cfg.clone());
(dir, cfg, raw)
}
#[tokio::test]
async fn raw_add_local_file_smoke() {
let (dir, _cfg, raw) = setup_raw();
let source = dir.path().join("source.txt");
fs::write(&source, "alpha\n").expect("write source");
let result = raw
.add_source(source.to_str().expect("source path"), None, false)
.await
.expect("raw add");
assert!(result.ok);
assert_eq!(result.path, "raw/source.txt");
assert_eq!(result.source, "local");
}
#[tokio::test]
async fn raw_add_rejects_path_traversal() {
let (_dir, _cfg, raw) = setup_raw();
let err = raw
.add_source("../etc/passwd", None, false)
.await
.expect_err("must fail");
assert!(err.to_string().contains("path traversal"));
}
#[tokio::test]
async fn raw_add_rejects_overwrite_without_force() {
let (dir, _cfg, raw) = setup_raw();
let source = dir.path().join("source.txt");
fs::write(&source, "alpha\n").expect("write source");
raw.add_source(source.to_str().expect("source path"), None, false)
.await
.expect("first add");
let err = raw
.add_source(source.to_str().expect("source path"), None, false)
.await
.expect_err("must fail overwrite");
assert!(err.to_string().contains("destination already exists"));
}
#[tokio::test]
async fn raw_add_enforces_size_cap() {
let (dir, mut cfg, _raw) = setup_raw();
cfg.raw.upload_max_bytes = 4;
let raw = RawOps::new(cfg);
let source = dir.path().join("big.txt");
fs::write(&source, "12345").expect("write big source");
let err = raw
.add_source(source.to_str().expect("source path"), None, false)
.await
.expect_err("must fail size cap");
assert!(err.to_string().contains("file too large"));
}
#[test]
fn raw_list_pagination_smoke() {
let (dir, _cfg, raw) = setup_raw();
fs::write(dir.path().join("raw/a.txt"), "a\n").expect("write a");
fs::write(dir.path().join("raw/b.txt"), "b\n").expect("write b");
let list = raw.list_sources(1, 1).expect("raw list");
assert_eq!(list.total, 2);
assert_eq!(list.offset, 1);
assert_eq!(list.limit, 1);
assert_eq!(list.files, vec!["b.txt"]);
assert!(!list.has_more);
}
#[tokio::test]
async fn raw_read_text_file_with_pagination() {
let (dir, _cfg, raw) = setup_raw();
fs::write(dir.path().join("raw/source.txt"), "l1\nl2\nl3\n").expect("source text");
let read = raw.read_source("source.txt", 2, 1).await.expect("raw read");
assert_eq!(read.format, "text");
assert_eq!(read.offset, 2);
assert_eq!(read.limit, 1);
assert_eq!(read.content, "l2");
assert!(read.has_more);
}
#[tokio::test]
async fn raw_read_unsupported_format_rejected() {
let (dir, _cfg, raw) = setup_raw();
fs::write(dir.path().join("raw/file.bin"), "abc").expect("bin source");
let err = raw
.read_source("file.bin", 1, 50)
.await
.expect_err("must fail unsupported");
assert!(err.to_string().contains("unsupported file type"));
}