Skip to content

Commit

Permalink
test(jans-lock): add unit test for LogStrategy
Browse files Browse the repository at this point in the history
Signed-off-by: Oleh Bohzok <[email protected]>
  • Loading branch information
olehbozhok committed Sep 21, 2024
1 parent 1253436 commit d081bca
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 32 deletions.
200 changes: 200 additions & 0 deletions jans-lock/cedarling/cedarling/src/log/log_strategy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,203 @@ impl LogStorage for LogStrategy {
}
}
}

#[cfg(test)]
mod tests {
use std::{
io::Write,
time::{SystemTime, UNIX_EPOCH},
};

use super::*;
use crate::{
log::stdout_logger::TestWriter,
models::log_entry::{LogEntry, LogType},
};
use uuid7::uuid7;

use crate::models::log_config;

#[test]
fn test_new_log_strategy_off() {
// Arrange
let config = LogConfig {
log_type: log_config::LogType::Off,
};

// Act
let strategy = LogStrategy::new(config);

// Assert
assert!(matches!(strategy, LogStrategy::OnlyWriter(_)));
}

#[test]
fn test_new_log_strategy_memory() {
// Arrange
let config = LogConfig {
log_type: log_config::LogType::Memory(log_config::MemoryLogConfig { log_ttl: 60 }),
};

// Act
let strategy = LogStrategy::new(config);

// Assert
assert!(matches!(strategy, LogStrategy::MemoryLogger(_)));
}

#[test]
fn test_new_logstrategy_stdout() {
// Arrange
let config = LogConfig {
log_type: log_config::LogType::StdOut,
};

// Act
let strategy = LogStrategy::new(config);

// Assert
assert!(matches!(strategy, LogStrategy::OnlyWriter(_)));
}

#[test]
fn test_log_memory_logger() {
// Arrange
let config = LogConfig {
log_type: log_config::LogType::Memory(log_config::MemoryLogConfig { log_ttl: 60 }),
};
let strategy = LogStrategy::new(config);
let entry = LogEntry {
id: uuid7(),
time: SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs(),
log_kind: LogType::Decision,
pdp_id: uuid7(),
application_id: "test_app".to_string(),
auth_info: None,
msg: "Test message".to_string(),
};

// Act
strategy.log(entry);

// Assert
match &strategy {
LogStrategy::MemoryLogger(memory_logger) => {
assert!(!memory_logger.get_log_ids().is_empty());
memory_logger.pop_logs();
// after popping, the memory logger should be empty
assert!(memory_logger.get_log_ids().is_empty());
// it is empty after popping, so we can continue testing
},
_ => panic!("Expected MemoryLogger"),
}

// make same test as for the memory logger
// create log entries
let entry1 = LogEntry::new_with_data(uuid7(), "app1".to_string(), LogType::Decision)
.set_message("some message".to_string());

let entry2 = LogEntry::new_with_data(uuid7(), "app2".to_string(), LogType::System);

// log entries
strategy.log(entry1.clone());
strategy.log(entry2.clone());

// check that we have two entries in the log database
assert_eq!(strategy.get_log_ids().len(), 2);
assert_eq!(
strategy.get_log_by_id(&entry1.id.to_string()).unwrap(),
entry1,
"Failed to get log entry by id"
);
assert_eq!(
strategy.get_log_by_id(&entry2.id.to_string()).unwrap(),
entry2,
"Failed to get log entry by id"
);

// get logs using `pop_logs`
let logs = strategy.pop_logs();
assert_eq!(logs.len(), 2);
assert_eq!(logs[0], entry1, "First log entry is incorrect");
assert_eq!(logs[1], entry2, "Second log entry is incorrect");

// check that we have no entries in the log database
assert!(
strategy.get_log_ids().is_empty(),
"Logs were not fully popped"
);
}

#[test]
fn test_log_stdout_logger() {
// Arrange
let log_entry = LogEntry {
id: uuid7(),
time: SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_secs(),
log_kind: LogType::Decision,
pdp_id: uuid7(),
application_id: "test_app".to_string(),
auth_info: None,
msg: "Test message".to_string(),
};
// Serialize the log entry to JSON
let json_str = serde_json::json!(&log_entry).to_string();

let test_writer = TestWriter::new();
let buffer = Box::new(test_writer.clone()) as Box<dyn Write + 'static>;
let logger = StdOutLogger::new_with(buffer);
let strategy = LogStrategy::OnlyWriter(Box::new(logger));

// Act
strategy.log(log_entry);

let logged_content = test_writer.into_inner_buf();

assert_eq!(logged_content, json_str + "\n");
}

#[test]
fn test_log_storage_for_only_writer() {
let strategy = LogStrategy::OnlyWriter(Box::new(NopLogger));

// make same test as for the memory logger
// create log entries
let entry1 = LogEntry::new_with_data(uuid7(), "app1".to_string(), LogType::Decision)
.set_message("some message".to_string());

let entry2 = LogEntry::new_with_data(uuid7(), "app2".to_string(), LogType::System);

// log entries
strategy.log(entry1.clone());
strategy.log(entry2.clone());

// check that we have two entries in the log database
// we should not have any entries in the memory logger
assert_eq!(strategy.get_log_ids().len(), 0);
assert!(
strategy.get_log_by_id(&entry1.id.to_string()).is_none(),
"We should not have entry1 entry in the memory logger"
);
assert!(
strategy.get_log_by_id(&entry2.id.to_string()).is_none(),
"We should not have entry2 entry in the memory logger"
);

// get logs using `pop_logs`
let logs = strategy.pop_logs();
assert_eq!(logs.len(), 0);

// check that we have no entries in the log database
assert!(
strategy.get_log_ids().is_empty(),
"We should not have any logs"
);
}
}
66 changes: 34 additions & 32 deletions jans-lock/cedarling/cedarling/src/log/stdout_logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

use super::interface::LogWriter;
use crate::models::log_entry::LogEntry;
use std::{cell::RefCell, io::Write};
use std::{cell::RefCell, io::Write, rc::Rc};

/// A logger that do nothing.
pub(crate) struct StdOutLogger {
Expand All @@ -25,7 +25,7 @@ impl StdOutLogger {
// Create a new StdOutLogger with custom writer.
// is used in tests.
#[allow(dead_code)]
fn new_with(writer: Box<dyn Write>) -> Self {
pub(crate) fn new_with(writer: Box<dyn Write>) -> Self {
Self {
writer_ok: RefCell::new(writer),
}
Expand All @@ -42,48 +42,50 @@ impl LogWriter for StdOutLogger {
}
}

// Test writer created for mocking LogWriter
#[allow(dead_code)]
#[derive(Clone)]
pub(crate) struct TestWriter {
buf: Rc<RefCell<Vec<u8>>>,
}

#[allow(dead_code)]
impl TestWriter {
pub(crate) fn new() -> Self {
Self {
buf: Rc::new(RefCell::new(Vec::new())),
}
}

pub(crate) fn into_inner_buf(self) -> String {
let buf = self.buf.take();
String::from_utf8_lossy(buf.as_slice()).into_owned()
}
}

impl Write for TestWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.buf.borrow_mut().extend_from_slice(buf);
Ok(buf.len())
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

#[cfg(test)]
mod tests {
use crate::models::log_entry::LogType;

use super::*;
use std::{
io::Write,
rc::Rc,
time::{SystemTime, UNIX_EPOCH},
};

use uuid7::uuid7;

#[derive(Clone)]
struct TestWriter {
buf: Rc<RefCell<Vec<u8>>>,
}

impl TestWriter {
fn new() -> Self {
Self {
buf: Rc::new(RefCell::new(Vec::new())),
}
}

fn into_inner_buf(self) -> String {
let buf = self.buf.take();
String::from_utf8_lossy(buf.as_slice()).into_owned()
}
}

impl Write for TestWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.buf.borrow_mut().extend_from_slice(buf);
Ok(buf.len())
}

fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}

#[test]
fn write_log_ok() {
// Create a log entry
Expand Down

0 comments on commit d081bca

Please sign in to comment.