#![allow(clippy::uninlined_format_args, clippy::redundant_clone)]
mod common;
use beads_rust::storage::SqliteStorage;
use common::cli::{BrWorkspace, run_br};
use serde_json::Value;
use std::fs;
#[cfg(unix)]
use std::os::unix::fs::symlink;
fn setup_workspace_with_issues() -> BrWorkspace {
let workspace = BrWorkspace::new();
let init = run_br(&workspace, ["init"], "init");
assert!(init.status.success(), "init failed: {}", init.stderr);
let _ = run_br(
&workspace,
["create", "Test issue 1", "-t", "task"],
"create1",
);
let _ = run_br(
&workspace,
["create", "Test issue 2", "-t", "bug"],
"create2",
);
let _ = run_br(
&workspace,
["create", "Test issue 3", "-t", "feature"],
"create3",
);
let export = run_br(&workspace, ["sync", "--flush-only"], "export");
assert!(export.status.success(), "export failed: {}", export.stderr);
workspace
}
#[test]
fn edge_case_import_rejects_partial_lines() {
let _log = common::test_log("edge_case_import_rejects_partial_lines");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let original = fs::read_to_string(&jsonl_path).expect("read jsonl");
let lines: Vec<&str> = original.lines().collect();
assert!(!lines.is_empty(), "JSONL should have content");
let first_line = lines[0];
let truncated = &first_line[..first_line.len() / 2]; let malformed = format!("{}\n{}", truncated, lines[1..].join("\n"));
fs::write(&jsonl_path, &malformed).expect("write malformed jsonl");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_partial",
);
let log = format!(
"=== PARTIAL LINE TEST ===\n\
Original line: {}\n\
Truncated to: {}\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
first_line, truncated, import.stdout, import.stderr, import.status
);
let log_path = workspace.log_dir.join("partial_line_test.log");
fs::write(&log_path, &log).expect("write log");
assert!(
!import.status.success(),
"SAFETY VIOLATION: Import should reject truncated JSONL!\n\
Truncated line: {truncated}\n\
Log: {}",
log_path.display()
);
assert!(
import.stderr.to_lowercase().contains("json")
|| import.stderr.to_lowercase().contains("invalid")
|| import.stderr.to_lowercase().contains("parse"),
"Error should mention JSON/parsing issue. Got: {}",
import.stderr
);
eprintln!(
"[PASS] Import correctly rejected partial line JSONL\n\
Error: {}",
import.stderr.lines().next().unwrap_or("(no error)")
);
}
#[test]
fn edge_case_rename_prefix_rejects_malformed_jsonl_before_config_write() {
let _log =
common::test_log("edge_case_rename_prefix_rejects_malformed_jsonl_before_config_write");
let workspace = setup_workspace_with_issues();
let beads_dir = workspace.root.join(".beads");
let db_path = beads_dir.join("beads.db");
let jsonl_path = beads_dir.join("issues.jsonl");
{
let mut storage = SqliteStorage::open(&db_path).expect("open storage");
storage
.delete_config("issue_prefix")
.expect("delete issue_prefix");
assert_eq!(
storage.get_config("issue_prefix").expect("read prefix"),
None
);
}
let original = fs::read_to_string(&jsonl_path).expect("read jsonl");
let first_issue = original
.lines()
.find(|line| !line.trim().is_empty())
.expect("exported issue line");
let mut foreign_issue: Value = serde_json::from_str(first_issue).expect("parse issue json");
foreign_issue["id"] = Value::String("foreign-abc12".to_string());
let foreign_line = serde_json::to_string(&foreign_issue).expect("serialize foreign issue");
fs::write(&jsonl_path, format!("{{not-json\n{foreign_line}\n")).expect("write malformed jsonl");
let import = run_br(
&workspace,
["sync", "--import-only", "--force", "--rename-prefix"],
"import_malformed_rename_prefix",
);
assert!(
!import.status.success(),
"rename-prefix import should reject malformed JSONL before config write"
);
assert!(
import.stderr.contains("Invalid JSON at line 1")
|| import.stderr.to_lowercase().contains("json"),
"error should mention JSON parse failure; stderr: {}",
import.stderr
);
let storage = SqliteStorage::open(&db_path).expect("reopen storage");
assert_eq!(
storage.get_config("issue_prefix").expect("read prefix"),
None,
"failed rename-prefix auto-detection must not persist the later valid issue prefix"
);
}
#[test]
fn edge_case_import_rejects_invalid_json() {
let _log = common::test_log("edge_case_import_rejects_invalid_json");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let invalid_json_cases = [
("{\"id\": \"test\", \"title\": ", "Missing closing brace"),
("{invalid json here}", "Not valid JSON"),
(
"{\"id\": \"test\", \"title\": \"unclosed string}",
"Unclosed string",
),
("{\"id\": \"test\", trailing: garbage}", "Trailing garbage"),
("not json at all", "Plain text"),
];
for (invalid_line, description) in invalid_json_cases {
fs::write(&jsonl_path, format!("{invalid_line}\n")).expect("write invalid jsonl");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
&format!("import_{}", description.replace(' ', "_")),
);
let log = format!(
"=== INVALID JSON TEST: {} ===\n\
Invalid line: {}\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
description, invalid_line, import.stdout, import.stderr, import.status
);
let log_path = workspace.log_dir.join(format!(
"invalid_json_{}.log",
description.replace(' ', "_")
));
fs::write(&log_path, &log).expect("write log");
assert!(
!import.status.success(),
"SAFETY VIOLATION: Import should reject invalid JSON ({})!\n\
Line: {invalid_line}\n\
Log: {}",
description,
log_path.display()
);
eprintln!(
"[PASS] Rejected invalid JSON ({}): {}",
description,
import.stderr.lines().next().unwrap_or("(no error)")
);
}
}
#[test]
fn edge_case_import_handles_empty_lines() {
let _log = common::test_log("edge_case_import_handles_empty_lines");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let original = fs::read_to_string(&jsonl_path).expect("read jsonl");
let with_empty = format!("\n\n{}\n\n\n", original.replace('\n', "\n\n"));
fs::write(&jsonl_path, &with_empty).expect("write with empty lines");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_empty_lines",
);
let log = format!(
"=== EMPTY LINES TEST ===\n\
JSONL with empty lines:\n{}\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
with_empty, import.stdout, import.stderr, import.status
);
let log_path = workspace.log_dir.join("empty_lines_test.log");
fs::write(&log_path, &log).expect("write log");
assert!(
import.status.success(),
"Import should handle empty lines gracefully.\n\
Log: {}",
log_path.display()
);
eprintln!("[PASS] Import handled empty lines gracefully");
}
#[test]
fn edge_case_import_rejects_conflict_markers() {
let _log = common::test_log("edge_case_import_rejects_conflict_markers");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let original = fs::read_to_string(&jsonl_path).expect("read jsonl");
let conflict_cases = [
(
format!(
"<<<<<<< HEAD\n{}\n=======\n{}\n>>>>>>> main",
original, original
),
"Full conflict block",
),
(
format!("<<<<<<< feature-branch\n{}", original),
"Start marker only",
),
(format!("=======\n{}", original), "Separator marker"),
(
format!("{}>>>>>>> origin/main", original),
"End marker only",
),
(
format!(
"{}\n<<<<<<< HEAD\n{{\"id\":\"conflict\"}}\n=======",
original
),
"Marker mid-file",
),
];
for (malformed, description) in conflict_cases {
fs::write(&jsonl_path, &malformed).expect("write conflicted jsonl");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
&format!("import_conflict_{}", description.replace(' ', "_")),
);
let log = format!(
"=== CONFLICT MARKER TEST: {} ===\n\
JSONL content:\n{}\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
description,
malformed.chars().take(500).collect::<String>(),
import.stdout,
import.stderr,
import.status
);
let log_path = workspace
.log_dir
.join(format!("conflict_{}.log", description.replace(' ', "_")));
fs::write(&log_path, &log).expect("write log");
assert!(
!import.status.success(),
"SAFETY VIOLATION: Import should reject JSONL with conflict markers ({})!\n\
Log: {}",
description,
log_path.display()
);
assert!(
import.stderr.to_lowercase().contains("conflict")
|| import.stderr.to_lowercase().contains("merge")
|| import.stderr.contains("<<<<<<<")
|| import.stderr.contains(">>>>>>>"),
"Error should mention conflict markers. Got: {}",
import.stderr
);
eprintln!(
"[PASS] Rejected conflict markers ({}): {}",
description,
import.stderr.lines().next().unwrap_or("(no error)")
);
fs::write(&jsonl_path, &original).expect("restore original");
}
}
#[test]
fn edge_case_path_traversal_blocked() {
let _log = common::test_log("edge_case_path_traversal_blocked");
let workspace = BrWorkspace::new();
let init = run_br(&workspace, ["init"], "init");
assert!(init.status.success(), "init failed");
let _ = run_br(&workspace, ["create", "Test issue"], "create");
let outside_file = workspace.root.join("secret.txt");
fs::write(&outside_file, "SECRET DATA").expect("write secret file");
let traversal_paths = [
workspace.root.join(".beads").join("..").join("secret.txt"),
workspace
.root
.join(".beads")
.join("..")
.join("..")
.join("etc")
.join("passwd"),
workspace
.root
.join(".beads")
.join("foo")
.join("..")
.join("..")
.join("secret.txt"),
];
for traversal_path in &traversal_paths {
eprintln!(
"[INFO] Would test traversal path: {}",
traversal_path.display()
);
}
let export = run_br(&workspace, ["sync", "--flush-only"], "export");
assert!(export.status.success(), "export failed");
let secret_content = fs::read_to_string(&outside_file).expect("read secret");
assert_eq!(
secret_content, "SECRET DATA",
"SAFETY VIOLATION: sync modified file outside .beads!"
);
eprintln!("[PASS] Path traversal protection verified - secret file untouched");
}
#[test]
#[cfg(unix)]
fn edge_case_symlink_escape_blocked() {
let _log = common::test_log("edge_case_symlink_escape_blocked");
let workspace = BrWorkspace::new();
let init = run_br(&workspace, ["init"], "init");
assert!(init.status.success(), "init failed");
let _ = run_br(&workspace, ["create", "Test issue"], "create");
let outside_file = workspace.root.join("outside_secret.txt");
fs::write(&outside_file, "OUTSIDE SECRET").expect("write outside file");
let beads_dir = workspace.root.join(".beads");
let symlink_path = beads_dir.join("escape_link");
if symlink(&outside_file, &symlink_path).is_ok() {
eprintln!(
"[INFO] Created symlink: {} -> {}",
symlink_path.display(),
outside_file.display()
);
assert!(symlink_path.exists() || symlink_path.is_symlink());
let export = run_br(&workspace, ["sync", "--flush-only"], "export_with_symlink");
let log = format!(
"=== SYMLINK ESCAPE TEST ===\n\
Symlink: {} -> {}\n\n\
Export stdout: {}\n\
Export stderr: {}\n\
Exit status: {}",
symlink_path.display(),
outside_file.display(),
export.stdout,
export.stderr,
export.status
);
let log_path = workspace.log_dir.join("symlink_escape_test.log");
fs::write(&log_path, &log).expect("write log");
let outside_content = fs::read_to_string(&outside_file).expect("read outside file");
assert_eq!(
outside_content, "OUTSIDE SECRET",
"SAFETY VIOLATION: Symlink escape modified file outside .beads!"
);
eprintln!("[PASS] Symlink escape attempt did not modify outside file");
} else {
eprintln!("[SKIP] Could not create symlink for test (permission or filesystem issue)");
}
}
#[test]
fn edge_case_huge_line() {
let _log = common::test_log("edge_case_huge_line");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let original = fs::read_to_string(&jsonl_path).expect("read jsonl");
let first_line = original.lines().next().expect("at least one line");
let mut issue: serde_json::Value = serde_json::from_str(first_line).expect("parse first line");
let huge_title = "X".repeat(1_000_000);
issue["title"] = serde_json::Value::String(huge_title.clone());
let huge_line = serde_json::to_string(&issue).expect("serialize huge issue");
fs::write(&jsonl_path, format!("{huge_line}\n")).expect("write huge line");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_huge",
);
let log = format!(
"=== HUGE LINE TEST ===\n\
Line size: {} bytes\n\
Title size: {} chars\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
huge_line.len(),
huge_title.len(),
import.stdout,
import.stderr,
import.status
);
let log_path = workspace.log_dir.join("huge_line_test.log");
fs::write(&log_path, &log).expect("write log");
eprintln!(
"[INFO] Huge line test: status={}, line_size={} bytes",
import.status,
huge_line.len()
);
let list = run_br(
&workspace,
["list", "--no-auto-import", "--allow-stale"],
"list_after_huge",
);
assert!(
list.status.success(),
"SAFETY VIOLATION: System in corrupted state after huge line test!\n\
List failed: {}\n\
Log: {}",
list.stderr,
log_path.display()
);
eprintln!("[PASS] Huge line handled without crash or corruption");
}
#[test]
fn edge_case_invalid_utf8() {
let _log = common::test_log("edge_case_invalid_utf8");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let original = fs::read(&jsonl_path).expect("read jsonl bytes");
let mut invalid_bytes = original.clone();
invalid_bytes.insert(10, 0xFF);
invalid_bytes.insert(11, 0xFE);
fs::write(&jsonl_path, &invalid_bytes).expect("write invalid utf8");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_invalid_utf8",
);
let log = format!(
"=== INVALID UTF-8 TEST ===\n\
Inserted bytes: [0xFF, 0xFE] at position 10-11\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
import.stdout, import.stderr, import.status
);
let log_path = workspace.log_dir.join("invalid_utf8_test.log");
fs::write(&log_path, &log).expect("write log");
assert!(
!import.status.success(),
"SAFETY VIOLATION: Import should reject invalid UTF-8!\n\
Log: {}",
log_path.display()
);
assert!(
import.stderr.to_lowercase().contains("utf")
|| import.stderr.to_lowercase().contains("invalid")
|| import.stderr.to_lowercase().contains("decode")
|| import.stderr.to_lowercase().contains("stream"),
"Error should mention UTF-8 or encoding issue. Got: {}",
import.stderr
);
eprintln!(
"[PASS] Invalid UTF-8 rejected: {}",
import.stderr.lines().next().unwrap_or("(no error)")
);
}
#[test]
fn edge_case_whitespace_only() {
let _log = common::test_log("edge_case_whitespace_only");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
fs::write(&jsonl_path, " \n\t\n \n\n").expect("write whitespace");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_whitespace",
);
let log = format!(
"=== WHITESPACE ONLY TEST ===\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
import.stdout, import.stderr, import.status
);
let log_path = workspace.log_dir.join("whitespace_only_test.log");
fs::write(&log_path, &log).expect("write log");
assert!(
import.status.success(),
"Import should handle whitespace-only JSONL gracefully.\n\
Log: {}",
log_path.display()
);
eprintln!("[PASS] Whitespace-only JSONL handled gracefully");
}
#[test]
fn edge_case_empty_file() {
let _log = common::test_log("edge_case_empty_file");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
fs::write(&jsonl_path, "").expect("write empty file");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_empty",
);
let log = format!(
"=== EMPTY FILE TEST ===\n\
File size: 0 bytes\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
import.stdout, import.stderr, import.status
);
let log_path = workspace.log_dir.join("empty_file_test.log");
fs::write(&log_path, &log).expect("write log");
assert!(
import.status.success(),
"Import should handle empty file gracefully.\n\
Log: {}",
log_path.display()
);
eprintln!("[PASS] Empty file handled gracefully");
}
#[test]
fn edge_case_deeply_nested_json() {
let _log = common::test_log("edge_case_deeply_nested_json");
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let mut nested = String::new();
let depth = 100;
for _ in 0..depth {
nested.push_str("{\"nested\":");
}
nested.push_str("\"leaf\"");
for _ in 0..depth {
nested.push('}');
}
let deep_json = format!(
"{{\"id\":\"deep-test\",\"title\":\"Deep\",\"status\":\"open\",\"data\":{nested}}}"
);
fs::write(&jsonl_path, format!("{deep_json}\n")).expect("write deeply nested");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_nested",
);
let log = format!(
"=== DEEPLY NESTED JSON TEST ===\n\
Nesting depth: {}\n\n\
Import stdout: {}\n\
Import stderr: {}\n\
Exit status: {}",
depth, import.stdout, import.stderr, import.status
);
let log_path = workspace.log_dir.join("deeply_nested_test.log");
fs::write(&log_path, &log).expect("write log");
eprintln!(
"[INFO] Deeply nested JSON test: status={}, depth={}",
import.status, depth
);
let list = run_br(
&workspace,
["list", "--no-auto-import", "--allow-stale"],
"list_after_nested",
);
assert!(
list.status.success(),
"System should remain stable after deeply nested JSON test"
);
eprintln!("[PASS] Deeply nested JSON handled without crash");
}
#[test]
fn edge_case_no_partial_writes_on_failure() {
let workspace = setup_workspace_with_issues();
let jsonl_path = workspace.root.join(".beads").join("issues.jsonl");
let list_before = run_br(&workspace, ["list", "--json"], "list_before");
let count_before = list_before.stdout.matches("\"id\"").count();
let original = fs::read_to_string(&jsonl_path).expect("read jsonl");
let malformed = format!(
"{}\n{{\"id\":\"new-valid\",\"title\":\"New Valid Issue\",\"status\":\"open\"}}\n{{invalid json here}}\n",
original.trim()
);
fs::write(&jsonl_path, &malformed).expect("write malformed");
let import = run_br(
&workspace,
["sync", "--import-only", "--force"],
"import_partial_fail",
);
let list_after = run_br(
&workspace,
["list", "--json", "--no-auto-import", "--allow-stale"],
"list_after",
);
let count_after = list_after.stdout.matches("\"id\"").count();
let log = format!(
"=== NO PARTIAL WRITES TEST ===\n\
Issues before: {}\n\
Issues after: {}\n\n\
Import status: {}\n\
Import stderr: {}",
count_before, count_after, import.status, import.stderr
);
let log_path = workspace.log_dir.join("no_partial_writes_test.log");
fs::write(&log_path, &log).expect("write log");
assert!(
!import.status.success(),
"Import should fail on invalid JSON"
);
eprintln!(
"[INFO] Partial write test: before={}, after={}, import_status={}",
count_before, count_after, import.status
);
let list_final = run_br(
&workspace,
["list", "--no-auto-import", "--allow-stale"],
"list_final",
);
assert!(
list_final.status.success(),
"System should remain in consistent state after failed import"
);
eprintln!("[PASS] System in consistent state after failed import");
}