Skip to content
65 linesCodeBlameRaw
1//! An account purged (`user.deleted`, identity's `account_deletion.rs`):
2//! what it wrote stays where it is and shows as `ghost`, and it is taken
3//! off what it was asked to do.
4//!
5//! Its issues, pull requests, comments and reviews, plans and messages to
6//! agents keep their place and their words; their author becomes `ghost`
7//! (`usr_ghost`), and so does whoever asked g1t for an issue or pull
8//! request. It is no longer assigned to anything or asked to review, and
9//! review requests that reached it through a team are dropped. Stored
10//! rather than mapped when read, so every reader agrees.
11
12use g1t_contracts::account_deletion::{GHOST_ID, GHOST_USERNAME};
13
14/// Each statement takes the account's id as `?1` and its username as `?2`
15/// (`g1t_kit::user_deleted`). `ghost`'s id and name are written in.
16pub(crate) fn statements() -> Vec<String> {
17 let mut sql = Vec::new();
18 for table in ["issues", "pulls", "comments", "plans", "agent_messages"] {
19 sql.push(format!(
20 "UPDATE {table} SET author_id = '{GHOST_ID}', author_name = '{GHOST_USERNAME}' WHERE author_id = ?1"
21 ));
22 }
23 for table in ["issues", "pulls"] {
24 sql.push(format!(
25 "UPDATE {table} SET requested_by_id = '{GHOST_ID}', requested_by_name = '{GHOST_USERNAME}' WHERE requested_by_id = ?1"
26 ));
27 }
28 for (table, column) in [("issues", "assignees"), ("pulls", "assignees"), ("pulls", "reviewers")] {
29 sql.push(format!(
30 "UPDATE {table} SET {column} = (SELECT json_group_array(value) FROM json_each({table}.{column}) WHERE value <> ?2)
31 WHERE ?1 IS NOT NULL AND json_valid({column}) AND EXISTS (SELECT 1 FROM json_each({table}.{column}) WHERE value = ?2)"
32 ));
33 }
34 sql.push("DELETE FROM team_review_requests WHERE ?1 IS NOT NULL AND username = ?2".to_owned());
35 sql
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41
42 #[test]
43 fn everything_it_wrote_becomes_ghosts() {
44 let sql = statements();
45 for table in ["issues", "pulls", "comments", "plans", "agent_messages"] {
46 assert!(
47 sql.iter().any(|s| s.starts_with(&format!("UPDATE {table} SET author_id = 'usr_ghost', author_name = 'ghost'"))),
48 "{table}"
49 );
50 }
51 assert!(sql.iter().any(|s| s.contains("UPDATE pulls SET requested_by_id = 'usr_ghost'")));
52 assert!(sql.iter().any(|s| s.contains("UPDATE pulls SET reviewers")));
53 assert!(sql.iter().any(|s| s.contains("team_review_requests")));
54 }
55
56 #[test]
57 fn every_statement_takes_both_binds() {
58 // D1 refuses a bind a statement does not name: each names ?1, and
59 // the ones that need the username name ?2 as well.
60 for s in statements() {
61 assert!(s.contains("?1"), "{s}");
62 assert_eq!(g1t_kit::user_deleted::binds(&s, "usr_1", "ada").len(), if s.contains("?2") { 2 } else { 1 });
63 }
64 }
65}