blob: 3f488c1560d757686bc99f3068229c54e188d5f5 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//! A fake implementation of `AccountHandlerContext` to simplify unit testing.
use crate::fake_authenticator::FakeAuthenticator;
use fidl::endpoints::{create_endpoints, create_proxy_and_stream};
use fidl_fuchsia_identity_account::Error as ApiError;
use fidl_fuchsia_identity_internal::{
AccountHandlerContextMarker, AccountHandlerContextProxy, AccountHandlerContextRequest,
AccountHandlerContextRequestStream,
};
use fuchsia_async as fasync;
use futures::prelude::*;
use log::error;
use std::collections::BTreeMap;
use std::sync::Arc;
/// A fake meant for tests which rely on an AccountHandlerContext, but the context itself
/// isn't under test. As opposed to the real type, this doesn't depend on any other
/// components. Panics when getting unexpected messages or args, as defined by the implementation.
pub struct FakeAccountHandlerContext {
fake_authenticators: BTreeMap<String, Arc<FakeAuthenticator>>,
}
impl FakeAccountHandlerContext {
/// Creates new fake account handler context
pub fn new() -> Self {
Self { fake_authenticators: BTreeMap::new() }
}
/// Inserts a fake authenticator. If an authentication mechanism
/// identified by `auth_mechanism_id` is requested, it will connect to
/// the fake authenticator.
pub fn insert_authenticator(&mut self, auth_mechanism_id: &str, fake: FakeAuthenticator) {
self.fake_authenticators.insert(auth_mechanism_id.to_string(), Arc::new(fake));
}
/// Asynchronously handles the supplied stream of `AccountHandlerContextRequest` messages.
pub async fn handle_requests_from_stream(
&self,
mut stream: AccountHandlerContextRequestStream,
) -> Result<(), fidl::Error> {
while let Some(req) = stream.try_next().await? {
self.handle_request(req).await?;
}
Ok(())
}
/// Asynchronously handles a single `AccountHandlerContextRequest`.
async fn handle_request(&self, req: AccountHandlerContextRequest) -> Result<(), fidl::Error> {
match req {
AccountHandlerContextRequest::GetOauth { responder, .. } => {
responder.send(&mut Err(ApiError::Internal))
}
AccountHandlerContextRequest::GetOpenIdConnect { responder, .. } => {
responder.send(&mut Err(ApiError::Internal))
}
AccountHandlerContextRequest::GetOauthOpenIdConnect { responder, .. } => {
responder.send(&mut Err(ApiError::Internal))
}
AccountHandlerContextRequest::GetStorageUnlockAuthMechanism {
auth_mechanism_id,
storage_unlock_mechanism,
responder,
} => {
let fake_authenticator = self.fake_authenticators.get(&auth_mechanism_id);
let mut response = match fake_authenticator {
Some(fake_authenticator) => {
let fake_authenticator = Arc::clone(fake_authenticator);
let stream = storage_unlock_mechanism.into_stream().unwrap();
fasync::Task::spawn(async move {
fake_authenticator
.handle_requests_from_stream(stream)
.await
.expect("Failed handling requests from stream");
})
.detach();
Ok(())
}
None => Err(ApiError::NotFound),
};
responder.send(&mut response)
}
}
}
}
/// Creates a new `AccountHandlerContext` channel, spawns a task to handle requests received on
/// this channel using the supplied `FakeAccountHandlerContext`, and returns the
/// `AccountHandlerContextProxy`.
pub fn spawn_context_channel(
context: Arc<FakeAccountHandlerContext>,
) -> AccountHandlerContextProxy {
let (proxy, stream) = create_proxy_and_stream::<AccountHandlerContextMarker>().unwrap();
let context_clone = Arc::clone(&context);
fasync::Task::spawn(async move {
context_clone
.handle_requests_from_stream(stream)
.await
.unwrap_or_else(|err| error!("Error handling FakeAccountHandlerContext: {:?}", err))
})
.detach();
proxy
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fake_authenticator::Expected;
use fidl::endpoints::create_proxy;
use lazy_static::lazy_static;
lazy_static! {
static ref TEST_PREKEY_MATERIAL: Vec<u8> = vec![13; 256];
static ref TEST_ENROLLMENT_DATA: Vec<u8> = vec![13, 37];
static ref AUTH_MECHANISM_ID_OK: &'static str = "auth-mech-id-1";
static ref AUTH_MECHANISM_ID_BAD: &'static str = "auth-mech-id-2";
}
#[fuchsia_async::run_until_stalled(test)]
async fn get_oauth() {
let fake_context = Arc::new(FakeAccountHandlerContext::new());
let proxy = spawn_context_channel(fake_context.clone());
let (_, ap_server_end) = create_endpoints().expect("failed creating channel pair");
assert_eq!(
proxy.get_oauth("dummy_auth_provider", ap_server_end).await.unwrap(),
Err(ApiError::Internal)
);
}
#[fuchsia_async::run_until_stalled(test)]
async fn with_fake_authenticator() {
let authenticator = FakeAuthenticator::new();
authenticator.enqueue(Expected::Enroll {
resp: Ok((TEST_ENROLLMENT_DATA.clone(), TEST_PREKEY_MATERIAL.clone())),
});
let mut fake_context = FakeAccountHandlerContext::new();
fake_context.insert_authenticator(&AUTH_MECHANISM_ID_OK, authenticator);
let proxy = spawn_context_channel(Arc::new(fake_context));
// Non-existing authenticator.
let (auth_mechanism_proxy, server_end) = create_proxy().unwrap();
assert_eq!(
proxy
.get_storage_unlock_auth_mechanism(&AUTH_MECHANISM_ID_BAD, server_end)
.await
.unwrap(),
Err(ApiError::NotFound)
);
assert!(auth_mechanism_proxy.enroll().await.is_err());
// Existing authenticator.
let (auth_mechanism_proxy, server_end) = create_proxy().unwrap();
assert_eq!(
proxy
.get_storage_unlock_auth_mechanism(&AUTH_MECHANISM_ID_OK, server_end)
.await
.unwrap(),
Ok(())
);
assert!(auth_mechanism_proxy.enroll().await.is_ok());
}
}