[identity] Run account manager in a nested environment for integ tests.

This creates a nested environment to run account manager in for
integ tests, allowing the creation of multiple tests without
persistent data leaking between tests.

Testing: Existing tests.

Change-Id: I213fe17f10d975e4428858e286f42c0abf87fc2a
diff --git a/src/identity/tests/account_manager_integration/BUILD.gn b/src/identity/tests/account_manager_integration/BUILD.gn
index 557b8e4..def340a 100644
--- a/src/identity/tests/account_manager_integration/BUILD.gn
+++ b/src/identity/tests/account_manager_integration/BUILD.gn
@@ -35,6 +35,7 @@
     "//src/identity/lib/account_common",
     "//third_party/rust_crates:failure",
     "//third_party/rust_crates:futures-preview",
+    "//third_party/rust_crates:lazy_static",
     "//third_party/rust_crates:log",
     "//third_party/rust_crates:parking_lot",
   ]
diff --git a/src/identity/tests/account_manager_integration/meta/account_manager_integration_lib_test.cmx b/src/identity/tests/account_manager_integration/meta/account_manager_integration_lib_test.cmx
index 7064183..66ef6c8 100644
--- a/src/identity/tests/account_manager_integration/meta/account_manager_integration_lib_test.cmx
+++ b/src/identity/tests/account_manager_integration/meta/account_manager_integration_lib_test.cmx
@@ -1,21 +1,12 @@
 {
-    "facets": {
-        "fuchsia.test": {
-            "injected-services": {
-                "fuchsia.auth.account.AccountManager": [
-                    "fuchsia-pkg://fuchsia.com/account_manager#meta/account_manager.cmx",
-                    "--dev-auth-providers"
-                ]
-            }
-        }
-    },
     "program": {
         "binary": "test/account_manager_integration_lib_test"
     },
     "sandbox": {
         "services": [
             "fuchsia.logger.LogSink",
-            "fuchsia.auth.account.AccountManager"
+            "fuchsia.sys.Environment",
+            "fuchsia.sys.Loader"
         ]
     }
 }
diff --git a/src/identity/tests/account_manager_integration/tests/account.rs b/src/identity/tests/account_manager_integration/tests/account.rs
index 73e657c..9a9aa9f 100644
--- a/src/identity/tests/account_manager_integration/tests/account.rs
+++ b/src/identity/tests/account_manager_integration/tests/account.rs
@@ -10,8 +10,23 @@
 use fidl_fuchsia_auth_account::{
     AccountManagerMarker, AccountManagerProxy, LocalAccountId, Status,
 };
+use fuchsia_async as fasync;
+use fuchsia_component::client::{launch, App};
+use fuchsia_component::fuchsia_single_component_package_url;
+use fuchsia_component::server::{NestedEnvironment, ServiceFs};
 use futures::future::join;
 use futures::prelude::*;
+use lazy_static::lazy_static;
+use std::ops::Deref;
+
+lazy_static! {
+    /// URL for account manager.
+    static ref ACCOUNT_MANAGER_URL: String =
+        String::from(fuchsia_single_component_package_url!("account_manager"));
+
+    /// Arguments passed to account manager started in test environment.
+    static ref ACCOUNT_MANAGER_ARGS: Vec<String> = vec![String::from("--dev-auth-providers")];
+}
 
 /// Calls provision_new_account on the supplied account_manager, returning an error on any
 /// non-OK responses, or the account ID on success.
@@ -57,19 +72,98 @@
     }
 }
 
+/// A proxy to an account manager running in a nested environment.
+struct NestedAccountManagerProxy {
+    /// Proxy to account manager.
+    account_manager_proxy: AccountManagerProxy,
+
+    /// Application object for account manager.  Needs to be kept in scope to
+    /// keep the nested environment alive.
+    _app: App,
+
+    /// The nested environment account manager is running in.  Needs to be kept
+    /// in scope to keep the nested environment alive.
+    _nested_envronment: NestedEnvironment,
+}
+
+impl Deref for NestedAccountManagerProxy {
+    type Target = AccountManagerProxy;
+
+    fn deref(&self) -> &AccountManagerProxy {
+        &self.account_manager_proxy
+    }
+}
+
+/// Start account manager in an isolated environment and return a proxy to it.
+fn create_account_manager() -> Result<NestedAccountManagerProxy, Error> {
+    let mut service_fs = ServiceFs::new();
+
+    // Use a salted environment name to ensure the environment is unique across
+    // calls.
+    let nested_environment = service_fs.create_salted_nested_environment("account_test_env")?;
+
+    let app = launch(
+        nested_environment.launcher(),
+        ACCOUNT_MANAGER_URL.clone(),
+        Some(ACCOUNT_MANAGER_ARGS.clone()),
+    )?;
+    fasync::spawn(service_fs.collect());
+    let account_manager_proxy = app.connect_to_service::<AccountManagerMarker>()?;
+
+    Ok(NestedAccountManagerProxy {
+        account_manager_proxy,
+        _app: app,
+        _nested_envronment: nested_environment,
+    })
+}
+
 // TODO(jsankey): Work with ComponentFramework and cramertj@ to develop a nice Rust equivalent of
-// the C++ TestWithEnvironment fixture to provide isolated environments for each test case. For now
-// we verify all functionality in a single test case.
+// the C++ TestWithEnvironment fixture to provide isolated environments for each test case.  We
+// are currently creating a new environment for account manager to run in, but the tests
+// themselves run in a single environment.
 #[fuchsia_async::run_singlethreaded(test)]
-async fn test_account_functionality() -> Result<(), Error> {
-    let account_manager = fuchsia_component::client::connect_to_service::<AccountManagerMarker>()
-        .expect("Failed to connect to account manager service");
+async fn test_provision_new_account() -> Result<(), Error> {
+    let account_manager =
+        create_account_manager().expect("Failed to launch account manager in nested environment.");
 
     // Verify we initially have no accounts.
     assert_eq!(await!(account_manager.get_account_ids())?, vec![]);
 
     // Provision a new account.
-    let mut account_1 = await!(provision_new_account(&account_manager))?;
+    let account_1 = await!(provision_new_account(&account_manager))?;
+    assert_eq!(
+        await!(account_manager.get_account_ids())?,
+        vec![LocalAccountId { id: account_1.id }]
+    );
+
+    // Provision a second new account and verify it has a different ID.
+    let account_2 = await!(provision_new_account(&account_manager))?;
+    assert_ne!(account_1.id, account_2.id);
+
+    let account_ids = await!(account_manager.get_account_ids())?;
+    assert_eq!(account_ids.len(), 2);
+    assert!(account_ids.contains(&account_1));
+    assert!(account_ids.contains(&account_2));
+
+    // Auth state should be unknown for the new accounts
+    let (_, auth_states) = await!(account_manager.get_account_auth_states())?;
+    assert_eq!(auth_states.len(), 2);
+    assert!(auth_states.iter().all(|state| state.auth_state.summary == AuthStateSummary::Unknown
+        && account_ids.contains(&state.account_id)));
+
+    Ok(())
+}
+
+#[fuchsia_async::run_singlethreaded(test)]
+async fn test_provision_new_account_from_auth_provider() -> Result<(), Error> {
+    let account_manager =
+        create_account_manager().expect("Failed to launch account manager in nested environment.");
+
+    // Verify we initially have no accounts.
+    assert_eq!(await!(account_manager.get_account_ids())?, vec![]);
+
+    // Provision a new account.
+    let account_1 = await!(provision_account_from_dev_auth_provider(&account_manager))?;
     assert_eq!(
         await!(account_manager.get_account_ids())?,
         vec![LocalAccountId { id: account_1.id }]
@@ -79,11 +173,33 @@
     let account_2 = await!(provision_account_from_dev_auth_provider(&account_manager))?;
     assert_ne!(account_1.id, account_2.id);
 
-    // Connect a channel to one of these accounts and verify it's usable.
+    let account_ids = await!(account_manager.get_account_ids())?;
+    assert_eq!(account_ids.len(), 2);
+    assert!(account_ids.contains(&account_1));
+    assert!(account_ids.contains(&account_2));
+
+    // Auth state should be unknown for the new accounts
+    let (_, auth_states) = await!(account_manager.get_account_auth_states())?;
+    assert_eq!(auth_states.len(), 2);
+    assert!(auth_states.iter().all(|state| state.auth_state.summary == AuthStateSummary::Unknown
+        && account_ids.contains(&state.account_id)));
+
+    Ok(())
+}
+
+#[fuchsia_async::run_singlethreaded(test)]
+async fn test_get_account_and_persona() -> Result<(), Error> {
+    let account_manager =
+        create_account_manager().expect("Failed to launch account manager in nested environment.");
+
+    assert_eq!(await!(account_manager.get_account_ids())?, vec![]);
+
+    let mut account = await!(provision_new_account(&account_manager))?;
+    // Connect a channel to the newly created account and verify it's usable.
     let (acp_client_end, _) = create_endpoints()?;
     let (account_client_end, account_server_end) = create_endpoints()?;
     assert_eq!(
-        await!(account_manager.get_account(&mut account_1, acp_client_end, account_server_end))?,
+        await!(account_manager.get_account(&mut account, acp_client_end, account_server_end))?,
         Status::Ok
     );
     let account_proxy = account_client_end.into_proxy()?;
@@ -103,13 +219,36 @@
     };
     assert_eq!(persona_auth_state.summary, AuthStateSummary::Unknown);
 
+    Ok(())
+}
+
+#[fuchsia_async::run_singlethreaded(test)]
+async fn test_account_deletion() -> Result<(), Error> {
+    let account_manager =
+        create_account_manager().expect("Failed to launch account manager in nested environment.");
+
+    assert_eq!(await!(account_manager.get_account_ids())?, vec![]);
+
+    let mut account_1 = await!(provision_new_account(&account_manager))?;
+    let account_2 = await!(provision_new_account(&account_manager))?;
+    let existing_accounts = await!(account_manager.get_account_ids())?;
+    assert!(existing_accounts.contains(&LocalAccountId { id: account_1.id }));
+    assert!(existing_accounts.contains(&LocalAccountId { id: account_2.id }));
+    assert_eq!(existing_accounts.len(), 2);
+
     // Delete an account and verify it is removed.
     assert_eq!(await!(account_manager.remove_account(&mut account_1))?, Status::Ok);
     assert_eq!(
         await!(account_manager.get_account_ids())?,
         vec![LocalAccountId { id: account_2.id }]
     );
-    // Deliberately leave an account as dirty state which will cause assert errors upon storage
-    // isolation violations across invocations of this test. No state should be preserved.
+    // Connecting to the deleted account should fail.
+    let (acp_client_end, _acp_server_end) = create_endpoints()?;
+    let (_account_client_end, account_server_end) = create_endpoints()?;
+    assert_eq!(
+        await!(account_manager.get_account(&mut account_1, acp_client_end, account_server_end))?,
+        Status::NotFound
+    );
+
     Ok(())
 }