parent
91459c16cc
commit
0a2e7daec9
10 changed files with 202 additions and 40 deletions
|
|
@ -4,7 +4,7 @@ CREATE TABLE IF NOT EXISTS projects (
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
cloned_from TEXT,
|
cloned_from TEXT,
|
||||||
base_branch TEXT NOT NULL DEFAULT 'main',
|
base_branch TEXT NOT NULL DEFAULT 'main',
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS tuleap_credentials (
|
CREATE TABLE IF NOT EXISTS tuleap_credentials (
|
||||||
|
|
@ -22,7 +22,7 @@ CREATE TABLE IF NOT EXISTS watched_trackers (
|
||||||
polling_interval INTEGER NOT NULL DEFAULT 10,
|
polling_interval INTEGER NOT NULL DEFAULT 10,
|
||||||
agent_config_json TEXT NOT NULL,
|
agent_config_json TEXT NOT NULL,
|
||||||
filters_json TEXT NOT NULL,
|
filters_json TEXT NOT NULL,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS processed_tickets (
|
CREATE TABLE IF NOT EXISTS processed_tickets (
|
||||||
|
|
@ -36,7 +36,7 @@ CREATE TABLE IF NOT EXISTS processed_tickets (
|
||||||
developer_report TEXT,
|
developer_report TEXT,
|
||||||
worktree_path TEXT,
|
worktree_path TEXT,
|
||||||
branch_name TEXT,
|
branch_name TEXT,
|
||||||
detected_at TEXT NOT NULL DEFAULT (datetime('now')),
|
detected_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
processed_at TEXT
|
processed_at TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -49,7 +49,7 @@ CREATE TABLE IF NOT EXISTS worktrees (
|
||||||
path TEXT NOT NULL,
|
path TEXT NOT NULL,
|
||||||
branch_name TEXT NOT NULL,
|
branch_name TEXT NOT NULL,
|
||||||
status TEXT NOT NULL DEFAULT 'Active',
|
status TEXT NOT NULL DEFAULT 'Active',
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
merged_at TEXT,
|
merged_at TEXT,
|
||||||
merged_into TEXT
|
merged_into TEXT
|
||||||
);
|
);
|
||||||
|
|
@ -62,5 +62,5 @@ CREATE TABLE IF NOT EXISTS notifications (
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL,
|
||||||
message TEXT NOT NULL,
|
message TEXT NOT NULL,
|
||||||
read INTEGER NOT NULL DEFAULT 0,
|
read INTEGER NOT NULL DEFAULT 0,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@ CREATE TABLE IF NOT EXISTS agents (
|
||||||
role TEXT NOT NULL,
|
role TEXT NOT NULL,
|
||||||
tool TEXT NOT NULL,
|
tool TEXT NOT NULL,
|
||||||
custom_prompt TEXT NOT NULL DEFAULT '',
|
custom_prompt TEXT NOT NULL DEFAULT '',
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
ALTER TABLE watched_trackers ADD COLUMN analyst_agent_id TEXT;
|
ALTER TABLE watched_trackers ADD COLUMN analyst_agent_id TEXT;
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ SELECT
|
||||||
'codex',
|
'codex',
|
||||||
'',
|
'',
|
||||||
1,
|
1,
|
||||||
datetime('now'),
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
||||||
datetime('now')
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
||||||
WHERE NOT EXISTS (
|
WHERE NOT EXISTS (
|
||||||
SELECT 1 FROM agents WHERE role = 'analyst' AND is_default = 1
|
SELECT 1 FROM agents WHERE role = 'analyst' AND is_default = 1
|
||||||
);
|
);
|
||||||
|
|
@ -26,8 +26,8 @@ SELECT
|
||||||
'claude_code',
|
'claude_code',
|
||||||
'',
|
'',
|
||||||
1,
|
1,
|
||||||
datetime('now'),
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
||||||
datetime('now')
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
||||||
WHERE NOT EXISTS (
|
WHERE NOT EXISTS (
|
||||||
SELECT 1 FROM agents WHERE role = 'developer' AND is_default = 1
|
SELECT 1 FROM agents WHERE role = 'developer' AND is_default = 1
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ CREATE TABLE IF NOT EXISTS project_modules (
|
||||||
description TEXT NOT NULL DEFAULT '',
|
description TEXT NOT NULL DEFAULT '',
|
||||||
enabled INTEGER NOT NULL DEFAULT 1,
|
enabled INTEGER NOT NULL DEFAULT 1,
|
||||||
config_json TEXT NOT NULL DEFAULT '{}',
|
config_json TEXT NOT NULL DEFAULT '{}',
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
UNIQUE(project_id, module_key)
|
UNIQUE(project_id, module_key)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -19,8 +19,8 @@ CREATE TABLE IF NOT EXISTS project_live_sessions (
|
||||||
agent_id TEXT NOT NULL REFERENCES agents(id),
|
agent_id TEXT NOT NULL REFERENCES agents(id),
|
||||||
title TEXT NOT NULL,
|
title TEXT NOT NULL,
|
||||||
status TEXT NOT NULL DEFAULT 'active',
|
status TEXT NOT NULL DEFAULT 'active',
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
updated_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_live_sessions_project_id ON project_live_sessions(project_id);
|
CREATE INDEX IF NOT EXISTS idx_live_sessions_project_id ON project_live_sessions(project_id);
|
||||||
|
|
@ -30,7 +30,7 @@ CREATE TABLE IF NOT EXISTS project_live_messages (
|
||||||
session_id TEXT NOT NULL REFERENCES project_live_sessions(id) ON DELETE CASCADE,
|
session_id TEXT NOT NULL REFERENCES project_live_sessions(id) ON DELETE CASCADE,
|
||||||
sender TEXT NOT NULL,
|
sender TEXT NOT NULL,
|
||||||
content TEXT NOT NULL,
|
content TEXT NOT NULL,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now'))
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE INDEX IF NOT EXISTS idx_live_messages_session_id ON project_live_messages(session_id);
|
CREATE INDEX IF NOT EXISTS idx_live_messages_session_id ON project_live_messages(session_id);
|
||||||
|
|
@ -44,7 +44,7 @@ CREATE TABLE IF NOT EXISTS project_agent_tasks (
|
||||||
status TEXT NOT NULL DEFAULT 'pending',
|
status TEXT NOT NULL DEFAULT 'pending',
|
||||||
result TEXT,
|
result TEXT,
|
||||||
error TEXT,
|
error TEXT,
|
||||||
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
created_at TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ', 'now')),
|
||||||
started_at TEXT,
|
started_at TEXT,
|
||||||
finished_at TEXT
|
finished_at TEXT
|
||||||
);
|
);
|
||||||
|
|
@ -71,8 +71,8 @@ SELECT
|
||||||
'Surveille Tuleap et lance le pipeline analyste/developpeur.',
|
'Surveille Tuleap et lance le pipeline analyste/developpeur.',
|
||||||
1,
|
1,
|
||||||
'{}',
|
'{}',
|
||||||
datetime('now'),
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
||||||
datetime('now')
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
||||||
FROM projects p;
|
FROM projects p;
|
||||||
|
|
||||||
INSERT OR IGNORE INTO project_modules (
|
INSERT OR IGNORE INTO project_modules (
|
||||||
|
|
@ -94,8 +94,8 @@ SELECT
|
||||||
'Discussion live avec un agent sur le contexte du projet.',
|
'Discussion live avec un agent sur le contexte du projet.',
|
||||||
1,
|
1,
|
||||||
'{}',
|
'{}',
|
||||||
datetime('now'),
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
||||||
datetime('now')
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
||||||
FROM projects p;
|
FROM projects p;
|
||||||
|
|
||||||
INSERT OR IGNORE INTO project_modules (
|
INSERT OR IGNORE INTO project_modules (
|
||||||
|
|
@ -117,6 +117,6 @@ SELECT
|
||||||
'File de tâches asynchrones traitées par des agents pré-définis.',
|
'File de tâches asynchrones traitées par des agents pré-définis.',
|
||||||
1,
|
1,
|
||||||
'{}',
|
'{}',
|
||||||
datetime('now'),
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now'),
|
||||||
datetime('now')
|
strftime('%Y-%m-%dT%H:%M:%fZ', 'now')
|
||||||
FROM projects p;
|
FROM projects p;
|
||||||
|
|
|
||||||
93
src-tauri/migrations/007_normalize_timestamps_rfc3339.sql
Normal file
93
src-tauri/migrations/007_normalize_timestamps_rfc3339.sql
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
BEGIN;
|
||||||
|
|
||||||
|
UPDATE projects
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE watched_trackers
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE watched_trackers
|
||||||
|
SET last_polled_at = strftime('%Y-%m-%dT%H:%M:%fZ', last_polled_at)
|
||||||
|
WHERE last_polled_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', last_polled_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE processed_tickets
|
||||||
|
SET detected_at = strftime('%Y-%m-%dT%H:%M:%fZ', detected_at)
|
||||||
|
WHERE detected_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', detected_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE processed_tickets
|
||||||
|
SET processed_at = strftime('%Y-%m-%dT%H:%M:%fZ', processed_at)
|
||||||
|
WHERE processed_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', processed_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE worktrees
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE worktrees
|
||||||
|
SET merged_at = strftime('%Y-%m-%dT%H:%M:%fZ', merged_at)
|
||||||
|
WHERE merged_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', merged_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE notifications
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE agents
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE agents
|
||||||
|
SET updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', updated_at)
|
||||||
|
WHERE updated_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', updated_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_modules
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_modules
|
||||||
|
SET updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', updated_at)
|
||||||
|
WHERE updated_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', updated_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_live_sessions
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_live_sessions
|
||||||
|
SET updated_at = strftime('%Y-%m-%dT%H:%M:%fZ', updated_at)
|
||||||
|
WHERE updated_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', updated_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_live_messages
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_agent_tasks
|
||||||
|
SET created_at = strftime('%Y-%m-%dT%H:%M:%fZ', created_at)
|
||||||
|
WHERE created_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', created_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_agent_tasks
|
||||||
|
SET started_at = strftime('%Y-%m-%dT%H:%M:%fZ', started_at)
|
||||||
|
WHERE started_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', started_at) IS NOT NULL;
|
||||||
|
|
||||||
|
UPDATE project_agent_tasks
|
||||||
|
SET finished_at = strftime('%Y-%m-%dT%H:%M:%fZ', finished_at)
|
||||||
|
WHERE finished_at IS NOT NULL
|
||||||
|
AND strftime('%Y-%m-%dT%H:%M:%fZ', finished_at) IS NOT NULL;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
@ -7,6 +7,7 @@ const MIGRATION_003: &str = include_str!("../migrations/003_add_agents.sql");
|
||||||
const MIGRATION_004: &str = include_str!("../migrations/004_default_agents.sql");
|
const MIGRATION_004: &str = include_str!("../migrations/004_default_agents.sql");
|
||||||
const MIGRATION_005: &str = include_str!("../migrations/005_orchestration_modules_chat_tasks.sql");
|
const MIGRATION_005: &str = include_str!("../migrations/005_orchestration_modules_chat_tasks.sql");
|
||||||
const MIGRATION_006: &str = include_str!("../migrations/006_processed_tickets_unique_index.sql");
|
const MIGRATION_006: &str = include_str!("../migrations/006_processed_tickets_unique_index.sql");
|
||||||
|
const MIGRATION_007: &str = include_str!("../migrations/007_normalize_timestamps_rfc3339.sql");
|
||||||
|
|
||||||
pub fn init(db_path: &Path) -> Result<Connection> {
|
pub fn init(db_path: &Path) -> Result<Connection> {
|
||||||
let conn = Connection::open(db_path)?;
|
let conn = Connection::open(db_path)?;
|
||||||
|
|
@ -56,6 +57,10 @@ fn migrate(conn: &Connection) -> Result<()> {
|
||||||
conn.execute_batch(MIGRATION_006)?;
|
conn.execute_batch(MIGRATION_006)?;
|
||||||
conn.pragma_update(None, "user_version", 6)?;
|
conn.pragma_update(None, "user_version", 6)?;
|
||||||
}
|
}
|
||||||
|
if version < 7 {
|
||||||
|
conn.execute_batch(MIGRATION_007)?;
|
||||||
|
conn.pragma_update(None, "user_version", 7)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +116,7 @@ mod tests {
|
||||||
let version: i32 = conn
|
let version: i32 = conn
|
||||||
.pragma_query_value(None, "user_version", |row| row.get(0))
|
.pragma_query_value(None, "user_version", |row| row.get(0))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(version, 6);
|
assert_eq!(version, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -146,9 +146,10 @@ impl ProcessedTicket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_developer_report(conn: &Connection, id: &str, report: &str) -> Result<()> {
|
pub fn set_developer_report(conn: &Connection, id: &str, report: &str) -> Result<()> {
|
||||||
|
let now = chrono::Utc::now().to_rfc3339();
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"UPDATE processed_tickets SET developer_report = ?1, processed_at = datetime('now') WHERE id = ?2",
|
"UPDATE processed_tickets SET developer_report = ?1, processed_at = ?2 WHERE id = ?3",
|
||||||
params![report, id],
|
params![report, now, id],
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -177,9 +178,10 @@ impl ProcessedTicket {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_error(conn: &Connection, id: &str, error_message: &str) -> Result<()> {
|
pub fn set_error(conn: &Connection, id: &str, error_message: &str) -> Result<()> {
|
||||||
|
let now = chrono::Utc::now().to_rfc3339();
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"UPDATE processed_tickets SET status = 'Error', analyst_report = COALESCE(analyst_report, '') || ?1, processed_at = datetime('now') WHERE id = ?2",
|
"UPDATE processed_tickets SET status = 'Error', analyst_report = COALESCE(analyst_report, '') || ?1, processed_at = ?2 WHERE id = ?3",
|
||||||
params![error_message, id],
|
params![error_message, now, id],
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -378,8 +380,15 @@ mod tests {
|
||||||
|
|
||||||
ProcessedTicket::set_developer_report(&conn, &ticket.id, "Fixed in main.rs").unwrap();
|
ProcessedTicket::set_developer_report(&conn, &ticket.id, "Fixed in main.rs").unwrap();
|
||||||
let updated = ProcessedTicket::get_by_id(&conn, &ticket.id).unwrap();
|
let updated = ProcessedTicket::get_by_id(&conn, &ticket.id).unwrap();
|
||||||
assert_eq!(updated.developer_report.unwrap(), "Fixed in main.rs");
|
assert_eq!(
|
||||||
assert!(updated.processed_at.is_some());
|
updated.developer_report.as_deref(),
|
||||||
|
Some("Fixed in main.rs")
|
||||||
|
);
|
||||||
|
let processed_at = updated
|
||||||
|
.processed_at
|
||||||
|
.as_deref()
|
||||||
|
.expect("processed_at should be set");
|
||||||
|
assert!(chrono::DateTime::parse_from_rfc3339(processed_at).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -422,6 +431,14 @@ mod tests {
|
||||||
ProcessedTicket::set_error(&conn, &ticket.id, "CLI timeout after 600s").unwrap();
|
ProcessedTicket::set_error(&conn, &ticket.id, "CLI timeout after 600s").unwrap();
|
||||||
let updated = ProcessedTicket::get_by_id(&conn, &ticket.id).unwrap();
|
let updated = ProcessedTicket::get_by_id(&conn, &ticket.id).unwrap();
|
||||||
assert_eq!(updated.status, "Error");
|
assert_eq!(updated.status, "Error");
|
||||||
assert_eq!(updated.analyst_report.unwrap(), "CLI timeout after 600s");
|
assert_eq!(
|
||||||
|
updated.analyst_report.as_deref(),
|
||||||
|
Some("CLI timeout after 600s")
|
||||||
|
);
|
||||||
|
let processed_at = updated
|
||||||
|
.processed_at
|
||||||
|
.as_deref()
|
||||||
|
.expect("processed_at should be set");
|
||||||
|
assert!(chrono::DateTime::parse_from_rfc3339(processed_at).is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -516,7 +516,11 @@ mod tests {
|
||||||
.expect("update_last_polled should succeed");
|
.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());
|
let last_polled_at = updated
|
||||||
|
.last_polled_at
|
||||||
|
.as_deref()
|
||||||
|
.expect("last_polled_at should be set");
|
||||||
|
assert!(chrono::DateTime::parse_from_rfc3339(last_polled_at).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -88,9 +88,10 @@ impl Worktree {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_merged(conn: &Connection, id: &str, target_branch: &str) -> Result<()> {
|
pub fn set_merged(conn: &Connection, id: &str, target_branch: &str) -> Result<()> {
|
||||||
|
let now = chrono::Utc::now().to_rfc3339();
|
||||||
conn.execute(
|
conn.execute(
|
||||||
"UPDATE worktrees SET status = 'Merged', merged_at = datetime('now'), merged_into = ?1 WHERE id = ?2",
|
"UPDATE worktrees SET status = 'Merged', merged_at = ?1, merged_into = ?2 WHERE id = ?3",
|
||||||
params![target_branch, id],
|
params![now, target_branch, id],
|
||||||
)?;
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
@ -206,7 +207,11 @@ mod tests {
|
||||||
let updated = Worktree::get_by_id(&conn, &wt.id).unwrap();
|
let updated = Worktree::get_by_id(&conn, &wt.id).unwrap();
|
||||||
assert_eq!(updated.status, "Merged");
|
assert_eq!(updated.status, "Merged");
|
||||||
assert_eq!(updated.merged_into.unwrap(), "feature/login");
|
assert_eq!(updated.merged_into.unwrap(), "feature/login");
|
||||||
assert!(updated.merged_at.is_some());
|
let merged_at = updated
|
||||||
|
.merged_at
|
||||||
|
.as_deref()
|
||||||
|
.expect("merged_at should be set");
|
||||||
|
assert!(chrono::DateTime::parse_from_rfc3339(merged_at).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -84,12 +84,12 @@ fn should_poll(tracker: &WatchedTracker) -> bool {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
};
|
};
|
||||||
|
|
||||||
let last = match chrono::DateTime::parse_from_rfc3339(last_polled_at) {
|
let last = match parse_timestamp(last_polled_at) {
|
||||||
Ok(dt) => dt,
|
Some(dt) => dt,
|
||||||
Err(e) => {
|
None => {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"poller: failed to parse last_polled_at '{}': {}",
|
"poller: failed to parse last_polled_at '{}': unsupported format",
|
||||||
last_polled_at, e
|
last_polled_at
|
||||||
);
|
);
|
||||||
return true; // Treat as never polled on parse error
|
return true; // Treat as never polled on parse error
|
||||||
}
|
}
|
||||||
|
|
@ -99,6 +99,21 @@ fn should_poll(tracker: &WatchedTracker) -> bool {
|
||||||
elapsed >= tracker.polling_interval as i64
|
elapsed >= tracker.polling_interval as i64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_timestamp(value: &str) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
|
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(value) {
|
||||||
|
return Some(dt.with_timezone(&chrono::Utc));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(naive) = chrono::NaiveDateTime::parse_from_str(value, "%Y-%m-%d %H:%M:%S%.f") {
|
||||||
|
return Some(chrono::DateTime::<chrono::Utc>::from_naive_utc_and_offset(
|
||||||
|
naive,
|
||||||
|
chrono::Utc,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
async fn poll_single_tracker(
|
async fn poll_single_tracker(
|
||||||
db: &Arc<Mutex<Connection>>,
|
db: &Arc<Mutex<Connection>>,
|
||||||
client: &TuleapClient,
|
client: &TuleapClient,
|
||||||
|
|
@ -230,3 +245,26 @@ async fn poll_single_tracker(
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::parse_timestamp;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_timestamp_supports_rfc3339() {
|
||||||
|
let parsed = parse_timestamp("2026-04-16T10:15:30.123Z");
|
||||||
|
assert!(parsed.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_timestamp_supports_legacy_sqlite_datetime() {
|
||||||
|
let parsed = parse_timestamp("2026-04-16 10:15:30");
|
||||||
|
assert!(parsed.is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_timestamp_rejects_invalid_values() {
|
||||||
|
let parsed = parse_timestamp("not-a-date");
|
||||||
|
assert!(parsed.is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue