Skip to main content

sofie_identity/
testing.rs

1use sofie::serde_json::json;
2use sofie::values::Email;
3use sofie_testing::{TestApp, TestPrincipal, TestSession, fixture_value};
4
5#[cfg(feature = "permissions")]
6use crate::Permitted;
7#[cfg(feature = "permissions")]
8use sofie::db::Aggregate as _;
9
10use crate::MemberArea;
11
12/// Long enough for any password rule; fixtures never sign in with it.
13const MEMBER_PASSWORD: &str = "fixture member passphrase";
14
15pub trait TestMember: Sized {
16    fn member(app: &TestApp) -> impl Future<Output = Self>;
17}
18
19impl<A: MemberArea> TestMember for TestPrincipal<A> {
20    async fn member(app: &TestApp) -> Self {
21        let email: Email = fixture_value(app);
22        let session = signed_up::<A>(app, email.as_str(), MEMBER_PASSWORD).await;
23        app.principal_of(&session)
24            .expect("the app's own session signs in to the area")
25    }
26}
27
28/// Signs `email` up to `A` and then in, as a person does: signing up signs nobody in. Where
29/// `A` asks for a confirmed address, the account is confirmed between the two, as the mailed
30/// link would.
31pub async fn signed_up<A: MemberArea>(app: &TestApp, email: &str, password: &str) -> TestSession {
32    let sign_up =
33        A::SIGN_UP.unwrap_or_else(|| panic!("area `{}` has no `SIGN_UP` to join it at", A::NAME));
34    let credentials = json!({ "email": email, "password": password });
35    app.post(sign_up).json(&credentials).await.assert_ok();
36    #[cfg(feature = "email-verification")]
37    if A::VERIFIED_EMAIL {
38        confirm(app, email).await;
39    }
40    let signed_in = app.post(A::SIGN_IN).json(&credentials).await;
41    signed_in.session().unwrap_or_else(|| {
42        panic!(
43            "signing in to `{}` after signing up answered {}: {}",
44            A::NAME,
45            signed_in.status,
46            signed_in.body
47        )
48    })
49}
50
51/// Written directly rather than through the mailed link, so a test needs no worker running.
52#[cfg(feature = "email-verification")]
53async fn confirm(app: &TestApp, email: &str) {
54    let address = Email::parse(email).expect("a signed-up address is an email");
55    let mut account = crate::Account::of_email(&app.db().read(), address)
56        .await
57        .expect("reading the signed-up account")
58        .unwrap_or_else(|| panic!("signing up as `{email}` opened no account"));
59    account.confirm_email(app.db().clock().now());
60    app.db()
61        .commit(&mut account)
62        .await
63        .expect("confirming the signed-up account");
64}
65
66/// A new member of the `Permitted` area `A` whose role holds exactly `permissions`, written
67/// directly, so `A` needs no sign-up; act as it with `app.as_(&principal)`.
68#[cfg(feature = "permissions")]
69pub async fn granted<A: MemberArea<Principal = Permitted>>(
70    app: &TestApp,
71    permissions: &[&str],
72) -> TestPrincipal<A> {
73    let db = app.db();
74    let read = db.read();
75    let email: Email = fixture_value(app);
76    let mut role = crate::create_role(&read, &format!("granted {}", email.as_str()))
77        .await
78        .expect("a role named after a fresh address is free");
79    db.commit(&mut role).await.expect("creating the test role");
80    for permission in permissions {
81        let mut grant = crate::grant(&read, role.id(), permission)
82            .await
83            .unwrap_or_else(|error| panic!("granting `{permission}`: {error}"));
84        db.commit(&mut grant).await.expect("granting the test role");
85    }
86    let mut added = crate::add_member(&read, &db.clock(), email, A::NAME)
87        .await
88        .expect("adding a fresh address to the area")
89        .with_role(&read, &db.clock(), role.id())
90        .await
91        .expect("giving the test member its role");
92    db.commit(&mut added).await.expect("adding the test member");
93    TestPrincipal::new(sofie::db::Id::from_uuid(added.account().uuid()))
94}