fabro/lib/crates/fabro-util/tests/dev_token.rs
Bryan Helmkamp 19939c5f07
lint(clippy): disallow blocking std::fs on Tokio paths
Phase 2/3 of the std::fs lint initiative (Phase 1 refactors landed in
commit 9d1c0d98c).

clippy.toml additions (appended to disallowed-methods):
  std::fs::read, read_to_string, write, read_dir, copy, canonicalize
  std::fs::File::open, File::create, File::create_new
  std::fs::OpenOptions::open

File::options was deliberately excluded — it returns an OpenOptions
builder with no syscall. OpenOptions::open is where the block happens.
Non-blocking std::fs items (metadata, exists, create_dir_all, remove_*,
rename, and all std::fs types) remain legal.

Annotation policy (per updated plan):
  - Mixed async/sync production source: function- or statement-scoped
    #[expect(...)] so future accidental Tokio-path regressions in the
    same file still fire.
  - Fully-sync production source, test modules, integration tests,
    build.rs: file-level #![expect(...)].
  - Every #[expect] has a specific reason identifying the sync context.

Annotations added in ~90 files across the workspace. Notable narrow
placements: fabro-server server.rs current_server_target,
build_disk_usage_response, create_test_app_state_with_session_key;
fabro-server install.rs read_to_string rollback snapshot;
fabro-sandbox local.rs list_recursive; fabro-agent cli.rs FOLLOW-UP on
the JSON-stdout writer; fabro-llm providers/common.rs FOLLOW-UP for
load_file_as_base64 (7 translator call sites; revisit if file:// URL
usage grows).

build.rs blanket allows: fabro-api/build.rs, fabro-util/build.rs.

Pre-existing unrelated nightly-clippy warnings fixed under scope:
fabro-sandbox sandbox_spec.rs (unused_imports, unused_async),
reconnect.rs (unused_variables, unused_async).

Verified: cargo +nightly-2026-04-14 clippy --workspace --all-targets
-- -D warnings passes; fmt clean; 4129/4131 tests pass (two known
flakes under parallel nextest load, both pass individually and are
unrelated to this change).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 17:22:21 -04:00

104 lines
2.6 KiB
Rust

#![expect(
clippy::disallowed_methods,
reason = "integration tests that exercise sync dev-token file operations"
)]
use std::fs;
use fabro_util::Home;
use fabro_util::dev_token::{
DEV_TOKEN_PREFIX, generate_dev_token, load_or_create_dev_token, validate_dev_token_format,
};
#[test]
fn generate_has_correct_prefix_and_length() {
let token = generate_dev_token();
assert!(token.starts_with(DEV_TOKEN_PREFIX));
assert_eq!(token.len(), 74);
assert!(validate_dev_token_format(&token));
}
#[test]
fn generate_is_unique() {
assert_ne!(generate_dev_token(), generate_dev_token());
}
#[test]
fn validate_format_accepts_valid() {
let token = format!("{DEV_TOKEN_PREFIX}{}", "ab".repeat(32));
assert!(validate_dev_token_format(&token));
}
#[test]
fn validate_format_rejects_short() {
let token = format!("{DEV_TOKEN_PREFIX}{}", "ab".repeat(31));
assert!(!validate_dev_token_format(&token));
}
#[test]
fn validate_format_rejects_non_hex() {
let token = format!("{DEV_TOKEN_PREFIX}{}zz", "ab".repeat(31));
assert!(!validate_dev_token_format(&token));
}
#[test]
fn validate_format_rejects_wrong_prefix() {
let token = format!("fabro_nope_{}", "ab".repeat(32));
assert!(!validate_dev_token_format(&token));
}
#[test]
fn load_or_create_creates_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("dev-token");
let token = load_or_create_dev_token(&path).unwrap();
assert!(validate_dev_token_format(&token));
assert_eq!(fs::read_to_string(&path).unwrap(), token);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mode = fs::metadata(&path).unwrap().permissions().mode() & 0o777;
assert_eq!(mode, 0o600);
}
}
#[test]
fn load_or_create_reads_existing() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("dev-token");
let token = format!("{DEV_TOKEN_PREFIX}{}", "cd".repeat(32));
fs::write(&path, &token).unwrap();
let loaded = load_or_create_dev_token(&path).unwrap();
assert_eq!(loaded, token);
}
#[test]
fn load_or_create_rejects_malformed_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("dev-token");
fs::write(&path, "not-a-token").unwrap();
let error = load_or_create_dev_token(&path).unwrap_err();
assert!(error.to_string().contains("invalid"));
}
#[test]
fn home_dev_token_path_is_relative_to_root() {
let home = Home::new("/tmp/fabro-home");
assert_eq!(
home.dev_token_path(),
std::path::Path::new("/tmp/fabro-home/dev-token")
);
}