fix(clippy): reduce tracker insert/add argument count

This commit is contained in:
thibaud-lclr 2026-04-14 17:23:30 +02:00
parent 7fc7a674f0
commit 2d87ad5344
7 changed files with 278 additions and 165 deletions

View file

@ -2,10 +2,11 @@ use crate::error::AppError;
use crate::models::agent::{Agent, AgentRole}; use crate::models::agent::{Agent, AgentRole};
use crate::models::credential::TuleapCredentials; use crate::models::credential::TuleapCredentials;
use crate::models::ticket::ProcessedTicket; use crate::models::ticket::ProcessedTicket;
use crate::models::tracker::{FilterGroup, TrackerUpdate, WatchedTracker}; use crate::models::tracker::{FilterGroup, NewWatchedTracker, TrackerUpdate, WatchedTracker};
use crate::services::crypto; use crate::services::crypto;
use crate::services::tuleap_client::TuleapClient; use crate::services::tuleap_client::TuleapClient;
use crate::AppState; use crate::AppState;
use serde::Deserialize;
use tauri::State; use tauri::State;
fn build_tuleap_client(state: &State<AppState>) -> Result<TuleapClient, AppError> { fn build_tuleap_client(state: &State<AppState>) -> Result<TuleapClient, AppError> {
@ -17,8 +18,8 @@ fn build_tuleap_client(state: &State<AppState>) -> Result<TuleapClient, AppError
let cred = TuleapCredentials::get(&db)? let cred = TuleapCredentials::get(&db)?
.ok_or_else(|| AppError::from("No Tuleap credentials configured".to_string()))?; .ok_or_else(|| AppError::from("No Tuleap credentials configured".to_string()))?;
let password = crypto::decrypt(&state.encryption_key, &cred.password_encrypted) let password =
.map_err(AppError::from)?; crypto::decrypt(&state.encryption_key, &cred.password_encrypted).map_err(AppError::from)?;
Ok(TuleapClient::new( Ok(TuleapClient::new(
&state.http_client, &state.http_client,
@ -28,7 +29,11 @@ fn build_tuleap_client(state: &State<AppState>) -> Result<TuleapClient, AppError
)) ))
} }
fn ensure_agent_role(db: &rusqlite::Connection, agent_id: &str, expected: AgentRole) -> Result<(), AppError> { fn ensure_agent_role(
db: &rusqlite::Connection,
agent_id: &str,
expected: AgentRole,
) -> Result<(), AppError> {
let agent = Agent::get_by_id(db, agent_id)?; let agent = Agent::get_by_id(db, agent_id)?;
if agent.role != expected { if agent.role != expected {
return Err(AppError::from(format!( return Err(AppError::from(format!(
@ -41,34 +46,42 @@ fn ensure_agent_role(db: &rusqlite::Connection, agent_id: &str, expected: AgentR
Ok(()) Ok(())
} }
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AddTrackerPayload {
pub project_id: String,
pub tracker_id: i32,
pub tracker_label: String,
pub polling_interval: i32,
pub analyst_agent_id: String,
pub developer_agent_id: String,
pub filters: Vec<FilterGroup>,
}
#[tauri::command] #[tauri::command]
pub fn add_tracker( pub fn add_tracker(
state: State<'_, AppState>, state: State<'_, AppState>,
project_id: String, payload: AddTrackerPayload,
tracker_id: i32,
tracker_label: String,
polling_interval: i32,
analyst_agent_id: String,
developer_agent_id: String,
filters: Vec<FilterGroup>,
) -> Result<WatchedTracker, AppError> { ) -> Result<WatchedTracker, AppError> {
let db = state let db = state
.db .db
.lock() .lock()
.map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?; .map_err(|e| AppError::from(format!("Database lock failed: {}", e)))?;
ensure_agent_role(&db, &analyst_agent_id, AgentRole::Analyst)?; ensure_agent_role(&db, &payload.analyst_agent_id, AgentRole::Analyst)?;
ensure_agent_role(&db, &developer_agent_id, AgentRole::Developer)?; ensure_agent_role(&db, &payload.developer_agent_id, AgentRole::Developer)?;
let tracker = WatchedTracker::insert( let tracker = WatchedTracker::insert(
&db, &db,
&project_id, NewWatchedTracker {
tracker_id, project_id: payload.project_id,
&tracker_label, tracker_id: payload.tracker_id,
polling_interval, tracker_label: payload.tracker_label,
&analyst_agent_id, polling_interval: payload.polling_interval,
&developer_agent_id, analyst_agent_id: payload.analyst_agent_id,
filters, developer_agent_id: payload.developer_agent_id,
filters: payload.filters,
},
)?; )?;
Ok(tracker) Ok(tracker)

View file

@ -247,7 +247,7 @@ mod tests {
use super::*; use super::*;
use crate::db; use crate::db;
use crate::models::project::Project; use crate::models::project::Project;
use crate::models::tracker::WatchedTracker; use crate::models::tracker::{NewWatchedTracker, WatchedTracker};
fn setup() -> Connection { fn setup() -> Connection {
db::init_in_memory().expect("db init should succeed") db::init_in_memory().expect("db init should succeed")
@ -302,10 +302,9 @@ mod tests {
"new script", "new script",
) )
.unwrap_err(); .unwrap_err();
assert!( assert!(err
err.to_string() .to_string()
.contains("Default agents cannot change name, role, or tool") .contains("Default agents cannot change name, role, or tool"));
);
Agent::update( Agent::update(
&conn, &conn,
@ -338,18 +337,20 @@ mod tests {
let analyst_default = Agent::get_default_by_role(&conn, AgentRole::Analyst).unwrap(); let analyst_default = Agent::get_default_by_role(&conn, AgentRole::Analyst).unwrap();
let developer_default = Agent::get_default_by_role(&conn, AgentRole::Developer).unwrap(); let developer_default = Agent::get_default_by_role(&conn, AgentRole::Developer).unwrap();
let analyst = Agent::insert(&conn, "Analyst", AgentRole::Analyst, AgentTool::Codex, "") let analyst =
.unwrap(); Agent::insert(&conn, "Analyst", AgentRole::Analyst, AgentTool::Codex, "").unwrap();
let tracker = WatchedTracker::insert( let tracker = WatchedTracker::insert(
&conn, &conn,
&project.id, NewWatchedTracker {
100, project_id: project.id.clone(),
"Bugs", tracker_id: 100,
10, tracker_label: "Bugs".to_string(),
&analyst.id, polling_interval: 10,
&developer_default.id, analyst_agent_id: analyst.id.clone(),
vec![], developer_agent_id: developer_default.id.clone(),
filters: vec![],
},
) )
.unwrap(); .unwrap();

View file

@ -60,7 +60,14 @@ impl ProcessedTicket {
"INSERT INTO processed_tickets \ "INSERT INTO processed_tickets \
(id, tracker_id, artifact_id, artifact_title, artifact_data, status, detected_at) \ (id, tracker_id, artifact_id, artifact_title, artifact_data, status, detected_at) \
VALUES (?1, ?2, ?3, ?4, ?5, 'Pending', ?6)", VALUES (?1, ?2, ?3, ?4, ?5, 'Pending', ?6)",
params![id, tracker_id, artifact_id, artifact_title, artifact_data, now], params![
id,
tracker_id,
artifact_id,
artifact_title,
artifact_data,
now
],
)?; )?;
let ticket = ProcessedTicket { let ticket = ProcessedTicket {
@ -183,7 +190,7 @@ mod tests {
use crate::db; use crate::db;
use crate::models::agent::{Agent, AgentRole, AgentTool}; use crate::models::agent::{Agent, AgentRole, AgentTool};
use crate::models::project::Project; use crate::models::project::Project;
use crate::models::tracker::WatchedTracker; use crate::models::tracker::{NewWatchedTracker, WatchedTracker};
fn setup() -> (Connection, String) { fn setup() -> (Connection, String) {
let conn = db::init_in_memory().expect("db init should succeed"); let conn = db::init_in_memory().expect("db init should succeed");
@ -193,13 +200,15 @@ mod tests {
Agent::insert(&conn, "D", AgentRole::Developer, AgentTool::ClaudeCode, "").unwrap(); Agent::insert(&conn, "D", AgentRole::Developer, AgentTool::ClaudeCode, "").unwrap();
let tracker = WatchedTracker::insert( let tracker = WatchedTracker::insert(
&conn, &conn,
&project.id, NewWatchedTracker {
456, project_id: project.id.clone(),
"Bugs", tracker_id: 456,
10, tracker_label: "Bugs".to_string(),
&analyst.id, polling_interval: 10,
&developer.id, analyst_agent_id: analyst.id.clone(),
vec![], developer_agent_id: developer.id.clone(),
filters: vec![],
},
) )
.unwrap(); .unwrap();
(conn, tracker.id) (conn, tracker.id)
@ -257,8 +266,8 @@ mod tests {
fn test_exists() { fn test_exists() {
let (conn, tracker_id) = setup(); let (conn, tracker_id) = setup();
let before = ProcessedTicket::exists(&conn, &tracker_id, 303) let before =
.expect("exists check should succeed"); ProcessedTicket::exists(&conn, &tracker_id, 303).expect("exists check should succeed");
assert!(!before); assert!(!before);
ProcessedTicket::insert_if_new(&conn, &tracker_id, 303, "Some ticket", "{}") ProcessedTicket::insert_if_new(&conn, &tracker_id, 303, "Some ticket", "{}")
@ -285,10 +294,15 @@ mod tests {
fn test_get_by_id() { fn test_get_by_id() {
let (conn, tracker_id) = setup(); let (conn, tracker_id) = setup();
let inserted = let inserted = ProcessedTicket::insert_if_new(
ProcessedTicket::insert_if_new(&conn, &tracker_id, 404, "Not Found Bug", "{\"id\": 404}") &conn,
.expect("insert should succeed") &tracker_id,
.expect("should be Some"); 404,
"Not Found Bug",
"{\"id\": 404}",
)
.expect("insert should succeed")
.expect("should be Some");
let found = let found =
ProcessedTicket::get_by_id(&conn, &inserted.id).expect("get_by_id should succeed"); ProcessedTicket::get_by_id(&conn, &inserted.id).expect("get_by_id should succeed");

View file

@ -41,6 +41,17 @@ pub struct TrackerUpdate {
pub enabled: bool, pub enabled: bool,
} }
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewWatchedTracker {
pub project_id: String,
pub tracker_id: i32,
pub tracker_label: String,
pub polling_interval: i32,
pub analyst_agent_id: String,
pub developer_agent_id: String,
pub filters: Vec<FilterGroup>,
}
fn normalize_agent_id(agent_id: &str) -> Option<String> { fn normalize_agent_id(agent_id: &str) -> Option<String> {
let trimmed = agent_id.trim(); let trimmed = agent_id.trim();
if trimmed.is_empty() { if trimmed.is_empty() {
@ -50,7 +61,10 @@ fn normalize_agent_id(agent_id: &str) -> Option<String> {
} }
} }
fn compute_status(analyst_agent_id: &Option<String>, developer_agent_id: &Option<String>) -> String { fn compute_status(
analyst_agent_id: &Option<String>,
developer_agent_id: &Option<String>,
) -> String {
if analyst_agent_id.is_some() && developer_agent_id.is_some() { if analyst_agent_id.is_some() && developer_agent_id.is_some() {
"valid".to_string() "valid".to_string()
} else { } else {
@ -62,8 +76,9 @@ fn from_row(row: &rusqlite::Row) -> rusqlite::Result<WatchedTracker> {
let filters_json: String = row.get(7)?; let filters_json: String = row.get(7)?;
let enabled_int: i32 = row.get(8)?; let enabled_int: i32 = row.get(8)?;
let filters: Vec<FilterGroup> = serde_json::from_str(&filters_json) let filters: Vec<FilterGroup> = serde_json::from_str(&filters_json).map_err(|e| {
.map_err(|e| rusqlite::Error::FromSqlConversionFailure(7, rusqlite::types::Type::Text, Box::new(e)))?; rusqlite::Error::FromSqlConversionFailure(7, rusqlite::types::Type::Text, Box::new(e))
})?;
Ok(WatchedTracker { Ok(WatchedTracker {
id: row.get(0)?, id: row.get(0)?,
@ -82,49 +97,50 @@ fn from_row(row: &rusqlite::Row) -> rusqlite::Result<WatchedTracker> {
} }
impl WatchedTracker { impl WatchedTracker {
pub fn insert( pub fn insert(conn: &Connection, new_tracker: NewWatchedTracker) -> Result<WatchedTracker> {
conn: &Connection, let NewWatchedTracker {
project_id: &str, project_id,
tracker_id: i32, tracker_id,
tracker_label: &str, tracker_label,
polling_interval: i32, polling_interval,
analyst_agent_id: &str, analyst_agent_id,
developer_agent_id: &str, developer_agent_id,
filters: Vec<FilterGroup>, filters,
) -> Result<WatchedTracker> { } = new_tracker;
let id = Uuid::new_v4().to_string(); let id = Uuid::new_v4().to_string();
let now = chrono::Utc::now().to_rfc3339(); let now = chrono::Utc::now().to_rfc3339();
let filters_json = serde_json::to_string(&filters) let filters_json = serde_json::to_string(&filters)
.map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?; .map_err(|e| rusqlite::Error::ToSqlConversionFailure(Box::new(e)))?;
let analyst_agent_id = normalize_agent_id(analyst_agent_id); let analyst_agent_id = normalize_agent_id(&analyst_agent_id);
let developer_agent_id = normalize_agent_id(developer_agent_id); let developer_agent_id = normalize_agent_id(&developer_agent_id);
let status = compute_status(&analyst_agent_id, &developer_agent_id); let status = compute_status(&analyst_agent_id, &developer_agent_id);
conn.execute( conn.execute(
"INSERT INTO watched_trackers (id, project_id, tracker_id, tracker_label, polling_interval, agent_config_json, filters_json, analyst_agent_id, developer_agent_id, status, created_at) \ "INSERT INTO watched_trackers (id, project_id, tracker_id, tracker_label, polling_interval, agent_config_json, filters_json, analyst_agent_id, developer_agent_id, status, created_at) \
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)", VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)",
params![ params![
id, &id,
project_id, &project_id,
tracker_id, tracker_id,
tracker_label, &tracker_label,
polling_interval, polling_interval,
"{}", "{}",
filters_json, filters_json,
analyst_agent_id, analyst_agent_id.as_deref(),
developer_agent_id, developer_agent_id.as_deref(),
status, &status,
now, &now,
], ],
)?; )?;
Ok(WatchedTracker { Ok(WatchedTracker {
id, id,
project_id: project_id.to_string(), project_id,
tracker_id, tracker_id,
tracker_label: tracker_label.to_string(), tracker_label,
polling_interval, polling_interval,
analyst_agent_id, analyst_agent_id,
developer_agent_id, developer_agent_id,
@ -232,14 +248,8 @@ mod tests {
} }
fn create_agents(conn: &Connection) -> (String, String) { fn create_agents(conn: &Connection) -> (String, String) {
let analyst = Agent::insert( let analyst =
conn, Agent::insert(conn, "Analyst", AgentRole::Analyst, AgentTool::Codex, "").unwrap();
"Analyst",
AgentRole::Analyst,
AgentTool::Codex,
"",
)
.unwrap();
let developer = Agent::insert( let developer = Agent::insert(
conn, conn,
@ -271,13 +281,15 @@ mod tests {
let tracker = WatchedTracker::insert( let tracker = WatchedTracker::insert(
&conn, &conn,
&pid, NewWatchedTracker {
42, project_id: pid.clone(),
"Bug Tracker", tracker_id: 42,
15, tracker_label: "Bug Tracker".to_string(),
&analyst_id, polling_interval: 15,
&developer_id, analyst_agent_id: analyst_id.clone(),
sample_filters(), developer_agent_id: developer_id.clone(),
filters: sample_filters(),
},
) )
.expect("insert should succeed"); .expect("insert should succeed");
@ -288,8 +300,14 @@ mod tests {
assert_eq!(tracker.polling_interval, 15); assert_eq!(tracker.polling_interval, 15);
assert!(tracker.enabled); assert!(tracker.enabled);
assert_eq!(tracker.status, "valid"); assert_eq!(tracker.status, "valid");
assert_eq!(tracker.analyst_agent_id.as_deref(), Some(analyst_id.as_str())); assert_eq!(
assert_eq!(tracker.developer_agent_id.as_deref(), Some(developer_id.as_str())); tracker.analyst_agent_id.as_deref(),
Some(analyst_id.as_str())
);
assert_eq!(
tracker.developer_agent_id.as_deref(),
Some(developer_id.as_str())
);
assert!(tracker.last_polled_at.is_none()); assert!(tracker.last_polled_at.is_none());
assert!(!tracker.created_at.is_empty()); assert!(!tracker.created_at.is_empty());
assert_eq!(tracker.filters.len(), 1); assert_eq!(tracker.filters.len(), 1);
@ -301,8 +319,32 @@ mod tests {
let pid = project_id(&conn); let pid = project_id(&conn);
let (analyst_id, developer_id) = create_agents(&conn); let (analyst_id, developer_id) = create_agents(&conn);
WatchedTracker::insert(&conn, &pid, 1, "Tracker A", 10, &analyst_id, &developer_id, vec![]).unwrap(); WatchedTracker::insert(
WatchedTracker::insert(&conn, &pid, 2, "Tracker B", 20, &analyst_id, &developer_id, vec![]).unwrap(); &conn,
NewWatchedTracker {
project_id: pid.clone(),
tracker_id: 1,
tracker_label: "Tracker A".to_string(),
polling_interval: 10,
analyst_agent_id: analyst_id.clone(),
developer_agent_id: developer_id.clone(),
filters: vec![],
},
)
.unwrap();
WatchedTracker::insert(
&conn,
NewWatchedTracker {
project_id: pid.clone(),
tracker_id: 2,
tracker_label: "Tracker B".to_string(),
polling_interval: 20,
analyst_agent_id: analyst_id.clone(),
developer_agent_id: developer_id.clone(),
filters: vec![],
},
)
.unwrap();
let trackers = WatchedTracker::list_by_project(&conn, &pid).expect("list should succeed"); let trackers = WatchedTracker::list_by_project(&conn, &pid).expect("list should succeed");
assert_eq!(trackers.len(), 2); assert_eq!(trackers.len(), 2);
@ -314,10 +356,35 @@ mod tests {
let pid = project_id(&conn); let pid = project_id(&conn);
let (analyst_id, developer_id) = create_agents(&conn); let (analyst_id, developer_id) = create_agents(&conn);
let valid = WatchedTracker::insert(&conn, &pid, 1, "Valid", 10, &analyst_id, &developer_id, vec![]).unwrap(); let valid = WatchedTracker::insert(
WatchedTracker::insert(&conn, &pid, 2, "Invalid", 10, "", &developer_id, vec![]).unwrap(); &conn,
NewWatchedTracker {
project_id: pid.clone(),
tracker_id: 1,
tracker_label: "Valid".to_string(),
polling_interval: 10,
analyst_agent_id: analyst_id.clone(),
developer_agent_id: developer_id.clone(),
filters: vec![],
},
)
.unwrap();
WatchedTracker::insert(
&conn,
NewWatchedTracker {
project_id: pid.clone(),
tracker_id: 2,
tracker_label: "Invalid".to_string(),
polling_interval: 10,
analyst_agent_id: "".to_string(),
developer_agent_id: developer_id.clone(),
filters: vec![],
},
)
.unwrap();
let enabled = WatchedTracker::list_all_enabled(&conn).expect("list_all_enabled should succeed"); let enabled =
WatchedTracker::list_all_enabled(&conn).expect("list_all_enabled should succeed");
assert_eq!(enabled.len(), 1); assert_eq!(enabled.len(), 1);
assert_eq!(enabled[0].id, valid.id); assert_eq!(enabled[0].id, valid.id);
} }
@ -330,17 +397,20 @@ mod tests {
let created = WatchedTracker::insert( let created = WatchedTracker::insert(
&conn, &conn,
&pid, NewWatchedTracker {
99, project_id: pid.clone(),
"My Tracker", tracker_id: 99,
30, tracker_label: "My Tracker".to_string(),
&analyst_id, polling_interval: 30,
&developer_id, analyst_agent_id: analyst_id.clone(),
sample_filters(), developer_agent_id: developer_id.clone(),
filters: sample_filters(),
},
) )
.unwrap(); .unwrap();
let found = WatchedTracker::get_by_id(&conn, &created.id).expect("get_by_id should succeed"); let found =
WatchedTracker::get_by_id(&conn, &created.id).expect("get_by_id should succeed");
assert_eq!(found.id, created.id); assert_eq!(found.id, created.id);
assert_eq!(found.tracker_id, 99); assert_eq!(found.tracker_id, 99);
assert_eq!(found.tracker_label, "My Tracker"); assert_eq!(found.tracker_label, "My Tracker");
@ -356,13 +426,15 @@ mod tests {
let created = WatchedTracker::insert( let created = WatchedTracker::insert(
&conn, &conn,
&pid, NewWatchedTracker {
10, project_id: pid.clone(),
"Original", tracker_id: 10,
5, tracker_label: "Original".to_string(),
&analyst_id, polling_interval: 5,
&developer_id, analyst_agent_id: analyst_id.clone(),
sample_filters(), developer_agent_id: developer_id.clone(),
filters: sample_filters(),
},
) )
.unwrap(); .unwrap();
@ -406,19 +478,22 @@ mod tests {
let created = WatchedTracker::insert( let created = WatchedTracker::insert(
&conn, &conn,
&pid, NewWatchedTracker {
5, project_id: pid.clone(),
"Poller", tracker_id: 5,
10, tracker_label: "Poller".to_string(),
&analyst_id, polling_interval: 10,
&developer_id, analyst_agent_id: analyst_id.clone(),
vec![], developer_agent_id: developer_id.clone(),
filters: vec![],
},
) )
.unwrap(); .unwrap();
assert!(created.last_polled_at.is_none()); assert!(created.last_polled_at.is_none());
WatchedTracker::update_last_polled(&conn, &created.id).expect("update_last_polled should succeed"); WatchedTracker::update_last_polled(&conn, &created.id)
.expect("update_last_polled should succeed");
let updated = WatchedTracker::get_by_id(&conn, &created.id).unwrap(); let updated = WatchedTracker::get_by_id(&conn, &created.id).unwrap();
assert!(updated.last_polled_at.is_some()); assert!(updated.last_polled_at.is_some());
@ -432,13 +507,15 @@ mod tests {
let created = WatchedTracker::insert( let created = WatchedTracker::insert(
&conn, &conn,
&pid, NewWatchedTracker {
7, project_id: pid.clone(),
"ToDelete", tracker_id: 7,
10, tracker_label: "ToDelete".to_string(),
&analyst_id, polling_interval: 10,
&developer_id, analyst_agent_id: analyst_id.clone(),
vec![], developer_agent_id: developer_id.clone(),
filters: vec![],
},
) )
.unwrap(); .unwrap();

View file

@ -31,7 +31,12 @@ const SELECT_ALL_COLS: &str = "SELECT id, ticket_id, path, branch_name, status,
created_at, merged_at, merged_into FROM worktrees"; created_at, merged_at, merged_into FROM worktrees";
impl Worktree { impl Worktree {
pub fn insert(conn: &Connection, ticket_id: &str, path: &str, branch_name: &str) -> Result<Worktree> { pub fn insert(
conn: &Connection,
ticket_id: &str,
path: &str,
branch_name: &str,
) -> Result<Worktree> {
let id = Uuid::new_v4().to_string(); let id = Uuid::new_v4().to_string();
let now = chrono::Utc::now().to_rfc3339(); let now = chrono::Utc::now().to_rfc3339();
@ -103,7 +108,7 @@ mod tests {
use crate::models::agent::{Agent, AgentRole, AgentTool}; use crate::models::agent::{Agent, AgentRole, AgentTool};
use crate::models::project::Project; use crate::models::project::Project;
use crate::models::ticket::ProcessedTicket; use crate::models::ticket::ProcessedTicket;
use crate::models::tracker::WatchedTracker; use crate::models::tracker::{NewWatchedTracker, WatchedTracker};
fn setup() -> (Connection, String) { fn setup() -> (Connection, String) {
let conn = db::init_in_memory().expect("db init"); let conn = db::init_in_memory().expect("db init");
@ -113,13 +118,15 @@ mod tests {
Agent::insert(&conn, "D", AgentRole::Developer, AgentTool::ClaudeCode, "").unwrap(); Agent::insert(&conn, "D", AgentRole::Developer, AgentTool::ClaudeCode, "").unwrap();
let tracker = WatchedTracker::insert( let tracker = WatchedTracker::insert(
&conn, &conn,
&project.id, NewWatchedTracker {
100, project_id: project.id.clone(),
"Bugs", tracker_id: 100,
10, tracker_label: "Bugs".to_string(),
&analyst.id, polling_interval: 10,
&developer.id, analyst_agent_id: analyst.id.clone(),
vec![], developer_agent_id: developer.id.clone(),
filters: vec![],
},
) )
.unwrap(); .unwrap();
let ticket = ProcessedTicket::insert_if_new(&conn, &tracker.id, 42, "Bug 42", "{}") let ticket = ProcessedTicket::insert_if_new(&conn, &tracker.id, 42, "Bug 42", "{}")
@ -165,13 +172,15 @@ mod tests {
Agent::insert(&conn, "D", AgentRole::Developer, AgentTool::ClaudeCode, "").unwrap(); Agent::insert(&conn, "D", AgentRole::Developer, AgentTool::ClaudeCode, "").unwrap();
let tracker = WatchedTracker::insert( let tracker = WatchedTracker::insert(
&conn, &conn,
&project.id, NewWatchedTracker {
100, project_id: project.id.clone(),
"Bugs", tracker_id: 100,
10, tracker_label: "Bugs".to_string(),
&analyst.id, polling_interval: 10,
&developer.id, analyst_agent_id: analyst.id.clone(),
vec![], developer_agent_id: developer.id.clone(),
filters: vec![],
},
) )
.unwrap(); .unwrap();
let t1 = ProcessedTicket::insert_if_new(&conn, &tracker.id, 1, "T1", "{}") let t1 = ProcessedTicket::insert_if_new(&conn, &tracker.id, 1, "T1", "{}")

View file

@ -142,7 +142,7 @@ mod tests {
use crate::models::agent::{Agent, AgentRole, AgentTool}; use crate::models::agent::{Agent, AgentRole, AgentTool};
use crate::models::project::Project; use crate::models::project::Project;
use crate::models::ticket::ProcessedTicket; use crate::models::ticket::ProcessedTicket;
use crate::models::tracker::WatchedTracker; use crate::models::tracker::{NewWatchedTracker, WatchedTracker};
fn setup() -> (Arc<Mutex<Connection>>, String) { fn setup() -> (Arc<Mutex<Connection>>, String) {
let conn = db::init_in_memory().expect("db init should succeed"); let conn = db::init_in_memory().expect("db init should succeed");
@ -159,25 +159,22 @@ mod tests {
let tracker = WatchedTracker::insert( let tracker = WatchedTracker::insert(
&conn, &conn,
project_id, NewWatchedTracker {
101, project_id: project_id.to_string(),
"Bugs", tracker_id: 101,
10, tracker_label: "Bugs".to_string(),
&analyst.id, polling_interval: 10,
&developer.id, analyst_agent_id: analyst.id.clone(),
vec![], developer_agent_id: developer.id.clone(),
filters: vec![],
},
) )
.expect("tracker insert should succeed"); .expect("tracker insert should succeed");
let ticket = ProcessedTicket::insert_if_new( let ticket =
&conn, ProcessedTicket::insert_if_new(&conn, &tracker.id, 1, "Ticket 1", "{\"id\":1}")
&tracker.id, .expect("ticket insert should succeed")
1, .expect("ticket should be inserted");
"Ticket 1",
"{\"id\":1}",
)
.expect("ticket insert should succeed")
.expect("ticket should be inserted");
ticket.id ticket.id
} }

View file

@ -99,13 +99,15 @@ export async function addTracker(
filters: FilterGroup[] filters: FilterGroup[]
): Promise<WatchedTracker> { ): Promise<WatchedTracker> {
return invoke("add_tracker", { return invoke("add_tracker", {
projectId, payload: {
trackerId, projectId,
trackerLabel, trackerId,
pollingInterval, trackerLabel,
analystAgentId, pollingInterval,
developerAgentId, analystAgentId,
filters, developerAgentId,
filters,
},
}); });
} }
export async function listTrackers(projectId: string): Promise<WatchedTracker[]> { export async function listTrackers(projectId: string): Promise<WatchedTracker[]> {