feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
use rusqlite::Connection;
|
|
|
|
|
use std::path::Path;
|
|
|
|
|
|
2026-04-05 22:50:16 +00:00
|
|
|
fn generate_key() -> String {
|
|
|
|
|
use rand::RngCore;
|
|
|
|
|
let mut bytes = [0u8; 32];
|
|
|
|
|
rand::rngs::OsRng.fill_bytes(&mut bytes);
|
|
|
|
|
hex::encode(bytes)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(unix)]
|
|
|
|
|
fn write_key_file(path: &Path, key: &str) -> anyhow::Result<()> {
|
|
|
|
|
use std::io::Write;
|
|
|
|
|
use std::os::unix::fs::OpenOptionsExt;
|
|
|
|
|
let mut f = std::fs::OpenOptions::new()
|
|
|
|
|
.write(true)
|
|
|
|
|
.create(true)
|
|
|
|
|
.truncate(true)
|
|
|
|
|
.mode(0o600)
|
|
|
|
|
.open(path)?;
|
|
|
|
|
f.write_all(key.as_bytes())?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
|
fn write_key_file(path: &Path, key: &str) -> anyhow::Result<()> {
|
|
|
|
|
std::fs::write(path, key)?;
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_db_key(data_dir: &Path) -> anyhow::Result<String> {
|
2026-04-05 04:37:05 +00:00
|
|
|
if let Ok(key) = std::env::var("TFTSR_DB_KEY") {
|
|
|
|
|
if !key.trim().is_empty() {
|
|
|
|
|
return Ok(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
|
return Ok("dev-key-change-in-prod".to_string());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 22:50:16 +00:00
|
|
|
// Release: load or auto-generate a per-installation key stored in the
|
|
|
|
|
// app data directory. This lets the app work out of the box without
|
|
|
|
|
// requiring users to set an environment variable.
|
|
|
|
|
let key_path = data_dir.join(".dbkey");
|
|
|
|
|
if key_path.exists() {
|
|
|
|
|
let key = std::fs::read_to_string(&key_path)?;
|
|
|
|
|
let key = key.trim().to_string();
|
|
|
|
|
if !key.is_empty() {
|
|
|
|
|
return Ok(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let key = generate_key();
|
|
|
|
|
std::fs::create_dir_all(data_dir)?;
|
|
|
|
|
write_key_file(&key_path, &key)?;
|
|
|
|
|
Ok(key)
|
2026-04-05 04:37:05 +00:00
|
|
|
}
|
|
|
|
|
|
feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
pub fn open_encrypted_db(path: &Path, key: &str) -> anyhow::Result<Connection> {
|
|
|
|
|
let conn = Connection::open(path)?;
|
2026-03-30 02:25:43 +00:00
|
|
|
// ALL cipher settings MUST be set before the first database access.
|
|
|
|
|
// cipher_page_size in particular must precede any read/write so it takes
|
|
|
|
|
// effect for both creation (new DB) and reopening (existing DB).
|
|
|
|
|
// 16384 matches 16KB kernel page size (Asahi Linux / Apple Silicon aarch64).
|
|
|
|
|
conn.execute_batch(&format!(
|
|
|
|
|
"PRAGMA key = '{}';\
|
|
|
|
|
PRAGMA cipher_page_size = 16384;\
|
|
|
|
|
PRAGMA kdf_iter = 256000;\
|
|
|
|
|
PRAGMA cipher_hmac_algorithm = HMAC_SHA512;\
|
feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;",
|
2026-03-30 02:25:43 +00:00
|
|
|
key.replace('\'', "''")
|
|
|
|
|
))?;
|
|
|
|
|
// Verify the key and settings work
|
|
|
|
|
conn.execute_batch("SELECT count(*) FROM sqlite_master;")?;
|
feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
Ok(conn)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn open_dev_db(path: &Path) -> anyhow::Result<Connection> {
|
|
|
|
|
let conn = Connection::open(path)?;
|
|
|
|
|
Ok(conn)
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 22:21:31 +00:00
|
|
|
/// Migrates a plain SQLite database to an encrypted SQLCipher database.
|
|
|
|
|
/// Creates a backup of the original file before migration.
|
|
|
|
|
fn migrate_plain_to_encrypted(db_path: &Path, key: &str) -> anyhow::Result<Connection> {
|
|
|
|
|
tracing::warn!("Detected plain SQLite database in release build - migrating to encrypted");
|
|
|
|
|
|
|
|
|
|
// Create backup of plain database
|
|
|
|
|
let backup_path = db_path.with_extension("db.plain-backup");
|
|
|
|
|
std::fs::copy(db_path, &backup_path)?;
|
|
|
|
|
tracing::info!("Backed up plain database to {:?}", backup_path);
|
|
|
|
|
|
|
|
|
|
// Open the plain database
|
|
|
|
|
let plain_conn = Connection::open(db_path)?;
|
|
|
|
|
|
|
|
|
|
// Create temporary encrypted database path
|
|
|
|
|
let temp_encrypted = db_path.with_extension("db.encrypted-temp");
|
|
|
|
|
|
|
|
|
|
// Attach and migrate to encrypted database using SQLCipher export
|
|
|
|
|
plain_conn.execute_batch(&format!(
|
|
|
|
|
"ATTACH DATABASE '{}' AS encrypted KEY '{}';\
|
|
|
|
|
PRAGMA encrypted.cipher_page_size = 16384;\
|
|
|
|
|
PRAGMA encrypted.kdf_iter = 256000;\
|
|
|
|
|
PRAGMA encrypted.cipher_hmac_algorithm = HMAC_SHA512;\
|
|
|
|
|
PRAGMA encrypted.cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;",
|
|
|
|
|
temp_encrypted.display(),
|
|
|
|
|
key.replace('\'', "''")
|
|
|
|
|
))?;
|
|
|
|
|
|
|
|
|
|
// Export all data to encrypted database
|
|
|
|
|
plain_conn.execute_batch("SELECT sqlcipher_export('encrypted');")?;
|
|
|
|
|
plain_conn.execute_batch("DETACH DATABASE encrypted;")?;
|
|
|
|
|
drop(plain_conn);
|
|
|
|
|
|
|
|
|
|
// Replace original with encrypted version
|
|
|
|
|
std::fs::rename(&temp_encrypted, db_path)?;
|
|
|
|
|
tracing::info!("Successfully migrated database to encrypted format");
|
|
|
|
|
|
|
|
|
|
// Open and return the encrypted database
|
|
|
|
|
open_encrypted_db(db_path, key)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Checks if a database file is plain SQLite by reading its header.
|
|
|
|
|
fn is_plain_sqlite(path: &Path) -> bool {
|
|
|
|
|
if let Ok(mut file) = std::fs::File::open(path) {
|
|
|
|
|
use std::io::Read;
|
|
|
|
|
let mut header = [0u8; 16];
|
|
|
|
|
if file.read_exact(&mut header).is_ok() {
|
|
|
|
|
// SQLite databases start with "SQLite format 3\0"
|
|
|
|
|
return &header == b"SQLite format 3\0";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
|
feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
pub fn init_db(data_dir: &Path) -> anyhow::Result<Connection> {
|
|
|
|
|
std::fs::create_dir_all(data_dir)?;
|
2026-04-06 18:22:02 +00:00
|
|
|
let db_path = data_dir.join("trcaa.db");
|
feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
|
2026-04-05 22:50:16 +00:00
|
|
|
let key = get_db_key(data_dir)?;
|
feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
|
|
|
|
|
let conn = if cfg!(debug_assertions) {
|
|
|
|
|
open_dev_db(&db_path)?
|
|
|
|
|
} else {
|
2026-04-06 22:21:31 +00:00
|
|
|
// In release mode, try encrypted first
|
|
|
|
|
match open_encrypted_db(&db_path, &key) {
|
|
|
|
|
Ok(conn) => conn,
|
|
|
|
|
Err(e) => {
|
|
|
|
|
// Check if error is due to trying to decrypt a plain SQLite database
|
|
|
|
|
if db_path.exists() && is_plain_sqlite(&db_path) {
|
|
|
|
|
// Auto-migrate from plain to encrypted
|
|
|
|
|
migrate_plain_to_encrypted(&db_path, &key)?
|
|
|
|
|
} else {
|
|
|
|
|
// Different error - propagate it
|
|
|
|
|
return Err(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
feat: initial implementation of TFTSR IT Triage & RCA application
Implements Phases 1-8 of the TFTSR implementation plan.
Rust backend (Tauri 2.x, src-tauri/):
- Multi-provider AI: OpenAI-compatible, Anthropic, Gemini, Mistral, Ollama
- PII detection engine: 11 regex patterns with overlap resolution
- SQLCipher AES-256 encrypted database with 10 versioned migrations
- 28 Tauri IPC commands for triage, analysis, document, and system ops
- Ollama: hardware probe, model recommendations, pull/delete with events
- RCA and blameless post-mortem Markdown document generators
- PDF export via printpdf
- Audit log: SHA-256 hash of every external data send
- Integration stubs for Confluence, ServiceNow, Azure DevOps (v0.2)
Frontend (React 18 + TypeScript + Vite, src/):
- 9 pages: full triage workflow NewIssue→LogUpload→Triage→Resolution→RCA→Postmortem→History+Settings
- 7 components: ChatWindow, TriageProgress, PiiDiffViewer, DocEditor, HardwareReport, ModelSelector, UI primitives
- 3 Zustand stores: session, settings (persisted), history
- Type-safe tauriCommands.ts matching Rust backend types exactly
- 8 IT domain system prompts (Linux, Windows, Network, K8s, DB, Virt, HW, Obs)
DevOps:
- .woodpecker/test.yml: rustfmt, clippy, cargo test, tsc, vitest on every push
- .woodpecker/release.yml: linux/amd64 + linux/arm64 builds, Gogs release upload
Verified:
- cargo check: zero errors
- tsc --noEmit: zero errors
- vitest run: 13/13 unit tests passing
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 03:36:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
crate::db::migrations::run_migrations(&conn)?;
|
|
|
|
|
Ok(conn)
|
|
|
|
|
}
|
2026-04-05 04:37:05 +00:00
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2026-04-05 22:50:16 +00:00
|
|
|
fn temp_dir(name: &str) -> std::path::PathBuf {
|
2026-04-06 22:21:31 +00:00
|
|
|
use std::time::SystemTime;
|
|
|
|
|
let timestamp = SystemTime::now()
|
|
|
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.as_nanos();
|
|
|
|
|
let dir = std::env::temp_dir().join(format!("tftsr-test-{}-{}", name, timestamp));
|
|
|
|
|
// Clean up if it exists
|
|
|
|
|
let _ = std::fs::remove_dir_all(&dir);
|
2026-04-05 22:50:16 +00:00
|
|
|
std::fs::create_dir_all(&dir).unwrap();
|
|
|
|
|
dir
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-05 04:37:05 +00:00
|
|
|
#[test]
|
|
|
|
|
fn test_get_db_key_uses_env_var_when_present() {
|
2026-04-06 22:21:31 +00:00
|
|
|
// Remove any existing env var first
|
|
|
|
|
std::env::remove_var("TFTSR_DB_KEY");
|
2026-04-05 22:50:16 +00:00
|
|
|
let dir = temp_dir("env-var");
|
2026-04-05 04:37:05 +00:00
|
|
|
std::env::set_var("TFTSR_DB_KEY", "test-db-key");
|
2026-04-05 22:50:16 +00:00
|
|
|
let key = get_db_key(&dir).unwrap();
|
2026-04-05 04:37:05 +00:00
|
|
|
assert_eq!(key, "test-db-key");
|
|
|
|
|
std::env::remove_var("TFTSR_DB_KEY");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_get_db_key_debug_fallback_for_empty_env() {
|
2026-04-06 22:21:31 +00:00
|
|
|
// Remove any existing env var first
|
|
|
|
|
std::env::remove_var("TFTSR_DB_KEY");
|
2026-04-05 22:50:16 +00:00
|
|
|
let dir = temp_dir("empty-env");
|
2026-04-05 04:37:05 +00:00
|
|
|
std::env::set_var("TFTSR_DB_KEY", " ");
|
2026-04-05 22:50:16 +00:00
|
|
|
let key = get_db_key(&dir).unwrap();
|
2026-04-05 04:37:05 +00:00
|
|
|
assert_eq!(key, "dev-key-change-in-prod");
|
|
|
|
|
std::env::remove_var("TFTSR_DB_KEY");
|
|
|
|
|
}
|
2026-04-06 22:21:31 +00:00
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_is_plain_sqlite_detects_plain_database() {
|
|
|
|
|
let dir = temp_dir("plain-detect");
|
|
|
|
|
let db_path = dir.join("test.db");
|
|
|
|
|
|
|
|
|
|
// Create a plain SQLite database
|
|
|
|
|
let conn = Connection::open(&db_path).unwrap();
|
|
|
|
|
conn.execute("CREATE TABLE test (id INTEGER)", []).unwrap();
|
|
|
|
|
drop(conn);
|
|
|
|
|
|
|
|
|
|
assert!(is_plain_sqlite(&db_path));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_is_plain_sqlite_rejects_encrypted() {
|
|
|
|
|
let dir = temp_dir("encrypted-detect");
|
|
|
|
|
let db_path = dir.join("test.db");
|
|
|
|
|
|
|
|
|
|
// Create an encrypted database
|
|
|
|
|
let conn = Connection::open(&db_path).unwrap();
|
|
|
|
|
conn.execute_batch(
|
|
|
|
|
"PRAGMA key = 'test-key';\
|
|
|
|
|
PRAGMA cipher_page_size = 16384;",
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
conn.execute("CREATE TABLE test (id INTEGER)", []).unwrap();
|
|
|
|
|
drop(conn);
|
|
|
|
|
|
|
|
|
|
assert!(!is_plain_sqlite(&db_path));
|
|
|
|
|
}
|
2026-04-05 04:37:05 +00:00
|
|
|
}
|