better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
use super::*;
use better_auth_core::{FieldArray, FieldFunction, FieldObject, FromFieldMap};
use std::sync::atomic::{AtomicUsize, Ordering};

#[tokio::test]
async fn omitted_record_serialization_reaches_both_writes_before_a_reference_failure()
-> AuthResult<()> {
    let cache = Arc::new(Cache::default());
    cache.seed(INDEX, "{}".into());
    let calls = Arc::new(AtomicUsize::new(0));
    let observed = calls.clone();
    let mut fields = record("key".into()).field_values()?;
    let _ = fields.insert(
        "toJSON".into(),
        FieldFunction::contextual(move |receiver, arguments| {
            assert_eq!(receiver.model_property("id")?, "key".into());
            assert_eq!(arguments, &[FieldValue::from("")]);
            let _ = observed.fetch_add(1, Ordering::SeqCst);
            Ok(FieldValue::Undefined)
        })
        .into(),
    );
    let key = ApiKey::from_field_values(fields)?;
    let error = put(cache.clone(), &key, false).await.unwrap_err();
    assert!(error.to_string().contains("includes"));
    assert_eq!(calls.load(Ordering::SeqCst), 1);
    assert_eq!(cache.value("api-key:hash"), Some(FieldValue::Undefined));
    assert_eq!(
        cache.value("api-key:by-id:key"),
        Some(FieldValue::Undefined)
    );
    assert_eq!(cache.value(INDEX), Some("{}".into()));
    assert_eq!(
        *cache.writes.lock().unwrap(),
        vec![
            Write::Set("api-key:hash".into(), FieldValue::Undefined),
            Write::Set("api-key:by-id:key".into(), FieldValue::Undefined),
        ]
    );
    Ok(())
}

#[tokio::test]
async fn omitted_reference_serialization_is_a_write_after_key_deletion() -> AuthResult<()> {
    for result in [
        FieldValue::Undefined,
        FieldFunction::contextual(|_, _| Ok(FieldValue::Undefined)).into(),
    ] {
        let cache = Arc::new(Cache::default());
        let calls = Arc::new(AtomicUsize::new(0));
        let observed = calls.clone();
        let output: FieldValue = FieldMap::from([
            ("length".into(), 1.into()),
            (
                "toJSON".into(),
                FieldFunction::contextual(move |_, arguments| {
                    assert_eq!(arguments, &[FieldValue::from("")]);
                    let _ = observed.fetch_add(1, Ordering::SeqCst);
                    Ok(result.clone())
                })
                .into(),
            ),
        ])
        .into();
        let index = FieldArray::from_values_with_properties(
            vec!["key".into()],
            FieldObject::from(FieldMap::from([(
                "filter".into(),
                FieldFunction::contextual(move |_, _| Ok(output.clone())).into(),
            )])),
        );
        cache.seed("api-key:hash", "old-hash".into());
        cache.seed("api-key:by-id:key", "old-id".into());
        cache.seed(INDEX, index.into());
        remove_cached(cache.clone(), &record("key".into()), false).await?;
        assert_eq!(calls.load(Ordering::SeqCst), 1);
        assert_eq!(cache.value("api-key:hash"), None);
        assert_eq!(cache.value("api-key:by-id:key"), None);
        assert_eq!(cache.value(INDEX), Some(FieldValue::Undefined));
        assert_eq!(
            *cache.writes.lock().unwrap(),
            vec![
                Write::Delete("api-key:hash".into()),
                Write::Delete("api-key:by-id:key".into()),
                Write::Set(INDEX.into(), FieldValue::Undefined),
            ]
        );
    }
    Ok(())
}