better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
#![expect(
    clippy::unwrap_used,
    clippy::indexing_slicing,
    reason = "Pinned encoding contracts must fail immediately on invalid fixture shapes or callback observations."
)]

use super::*;
use crate::plugins::test_helpers::{
    native_observation::NativeObservation,
    verification_consumer_native::{ReplayFailures, SECRET, assert_replay, object::Root},
};
use better_auth_core::{FieldFunction, SecretKey, VersionedSecret};
use serde_json::{Value as Json, json};
use std::sync::Mutex;

async fn capture_encoding(scenario: &Json) -> AuthResult<Json> {
    let observer = Arc::new(Mutex::new(NativeObservation::default()));
    let root = Root::new(if scenario["value"] == "function" {
        "function"
    } else {
        "object"
    });
    if scenario["value"] == "object" {
        root.data("marker", "native-generated-codes".into());
        let expected = root.owner();
        let observed = observer.clone();
        let method: FieldValue = FieldFunction::contextual(move |receiver, arguments| {
            let owner = expected.upgrade().unwrap();
            let same_receiver = receiver.strict_equals(owner.get().unwrap());
            assert!(same_receiver);
            assert_eq!(arguments, &[FieldValue::from("")]);
            let mut observer = observed.lock().unwrap();
            let key = observer.observe(&arguments[0])?;
            observer.record(
                "toJSON",
                [
                    ("sameReceiver".into(), same_receiver.into()),
                    ("key".into(), key),
                ],
            );
            Ok(FieldValue::Undefined)
        })
        .into();
        root.getter("toJSON", false, move |_| Ok(method.clone()));
    }
    let before = observer.lock().unwrap().observe(&root.value)?;
    let keys = [VersionedSecret::new(3, SECRET)];
    let key = if scenario["key"] == "single" {
        SecretKey::Single(SECRET)
    } else {
        SecretKey::Versioned {
            keys: &keys,
            legacy_secret: None,
        }
    };
    let storage = if scenario["storage"] == "plain" {
        BackupCodeStorage::Plain
    } else {
        BackupCodeStorage::Encrypted
    };
    let observed = observer.clone();
    let generated = root.value.clone();
    let options = BackupCodeOptions {
        storage,
        generate: Some(Arc::new(|| vec!["typed-fallback".to_owned()])),
        generate_native: Some(Arc::new(move || {
            observed.lock().unwrap().record("generate", []);
            Ok(generated.clone())
        })),
        ..Default::default()
    };
    let result = options.generate(key).await;
    let mut observer = observer.lock().unwrap();
    let outcome = match result {
        Ok(generated) => {
            let same_codes = generated.backup_codes.strict_equals(&root.value);
            let value = FieldValue::from(FieldMap::from([
                ("backupCodes".into(), generated.backup_codes),
                (
                    "encryptedBackupCodes".into(),
                    generated.encrypted_backup_codes,
                ),
            ]));
            json!({"returned":true,"sameCodes":same_codes,"result":observer.observe(&value)?})
        }
        Err(error) => {
            let error = error.into_thrown_value()?;
            json!({"returned":false,"error":{
                "name":observer.observe(&error.model_property("name")?)?,
                "message":observer.observe(&error.model_property("message")?)?,
                "ownEnumerable":observer.observe(&error)?,
            }})
        }
    };
    Ok(
        json!({"before":before,"events":observer.events,"outcome":outcome,
        "after":observer.observe(&root.value)?}),
    )
}

#[tokio::test]
async fn backup_code_encoding_matches_native_callbacks_and_undefined_boundary() -> AuthResult<()> {
    let fixture: Json = serde_json::from_str(
        &std::fs::read_to_string(
            std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
                .join("../../tests/fixtures/two-factor-backup-code-encoding-1.7.6.json"),
        )
        .unwrap(),
    )?;
    assert_eq!(fixture["version"], "1.7.6");
    assert_eq!(fixture["contract"], "two-factor-backup-code-encoding");
    let cases = fixture["cases"].as_array().unwrap();
    assert_eq!(cases.len(), 4);
    let mut failures = ReplayFailures::default();
    for case in cases {
        let name = case["name"].as_str().unwrap();
        let actual = super::backup_code_native_contract::capture(&case["scenario"]).await?;
        failures.record(name, || assert_replay(&actual, &case["runtime"], name));
    }
    let encodings = fixture["encodings"].as_array().unwrap();
    assert_eq!(encodings.len(), 4);
    for case in encodings {
        let name = case["name"].as_str().unwrap();
        let actual = capture_encoding(&case["scenario"]).await?;
        failures.record(name, || assert_replay(&actual, &case["runtime"], name));
    }
    failures.finish();
    Ok(())
}