fix(clippy): reduce tracker insert/add argument count
This commit is contained in:
parent
7fc7a674f0
commit
2d87ad5344
7 changed files with 278 additions and 165 deletions
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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");
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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", "{}")
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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[]> {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue