Bug: sort_order column was set to 0 when creating a task. Thank you tests

This commit is contained in:
Joseph Ferano 2023-06-17 16:16:22 +07:00
parent 514c600bb1
commit 9a779b1abb
4 changed files with 51 additions and 31 deletions

View File

@ -5,7 +5,7 @@ create table if not exists task
id integer primary key autoincrement, id integer primary key autoincrement,
title text not null, title text not null,
description text not null, description text not null,
sort_order integer not null default 0, sort_order integer not null,
column_id integer, column_id integer,
foreign key (column_id) references kb_column(id) foreign key (column_id) references kb_column(id)
); );

View File

@ -218,9 +218,10 @@ impl<'a> State<'a> {
} else { } else {
column.selected_task_idx -= 1; column.selected_task_idx -= 1;
} }
let task_idx = column.selected_task_idx;
let col_id = column.id; let col_id = column.id;
self.db_conn.swap_task_order(t2_id, t1_id)?; self.db_conn.swap_task_order(t1_id, t2_id)?;
self.db_conn self.db_conn
.set_selected_task_for_column(task_idx, col_id)?; .set_selected_task_for_column(task_idx, col_id)?;
} }

View File

@ -86,7 +86,13 @@ impl DBConn {
column_id: i64, column_id: i64,
) -> Result<Task> { ) -> Result<Task> {
let mut stmt = let mut stmt =
self.prepare("insert into task(title, description, column_id) values (?1, ?2, ?3)")?; self.prepare(
"insert into task(title, description, column_id, sort_order)
values (?1, ?2, ?3,
(coalesce(1 +
(select sort_order from task
where column_id = ?3 order by sort_order desc limit 1),
0)))")?;
stmt.execute(params![title, description, column_id])?; stmt.execute(params![title, description, column_id])?;
let id = self.last_insert_rowid(); let id = self.last_insert_rowid();
Ok(Task { Ok(Task {
@ -135,42 +141,56 @@ impl DBConn {
where column_id = ?2 order by sort_order desc limit 1), where column_id = ?2 order by sort_order desc limit 1),
0) 0)
where task.id = ?1", where task.id = ?1",
) )?;
.unwrap(); stmt.execute((&task.id, &target_column.id))?;
stmt.execute((&task.id, &target_column.id)).unwrap();
self.set_selected_task_for_column(target_column.selected_task_idx, target_column.id)?; self.set_selected_task_for_column(target_column.selected_task_idx, target_column.id)?;
Ok(()) Ok(())
} }
/// This is a helper function in case we need to debug sort_order, because I ran into
/// a bug when I forgot to insert the sort_order when creating a task
#[allow(dead_code)]
fn get_sort_order(&self) -> Result<Vec<(i32, String, usize)>> {
let mut stmt = self.prepare(
"select id,title,sort_order from task where column_id = 1")?;
let mut rows = stmt.query(())?;
let mut tasks = Vec::new();
while let Some(row) = rows.next()? {
tasks.push((row.get(0)?, row.get(1)?, row.get(2)?,))
}
Ok(tasks)
}
/// . /// .
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if something goes wrong with the SQL /// Panics if something goes wrong with the SQL
pub fn swap_task_order(&mut self, task1_id: i64, task2_id: i64) -> Result<()> { pub fn swap_task_order(&mut self, task1_id: i64, task2_id: i64) -> Result<()> {
let tx = self.transaction().unwrap(); let tx = self.transaction()?;
tx.execute( tx.execute(
"create temp table temp_order as select sort_order from task where id = ?1", "create temp table temp_order as select sort_order from task where id = ?1",
[&task1_id], [&task1_id],
) )?;
.unwrap();
tx.execute( tx.execute(
"update task set sort_order = (select sort_order from task where id = ?2) "update task
set sort_order = (select sort_order from task where id = ?2)
where id = ?1", where id = ?1",
(task1_id, task2_id), (task1_id, task2_id),
) )?;
.unwrap();
tx.execute( tx.execute(
"update task set sort_order = (select sort_order from temp_order) where id = ?1", "update task set sort_order = (select sort_order from temp_order) where id = ?1",
[&task2_id], [&task2_id],
) )?;
.unwrap();
tx.execute("drop table temp_order", ()).unwrap(); tx.execute("drop table temp_order", ())?;
tx.commit()?;
tx.commit().unwrap();
Ok(()) Ok(())
} }
@ -190,8 +210,9 @@ impl DBConn {
pub fn get_selected_column(&self) -> Result<usize> { pub fn get_selected_column(&self) -> Result<usize> {
let mut stmt = self.prepare("select value from app_state where key = ?1")?; let mut stmt = self.prepare("select value from app_state where key = ?1")?;
stmt.query_row(["selected_column"], |row| { stmt.query_row(["selected_column"], |row| {
let value: String = row.get::<usize, String>(0).unwrap(); let value: String = row.get::<usize, String>(0)?;
Ok(value.parse::<usize>().unwrap()) value.parse::<usize>()
.map_err(|_| rusqlite::Error::InvalidQuery)
}) })
} }

View File

@ -1,12 +1,6 @@
// - [X] Adding
// - [X] Selecting general
// - [X] Selecting top/bottom
// - [X] Moving
// - [X] Editing
// - [X] Deleting
#[cfg(test)] #[cfg(test)]
mod app_tests { mod app_tests {
use anyhow::Error; use anyhow::{Error};
use kanban_tui::State; use kanban_tui::State;
use rusqlite::Connection; use rusqlite::Connection;
@ -165,10 +159,12 @@ mod app_tests {
state.add_new_task(String::from("T1"), String::from("D1"))?; state.add_new_task(String::from("T1"), String::from("D1"))?;
state.add_new_task(String::from("T2"), String::from("D2"))?; state.add_new_task(String::from("T2"), String::from("D2"))?;
state.select_previous_task()?; state.select_previous_task()?;
state.move_task_up()?; state.move_task_up()?;
state.move_task_down()?; state.move_task_down()?;
state.move_task_down()?; state.move_task_down()?;
assert_eq!(&state.columns[0].tasks[1].title, "T1"); assert_eq!(&state.columns[0].tasks[1].title, "T1");
assert_eq!(&state.columns[0].tasks[0].title, "T2"); assert_eq!(&state.columns[0].tasks[0].title, "T2");
state.select_next_column()?; state.select_next_column()?;
@ -176,12 +172,14 @@ mod app_tests {
state.move_task_next_column()?; state.move_task_next_column()?;
assert_eq!(state.columns[1].tasks.len(), 0); assert_eq!(state.columns[1].tasks.len(), 0);
assert_eq!(state.columns[2].tasks.len(), 1); assert_eq!(state.columns[2].tasks.len(), 1);
for _ in 0..5 { for _ in 0..5 {
state.move_task_next_column()?; state.move_task_next_column()?;
} }
for _ in 0..4 { for _ in 0..4 {
state.move_task_previous_column()?; state.move_task_previous_column()?;
} }
assert_eq!(state.columns[0].tasks.len(), 3); assert_eq!(state.columns[0].tasks.len(), 3);
assert_eq!(state.columns[1].tasks.len(), 0); assert_eq!(state.columns[1].tasks.len(), 0);
assert_eq!(state.columns[2].tasks.len(), 0); assert_eq!(state.columns[2].tasks.len(), 0);
@ -202,11 +200,11 @@ mod app_tests {
// make sure everything was saved correctly // make sure everything was saved correctly
let mut state = State::new(state.db_conn.0)?; let mut state = State::new(state.db_conn.0)?;
assert_eq!(state.get_selected_task().unwrap().title, "T2");
state.select_next_task()?;
assert_eq!(state.get_selected_task().unwrap().title, "T1"); assert_eq!(state.get_selected_task().unwrap().title, "T1");
state.select_next_task()?; state.select_next_task()?;
assert_eq!(state.get_selected_task().unwrap().title, "T3"); assert_eq!(state.get_selected_task().unwrap().title, "T3");
state.select_next_task()?;
assert_eq!(state.get_selected_task().unwrap().title, "T3");
state.select_first_task()?; state.select_first_task()?;
assert_eq!(state.get_selected_task().unwrap().title, "T2"); assert_eq!(state.get_selected_task().unwrap().title, "T2");
state.select_last_task()?; state.select_last_task()?;
@ -256,8 +254,8 @@ mod app_tests {
// make sure everything was saved correctly // make sure everything was saved correctly
let state = State::new(state.db_conn.0)?; let state = State::new(state.db_conn.0)?;
assert_eq!(state.get_selected_task().unwrap().title, "T2"); assert_eq!(state.get_selected_task().unwrap().title, "T3");
assert_eq!(state.get_selected_task().unwrap().description, "D1"); assert_eq!(state.get_selected_task().unwrap().description, "D3");
Ok(()) Ok(())
} }