//! E2E bootstrap fixture tests (todo 31). //! //! All passwords and TOTP secrets below are synthetic test values in temp //! dirs — never the real `config/` tree, never real credentials. use std::fs; use provision::e2e::{ CHANGE_CONFIRM_FIELD, CHANGE_NEW_PASSWORD_FIELD, CONSENT_APPROVE_VALUE, CONSENT_DECISION_FIELD, CONSENT_REQUEST_ID_FIELD, CSRF_FIELD, E2eError, LOGIN_CODE_FIELD, LOGIN_EMAIL_FIELD, LOGIN_PASSWORD_FIELD, MISSING_FIXTURE_HINT, WriteOutcome, load, write_initial, }; #[cfg(unix)] fn assert_0600(path: &std::path::Path) { use std::os::unix::fs::PermissionsExt as _; let mode = fs::metadata(path) .expect("fixture metadata") .permissions() .mode() & 0o777; assert_eq!(mode, 0o600, "fixture at `{}` is not 0600", path.display()); } #[test] fn writes_email_and_password_0600_only_when_absent() { let dir = tempfile::tempdir().expect("tempdir"); let out = dir.path().join("e2e.json"); let outcome = write_initial(&out, "demo@example.com", "test-password-01").expect("first write"); assert_eq!(outcome, WriteOutcome::Created); let raw = fs::read_to_string(&out).expect("fixture readable"); let value: serde_json::Value = serde_json::from_str(&raw).expect("fixture is JSON"); assert_eq!( value.get("email").and_then(|email| email.as_str()), Some("demo@example.com") ); assert_eq!( value.get("password").and_then(|password| password.as_str()), Some("test-password-01") ); assert!( value.get("totp_secret").is_none(), "provision must not write the spec-owned totp_secret field" ); #[cfg(unix)] assert_0600(&out); } #[test] fn leaves_an_existing_file_byte_for_byte_untouched() { let dir = tempfile::tempdir().expect("tempdir"); let out = dir.path().join("e2e.json"); // A spec-owned file: rotated password plus a captured TOTP secret. let spec_owned = r#"{"email":"demo@example.com","password":"rotated-by-spec","totp_secret":"JBSWY3DPEHPK3PXP"}"#; fs::write(&out, spec_owned).expect("pre-existing fixture"); let outcome = write_initial(&out, "demo@example.com", "initial-password").expect("second write"); assert_eq!(outcome, WriteOutcome::Kept); let raw = fs::read_to_string(&out).expect("fixture readable"); assert_eq!(raw, spec_owned, "existing fixture must be untouched"); } #[test] fn written_fixture_round_trips_through_load() { let dir = tempfile::tempdir().expect("tempdir"); let out = dir.path().join("nested/e2e.json"); write_initial(&out, "demo@example.com", "test-password-01").expect("write"); let fixture = load(&out).expect("load"); assert_eq!(fixture.email, "demo@example.com"); assert_eq!(fixture.password, "test-password-01"); assert_eq!(fixture.totp_secret, None); } #[test] fn missing_fixture_load_fails_with_run_provision_first_hint() { let dir = tempfile::tempdir().expect("tempdir"); let out = dir.path().join("e2e.json"); let result = load(&out); assert!( matches!(result, Err(E2eError::Missing { .. })), "expected Missing, got {result:?}" ); let message = result.expect_err("missing fixture").to_string(); assert!( message.contains("provision"), "hint must name provision: {message}" ); assert!( message.contains(MISSING_FIXTURE_HINT), "hint must match the documented contract: {message}" ); } #[test] fn malformed_fixture_is_a_typed_parse_error() { let dir = tempfile::tempdir().expect("tempdir"); let out = dir.path().join("e2e.json"); fs::write(&out, "{not json").expect("malformed fixture"); let result = load(&out); assert!(matches!(result, Err(E2eError::Parse { .. }))); } #[test] fn debug_redacts_password_and_totp_secret() { let dir = tempfile::tempdir().expect("tempdir"); let out = dir.path().join("e2e.json"); write_initial(&out, "demo@example.com", "S3CRET-PW").expect("write"); let mut fixture = load(&out).expect("load"); fixture.totp_secret = Some("S3CRET-TOTP".to_owned()); let rendered = format!("{fixture:?}"); assert!( !rendered.contains("S3CRET-PW"), "password leaked: {rendered}" ); assert!( !rendered.contains("S3CRET-TOTP"), "totp secret leaked: {rendered}" ); assert!(rendered.contains("demo@example.com")); } #[test] fn documented_selectors_match_the_d_oauth_templates() { // Login form (`src/ui/login.rs` `LoginTemplate`). assert_eq!(LOGIN_EMAIL_FIELD, "email"); assert_eq!(LOGIN_PASSWORD_FIELD, "password"); assert_eq!(LOGIN_CODE_FIELD, "code"); // Hidden form token on every d-OAuth form. assert_eq!(CSRF_FIELD, "csrf"); // Password-change form (`PasswordTemplate`). assert_eq!(CHANGE_NEW_PASSWORD_FIELD, "new_password"); assert_eq!(CHANGE_CONFIRM_FIELD, "confirm"); // Consent form (`src/ui/consent.rs` `ConsentTemplate`/`ConsentForm`). assert_eq!(CONSENT_REQUEST_ID_FIELD, "request_id"); assert_eq!(CONSENT_DECISION_FIELD, "decision"); assert_eq!(CONSENT_APPROVE_VALUE, "approve"); }