karma: log bump events; rebuild web page with podium, sparklines, activity feed and givers
All checks were successful
ci / check (push) Successful in 44s

This commit is contained in:
Jean Chevronnet 2026-07-29 23:45:29 +00:00
parent 9a3393153a
commit f290554325
No known key found for this signature in database
GPG key ID: 439666D63A9477E4
7 changed files with 710 additions and 141 deletions

View file

@ -1,5 +1,5 @@
use rustbot::bot::module::{Action, Chat, Command, Module};
use rustbot::bot::modules::karma::{extract_reason, parse_changes, Karma};
use rustbot::bot::modules::karma::{extract_reason, format_event, parse_changes, Karma};
fn chat<'a>(sender: &'a str, text: &'a str) -> Chat<'a> {
Chat {
@ -166,3 +166,45 @@ fn milestone_announced_at_ten() {
assert!(msg.contains("rust reached 10 karma"), "{msg}");
let _ = std::fs::remove_file(&path);
}
#[test]
fn format_event_is_one_json_line() {
let line = format_event(1700000000, "rust", 1, "alice", Some("clean PR"));
assert!(line.ends_with('\n'));
assert_eq!(line.matches('\n').count(), 1);
assert!(line.contains("\"t\":1700000000"), "{line}");
assert!(line.contains("\"o\":\"rust\""), "{line}");
assert!(line.contains("\"d\":1"), "{line}");
assert!(line.contains("\"by\":\"alice\""), "{line}");
assert!(line.contains("\"why\":\"clean PR\""), "{line}");
// no reason -> no why field
let bare = format_event(1, "docs", -1, "bob", None);
assert!(!bare.contains("why"), "{bare}");
// quotes in a reason are escaped
let q = format_event(1, "x", 1, "y", Some("he said \"hi\""));
assert!(q.contains("\\\"hi\\\""), "{q}");
}
#[test]
fn on_message_appends_to_the_event_log() {
let path = std::env::temp_dir().join("rubot-test-karma-ev.json");
let log = std::env::temp_dir().join("rubot-test-karma-ev.events.jsonl");
let _ = std::fs::remove_file(&path);
let _ = std::fs::remove_file(&log);
let mut k = Karma::with_path(path.clone());
k.on_message(&chat("alice", "rust++ for the clean PR"));
k.on_message(&chat("bob", "rust-- python++"));
let body = std::fs::read_to_string(&log).expect("event log written");
let lines: Vec<&str> = body.lines().collect();
assert_eq!(lines.len(), 3, "one line per bump: {body}");
assert!(lines[0].contains("\"o\":\"rust\"") && lines[0].contains("\"by\":\"alice\""));
assert!(
lines[0].contains("\"why\":\"the clean PR\""),
"{}",
lines[0]
);
// the two-bump message attributes no reason
assert!(!lines[1].contains("why") && !lines[2].contains("why"));
let _ = std::fs::remove_file(&path);
let _ = std::fs::remove_file(&log);
}