summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/fixtures/channel.rs24
-rw-r--r--src/test/fixtures/error.rs14
-rw-r--r--src/test/fixtures/future.rs55
-rw-r--r--src/test/fixtures/identity.rs27
-rw-r--r--src/test/fixtures/login.rs44
-rw-r--r--src/test/fixtures/message.rs26
-rw-r--r--src/test/fixtures/mod.rs28
-rw-r--r--src/test/mod.rs1
8 files changed, 219 insertions, 0 deletions
diff --git a/src/test/fixtures/channel.rs b/src/test/fixtures/channel.rs
new file mode 100644
index 0000000..0558395
--- /dev/null
+++ b/src/test/fixtures/channel.rs
@@ -0,0 +1,24 @@
+use faker_rand::{
+ en_us::{addresses::CityName, names::FullName},
+ faker_impl_from_templates,
+};
+use rand;
+
+use crate::{app::App, repo::channel::Channel};
+
+pub async fn create(app: &App) -> Channel {
+ let name = propose();
+ app.channels()
+ .create(&name)
+ .await
+ .expect("should always succeed if the channel is actually new")
+}
+
+pub fn propose() -> String {
+ rand::random::<Name>().to_string()
+}
+
+struct Name(String);
+faker_impl_from_templates! {
+ Name; "{} {}", CityName, FullName;
+}
diff --git a/src/test/fixtures/error.rs b/src/test/fixtures/error.rs
new file mode 100644
index 0000000..559afee
--- /dev/null
+++ b/src/test/fixtures/error.rs
@@ -0,0 +1,14 @@
+macro_rules! expected {
+ ($expr:expr, $expect:pat $(,)?) => {
+ $crate::test::fixtures::error::expected!($expr, $expect, ())
+ };
+
+ ($expr:expr, $expect:pat, $body:expr $(,)?) => {
+ match $expr {
+ $expect => $body,
+ other => panic!("expected {}, found {other:#?}", stringify!($expect)),
+ }
+ };
+}
+
+pub(crate) use expected;
diff --git a/src/test/fixtures/future.rs b/src/test/fixtures/future.rs
new file mode 100644
index 0000000..bbdc9f8
--- /dev/null
+++ b/src/test/fixtures/future.rs
@@ -0,0 +1,55 @@
+use std::{future::IntoFuture, time::Duration};
+
+use futures::{stream, Stream};
+use tokio::time::timeout;
+
+async fn immediately<F>(fut: F) -> F::Output
+where
+ F: IntoFuture,
+{
+ // I haven't been particularly rigorous here. Zero delay _seems to work_,
+ // but this can be set higher; it makes tests that fail to meet the
+ // "immediate" expectation take longer, but gives slow tests time to
+ // succeed, as well.
+ let duration = Duration::from_nanos(0);
+ timeout(duration, fut)
+ .await
+ .expect("expected result immediately")
+}
+
+// This is only intended for streams, since their `next()`, `collect()`, and
+// so on can all block indefinitely on an empty stream. There's no need to
+// force immediacy on futures that "can't" block forever, and it can hide logic
+// errors if you do that.
+//
+// The impls below _could_ be replaced with a blanket impl for all future
+// types, otherwise. The choice to restrict impls to stream futures is
+// deliberate.
+pub trait Immediately {
+ type Output;
+
+ async fn immediately(self) -> Self::Output;
+}
+
+impl<'a, St> Immediately for stream::Next<'a, St>
+where
+ St: Stream + Unpin + ?Sized,
+{
+ type Output = Option<<St as Stream>::Item>;
+
+ async fn immediately(self) -> Self::Output {
+ immediately(self).await
+ }
+}
+
+impl<St, C> Immediately for stream::Collect<St, C>
+where
+ St: Stream,
+ C: Default + Extend<<St as Stream>::Item>,
+{
+ type Output = C;
+
+ async fn immediately(self) -> Self::Output {
+ immediately(self).await
+ }
+}
diff --git a/src/test/fixtures/identity.rs b/src/test/fixtures/identity.rs
new file mode 100644
index 0000000..16463aa
--- /dev/null
+++ b/src/test/fixtures/identity.rs
@@ -0,0 +1,27 @@
+use uuid::Uuid;
+
+use crate::{app::App, clock::RequestedAt, login::extract::IdentityToken};
+
+pub fn not_logged_in() -> IdentityToken {
+ IdentityToken::new()
+}
+
+pub async fn logged_in(app: &App, login: &(String, String), now: &RequestedAt) -> IdentityToken {
+ let (name, password) = login;
+ let token = app
+ .logins()
+ .login(name, password, now)
+ .await
+ .expect("should succeed given known-valid credentials");
+
+ IdentityToken::new().set(&token)
+}
+
+pub fn secret(identity: &IdentityToken) -> &str {
+ identity.secret().expect("identity contained a secret")
+}
+
+pub fn fictitious() -> IdentityToken {
+ let token = Uuid::new_v4().to_string();
+ IdentityToken::new().set(&token)
+}
diff --git a/src/test/fixtures/login.rs b/src/test/fixtures/login.rs
new file mode 100644
index 0000000..b2a4292
--- /dev/null
+++ b/src/test/fixtures/login.rs
@@ -0,0 +1,44 @@
+use faker_rand::en_us::internet;
+use uuid::Uuid;
+
+use crate::{
+ app::App,
+ repo::login::{self, Login},
+};
+
+pub async fn create_for_login(app: &App) -> (String, String) {
+ let (name, password) = propose();
+ app.logins()
+ .create(&name, &password)
+ .await
+ .expect("should always succeed if the login is actually new");
+
+ (name, password)
+}
+
+pub async fn create(app: &App) -> Login {
+ let (name, password) = propose();
+ app.logins()
+ .create(&name, &password)
+ .await
+ .expect("should always succeed if the login is actually new")
+}
+
+pub fn fictitious() -> Login {
+ Login {
+ id: login::Id::generate(),
+ name: name(),
+ }
+}
+
+pub fn propose() -> (String, String) {
+ (name(), propose_password())
+}
+
+fn name() -> String {
+ rand::random::<internet::Username>().to_string()
+}
+
+pub fn propose_password() -> String {
+ Uuid::new_v4().to_string()
+}
diff --git a/src/test/fixtures/message.rs b/src/test/fixtures/message.rs
new file mode 100644
index 0000000..7fe3cb9
--- /dev/null
+++ b/src/test/fixtures/message.rs
@@ -0,0 +1,26 @@
+use faker_rand::lorem::Paragraphs;
+
+use crate::{
+ app::App,
+ clock::RequestedAt,
+ events::repo::broadcast,
+ repo::{channel::Channel, login::Login},
+};
+
+pub async fn send(
+ app: &App,
+ login: &Login,
+ channel: &Channel,
+ sent_at: &RequestedAt,
+) -> broadcast::Message {
+ let body = propose();
+
+ app.channels()
+ .send(login, &channel.id, &body, sent_at)
+ .await
+ .expect("should succeed if the channel exists")
+}
+
+pub fn propose() -> String {
+ rand::random::<Paragraphs>().to_string()
+}
diff --git a/src/test/fixtures/mod.rs b/src/test/fixtures/mod.rs
new file mode 100644
index 0000000..05e3f3f
--- /dev/null
+++ b/src/test/fixtures/mod.rs
@@ -0,0 +1,28 @@
+use chrono::{TimeDelta, Utc};
+
+use crate::{app::App, clock::RequestedAt, repo::pool};
+
+pub mod channel;
+pub mod error;
+pub mod future;
+pub mod identity;
+pub mod login;
+pub mod message;
+
+pub async fn scratch_app() -> App {
+ let pool = pool::prepare("sqlite::memory:")
+ .await
+ .expect("setting up in-memory sqlite database");
+ App::from(pool)
+ .await
+ .expect("creating an app from a fresh, in-memory database")
+}
+
+pub fn now() -> RequestedAt {
+ Utc::now().into()
+}
+
+pub fn ancient() -> RequestedAt {
+ let timestamp = Utc::now() - TimeDelta::days(365);
+ timestamp.into()
+}
diff --git a/src/test/mod.rs b/src/test/mod.rs
new file mode 100644
index 0000000..d066349
--- /dev/null
+++ b/src/test/mod.rs
@@ -0,0 +1 @@
+pub mod fixtures;