Compare commits
	
		
			3 Commits 
		
	
	
		
			5f06e0847f
			...
			73988d6264
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
									
								
								 | 
						73988d6264 | |
| 
							
							
								
									
								
								 | 
						22016fe0e9 | |
| 
							
							
								
									
								
								 | 
						fe8939c07e | 
| 
						 | 
				
			
			@ -1 +1 @@
 | 
			
		|||
DATABASE_URL=data/otter.sqlite3
 | 
			
		||||
DATABASE_URL=otter.sqlite3
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
otter.sqlite3
 | 
			
		||||
| 
						 | 
				
			
			@ -2,7 +2,7 @@
 | 
			
		|||
# see https://diesel.rs/guides/configuring-diesel-cli
 | 
			
		||||
 | 
			
		||||
[print_schema]
 | 
			
		||||
file = "src/db/schema.rs"
 | 
			
		||||
file = "src/schema.rs"
 | 
			
		||||
custom_type_derives = ["diesel::query_builder::QueryId", "Clone"]
 | 
			
		||||
sqlite_integer_primary_key_is_bigint = true
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,6 +98,10 @@ pub fn initialize_db(path: impl AsRef<Path>, run_migrations: bool) -> Result<DbP
 | 
			
		|||
pub fn initialize_db_in_memory(run_migrations: bool) -> Result<DbPool, DbError> {
 | 
			
		||||
    let manager = ConnectionManager::<SqliteConnection>::new(":memory:");
 | 
			
		||||
    let pool = Pool::builder()
 | 
			
		||||
        // An in-memory database is recreated for each opened connection, so we try to force only a
 | 
			
		||||
        // single connection to stay alive
 | 
			
		||||
        .min_idle(Some(1))
 | 
			
		||||
        .max_size(1)
 | 
			
		||||
        .connection_customizer(Box::new(AddQueryDebugLogs))
 | 
			
		||||
        .build(manager)?;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
use std::collections::HashSet;
 | 
			
		||||
 | 
			
		||||
use chrono::{DateTime, Utc};
 | 
			
		||||
use diesel::{alias, dsl::not, prelude::*};
 | 
			
		||||
use diesel::{alias, dsl::not, prelude::*, sqlite::Sqlite};
 | 
			
		||||
use gpodder::AuthErr;
 | 
			
		||||
 | 
			
		||||
use super::SqliteRepository;
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +70,8 @@ impl gpodder::DeviceRepository for SqliteRepository {
 | 
			
		|||
        patch: gpodder::DevicePatch,
 | 
			
		||||
    ) -> Result<(), gpodder::AuthErr> {
 | 
			
		||||
        (|| {
 | 
			
		||||
            let conn = &mut self.pool.get()?;
 | 
			
		||||
 | 
			
		||||
            if let Some(mut device) = devices::table
 | 
			
		||||
                .select(Device::as_select())
 | 
			
		||||
                .filter(
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +79,7 @@ impl gpodder::DeviceRepository for SqliteRepository {
 | 
			
		|||
                        .eq(user.id)
 | 
			
		||||
                        .and(devices::device_id.eq(device_id)),
 | 
			
		||||
                )
 | 
			
		||||
                .get_result(&mut self.pool.get()?)
 | 
			
		||||
                .get_result(conn)
 | 
			
		||||
                .optional()?
 | 
			
		||||
            {
 | 
			
		||||
                if let Some(caption) = patch.caption {
 | 
			
		||||
| 
						 | 
				
			
			@ -93,7 +95,7 @@ impl gpodder::DeviceRepository for SqliteRepository {
 | 
			
		|||
                        devices::caption.eq(&device.caption),
 | 
			
		||||
                        devices::type_.eq(&device.type_),
 | 
			
		||||
                    ))
 | 
			
		||||
                    .execute(&mut self.pool.get()?)?;
 | 
			
		||||
                    .execute(conn)?;
 | 
			
		||||
            } else {
 | 
			
		||||
                let device = NewDevice {
 | 
			
		||||
                    device_id: device_id.to_string(),
 | 
			
		||||
| 
						 | 
				
			
			@ -104,7 +106,7 @@ impl gpodder::DeviceRepository for SqliteRepository {
 | 
			
		|||
 | 
			
		||||
                diesel::insert_into(devices::table)
 | 
			
		||||
                    .values(device)
 | 
			
		||||
                    .execute(&mut self.pool.get()?)?;
 | 
			
		||||
                    .execute(conn)?;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Ok::<_, DbError>(())
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -102,3 +102,43 @@ fn test_refresh_session() {
 | 
			
		|||
        Some(new_session)
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_remove_old_sessions() {
 | 
			
		||||
    let (store, users) = common::setup();
 | 
			
		||||
 | 
			
		||||
    let now = chrono::Utc::now().trunc_subsecs(0);
 | 
			
		||||
    let timestamps = [
 | 
			
		||||
        now,
 | 
			
		||||
        now - TimeDelta::seconds(1),
 | 
			
		||||
        now - TimeDelta::seconds(2),
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    for (i, ts) in timestamps.into_iter().enumerate() {
 | 
			
		||||
        store
 | 
			
		||||
            .insert_session(&Session {
 | 
			
		||||
                id: i as i64,
 | 
			
		||||
                last_seen: ts,
 | 
			
		||||
                user: users[0].clone(),
 | 
			
		||||
            })
 | 
			
		||||
            .expect("insert shouldn't fail");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        store
 | 
			
		||||
            .remove_old_sessions(now)
 | 
			
		||||
            .expect("remove old sessions shouldn't fail"),
 | 
			
		||||
        2
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    assert!(matches!(store.get_session(1), Ok(None)));
 | 
			
		||||
    assert!(matches!(store.get_session(2), Ok(None)));
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        store.get_session(0).expect("get session shouldn't fail"),
 | 
			
		||||
        Some(Session {
 | 
			
		||||
            id: 0,
 | 
			
		||||
            last_seen: now,
 | 
			
		||||
            user: users[0].clone(),
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
mod common;
 | 
			
		||||
 | 
			
		||||
use gpodder::{DevicePatch, DeviceRepository, DeviceType};
 | 
			
		||||
 | 
			
		||||
#[test]
 | 
			
		||||
fn test_insert_devices() {
 | 
			
		||||
    let (store, users) = common::setup();
 | 
			
		||||
 | 
			
		||||
    store.devices_for_user(&users[0]).unwrap();
 | 
			
		||||
 | 
			
		||||
    store
 | 
			
		||||
        .update_device_info(
 | 
			
		||||
            &users[0],
 | 
			
		||||
            "device1",
 | 
			
		||||
            DevicePatch {
 | 
			
		||||
                caption: Some("caption1".to_string()),
 | 
			
		||||
                r#type: Some(DeviceType::Other),
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        .expect("update info shouldn't fail");
 | 
			
		||||
 | 
			
		||||
    store
 | 
			
		||||
        .update_device_info(
 | 
			
		||||
            &users[1],
 | 
			
		||||
            "device2",
 | 
			
		||||
            DevicePatch {
 | 
			
		||||
                caption: Some("caption2".to_string()),
 | 
			
		||||
                r#type: Some(DeviceType::Laptop),
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
        .expect("update info shouldn't fail");
 | 
			
		||||
 | 
			
		||||
    let devices = store.devices_for_user(&users[0]);
 | 
			
		||||
    assert!(matches!(devices, Ok(_)));
 | 
			
		||||
 | 
			
		||||
    let devices = devices.unwrap();
 | 
			
		||||
    assert_eq!(devices.len(), 1);
 | 
			
		||||
 | 
			
		||||
    assert_eq!(devices[0].caption, "caption1");
 | 
			
		||||
    assert_eq!(devices[0].r#type, DeviceType::Other);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue