|
| 1 | +use std::ops::Range; |
1 | 2 | use std::thread::sleep; |
2 | 3 | use std::time::Duration; |
3 | 4 |
|
4 | 5 | use crate::auth_monitor::AuthMonitor; |
5 | 6 | use crate::auth_monitor_options::AuthMonitorOptions; |
6 | 7 | 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; |
8 | 9 |
|
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 | +} |
20 | 13 |
|
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, |
31 | 20 | }) |
32 | 21 | .expect("Error creating AuthMonitor"); |
| 22 | + return AuthMonitorTest { auth_monitor }; |
| 23 | + } |
33 | 24 |
|
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 | + }); |
50 | 29 | } |
51 | | -} |
52 | 30 |
|
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 | + } |
57 | 38 | } |
58 | 39 |
|
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(); |
69 | 56 | } |
| 57 | + |
| 58 | + file.write_auth_failed_message(0); |
| 59 | + test.expect_update_callback_is_called_once(); |
70 | 60 | } |
71 | 61 |
|
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(); |
80 | 85 | } |
81 | 86 | } |
82 | 87 |
|
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(); |
91 | 98 |
|
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 | + } |
96 | 104 | } |
97 | 105 |
|
98 | 106 | #[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(); |
100 | 109 | let options = AuthMonitorOptions { |
101 | | - max_failed_attempts: 3, |
102 | 110 | reset_after_seconds: 5, |
| 111 | + ..AuthMonitorOptions::default() |
103 | 112 | }; |
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 | + } |
117 | 122 |
|
118 | 123 | let sleep_duration = Duration::from_secs((options.reset_after_seconds + 1) as u64); |
119 | 124 | println!("Sleeping for {} sec", sleep_duration.as_secs()); |
120 | 125 | sleep(sleep_duration); |
121 | 126 |
|
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 | + } |
127 | 131 |
|
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(); |
132 | 134 | } |
133 | 135 |
|
134 | 136 | #[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(); |
147 | 143 |
|
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(); |
154 | 146 |
|
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(); |
157 | 149 | } |
158 | 150 |
|
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(); |
161 | 166 | } |
162 | 167 |
|
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(); |
169 | 181 | } |
0 commit comments