Skip to content

Commit 5855024

Browse files
committed
Refactor and extend AuthMonitor unit tests
1 parent c49b092 commit 5855024

File tree

2 files changed

+173
-125
lines changed

2 files changed

+173
-125
lines changed

src/auth_monitor.test.rs

Lines changed: 137 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,169 +1,181 @@
1+
use std::ops::Range;
12
use std::thread::sleep;
23
use std::time::Duration;
34

45
use crate::auth_monitor::AuthMonitor;
56
use crate::auth_monitor_options::AuthMonitorOptions;
67
use crate::auth_monitor_params::AuthMonitorParams;
7-
use crate::test_utils::test_file::{create_log_line, TestFile};
8+
use crate::test_utils::test_file::TestFile;
89

9-
const AUTH_FAILED_TEST_MESSAGES: [&str; 2] = [
10-
"workstation sudo: pam_unix(sudo:auth): authentication failure; logname=john uid=1000 euid=0 tty=/dev/pts/7 ruser=john rhost= user=john",
11-
"workstation kscreenlocker_greet: pam_unix(kde:auth): authentication failure; logname= uid=1000 euid=1000 tty= ruser= rhost= user=john",
12-
];
13-
14-
const OTHER_TEST_MESSAGES: [&str; 4] = [
15-
"workstation dbus-daemon[1988]: [system] Failed to activate service 'org.bluez': timed out (service_start_timeout=25000ms)",
16-
"workstation CRON[9419]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)",
17-
"workstation CRON[9419]: pam_unix(cron:session): session closed for user root",
18-
"workstation PackageKit: uid 1000 is trying to obtain org.freedesktop.packagekit.system-sources-refresh auth (only_trusted:0)",
19-
];
10+
struct AuthMonitorTest {
11+
auth_monitor: AuthMonitor,
12+
}
2013

21-
#[test]
22-
pub fn when_max_failed_attempts_limit_is_reached_then_update_callback_is_called() {
23-
for max_failed_attempts in 2..10 {
24-
let mut file = TestFile::with_unique_name();
25-
let mut auth_monitor = AuthMonitor::new(AuthMonitorParams {
26-
filepath: String::from(file.path()),
27-
options: AuthMonitorOptions {
28-
max_failed_attempts,
29-
..AuthMonitorOptions::default()
30-
},
14+
impl AuthMonitorTest {
15+
pub fn new(path: &str, options: AuthMonitorOptions) -> AuthMonitorTest {
16+
println!("Creating AuthMonitor with options: {}", options);
17+
let auth_monitor = AuthMonitor::new(AuthMonitorParams {
18+
filepath: String::from(path),
19+
options,
3120
})
3221
.expect("Error creating AuthMonitor");
22+
return AuthMonitorTest { auth_monitor };
23+
}
3324

34-
expect_no_update_callback_call(&mut auth_monitor);
35-
expect_update_callback_is_not_called_after_writing_auth_failed_messages(
36-
&mut auth_monitor,
37-
&mut file,
38-
(max_failed_attempts - 1) as usize,
39-
);
40-
41-
expect_update_callback_is_not_called_after_writing_other_messages(
42-
&mut auth_monitor,
43-
&mut file,
44-
);
45-
46-
expect_update_callback_is_called_after_writing_auth_failed_message(
47-
&mut auth_monitor,
48-
&mut file,
49-
);
25+
pub fn expect_no_update_callback_call(&mut self) {
26+
self.auth_monitor.update(|| {
27+
panic!("Callback call was not expected");
28+
});
5029
}
51-
}
5230

53-
fn expect_no_update_callback_call(auth_monitor: &mut AuthMonitor) {
54-
auth_monitor.update(|| {
55-
panic!("Callback call was not expected");
56-
});
31+
pub fn expect_update_callback_is_called_once(&mut self) {
32+
let mut call_count = 0;
33+
self.auth_monitor.update(|| {
34+
call_count += 1;
35+
});
36+
assert_eq!(call_count, 1, "One callback call was expected")
37+
}
5738
}
5839

59-
fn expect_update_callback_is_not_called_after_writing_auth_failed_messages(
60-
auth_monitor: &mut AuthMonitor,
61-
file: &mut TestFile,
62-
message_count: usize,
63-
) {
64-
for i in 0usize..message_count {
65-
let message_index = i % AUTH_FAILED_TEST_MESSAGES.len();
66-
let message = create_log_line(AUTH_FAILED_TEST_MESSAGES[message_index]);
67-
file.write(&message);
68-
expect_no_update_callback_call(auth_monitor);
40+
const MAX_FAILED_ATTEMPTS_TEST_RANGE: Range<i32> = 2..15;
41+
42+
#[test]
43+
fn when_file_does_not_exist_then_changes_are_monitored_after_it_is_created() {
44+
let mut file = TestFile::with_unique_name();
45+
file.remove();
46+
47+
let options = AuthMonitorOptions::default();
48+
let mut test = AuthMonitorTest::new(file.path(), options);
49+
test.expect_no_update_callback_call();
50+
51+
file.create();
52+
53+
for i in 0usize..(options.max_failed_attempts - 1) as usize {
54+
file.write_auth_failed_message(i);
55+
test.expect_no_update_callback_call();
6956
}
57+
58+
file.write_auth_failed_message(0);
59+
test.expect_update_callback_is_called_once();
7060
}
7161

72-
fn expect_update_callback_is_not_called_after_writing_other_messages(
73-
auth_monitor: &mut AuthMonitor,
74-
file: &mut TestFile,
75-
) {
76-
for other_message in OTHER_TEST_MESSAGES {
77-
let message = create_log_line(other_message);
78-
file.write(&message);
79-
expect_no_update_callback_call(auth_monitor);
62+
#[test]
63+
pub fn when_auth_failure_limit_is_reached_then_update_callback_is_invoked() {
64+
for max_failed_attempts in MAX_FAILED_ATTEMPTS_TEST_RANGE {
65+
let mut file = TestFile::with_unique_name();
66+
let options = AuthMonitorOptions {
67+
max_failed_attempts,
68+
..AuthMonitorOptions::default()
69+
};
70+
let mut test = AuthMonitorTest::new(file.path(), options);
71+
test.expect_no_update_callback_call();
72+
73+
for i in 0usize..(max_failed_attempts - 1) as usize {
74+
file.write_auth_failed_message(i);
75+
test.expect_no_update_callback_call();
76+
}
77+
78+
for i in 0usize..(max_failed_attempts * 2) as usize {
79+
file.write_other_message(i);
80+
test.expect_no_update_callback_call();
81+
}
82+
83+
file.write_auth_failed_message(0);
84+
test.expect_update_callback_is_called_once();
8085
}
8186
}
8287

83-
fn expect_update_callback_is_called_after_writing_auth_failed_message(
84-
auth_monitor: &mut AuthMonitor,
85-
file: &mut TestFile,
86-
) {
87-
let message = create_log_line(AUTH_FAILED_TEST_MESSAGES[0]);
88-
file.write(&message);
89-
expect_update_callback_is_called_once(auth_monitor);
90-
}
88+
#[test]
89+
pub fn when_auth_failure_limit_is_reached_between_updates_then_next_update_invokes_callback() {
90+
for max_failed_attempts in MAX_FAILED_ATTEMPTS_TEST_RANGE {
91+
let mut file = TestFile::with_unique_name();
92+
let options = AuthMonitorOptions {
93+
max_failed_attempts,
94+
..AuthMonitorOptions::default()
95+
};
96+
let mut test = AuthMonitorTest::new(file.path(), options);
97+
test.expect_no_update_callback_call();
9198

92-
fn expect_update_callback_is_called_once(auth_monitor: &mut AuthMonitor) {
93-
let mut call_count = 0;
94-
auth_monitor.update(|| call_count += 1);
95-
assert_eq!(call_count, 1, "One callback call was expected")
99+
file.write_auth_failed_messages(max_failed_attempts as usize);
100+
file.write_other_messages(max_failed_attempts as usize);
101+
102+
test.expect_update_callback_is_called_once();
103+
}
96104
}
97105

98106
#[test]
99-
pub fn when_reset_after_seconds_passed_then_failed_attempts_count_is_reset() {
107+
pub fn when_reset_time_has_passed_then_reset_failed_attempt_counter() {
108+
let mut file = TestFile::with_unique_name();
100109
let options = AuthMonitorOptions {
101-
max_failed_attempts: 3,
102110
reset_after_seconds: 5,
111+
..AuthMonitorOptions::default()
103112
};
104-
let mut file = TestFile::with_unique_name();
105-
let mut auth_monitor = AuthMonitor::new(AuthMonitorParams {
106-
filepath: String::from(file.path()),
107-
options,
108-
})
109-
.expect("Error creating AuthMonitor");
110-
111-
expect_no_update_callback_call(&mut auth_monitor);
112-
expect_update_callback_is_not_called_after_writing_auth_failed_messages(
113-
&mut auth_monitor,
114-
&mut file,
115-
(options.max_failed_attempts - 1) as usize,
116-
);
113+
let mut test = AuthMonitorTest::new(file.path(), options);
114+
test.expect_no_update_callback_call();
115+
116+
let failed_attempts_safe_limit = (options.max_failed_attempts - 1) as usize;
117+
118+
for i in 0usize..failed_attempts_safe_limit {
119+
file.write_auth_failed_message(i);
120+
test.expect_no_update_callback_call();
121+
}
117122

118123
let sleep_duration = Duration::from_secs((options.reset_after_seconds + 1) as u64);
119124
println!("Sleeping for {} sec", sleep_duration.as_secs());
120125
sleep(sleep_duration);
121126

122-
expect_update_callback_is_not_called_after_writing_auth_failed_messages(
123-
&mut auth_monitor,
124-
&mut file,
125-
(options.max_failed_attempts - 1) as usize,
126-
);
127+
for i in 0usize..failed_attempts_safe_limit {
128+
file.write_auth_failed_message(i);
129+
test.expect_no_update_callback_call();
130+
}
127131

128-
expect_update_callback_is_called_after_writing_auth_failed_message(
129-
&mut auth_monitor,
130-
&mut file,
131-
);
132+
file.write_auth_failed_message(0);
133+
test.expect_update_callback_is_called_once();
132134
}
133135

134136
#[test]
135-
pub fn when_max_failed_attempts_limit_is_reached_before_update_is_called_then_update_callback_is_called(
136-
) {
137-
for max_failed_attempts in 2..10 {
138-
let mut file = TestFile::with_unique_name();
139-
let mut auth_monitor = AuthMonitor::new(AuthMonitorParams {
140-
filepath: String::from(file.path()),
141-
options: AuthMonitorOptions {
142-
max_failed_attempts,
143-
..AuthMonitorOptions::default()
144-
},
145-
})
146-
.expect("Error creating AuthMonitor");
137+
fn when_file_is_deleted_and_new_one_is_created_then_changes_are_still_monitored() {
138+
let mut file = TestFile::with_unique_name();
139+
let options = AuthMonitorOptions::default();
140+
let mut test = AuthMonitorTest::new(file.path(), options);
141+
file.remove();
142+
test.expect_no_update_callback_call();
147143

148-
expect_no_update_callback_call(&mut auth_monitor);
149-
write_auth_failed_messages(&mut file, max_failed_attempts as usize);
150-
write_other_messages(&mut file, max_failed_attempts as usize);
151-
expect_update_callback_is_called_once(&mut auth_monitor);
152-
}
153-
}
144+
file.create();
145+
test.expect_no_update_callback_call();
154146

155-
fn write_auth_failed_messages(file: &mut TestFile, count: usize) {
156-
write_messages(file, &AUTH_FAILED_TEST_MESSAGES, count);
147+
file.write_auth_failed_messages(options.max_failed_attempts as usize);
148+
test.expect_update_callback_is_called_once();
157149
}
158150

159-
fn write_other_messages(file: &mut TestFile, count: usize) {
160-
write_messages(file, &OTHER_TEST_MESSAGES, count);
151+
#[test]
152+
fn when_file_is_renamed_and_new_one_is_created_then_changes_are_still_monitored() {
153+
let mut file = TestFile::with_unique_name();
154+
let options = AuthMonitorOptions::default();
155+
let mut test = AuthMonitorTest::new(file.path(), options);
156+
157+
let filepath = String::from(file.path());
158+
let new_filepath = format!("{}.bak", file.path());
159+
file.rename(&new_filepath);
160+
test.expect_no_update_callback_call();
161+
162+
let mut new_file = TestFile::new(&filepath);
163+
test.expect_no_update_callback_call();
164+
new_file.write_auth_failed_messages(options.max_failed_attempts as usize);
165+
test.expect_update_callback_is_called_once();
161166
}
162167

163-
fn write_messages(file: &mut TestFile, messages: &[&str], count: usize) {
164-
for i in 0usize..count {
165-
let message_index = i % messages.len();
166-
let message = create_log_line(messages[message_index]);
167-
file.write(&message);
168-
}
168+
#[test]
169+
fn when_file_is_truncated_then_changes_are_still_monitored() {
170+
let mut file = TestFile::with_unique_name();
171+
file.write_other_messages(5);
172+
173+
let options = AuthMonitorOptions::default();
174+
let mut test = AuthMonitorTest::new(file.path(), options);
175+
176+
file.truncate();
177+
test.expect_no_update_callback_call();
178+
179+
file.write_auth_failed_messages(options.max_failed_attempts as usize);
180+
test.expect_update_callback_is_called_once();
169181
}

src/test_utils/test_file.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ use std::sync::atomic::{AtomicUsize, Ordering};
55

66
use chrono::Local;
77

8+
const AUTH_FAILED_TEST_MESSAGES: [&str; 2] = [
9+
"workstation sudo: pam_unix(sudo:auth): authentication failure; logname=john uid=1000 euid=0 tty=/dev/pts/7 ruser=john rhost= user=john",
10+
"workstation kscreenlocker_greet: pam_unix(kde:auth): authentication failure; logname= uid=1000 euid=1000 tty= ruser= rhost= user=john",
11+
];
12+
13+
const OTHER_TEST_MESSAGES: [&str; 4] = [
14+
"workstation dbus-daemon[1988]: [system] Failed to activate service 'org.bluez': timed out (service_start_timeout=25000ms)",
15+
"workstation CRON[9419]: pam_unix(cron:session): session opened for user root(uid=0) by (uid=0)",
16+
"workstation CRON[9419]: pam_unix(cron:session): session closed for user root",
17+
"workstation PackageKit: uid 1000 is trying to obtain org.freedesktop.packagekit.system-sources-refresh auth (only_trusted:0)",
18+
];
19+
820
pub struct TestFile {
921
path: String,
1022
file: File,
@@ -53,6 +65,30 @@ impl TestFile {
5365
assert_eq!(bytes_written, bytes_to_add.len());
5466
}
5567

68+
pub fn write_auth_failed_messages(&mut self, count: usize) {
69+
for i in 0usize..count {
70+
self.write_auth_failed_message(i);
71+
}
72+
}
73+
74+
pub fn write_auth_failed_message(&mut self, index: usize) {
75+
let message_index = index % AUTH_FAILED_TEST_MESSAGES.len();
76+
let message = create_log_line(AUTH_FAILED_TEST_MESSAGES[message_index]);
77+
self.write(&message);
78+
}
79+
80+
pub fn write_other_messages(&mut self, count: usize) {
81+
for i in 0usize..count {
82+
self.write_other_message(i);
83+
}
84+
}
85+
86+
pub fn write_other_message(&mut self, index: usize) {
87+
let message_index = index % OTHER_TEST_MESSAGES.len();
88+
let message = create_log_line(OTHER_TEST_MESSAGES[message_index]);
89+
self.write(&message);
90+
}
91+
5692
pub fn truncate(&mut self) {
5793
println!("Truncating test file: {}", self.path);
5894
self.file.set_len(0).expect("Error truncating file");

0 commit comments

Comments
 (0)