init
This commit is contained in:
commit
e54390a9f8
85 files changed
+29923
No files matched your search
@@ -0,0 +1,178 @@
|
||||
/* test_archive.c - 存档命名规则与读写往返(不需要网络) */
|
||||
#include "archive.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
static int failures = 0;
|
||||
static int checks = 0;
|
||||
|
||||
#define CHECK(cond, ...) \
|
||||
do { \
|
||||
checks++; \
|
||||
if (!(cond)) { \
|
||||
failures++; \
|
||||
printf(" ✗ %s:%d: ", __FILE__, __LINE__); \
|
||||
printf(__VA_ARGS__); \
|
||||
printf("\n"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static int is_alnum_lower(const char *s, size_t n)
|
||||
{
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (!((s[i] >= 'a' && s[i] <= 'z') || (s[i] >= '0' && s[i] <= '9')))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char tmpl[] = "/tmp/ligent_arch_XXXXXX";
|
||||
char *dir = mkdtemp(tmpl);
|
||||
if (!dir) { perror("mkdtemp"); return 2; }
|
||||
char err[256];
|
||||
|
||||
puts("根名:8 位小写字母数字");
|
||||
char *n1 = NULL;
|
||||
CHECK(archive_next_name(dir, NULL, NULL, 0, &n1, err, sizeof err) == 0, "生成失败: %s", err);
|
||||
CHECK(n1 && strlen(n1) == ARCHIVE_ROOT_LEN, "根名应为 %d 位,实际 %s",
|
||||
ARCHIVE_ROOT_LEN, n1 ? n1 : "(null)");
|
||||
CHECK(n1 && is_alnum_lower(n1, strlen(n1)), "根名字符集应为 [a-z0-9]:%s", n1);
|
||||
free(n1);
|
||||
|
||||
/* 空字符串与 NULL 等价:都表示"没有来源" */
|
||||
char *n2 = NULL;
|
||||
CHECK(archive_next_name(dir, "", NULL, 0, &n2, err, sizeof err) == 0, "空 lineage 应可用");
|
||||
CHECK(n2 && strlen(n2) == ARCHIVE_ROOT_LEN, "空 lineage 也应给出根名");
|
||||
free(n2);
|
||||
|
||||
puts("分支名:来源 + _ + 3 位");
|
||||
char *b = NULL;
|
||||
CHECK(archive_next_name(dir, "a1b2c3d4", NULL, 0, &b, err, sizeof err) == 0,
|
||||
"分支生成失败: %s", err);
|
||||
CHECK(b && strncmp(b, "a1b2c3d4_", 9) == 0, "应以来源加下划线开头:%s", b);
|
||||
CHECK(b && strlen(b) == 8 + 1 + ARCHIVE_BRANCH_LEN, "分支长度不对:%s", b);
|
||||
CHECK(b && is_alnum_lower(b + 9, ARCHIVE_BRANCH_LEN), "后缀字符集不对:%s", b);
|
||||
free(b);
|
||||
|
||||
/* 多次生成应当不同(随机性) */
|
||||
char *x1 = NULL, *x2 = NULL;
|
||||
archive_next_name(dir, "a1b2c3d4", NULL, 0, &x1, err, sizeof err);
|
||||
archive_next_name(dir, "a1b2c3d4", NULL, 0, &x2, err, sizeof err);
|
||||
CHECK(x1 && x2 && strcmp(x1, x2) != 0, "两次生成不应相同:%s / %s", x1, x2);
|
||||
free(x1); free(x2);
|
||||
|
||||
puts("长度上限:接不下就重新起根");
|
||||
/* 边界:来源长度 + 1 + 3 + ".json" <= 240 */
|
||||
size_t maxlineage = ARCHIVE_NAME_MAX - 1 - ARCHIVE_BRANCH_LEN - strlen(".json");
|
||||
char *ok = xmalloc(maxlineage + 1);
|
||||
memset(ok, 'a', maxlineage);
|
||||
ok[maxlineage] = '\0';
|
||||
|
||||
char *edge = NULL;
|
||||
CHECK(archive_next_name(dir, ok, NULL, 0, &edge, err, sizeof err) == 0, "边界生成失败");
|
||||
CHECK(edge && strncmp(edge, "aaa", 3) == 0, "刚好装得下时应继续接分支");
|
||||
CHECK(edge && strlen(edge) + strlen(".json") <= ARCHIVE_NAME_MAX,
|
||||
"完整文件名不应超上限:%zu", edge ? strlen(edge) + 5 : 0);
|
||||
free(edge);
|
||||
|
||||
/* 再多一位就装不下了 */
|
||||
char *too = xmalloc(maxlineage + 2);
|
||||
memset(too, 'a', maxlineage + 1);
|
||||
too[maxlineage + 1] = '\0';
|
||||
char *fresh = NULL;
|
||||
CHECK(archive_next_name(dir, too, NULL, 0, &fresh, err, sizeof err) == 0, "超限生成失败");
|
||||
CHECK(fresh && strlen(fresh) == ARCHIVE_ROOT_LEN,
|
||||
"超限时应退回 %d 位根名,实际 %s(%zu 位)",
|
||||
ARCHIVE_ROOT_LEN, fresh ? fresh : "(null)", fresh ? strlen(fresh) : 0);
|
||||
free(fresh);
|
||||
puts("前缀:临时存档");
|
||||
char *tp = NULL;
|
||||
CHECK(archive_next_name(dir, "a1b2c3d4", "tmp_", 0, &tp, err, sizeof err) == 0,
|
||||
"带前缀生成失败");
|
||||
CHECK(tp && strncmp(tp, "tmp_a1b2c3d4_", 13) == 0,
|
||||
"前缀应在最前面、其后才是分支名:%s", tp ? tp : "(null)");
|
||||
free(tp);
|
||||
|
||||
char *tr = NULL;
|
||||
CHECK(archive_next_name(dir, NULL, "tmp_", 0, &tr, err, sizeof err) == 0, "带前缀建根失败");
|
||||
CHECK(tr && strncmp(tr, "tmp_", 4) == 0 &&
|
||||
strlen(tr) == 4 + ARCHIVE_ROOT_LEN,
|
||||
"新根也应带前缀:%s", tr ? tr : "(null)");
|
||||
free(tr);
|
||||
|
||||
/* 前缀要算进长度上限:原本刚好装得下的,加了 tmp_ 就该退回新根 */
|
||||
char *edgep = NULL;
|
||||
CHECK(archive_next_name(dir, ok, "tmp_", 0, &edgep, err, sizeof err) == 0, "边界失败");
|
||||
CHECK(edgep && strlen(edgep) == 4 + ARCHIVE_ROOT_LEN,
|
||||
"加了前缀装不下时应退回根名,实际 %s(%zu 位)",
|
||||
edgep ? edgep : "(null)", edgep ? strlen(edgep) : 0);
|
||||
free(edgep);
|
||||
|
||||
free(ok); free(too);
|
||||
|
||||
puts("导出与载入往返");
|
||||
JsonValue *msgs = json_new_array();
|
||||
JsonValue *m = json_new_object();
|
||||
json_object_set_string(m, "role", "user");
|
||||
json_object_set_string(m, "content", "你好,暗号是紫水晶");
|
||||
json_array_push(msgs, m);
|
||||
|
||||
char *path = NULL, *name = NULL;
|
||||
time_t t = 1700000000;
|
||||
CHECK(archive_export(msgs, dir, "parentname", NULL, 0, t, t + 5,
|
||||
"test-model", "http://example/v1",
|
||||
&path, &name, err, sizeof err) == 0,
|
||||
"导出失败: %s", err);
|
||||
CHECK(name && strncmp(name, "parentname_", 11) == 0,
|
||||
"导出名应挂在来源下:%s", name ? name : "(null)");
|
||||
CHECK(path && access(path, R_OK) == 0, "文件应存在:%s", path ? path : "");
|
||||
|
||||
char *created = NULL, *saved = NULL, *model = NULL, *iname = NULL, *iparent = NULL;
|
||||
JsonValue *back = archive_load(path, &created, &saved, &model, &iname, &iparent,
|
||||
err, sizeof err);
|
||||
CHECK(back != NULL, "载入失败: %s", err);
|
||||
CHECK(back && json_array_size(back) == 1, "消息条数应为 1");
|
||||
CHECK(iname && name && strcmp(iname, name) == 0, "内容里的 name 应与文件名一致");
|
||||
CHECK(iparent && strcmp(iparent, "parentname") == 0,
|
||||
"内容里的 parent 应记下来源:%s", iparent ? iparent : "(null)");
|
||||
CHECK(model && strcmp(model, "test-model") == 0, "model 应往返");
|
||||
CHECK(created && strstr(created, "2023-") != NULL,
|
||||
"created 应可读:%s", created ? created : "(null)");
|
||||
/* saved 是"这份存档写出时的时间",与 created(这段对话的开始时间)是两回事 */
|
||||
CHECK(saved && strstr(saved, "2023-") != NULL,
|
||||
"saved 应可读:%s", saved ? saved : "(null)");
|
||||
|
||||
json_free(back); json_free(msgs);
|
||||
free(created); free(saved); free(model); free(iname); free(iparent);
|
||||
|
||||
puts("列举");
|
||||
ArchiveEntry *list = NULL;
|
||||
size_t n = archive_list(dir, &list);
|
||||
CHECK(n == 1, "应有 1 份存档,实际 %zu", n);
|
||||
if (n == 1) {
|
||||
CHECK(strncmp(list[0].name, "parentname_", 11) == 0, "名字应保留");
|
||||
CHECK(list[0].count == 1, "条数应为 1,实际 %ld", list[0].count);
|
||||
CHECK(list[0].created != NULL, "时间应从内容里读出来");
|
||||
}
|
||||
archive_list_free(list, n);
|
||||
|
||||
/* 非 ligent 存档(没有 format 标记)应被忽略 */
|
||||
char *jd = xasprintf("%s/junk.json", dir);
|
||||
FILE *j2 = fopen(jd, "w");
|
||||
if (j2) { fputs("{\"hello\":1}", j2); fclose(j2); }
|
||||
n = archive_list(dir, &list);
|
||||
CHECK(n == 1, "非 ligent 存档应被忽略,实际 %zu", n);
|
||||
archive_list_free(list, n);
|
||||
unlink(jd); free(jd);
|
||||
|
||||
unlink(path); free(path); free(name);
|
||||
rmdir(dir);
|
||||
printf("\n%d 项检查,%d 项失败\n", checks, failures);
|
||||
return failures ? 1 : 0;
|
||||
}
|
||||
Reference in new issue
Block a user