Files
doctate/server/tests/common/paths.rs
T
Brummel 16ef0bbe78 Introduce boot_time to evaluate_state
The `boot_time` parameter is introduced to `evaluate_state` and its
callers. This parameter acts as a lower bound for the "seen at"
timestamp used in determining the `Idle` state for cases.

Previously, the `elapsed` duration was calculated as
`now.duration_since(latest_mtime)`. This meant that a server restart
could immediately cause old recordings to be classified as `Idle` if
their modification times were significantly in the past, potentially
triggering a large number of LLM calls upon the first render of the
`/cases` page.

By using `seen_at = std::cmp::max(latest_mtime, boot_time)`, the idle
calculation now ensures that a fresh server start respects a grace
period. Old recordings will not satisfy the idle threshold until the
elapsed time since the server's boot (or the recording's modification
time, whichever is later) exceeds the `idle_threshold`.

`SystemTime::UNIX_EPOCH` is used as the default `boot_time` in tests and
in `create_router_and_session_store_with_settings`. This preserves
existing test behavior where the idle grace period is effectively
disabled. For production, `main.rs` now captures the server's actual
boot time and passes it down.

This change also refactors `evaluate_case` and `try_enqueue` to accept
the new `boot_time` parameter. `try_enqueue_all_for_user` is updated to
pass `boot_time` along. `boot_scan_state` and `compute_state_for_user`
in `recovery.rs` also receive `boot_time`.

Additionally, a new test case
`analyze_required_endpoint_enqueues_only_required_cases` is added to
verify the behavior of the `/cases/analyze-required` endpoint. This
endpoint now correctly enqueues only `Required`
2026-05-05 15:12:08 +02:00

77 lines
2.5 KiB
Rust

//! URL path constants and builders.
//!
//! Centralising these catches routing refactors at compile time (a
//! rename to the constant/fn shows up in every test file in one
//! `cargo check`) rather than as a scatter of 404s.
//!
//! The public `/api/*` surface lives in `doctate_common` and is
//! re-exported from there (see `ONELINERS_PATH`, `UPLOAD_PATH`).
pub use doctate_common::{ONELINERS_PATH, UPLOAD_PATH};
// -- browser UI routes --
/// `POST /login` — form login (slug, password).
pub const LOGIN: &str = "/login";
/// `POST /logout` — CSRF-protected logout.
pub const LOGOUT: &str = "/logout";
/// `GET /cases` — case list; also triggers the lazy retention sweep.
pub const CASES: &str = "/cases";
/// `POST /cases/bulk` — admin bulk action (close/analyze/reset).
pub const BULK: &str = "/cases/bulk";
/// `POST /cases/analyze-required` — user-facing bulk "Aktualisieren"
/// button: enqueues every Required/Idle case for the authenticated user.
pub const ANALYZE_REQUIRED: &str = "/cases/analyze-required";
/// `POST /cases/purge-closed` — admin hard-delete of closed cases.
pub const PURGE_CLOSED: &str = "/cases/purge-closed";
/// `GET /events` — SSE stream (per-user scoped).
pub const EVENTS: &str = "/events";
// -- per-case builders --
/// `/cases/{case_id}`.
pub fn case_detail(case_id: &str) -> String {
format!("/cases/{case_id}")
}
/// `/cases/{case_id}/recordings`.
pub fn case_recordings(case_id: &str) -> String {
format!("/cases/{case_id}/recordings")
}
/// `POST /cases/{case_id}/close`.
pub fn case_close(case_id: &str) -> String {
format!("/cases/{case_id}/close")
}
/// `POST /cases/{case_id}/reopen`.
pub fn case_reopen(case_id: &str) -> String {
format!("/cases/{case_id}/reopen")
}
/// `POST /cases/{case_id}/analyze`.
pub fn case_analyze(case_id: &str) -> String {
format!("/cases/{case_id}/analyze")
}
/// `POST /cases/{case_id}/reset` — admin-only hard-reset.
pub fn case_reset(case_id: &str) -> String {
format!("/cases/{case_id}/reset")
}
/// `POST /cases/{case_id}/recordings/delete` — per-recording delete.
pub fn recording_delete(case_id: &str) -> String {
format!("/cases/{case_id}/recordings/delete")
}
/// `POST /cases/{case_id}/oneliner` — session-authenticated manual
/// oneliner override (browser web UI).
pub fn case_oneliner_web(case_id: &str) -> String {
format!("/cases/{case_id}/oneliner")
}
/// `GET /magic?token={token}` — magic-link consumption.
pub fn magic(token: &str) -> String {
format!("/magic?token={token}")
}