Line data Source code
1 : #include "test_helpers.h"
2 : #include "logger.h"
3 : #include "fs_util.h"
4 : #include "raii.h"
5 : #include <stdio.h>
6 : #include <string.h>
7 : #include <stdlib.h>
8 : #include <unistd.h>
9 : #include <sys/stat.h>
10 :
11 1 : void test_logger(void) {
12 1 : const char *test_log_dir = "/tmp/tg-cli-log-test";
13 1 : const char *test_log_file = "/tmp/tg-cli-log-test/test.log";
14 1 : const char *rotated_log_file = "/tmp/tg-cli-log-test/test.log.1";
15 :
16 : // 1. Prepare directory
17 1 : fs_mkdir_p(test_log_dir, 0700);
18 :
19 : // 2. Test Init
20 1 : int res = logger_init(test_log_file, LOG_DEBUG);
21 1 : ASSERT(res == 0, "logger_init should return 0");
22 :
23 : // 3. Test Logging at various levels
24 1 : logger_log(LOG_DEBUG, "Test debug message");
25 1 : logger_log(LOG_INFO, "Test info message");
26 1 : logger_log(LOG_WARN, "Test warn message");
27 1 : logger_log(LOG_ERROR, "Test error message");
28 :
29 : // 4. Verify file exists and has content
30 : struct stat st;
31 1 : ASSERT(stat(test_log_file, &st) == 0, "Log file should exist");
32 1 : ASSERT(st.st_size > 0, "Log file should not be empty");
33 :
34 : // 4a. FEAT-21: log file must be mode 0600 (never world-readable)
35 1 : ASSERT((st.st_mode & 0777) == 0600,
36 : "Log file must have permissions 0600");
37 :
38 : // 4b. FEAT-21: logs directory must be mode 0700
39 : struct stat dir_st;
40 1 : ASSERT(stat(test_log_dir, &dir_st) == 0, "Log directory should exist");
41 1 : ASSERT((dir_st.st_mode & 0777) == 0700,
42 : "Log directory must have permissions 0700");
43 :
44 : // 5. Test level filtering: reinit with LOG_ERROR, lower-level messages suppressed
45 1 : logger_close();
46 1 : res = logger_init(test_log_file, LOG_ERROR);
47 1 : ASSERT(res == 0, "logger_init with LOG_ERROR should succeed");
48 1 : logger_log(LOG_DEBUG, "This should be filtered out");
49 1 : logger_log(LOG_INFO, "This should be filtered out");
50 1 : logger_log(LOG_WARN, "This should be filtered out");
51 :
52 : // 6. Test Clean Logs
53 : {
54 2 : RAII_FILE FILE *f = fopen("/tmp/tg-cli-log-test/session.log.old", "w");
55 1 : if (f) {
56 1 : fprintf(f, "old data");
57 : }
58 : }
59 1 : res = logger_clean_logs(test_log_dir);
60 1 : ASSERT(res == 0, "logger_clean_logs should return 0");
61 1 : ASSERT(access("/tmp/tg-cli-log-test/session.log.old", F_OK) == -1,
62 : "Old log should be deleted");
63 :
64 : // 7. Close and verify logger_log with NULL fp does not crash
65 1 : logger_close();
66 1 : logger_log(LOG_ERROR, "Should not crash with NULL fp");
67 :
68 : // 8. Test logger_init with invalid (non-existent) path
69 1 : res = logger_init("/nonexistent/dir/path/test.log", LOG_INFO);
70 1 : ASSERT(res == -1, "logger_init should return -1 for invalid path");
71 :
72 : // 9. Test logger_clean_logs with non-existent directory
73 1 : res = logger_clean_logs("/nonexistent/dir/path");
74 1 : ASSERT(res == -1, "logger_clean_logs should return -1 for non-existent dir");
75 :
76 : // QA-16 guard: calling logger_init twice must not leak the previous
77 : // path+fd. Valgrind would flag these as lost blocks without the fix.
78 1 : res = logger_init(test_log_file, LOG_INFO);
79 1 : ASSERT(res == 0, "logger_init first call ok");
80 1 : res = logger_init(test_log_file, LOG_INFO);
81 1 : ASSERT(res == 0, "logger_init second call ok — no leak");
82 1 : logger_close();
83 :
84 : // 4c. FEAT-21: opening a pre-existing file with wrong permissions fixes them
85 1 : unlink(test_log_file);
86 : {
87 2 : RAII_FILE FILE *f = fopen(test_log_file, "w");
88 1 : if (f) fprintf(f, "existing\n");
89 : }
90 1 : chmod(test_log_file, 0644); /* set overly-permissive mode */
91 1 : res = logger_init(test_log_file, LOG_INFO);
92 1 : ASSERT(res == 0, "logger_init should succeed on pre-existing file");
93 : {
94 : struct stat fix_st;
95 1 : ASSERT(stat(test_log_file, &fix_st) == 0, "Pre-existing log should exist");
96 1 : ASSERT((fix_st.st_mode & 0777) == 0600,
97 : "Pre-existing log must be fixed to 0600 by logger_init");
98 : }
99 1 : logger_close();
100 :
101 : // 10. Test log rotation: create a file > 5MB, then init should rotate it
102 1 : unlink(test_log_file);
103 : {
104 2 : RAII_FILE FILE *big = fopen(test_log_file, "wb");
105 1 : if (big) {
106 1 : fseek(big, 5 * 1024 * 1024, SEEK_SET);
107 1 : fputc('\0', big);
108 : }
109 : }
110 1 : res = logger_init(test_log_file, LOG_INFO);
111 1 : ASSERT(res == 0, "logger_init should succeed after rotating oversized log");
112 1 : ASSERT(stat(test_log_file, &st) == 0, "Log file should exist after rotation");
113 1 : ASSERT(st.st_size < 5 * 1024 * 1024,
114 : "Current log should be small after rotation");
115 1 : ASSERT(access(rotated_log_file, F_OK) == 0,
116 : "Rotated log file should exist");
117 1 : logger_close();
118 :
119 : // Manual cleanup
120 1 : unlink(test_log_file);
121 1 : unlink(rotated_log_file);
122 1 : rmdir(test_log_dir);
123 : }
|