Skip to content

Commit

Permalink
feat: metadata get by contact id
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonribble committed Sep 29, 2024
1 parent e0e1a6c commit 0f37624
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 33 deletions.
5 changes: 5 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ chrono = "0.4.38"
clap = { version = "4.5.9", features = ["derive"] }
dotenvy = "0.15.0"
regex = "1.5.4"
sqlx = { version = "0.7.4", features = ["runtime-tokio-native-tls", "sqlite"] }
sqlx = { version = "0.7.4", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] }
tokio = { version = "1.28.0", features = ["full", "test-util"] }

[dev-dependencies]
Expand Down
113 changes: 82 additions & 31 deletions src/db/metadata_repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use sqlx::SqlitePool;
#[async_trait]
pub trait MetadataRepo {
async fn create(&self, metadata: models::Metadata) -> anyhow::Result<i64>;
async fn get_by_id(&self, contact_id: i64) -> anyhow::Result<models::Metadata>;
}

pub struct Connection {
Expand All @@ -25,8 +26,8 @@ impl Connection {

#[async_trait]
impl MetadataRepo for Connection {
async fn create(&self, metadata: models::Metadata) -> anyhow::Result<i64> {
let query = "INSERT INTO contact_metadata
async fn create(&self, metadata: models::Metadata) -> anyhow::Result<i64> {
let query = "INSERT INTO contact_metadata
(contact_id,
starred,
is_archived,
Expand All @@ -38,39 +39,66 @@ impl MetadataRepo for Connection {
last_reminder_at)
VALUES (?,?,?,?,?,?,?,?,?)";

let result = sqlx::query(query)
.bind(metadata.contact_id)
.bind(metadata.starred)
.bind(metadata.is_archived)
.bind(metadata.frequency)

.bind(metadata.created_at.to_rfc3339_opts(SecondsFormat::Millis, true))
.bind(metadata.updated_at.to_rfc3339_opts(SecondsFormat::Millis, true))
let result = sqlx::query(query)
.bind(metadata.contact_id)
.bind(metadata.starred)
.bind(metadata.is_archived)
.bind(metadata.frequency)
.bind(
metadata
.created_at
.to_rfc3339_opts(SecondsFormat::Millis, true),
)
.bind(
metadata
.updated_at
.to_rfc3339_opts(SecondsFormat::Millis, true),
)
.bind(
metadata
.last_seen_at
.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true)),
)
.bind(
metadata
.next_reminder_at
.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true)),
)
.bind(
metadata
.last_reminder_at
.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true)),
)
.execute(&*self.sqlite_pool)
.await?;

Ok(result.last_insert_rowid())
}
async fn get_by_id(&self, contact_id: i64) -> anyhow::Result<models::Metadata> {
let query_get_by_id = "SELECT * FROM metadata WHERE contact_id=$1";

.bind(metadata.last_seen_at.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true)))
.bind(metadata.next_reminder_at.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true)))
.bind(metadata.last_reminder_at.map(|dt| dt.to_rfc3339_opts(SecondsFormat::Millis, true)))
.execute(&*self.sqlite_pool)
.await?;
let metadata: models::Metadata = sqlx::query_as::<_, models::Metadata>(query_get_by_id)
.bind(contact_id)
.fetch_one(&*self.sqlite_pool)
.await?;

Ok(result.last_insert_rowid())
}
Ok(metadata)
}
}

#[cfg(test)]
mod tests {
use super::*;
use mockall::predicate::*;
use crate::models;
use mockall::predicate::*;
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};


async fn setup_test_db() -> SqlitePool {
let pool = SqlitePoolOptions::new()
.connect("sqlite::memory:")
.await
.expect("Failed to create in-memory SQLite database");

sqlx::query(
"CREATE TABLE IF NOT EXISTS contact_metadata (
contact_id INTEGER NOT NULL,
Expand All @@ -82,33 +110,31 @@ mod tests {
next_reminder_at TEXT,
frequency INTEGER,
last_reminder_at TEXT
)"
)",
)
.execute(&pool)
.await
.expect("Failed to create contact_metadata table");

pool
}
#[tokio::test]
async fn test_create_metadata_sqlite() {
let pool = setup_test_db().await;
let repo = Connection::new(pool);
let pool = setup_test_db().await;
let repo = Connection::new(pool);

let test_metadata = models::Metadata::default();
let test_metadata = models::Metadata::default();

let result = repo.create(test_metadata.clone()).await.unwrap();
assert!(result > 0);
}

let result = repo.create(test_metadata.clone()).await.unwrap();
assert!(result > 0);
}

#[tokio::test]
async fn test_create_metadata() {
let mut mock_metadata_repo = MockMetadataRepo::new();

let test_metadata = models::Metadata::default();


mock_metadata_repo
.expect_create()
.times(1)
Expand All @@ -121,6 +147,31 @@ mod tests {

assert_eq!(result, 1);
}
}

#[tokio::test]
async fn test_get_metadata() {
let mut mock_metadata_repo = MockMetadataRepo::new();

let test_metadata = models::Metadata {
contact_id: 1,
..models::Metadata::default()
};

// Clone test_metadata before using it in the closure
let test_metadata_clone = test_metadata.clone();

mock_metadata_repo
.expect_get_by_id()
.times(1)
.with(eq(1))
.returning(move |_| Ok(test_metadata_clone.clone()));

let result = mock_metadata_repo.get_by_id(1).await;

assert!(result.is_ok());

let expected_metadata = result.unwrap();

assert_eq!(expected_metadata, test_metadata);
}
}
2 changes: 1 addition & 1 deletion src/models/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use chrono::{DateTime, Utc};

#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, PartialEq, Eq, Clone, sqlx::FromRow)]
pub struct Metadata {
pub contact_id: i64,
pub starred: bool,
Expand Down

0 comments on commit 0f37624

Please sign in to comment.