Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion crates/buzz-cli/src/commands/channels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ use crate::commands::channel_templates::{self, ChannelTemplateRecord, TemplateAg
use crate::error::CliError;
use crate::validate::{parse_uuid, read_or_stdin, validate_hex64, validate_uuid};

/// Extracts the channel summary fields from a kind 39000 channel-metadata event.
///
/// The timestamp carried here is the time of the **last channel-metadata write**,
/// not the channel's creation: kind 39000 is addressable/replaceable, so its
/// `created_at` is overwritten on every metadata update and the channel's birth
/// time is not recoverable from it. `updated_at` names this value honestly;
/// `created_at` is a deprecated alias kept for one release so existing scripts
/// do not break silently — prefer `updated_at`.
fn extract_channel_metadata(e: &serde_json::Value) -> serde_json::Value {
let updated_at = e.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0);
serde_json::json!({
"channel_id": extract_d_tag(e),
"name": extract_tag_value(e, "name"),
"description": extract_tag_value(e, "about"),
"created_at": e.get("created_at").and_then(|v| v.as_u64()).unwrap_or(0),
"updated_at": updated_at,
"created_at": updated_at,
})
}

Expand Down Expand Up @@ -1748,3 +1758,50 @@ mod tests {
);
}
}

#[cfg(test)]
mod extract_channel_metadata_tests {
use super::extract_channel_metadata;
use serde_json::json;

#[test]
fn updated_at_carries_kind_39000_created_at() {
let ev = json!({
"kind": 39000,
"created_at": 1787300000u64,
"tags": [
["d", "11111111-1111-1111-1111-111111111111"],
["name", "general"],
["about", "desc"]
]
});
let meta = extract_channel_metadata(&ev);
assert_eq!(meta.get("updated_at"), Some(&json!(1787300000u64)));
}

#[test]
fn created_at_is_deprecated_alias_of_updated_at() {
let ev = json!({
"kind": 39000,
"created_at": 1787300000u64,
"tags": [["d", "11111111-1111-1111-1111-111111111111"]]
});
let meta = extract_channel_metadata(&ev);
assert_eq!(
meta.get("created_at"),
meta.get("updated_at"),
"created_at must stay pinned to updated_at until removal"
);
}

#[test]
fn missing_created_at_yields_zero_for_both_fields() {
let ev = json!({
"kind": 39000,
"tags": [["d", "11111111-1111-1111-1111-111111111111"]]
});
let meta = extract_channel_metadata(&ev);
assert_eq!(meta.get("updated_at"), Some(&json!(0u64)));
assert_eq!(meta.get("created_at"), Some(&json!(0u64)));
}
}
4 changes: 4 additions & 0 deletions crates/buzz-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,10 @@ pub enum MessagesCmd {
#[derive(Subcommand)]
pub enum ChannelsCmd {
/// List channels visible to the current identity
///
/// Each channel's `updated_at` is the time of its last channel-metadata
/// write (kind 39000 is replaceable), not its creation time. `created_at`
/// is a deprecated alias of `updated_at`.
#[command(
after_help = "Examples:\n buzz channels list\n buzz channels list --visibility open"
)]
Expand Down