refactor: consolidate bulk actions, URL assembly, and case-artefact filenames

Pulls three pattern groups into shared helpers so a rename or wire-format
tweak edits one file instead of 10+:

- BulkAction enum in doctate-common replaces the "close"/"analyze"/"reset"
  string literals that were duplicated between the bulk handler and 3
  attack/CSRF test files. FromStr preserves the exact "Unbekannte Aktion"
  error shape; the handler match is now exhaustive over the enum.
- join_url helper in doctate-common absorbs 7 identical
  trim_end_matches+format! call sites across client-core, client-desktop,
  server/transcribe (ollama, whisper), and server/analyze (llm).
- server/tests/common/artefacts.rs re-exports ONELINER_FILENAME
  (doctate-common), DOCUMENT_FILE + ANALYSIS_INPUT_FILE
  (doctate-server::analyze), and CLOSE_MARKER (doctate-server::paths) so
  test files reference the canonical name instead of inlining literals.

Also lifts client-desktop local duplication:
- paths.rs: project_path helper collapses 4 identical ProjectDirs chains
- main.rs: or_die helper replaces 4 eprintln!+exit(1) blocks
- app.rs: named RecordingContext struct replaces (Uuid, String) tuple
  at 3 sites around the ffmpeg-flush finalization path

Verification: 397 tests pass (baseline was 389; +8 for new unit tests on
BulkAction and join_url), 0 failed, 4 ignored (unchanged). clippy clean.
This commit is contained in:
2026-04-22 12:09:46 +02:00
parent af3377a6bc
commit 813fb896ff
25 changed files with 416 additions and 166 deletions
+13
View File
@@ -0,0 +1,13 @@
//! Re-exports of case-directory artefact filenames from their
//! canonical source-of-truth locations.
//!
//! Tests that need to read, write, or assert the absence of per-case
//! artefacts (`oneliner.json`, `.closed`, `document.md`,
//! `analysis_input.json`) pull these names from here instead of
//! inlining string literals. If the server renames a sidecar in the
//! future, test call-sites fail to compile instead of silently
//! asserting against the old name.
pub use doctate_common::ONELINER_FILENAME;
pub use doctate_server::analyze::{ANALYSIS_INPUT_FILE, DOCUMENT_FILE};
pub use doctate_server::paths::CLOSE_MARKER;
+2
View File
@@ -16,6 +16,7 @@
// warning is a false positive of the per-crate visibility model.
#![allow(unused_imports)]
pub mod artefacts;
pub mod config;
pub mod http;
pub mod paths;
@@ -24,6 +25,7 @@ pub mod session;
pub mod users;
// Re-exports for ergonomic `use common::*;` in most test files.
pub use artefacts::{ANALYSIS_INPUT_FILE, CLOSE_MARKER, DOCUMENT_FILE, ONELINER_FILENAME};
pub use config::{TestConfig, unique_tmpdir};
pub use http::{
body_bytes, body_json, body_string, csrf_form_post, form_post, get_with_api_key,
+4 -2
View File
@@ -11,7 +11,9 @@
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime};
use doctate_common::ONELINER_FILENAME;
use doctate_common::oneliners::OnelinerState;
use doctate_server::paths::CLOSE_MARKER;
use filetime::FileTime;
/// Create `<data_path>/<slug>/<case_id>/` and return its path. This is
@@ -71,7 +73,7 @@ pub fn seed_oneliner_ready(case_dir: &Path, text: &str) {
/// Persist an arbitrary [`OnelinerState`] as `oneliner.json`.
pub fn seed_oneliner_state(case_dir: &Path, state: &OnelinerState) {
let bytes = serde_json::to_vec(state).unwrap();
std::fs::write(case_dir.join("oneliner.json"), bytes).unwrap();
std::fs::write(case_dir.join(ONELINER_FILENAME), bytes).unwrap();
}
/// Write a `.closed` marker with a caller-chosen `closed_at` string.
@@ -79,7 +81,7 @@ pub fn seed_oneliner_state(case_dir: &Path, state: &OnelinerState) {
/// helper for that is [`past_rfc3339`].
pub fn write_closed_marker(case_dir: &Path, closed_at: &str) {
let json = format!(r#"{{"closed_at":"{closed_at}"}}"#);
std::fs::write(case_dir.join(".closed"), json).unwrap();
std::fs::write(case_dir.join(CLOSE_MARKER), json).unwrap();
}
/// Format "now - age" as RFC3339 UTC. Used to mint historical