diff --git a/src/aggregators/connections.rs b/src/aggregators/connections.rs index d4297c9..3a6203b 100644 --- a/src/aggregators/connections.rs +++ b/src/aggregators/connections.rs @@ -156,24 +156,34 @@ impl Aggregator for ConnectionsAggregator { ); println!("Total connection failures: {}", self.connection_failures); println!("Connections by host:"); - for (host, count) in &self.connections_by_host { + let mut entries: Vec<_> = self.connections_by_host.iter().collect(); + entries.sort_by(|a, b| b.1.cmp(a.1)); + for (host, count) in entries { println!(" {count:>6} {host}"); } println!("Connections by database:"); - for (db, count) in &self.connections_by_database { + let mut entries: Vec<_> = self.connections_by_database.iter().collect(); + entries.sort_by(|a, b| b.1.cmp(a.1)); + for (db, count) in entries { println!(" {count:>6} {db}"); } println!("Connections by user:"); - for (user, count) in &self.connections_by_user { + let mut entries: Vec<_> = self.connections_by_user.iter().collect(); + entries.sort_by(|a, b| b.1.cmp(a.1)); + for (user, count) in entries { println!(" {count:>6} {user}"); } println!("Connections by application name:"); - for (appname, count) in &self.connections_by_appname { + let mut entries: Vec<_> = self.connections_by_appname.iter().collect(); + entries.sort_by(|a, b| b.1.cmp(a.1)); + for (appname, count) in entries { println!(" {count:>6} {appname}"); } println!("Connections by time bucket:"); - for (appname, count) in &self.connection_attempts_by_time_bucket { - println!(" {count:>6} {appname}"); + let mut entries: Vec<_> = self.connection_attempts_by_time_bucket.iter().collect(); + entries.sort_by(|a, b| b.1.cmp(a.1)); + for (bucket, count) in entries { + println!(" {count:>6} {bucket}"); } } diff --git a/tests/connections.rs b/tests/connections.rs index 14ea345..cf3fd9f 100644 --- a/tests/connections.rs +++ b/tests/connections.rs @@ -13,3 +13,56 @@ fn simple_connection_aggregate() -> Result<(), Box> { Ok(()) } + +#[test] +fn connections_output_sorted_by_count() -> Result<(), Box> { + let output = Command::new(cargo::cargo_bin!("pgweasel")) + .args(["conn", "./tests/files/connections.log"]) + .output()?; + + assert!(output.status.success()); + let stdout = String::from_utf8(output.stdout)?; + + let count_sorted_sections = [ + "Connections by host:", + "Connections by database:", + "Connections by user:", + "Connections by application name:", + "Connections by time bucket:", + ]; + + for section in count_sorted_sections { + let section_start = stdout + .find(section) + .unwrap_or_else(|| panic!("Section '{section}' not found")); + let after_header = &stdout[section_start + section.len()..]; + let section_end = after_header + .find("\nConnections by ") + .unwrap_or(after_header.len()); + let section_text = &after_header[..section_end]; + + let counts: Vec = section_text + .lines() + .filter_map(|line| { + let trimmed = line.trim(); + if trimmed.is_empty() { + return None; + } + trimmed.split_whitespace().next()?.parse::().ok() + }) + .collect(); + + assert!( + counts.len() > 1, + "Section '{section}' should have more than one entry for sorting to matter" + ); + for i in 1..counts.len() { + assert!( + counts[i - 1] >= counts[i], + "Section '{section}' not sorted descending: {counts:?}" + ); + } + } + + Ok(()) +} diff --git a/testdata/connections.log b/tests/files/connections.log similarity index 100% rename from testdata/connections.log rename to tests/files/connections.log