amduat-api/tests/test_amduatd_space_workspace.c
2026-01-24 21:43:40 +01:00

399 lines
13 KiB
C

#ifndef _POSIX_C_SOURCE
#define _POSIX_C_SOURCE 200809L
#endif
#include "amduatd_space_workspace.h"
#include "amduatd_fed_cursor.h"
#include "amduatd_space.h"
#include "amduatd_space_manifest.h"
#include "amduatd_store.h"
#include "amduat/asl/asl_pointer_fs.h"
#include "amduat/asl/asl_store_fs_meta.h"
#include "amduat/asl/record.h"
#include "amduat/asl/ref_text.h"
#include "amduat/hash/asl1.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int failures = 0;
static void expect(bool cond, const char *msg) {
if (!cond) {
fprintf(stderr, "FAIL: %s\n", msg);
failures++;
}
}
static char *amduatd_test_make_temp_dir(void) {
char tmpl[] = "/tmp/amduatd-space-workspace-XXXXXX";
char *dir = mkdtemp(tmpl);
size_t len;
char *copy;
if (dir == NULL) {
perror("mkdtemp");
return NULL;
}
len = strlen(dir);
copy = (char *)malloc(len + 1u);
if (copy == NULL) {
fprintf(stderr, "failed to allocate temp dir copy\n");
return NULL;
}
memcpy(copy, dir, len + 1u);
return copy;
}
static bool amduatd_make_test_ref(uint8_t fill, amduat_reference_t *out_ref) {
uint8_t digest_bytes[32];
amduat_octets_t digest;
if (out_ref == NULL) {
return false;
}
memset(digest_bytes, fill, sizeof(digest_bytes));
if (!amduat_octets_clone(amduat_octets(digest_bytes, sizeof(digest_bytes)),
&digest)) {
return false;
}
*out_ref = amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256, digest);
return true;
}
static int amduatd_test_workspace_missing(void) {
char *root = amduatd_test_make_temp_dir();
amduat_asl_store_fs_config_t cfg;
amduatd_store_ctx_t store_ctx;
amduat_asl_store_t store;
amduat_asl_pointer_store_t pointer_store;
amduatd_space_t space;
amduatd_fed_cfg_t fed_cfg;
char *workspace_json = NULL;
size_t workspace_len = 0u;
amduatd_space_workspace_status_t status;
if (root == NULL) {
return 1;
}
memset(&cfg, 0, sizeof(cfg));
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
fprintf(stderr, "failed to init store root\n");
free(root);
return 1;
}
memset(&store_ctx, 0, sizeof(store_ctx));
memset(&store, 0, sizeof(store));
if (!amduatd_store_init(&store,
&cfg,
&store_ctx,
root,
AMDUATD_STORE_BACKEND_FS)) {
fprintf(stderr, "failed to init store\n");
free(root);
return 1;
}
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
fprintf(stderr, "failed to init pointer store\n");
free(root);
return 1;
}
if (!amduatd_space_init(&space, "alpha", false)) {
fprintf(stderr, "failed to init space\n");
free(root);
return 1;
}
amduatd_fed_cfg_init(&fed_cfg);
status = amduatd_space_workspace_get(&store,
&pointer_store,
&space,
&fed_cfg,
AMDUATD_STORE_BACKEND_FS,
&workspace_json,
&workspace_len);
expect(status == AMDUATD_SPACE_WORKSPACE_ERR_NOT_FOUND,
"missing manifest returns not found");
expect(workspace_json == NULL, "workspace json unset on missing");
free(root);
return failures == 0 ? 0 : 1;
}
static int amduatd_test_workspace_snapshot(void) {
char *root = amduatd_test_make_temp_dir();
amduat_asl_store_fs_config_t cfg;
amduatd_store_ctx_t store_ctx;
amduat_asl_store_t store;
amduat_asl_pointer_store_t pointer_store;
amduatd_space_t space;
amduat_reference_t pinned_ref;
char *pinned_hex = NULL;
char payload[512];
amduat_reference_t record_ref;
amduat_octets_t pointer_name = amduat_octets(NULL, 0u);
bool swapped = false;
amduatd_fed_cursor_record_t cursor;
amduat_reference_t cursor_ref;
amduat_reference_t cursor_last_ref;
char *cursor_last_hex = NULL;
char *manifest_ref_hex = NULL;
amduatd_fed_cfg_t fed_cfg;
char *workspace_json = NULL;
size_t workspace_len = 0u;
char *workspace_json_2 = NULL;
size_t workspace_len_2 = 0u;
amduatd_space_workspace_status_t status;
const char *m0 = NULL;
const char *m1 = NULL;
const char *m2 = NULL;
if (root == NULL) {
return 1;
}
memset(&cfg, 0, sizeof(cfg));
if (!amduat_asl_store_fs_init_root(root, NULL, &cfg)) {
fprintf(stderr, "failed to init store root\n");
free(root);
return 1;
}
memset(&store_ctx, 0, sizeof(store_ctx));
memset(&store, 0, sizeof(store));
if (!amduatd_store_init(&store,
&cfg,
&store_ctx,
root,
AMDUATD_STORE_BACKEND_FS)) {
fprintf(stderr, "failed to init store\n");
free(root);
return 1;
}
if (!amduat_asl_pointer_store_init(&pointer_store, root)) {
fprintf(stderr, "failed to init pointer store\n");
free(root);
return 1;
}
if (!amduatd_space_init(&space, "alpha", false)) {
fprintf(stderr, "failed to init space\n");
free(root);
return 1;
}
if (!amduatd_make_test_ref(0x11, &pinned_ref)) {
fprintf(stderr, "failed to make pinned ref\n");
free(root);
return 1;
}
if (!amduat_asl_ref_encode_hex(pinned_ref, &pinned_hex)) {
fprintf(stderr, "failed to encode pinned ref\n");
amduat_reference_free(&pinned_ref);
free(root);
return 1;
}
{
int n = snprintf(
payload,
sizeof(payload),
"{"
"\"version\":1,"
"\"mounts\":["
"{\"name\":\"beta\",\"peer_key\":\"peer-2\",\"space_id\":\"zeta\","
"\"mode\":\"track\"},"
"{\"name\":\"alpha\",\"peer_key\":\"peer-1\",\"space_id\":\"zeta\","
"\"mode\":\"pinned\",\"pinned_root_ref\":\"%s\"},"
"{\"name\":\"alpha\",\"peer_key\":\"peer-1\",\"space_id\":\"beta\","
"\"mode\":\"track\"}"
"]"
"}",
pinned_hex);
if (n <= 0 || (size_t)n >= sizeof(payload)) {
fprintf(stderr, "failed to build manifest payload\n");
free(pinned_hex);
amduat_reference_free(&pinned_ref);
free(root);
return 1;
}
}
free(pinned_hex);
if (amduat_asl_record_store_put(
&store,
amduat_octets(AMDUATD_SPACE_MANIFEST_1,
strlen(AMDUATD_SPACE_MANIFEST_1)),
amduat_octets((const uint8_t *)payload, strlen(payload)),
&record_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "failed to store manifest record\n");
amduat_reference_free(&pinned_ref);
free(root);
return 1;
}
if (!amduatd_space_scope_name(&space, "manifest/head", &pointer_name)) {
fprintf(stderr, "failed to build manifest pointer name\n");
amduat_reference_free(&record_ref);
amduat_reference_free(&pinned_ref);
free(root);
return 1;
}
if (amduat_asl_pointer_cas(&pointer_store,
(const char *)pointer_name.data,
false,
NULL,
&record_ref,
&swapped) != AMDUAT_ASL_POINTER_OK ||
!swapped) {
fprintf(stderr, "failed to set manifest pointer\n");
amduat_octets_free(&pointer_name);
amduat_reference_free(&record_ref);
amduat_reference_free(&pinned_ref);
free(root);
return 1;
}
amduat_octets_free(&pointer_name);
if (!amduatd_make_test_ref(0x22, &cursor_last_ref)) {
fprintf(stderr, "failed to make cursor ref\n");
amduat_reference_free(&record_ref);
amduat_reference_free(&pinned_ref);
free(root);
return 1;
}
if (!amduat_asl_ref_encode_hex(cursor_last_ref, &cursor_last_hex)) {
fprintf(stderr, "failed to encode cursor ref\n");
amduat_reference_free(&cursor_last_ref);
amduat_reference_free(&record_ref);
amduat_reference_free(&pinned_ref);
free(root);
return 1;
}
amduatd_fed_cursor_record_init(&cursor);
cursor.peer_key = strdup("peer-1");
cursor.space_id = strdup("alpha");
cursor.has_logseq = true;
cursor.last_logseq = 42u;
cursor.has_record_ref = true;
cursor.last_record_ref = cursor_last_ref;
memset(&cursor_ref, 0, sizeof(cursor_ref));
if (amduatd_fed_cursor_cas_set_remote(&store,
&pointer_store,
&space,
"peer-1",
"beta",
NULL,
&cursor,
&cursor_ref) !=
AMDUATD_FED_CURSOR_OK) {
fprintf(stderr, "failed to set cursor\n");
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&cursor_ref);
amduat_reference_free(&record_ref);
amduat_reference_free(&pinned_ref);
free(cursor_last_hex);
free(root);
return 1;
}
amduatd_fed_cursor_record_free(&cursor);
amduat_reference_free(&cursor_ref);
if (!amduat_asl_ref_encode_hex(record_ref, &manifest_ref_hex)) {
fprintf(stderr, "failed to encode manifest ref\n");
amduat_reference_free(&record_ref);
amduat_reference_free(&pinned_ref);
free(cursor_last_hex);
free(root);
return 1;
}
amduatd_fed_cfg_init(&fed_cfg);
status = amduatd_space_workspace_get(&store,
&pointer_store,
&space,
&fed_cfg,
AMDUATD_STORE_BACKEND_FS,
&workspace_json,
&workspace_len);
expect(status == AMDUATD_SPACE_WORKSPACE_OK, "workspace snapshot ok");
expect(workspace_json != NULL && workspace_len != 0u,
"workspace json populated");
status = amduatd_space_workspace_get(&store,
&pointer_store,
&space,
&fed_cfg,
AMDUATD_STORE_BACKEND_FS,
&workspace_json_2,
&workspace_len_2);
expect(status == AMDUATD_SPACE_WORKSPACE_OK, "workspace snapshot ok (repeat)");
expect(workspace_json_2 != NULL && workspace_len_2 != 0u,
"workspace json populated (repeat)");
if (workspace_json != NULL && workspace_json_2 != NULL) {
expect(workspace_len == workspace_len_2,
"workspace output length deterministic");
if (workspace_len == workspace_len_2) {
expect(memcmp(workspace_json, workspace_json_2, workspace_len) == 0,
"workspace output deterministic");
}
}
if (workspace_json != NULL) {
m0 = strstr(
workspace_json,
"\"name\":\"alpha\",\"peer_key\":\"peer-1\",\"remote_space_id\":\"beta\"");
m1 = strstr(
workspace_json,
"\"name\":\"alpha\",\"peer_key\":\"peer-1\",\"remote_space_id\":\"zeta\"");
m2 = strstr(
workspace_json,
"\"name\":\"beta\",\"peer_key\":\"peer-2\",\"remote_space_id\":\"zeta\"");
expect(m0 != NULL && m1 != NULL && m2 != NULL,
"workspace mounts include all entries");
if (m0 != NULL && m1 != NULL && m2 != NULL) {
expect(m0 < m1 && m1 < m2, "workspace mounts are in canonical order");
}
expect(strstr(workspace_json, "\"mode\":\"pinned\"") != NULL,
"pinned mode present");
expect(strstr(workspace_json, "\"pinned_root_ref\":\"") != NULL,
"pinned root ref present");
expect(strstr(workspace_json, "\"cursor_keying\":\"none\"") != NULL,
"pinned cursor keying present");
expect(strstr(workspace_json, "\"cursor_keying\":\"v2\"") != NULL,
"track cursor keying present");
expect(strstr(workspace_json, "\"pull_cursor\":{\"present\":true") != NULL,
"track cursor present true");
expect(strstr(workspace_json, "\"pull_cursor\":{\"present\":false") != NULL,
"pinned cursor present false");
expect(strstr(workspace_json, "\"last_logseq\":42") != NULL,
"cursor last_logseq present");
expect(strstr(workspace_json, cursor_last_hex) != NULL,
"cursor ref present");
expect(strstr(workspace_json, manifest_ref_hex) != NULL,
"manifest ref present");
expect(strstr(workspace_json, "\"store_backend\":\"fs\"") != NULL,
"store backend present");
expect(strstr(workspace_json, "\"transport\":\"stub\"") != NULL,
"federation transport present");
}
free(workspace_json);
free(workspace_json_2);
free(manifest_ref_hex);
amduat_reference_free(&record_ref);
amduat_reference_free(&pinned_ref);
free(cursor_last_hex);
free(root);
return failures == 0 ? 0 : 1;
}
int main(void) {
if (amduatd_test_workspace_missing() != 0) {
return 1;
}
if (amduatd_test_workspace_snapshot() != 0) {
return 1;
}
return failures == 0 ? 0 : 1;
}