Line data Source code
1 : #include "test_helpers.h"
2 : #include "cache_store.h"
3 : #include "fs_util.h"
4 : #include "raii.h"
5 : #include <string.h>
6 : #include <stdlib.h>
7 : #include <unistd.h>
8 : #include <sys/stat.h>
9 :
10 1 : static void test_cache_basic(void) {
11 1 : char *old_home = getenv("HOME");
12 1 : setenv("HOME", "/tmp/tg-cli-cache-test-home", 1);
13 :
14 1 : const char *category = "messages";
15 1 : const char *key = "12345/42";
16 :
17 : /* 1. Not cached initially */
18 1 : ASSERT(cache_exists(category, key) == 0, "cache_exists: should be 0 before save");
19 :
20 : {
21 2 : RAII_STRING char *loaded = cache_load(category, key);
22 1 : ASSERT(loaded == NULL, "cache_load: should return NULL before save");
23 : }
24 :
25 : /* 2. Save and verify existence */
26 1 : const char *content = "{\"text\":\"Cache Test\",\"chat_id\":12345}";
27 1 : int rc = cache_save(category, key, content, strlen(content));
28 1 : ASSERT(rc == 0, "cache_save: should return 0 on success");
29 1 : ASSERT(cache_exists(category, key) == 1, "cache_exists: should be 1 after save");
30 :
31 : /* 3. Load and verify content */
32 : {
33 2 : RAII_STRING char *loaded = cache_load(category, key);
34 1 : ASSERT(loaded != NULL, "cache_load: should not be NULL after save");
35 1 : ASSERT(strcmp(loaded, content) == 0, "cache_load: content mismatch");
36 : }
37 :
38 : /* 4. Overwrite with new content */
39 1 : const char *content2 = "{\"text\":\"Updated\",\"chat_id\":12345}";
40 1 : rc = cache_save(category, key, content2, strlen(content2));
41 1 : ASSERT(rc == 0, "cache_save: overwrite should return 0");
42 : {
43 2 : RAII_STRING char *loaded = cache_load(category, key);
44 1 : ASSERT(loaded != NULL, "cache_load: should not be NULL after overwrite");
45 1 : ASSERT(strcmp(loaded, content2) == 0, "cache_load: overwritten content mismatch");
46 : }
47 :
48 : /* 5. Different keys are independent */
49 1 : ASSERT(cache_exists(category, "12345/99") == 0, "cache_exists: different key should not exist");
50 :
51 : /* Cleanup */
52 2 : RAII_STRING char *path = cache_path(category, key);
53 1 : if (path) unlink(path);
54 :
55 1 : if (old_home) setenv("HOME", old_home, 1);
56 0 : else unsetenv("HOME");
57 : }
58 :
59 : /* ---- cache_load: NULL/missing paths ---- */
60 1 : static void test_cache_load_missing(void) {
61 1 : char *old_home = getenv("HOME");
62 1 : setenv("HOME", "/tmp/tg-cli-cache-missing", 1);
63 :
64 : /* no file exists → NULL */
65 2 : RAII_STRING char *v = cache_load("messages", "nonexistent/9999");
66 1 : ASSERT(v == NULL, "cache_load: missing file must return NULL");
67 :
68 1 : if (old_home) setenv("HOME", old_home, 1);
69 0 : else unsetenv("HOME");
70 : }
71 :
72 : /* ---- cache_save: failing to create parent dir returns -1 ---- */
73 1 : static void test_cache_save_mkdir_fails(void) {
74 1 : char *old_home = getenv("HOME");
75 : /* Create a regular file where tg-cli would place its cache dir so
76 : * fs_mkdir_p fails. */
77 1 : const char *bad_home = "/tmp/tg-cli-cache-bad-home";
78 : /* Clean slate */
79 : {
80 : char path[256];
81 1 : snprintf(path, sizeof(path), "%s/.cache/tg-cli/media", bad_home);
82 1 : (void)remove(path);
83 : }
84 1 : mkdir(bad_home, 0700);
85 : char file_in_way[256];
86 1 : snprintf(file_in_way, sizeof(file_in_way), "%s/.cache", bad_home);
87 : /* Make ".cache" a regular file → mkdir_p will fail */
88 1 : FILE *f = fopen(file_in_way, "w");
89 1 : if (f) { fputs("x", f); fclose(f); }
90 :
91 1 : setenv("HOME", bad_home, 1);
92 :
93 1 : int rc = cache_save("media", "key1", "data", 4);
94 1 : ASSERT(rc == -1, "cache_save: must fail when parent cannot be created");
95 :
96 : /* cleanup */
97 1 : unlink(file_in_way);
98 1 : rmdir(bad_home);
99 :
100 1 : if (old_home) setenv("HOME", old_home, 1);
101 0 : else unsetenv("HOME");
102 : }
103 :
104 : /* ---- cache_evict_stale: removes entries not in keep-list ---- */
105 1 : static void test_cache_evict_stale(void) {
106 1 : char *old_home = getenv("HOME");
107 1 : setenv("HOME", "/tmp/tg-cli-cache-evict", 1);
108 :
109 : /* Populate 3 entries */
110 1 : const char *cat = "messages";
111 1 : cache_save(cat, "a", "aa", 2);
112 1 : cache_save(cat, "b", "bb", 2);
113 1 : cache_save(cat, "c", "cc", 2);
114 1 : ASSERT(cache_exists(cat, "a") == 1, "a exists");
115 1 : ASSERT(cache_exists(cat, "b") == 1, "b exists");
116 1 : ASSERT(cache_exists(cat, "c") == 1, "c exists");
117 :
118 : /* Keep only "b" — "a" and "c" should be evicted */
119 1 : const char *keep[] = {"b"};
120 1 : cache_evict_stale(cat, keep, 1);
121 :
122 1 : ASSERT(cache_exists(cat, "a") == 0, "a evicted");
123 1 : ASSERT(cache_exists(cat, "b") == 1, "b preserved");
124 1 : ASSERT(cache_exists(cat, "c") == 0, "c evicted");
125 :
126 : /* cleanup */
127 2 : RAII_STRING char *pb = cache_path(cat, "b");
128 1 : if (pb) unlink(pb);
129 :
130 1 : if (old_home) setenv("HOME", old_home, 1);
131 0 : else unsetenv("HOME");
132 : }
133 :
134 : /* ---- cache_evict_stale: empty keep list removes everything ---- */
135 1 : static void test_cache_evict_stale_empty_keep(void) {
136 1 : char *old_home = getenv("HOME");
137 1 : setenv("HOME", "/tmp/tg-cli-cache-evict2", 1);
138 :
139 1 : const char *cat = "media";
140 1 : cache_save(cat, "x", "x", 1);
141 1 : cache_save(cat, "y", "y", 1);
142 1 : ASSERT(cache_exists(cat, "x") == 1, "x exists");
143 :
144 1 : const char *keep[] = {"__sentinel__"}; /* keep list with no matches */
145 1 : cache_evict_stale(cat, keep, 1);
146 :
147 1 : ASSERT(cache_exists(cat, "x") == 0, "x evicted");
148 1 : ASSERT(cache_exists(cat, "y") == 0, "y evicted");
149 :
150 1 : if (old_home) setenv("HOME", old_home, 1);
151 0 : else unsetenv("HOME");
152 : }
153 :
154 : /* ---- cache_evict_stale: nonexistent category is a no-op ---- */
155 1 : static void test_cache_evict_stale_no_dir(void) {
156 1 : char *old_home = getenv("HOME");
157 1 : setenv("HOME", "/tmp/tg-cli-cache-evict-nodir", 1);
158 :
159 1 : const char *keep[] = {"a"};
160 : /* Directory does not exist → function should return without error */
161 1 : cache_evict_stale("nonexistent_category", keep, 1);
162 :
163 1 : if (old_home) setenv("HOME", old_home, 1);
164 0 : else unsetenv("HOME");
165 1 : }
166 :
167 1 : void test_cache_store(void) {
168 1 : RUN_TEST(test_cache_basic);
169 1 : RUN_TEST(test_cache_load_missing);
170 1 : RUN_TEST(test_cache_save_mkdir_fails);
171 1 : RUN_TEST(test_cache_evict_stale);
172 1 : RUN_TEST(test_cache_evict_stale_empty_keep);
173 1 : RUN_TEST(test_cache_evict_stale_no_dir);
174 1 : }
|