better-auth-api 1.0.0-alpha.5

Plugin implementations for better-auth
#![allow(
    clippy::expect_used,
    reason = "Fixture setup and response assertions require successful fallible construction."
)]
use super::*;

#[tokio::test]
async fn completion_error_url_preserves_static_presence_and_runtime_origin() -> AuthResult<()> {
    use OptionUrl::{Auto, Dynamic, Static};

    for (name, option, runtime, error_url, expected, invalid) in [
        (
            "static wins",
            Static(ORIGIN),
            RUNTIME,
            None,
            "https://configured.proxy-runtime.test/api/auth/error",
            false,
        ),
        (
            "static path retained",
            Static("https://configured.proxy-runtime.test/tenant///"),
            RUNTIME,
            None,
            "https://configured.proxy-runtime.test/tenant/api/auth/error",
            false,
        ),
        (
            "static empty retained",
            Static(""),
            RUNTIME,
            None,
            "/api/auth/error",
            false,
        ),
        (
            "invalid static retained",
            Static("invalid"),
            RUNTIME,
            None,
            "invalid/api/auth/error",
            true,
        ),
        (
            "dynamic runtime origin",
            Dynamic,
            RUNTIME,
            None,
            "https://runtime.proxy-runtime.test/api/auth/error",
            false,
        ),
        (
            "absent runtime origin",
            Auto,
            RUNTIME,
            None,
            "https://runtime.proxy-runtime.test/api/auth/error",
            false,
        ),
        (
            "invalid runtime empty origin",
            Dynamic,
            "invalid",
            None,
            "/api/auth/error",
            false,
        ),
        (
            "opaque runtime empty origin",
            Dynamic,
            "data:text/plain,opaque",
            None,
            "/api/auth/error",
            false,
        ),
        (
            "empty runtime empty origin",
            Auto,
            "",
            None,
            "/api/auth/error",
            false,
        ),
        (
            "explicit error URL wins",
            Static(ORIGIN),
            RUNTIME,
            Some("/custom-error?retained=1"),
            "/custom-error?retained=1",
            false,
        ),
        (
            "empty error URL falls through",
            Static(ORIGIN),
            RUNTIME,
            Some(""),
            "https://configured.proxy-runtime.test/api/auth/error",
            false,
        ),
    ] {
        let fixture = Fixture::new(option, runtime, error_url);
        let mut request = AuthRequest::new(HttpMethod::Get, "/callback/test/oauth-proxy");
        request.query = Some(serde_json::json!({"callbackURL": "/complete"}));
        let root_config = Arc::new((*fixture.context.config).clone().base_url(ORIGIN));
        let root = AuthContext::new(root_config, fixture.store.clone())
            .expect("fixture context initializes")
            .initialize_request_context()
            .await?;
        let desired_config = fixture.context.config.clone();
        let request_ref = &request;
        let response = root
            .with_http_context(&request, |context| async move {
                let mut context = (*context).clone();
                context.config = desired_config;
                context.set_base_url(runtime);
                match OAuthProxyPlugin::new()
                    .on_request(request_ref, &context)
                    .await
                {
                    Ok(response) => response.unwrap().into_http_response(),
                    Err(error) => {
                        assert!(invalid, "{name}: {error}");
                        assert!(matches!(&error, AuthError::TypeError(_)), "{name}");
                        assert_eq!(error.to_string(), "Invalid URL", "{name}");
                        better_auth_core::api_error::handle_http_error(error, &context).await
                    }
                }
            })
            .await?;
        assert_eq!(response.status, if invalid { 500 } else { 302 }, "{name}");
        assert_eq!(
            response.headers.iter().count(),
            if invalid { 0 } else { 2 },
            "{name}"
        );
        assert_eq!(
            response.headers.get("content-type").map(String::as_str),
            (!invalid).then_some("application/json"),
            "{name}"
        );
        let expected = format!(
            "{expected}{}error=missing_profile",
            if expected.contains('?') { '&' } else { '?' }
        );
        assert_eq!(
            response.headers.get("location"),
            (!invalid).then_some(&expected),
            "{name}"
        );
        assert!(response.body.bytes()?.is_empty(), "{name}");
        let mut expected_logs = vec![(
            "error".into(),
            "OAuth proxy callback missing profile data".into(),
            vec![],
        )];
        if invalid {
            expected_logs.push((
                "error".into(),
                "TypeError".into(),
                vec!["Invalid URL".into()],
            ));
        }
        assert_eq!(*fixture.logs.0.lock().unwrap(), expected_logs, "{name}",);
        assert_eq!(root.base_url(), format!("{ORIGIN}/custom-auth"), "{name}");
        assert_eq!(root.config.base_url.as_static(), Some(ORIGIN), "{name}");
        fixture.assert_unchanged(option, runtime)?;
    }
    Ok(())
}