use oauth_device_flows::{DeviceFlow, DeviceFlowConfig, DeviceFlowError, Provider};
use std::time::Duration;
use wiremock::{
matchers::{method, path},
Mock, MockServer, ResponseTemplate,
};
#[tokio::test]
async fn test_microsoft_device_flow_initialization() {
let mock_server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/oauth2/v2.0/devicecode"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"device_code": "device123",
"user_code": "ABC123",
"verification_uri": "https://microsoft.com/devicelogin",
"expires_in": 900,
"interval": 5
})))
.mount(&mock_server)
.await;
let config = DeviceFlowConfig::new()
.client_id("test-client-id")
.scopes(vec!["user.read".to_string()]);
let device_flow = DeviceFlow::new(Provider::Microsoft, config).unwrap();
assert!(device_flow.config().validate(Provider::Microsoft).is_ok());
}
#[test]
fn test_device_flow_config_validation() {
let valid_config = DeviceFlowConfig::new()
.client_id("test-client-id")
.scopes(vec!["scope1".to_string()]);
assert!(valid_config.validate(Provider::Microsoft).is_ok());
let invalid_config = DeviceFlowConfig::new();
assert!(matches!(
invalid_config.validate(Provider::Microsoft),
Err(DeviceFlowError::InvalidClient(_))
));
}
#[test]
fn test_provider_endpoints() {
assert_eq!(
Provider::Microsoft.device_authorization_endpoint(),
"https://login.microsoftonline.com/common/oauth2/v2.0/devicecode"
);
assert_eq!(
Provider::GitHub.device_authorization_endpoint(),
"https://github.com/login/device/code"
);
assert_eq!(
Provider::Google.token_endpoint(),
"https://oauth2.googleapis.com/token"
);
}
#[test]
fn test_provider_configuration() {
assert!(!Provider::Microsoft.requires_client_secret());
assert!(Provider::Google.requires_client_secret());
let microsoft_scopes = Provider::Microsoft.default_scopes();
assert!(!microsoft_scopes.is_empty());
assert!(microsoft_scopes.contains(&"https://graph.microsoft.com/.default"));
}
#[test]
fn test_config_builder_pattern() {
let config = DeviceFlowConfig::new()
.client_id("test-id")
.scope("scope1")
.scope("scope2")
.poll_interval(Duration::from_secs(10))
.max_attempts(30)
.header("Custom-Header", "value");
assert_eq!(config.client_id, "test-id");
assert_eq!(config.scopes, vec!["scope1", "scope2"]);
assert_eq!(config.poll_interval, Duration::from_secs(10));
assert_eq!(config.max_attempts, 30);
assert!(config.additional_headers.contains_key("Custom-Header"));
}
#[test]
fn test_error_types() {
let auth_error = DeviceFlowError::oauth_error("invalid_request", "Invalid request");
assert!(matches!(auth_error, DeviceFlowError::OAuth { .. }));
let client_error = DeviceFlowError::invalid_client("Missing client ID");
assert!(matches!(client_error, DeviceFlowError::InvalidClient(_)));
assert!(DeviceFlowError::AuthorizationPending.is_retryable());
assert!(DeviceFlowError::SlowDown.is_retryable());
assert!(!DeviceFlowError::AuthorizationDenied.is_retryable());
assert!(!DeviceFlowError::ExpiredToken.is_retryable());
}