gatewayd: serve, posting approvals, marking them, and the listener

Implemented-By: OpenCode session (model recorded in docs/implementer-log.md)
This commit is contained in:
2026-09-24 05:07:50 -07:00
parent a33ff240c4
commit c38e450f44
6 changed files with 580 additions and 2 deletions
+50
View File
@@ -30,6 +30,10 @@ struct Inner {
posts: Vec<(String, String, String)>,
/// Every REST call: method and path.
calls: Vec<(String, String)>,
/// Every edit: the post and its new text.
edits: Vec<(String, String)>,
/// When set, `POST /posts` is refused with 400.
refuse_posts: bool,
}
pub struct FakeMm {
@@ -223,6 +227,28 @@ impl FakeMm {
pub fn calls(&self) -> Vec<(String, String)> {
self.inner.lock().unwrap().calls.clone()
}
/// The id of the n-th post made (from 0).
pub fn post_id(n: usize) -> String {
id('x', u32::try_from(n).unwrap())
}
pub fn edits(&self) -> Vec<(String, String)> {
self.inner.lock().unwrap().edits.clone()
}
/// Wait until at least `n` edits were made, for at most `wait`.
pub fn wait_edits(&self, n: usize, wait: Duration) -> Vec<(String, String)> {
let until = Instant::now() + wait;
while self.edits().len() < n && Instant::now() < until {
std::thread::sleep(Duration::from_millis(10));
}
self.edits()
}
pub fn refuse_posts(&self, refuse: bool) {
self.inner.lock().unwrap().refuse_posts = refuse;
}
}
fn connection(mut stream: TcpStream, inner: &Mutex<Inner>, ws_tx: &mpsc::Sender<WsPeer>) {
@@ -298,6 +324,9 @@ fn rest(inner: &Mutex<Inner>, method: &str, path: &str, body: &[u8]) -> (u16, Va
};
(201, json!({"id": channel, "type": "D"}))
}
("POST", "/api/v4/posts") if inner.refuse_posts => {
(400, json!({"id": "api.post.create_post.refused"}))
}
("POST", "/api/v4/posts") => {
let p: Value = serde_json::from_slice(body).unwrap();
let n = u32::try_from(inner.posts.len()).unwrap();
@@ -326,6 +355,27 @@ fn rest(inner: &Mutex<Inner>, method: &str, path: &str, body: &[u8]) -> (u16, Va
.unwrap_or(json!({"order": [], "posts": {}})),
)
}
("PUT", p) if p.starts_with("/api/v4/posts/") && p.ends_with("/patch") => {
let post_id = p
.trim_start_matches("/api/v4/posts/")
.trim_end_matches("/patch")
.to_string();
let message = serde_json::from_slice::<Value>(body).unwrap()["message"]
.as_str()
.unwrap()
.to_string();
inner.edits.push((post_id.clone(), message.clone()));
(200, post(&post_id, BOT, DM, "", &message, 1))
}
("GET", p) if p.starts_with("/api/v4/users/") => {
let user = p.trim_start_matches("/api/v4/users/");
let name = if user == KYLE {
"kyle".to_string()
} else {
format!("user-{}", &user[..4])
};
(200, json!({"id": user, "username": name}))
}
_ => (404, json!({"message": "not found"})),
}
}