Skip to content
Open
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
47 changes: 45 additions & 2 deletions px-auth/src/infrastructure/yaml_allowlist_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,18 @@ use crate::domain::allowlist_store::AllowlistStore;
use async_trait::async_trait;
use px_errors::AppError;
use serde::Deserialize;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::path::Path;

#[derive(Debug, Deserialize)]
struct AllowlistFile {
entries: Vec<AllowlistEntry>,
}

/// `BTreeMap` keeps `list()` output in domain-sorted order so operator output
/// (`px-cli allowlist list`) and audit diffs are reproducible across reloads.
pub struct YamlAllowlistStore {
by_domain: HashMap<String, AllowlistEntry>,
by_domain: BTreeMap<String, AllowlistEntry>,
}

impl YamlAllowlistStore {
Expand Down Expand Up @@ -51,3 +53,44 @@ impl AllowlistStore for YamlAllowlistStore {
Ok(self.by_domain.values().cloned().collect())
}
}

#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;

fn entry(domain: &str) -> AllowlistEntry {
AllowlistEntry {
domain: domain.into(),
tos_reviewed: true,
justification: "test".into(),
handler: None,
}
}

#[tokio::test]
async fn list_returns_entries_sorted_by_domain() {
let store = YamlAllowlistStore::from_entries(vec![
entry("zeta.example"),
entry("alpha.example"),
entry("mu.example"),
]);
let listed: Vec<String> = store
.list()
.await
.expect("list")
.into_iter()
.map(|e| e.domain)
.collect();
assert_eq!(listed, vec!["alpha.example", "mu.example", "zeta.example"]);
}

#[tokio::test]
async fn lookup_finds_inserted_entry() {
let store = YamlAllowlistStore::from_entries(vec![entry("example.com")]);
let hit = store.lookup("example.com").await.expect("lookup");
assert_eq!(hit.map(|e| e.domain).as_deref(), Some("example.com"));
let miss = store.lookup("missing.example").await.expect("lookup");
assert!(miss.is_none());
}
}
Loading