#ifndef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 200809L #endif #include "amduatd_space_mounts_sync.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/ref_text.h" #include "amduat/hash/asl1.h" #include #include #include #include typedef struct { size_t calls; } amduatd_test_sync_transport_t; 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-mounts-sync-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 bool amduatd_test_get_records(void *ctx, uint32_t domain_id, uint64_t from_logseq, uint64_t limit, int *out_status, amduat_fed_record_t **out_records, size_t *out_len, char **out_body) { amduatd_test_sync_transport_t *t = (amduatd_test_sync_transport_t *)ctx; (void)domain_id; (void)from_logseq; (void)limit; if (out_status == NULL || out_records == NULL || out_len == NULL) { return false; } *out_status = 200; *out_records = NULL; *out_len = 0u; if (out_body != NULL) { *out_body = NULL; } if (t != NULL) { t->calls++; } return true; } static void amduatd_test_free_records(void *ctx, amduat_fed_record_t *records, size_t len) { (void)ctx; (void)records; (void)len; } static bool amduatd_test_get_artifact(void *ctx, amduat_reference_t ref, int *out_status, amduat_octets_t *out_bytes, char **out_body) { (void)ctx; (void)ref; if (out_status == NULL || out_bytes == NULL) { return false; } *out_status = 404; *out_bytes = amduat_octets(NULL, 0u); if (out_body != NULL) { *out_body = NULL; } return true; } static bool amduatd_test_seed_manifest(amduat_asl_store_t *store, amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *space, const char *json) { amduat_reference_t ref; amduatd_space_manifest_t manifest; amduat_octets_t payload = amduat_octets(json, strlen(json)); amduatd_space_manifest_status_t status; memset(&ref, 0, sizeof(ref)); memset(&manifest, 0, sizeof(manifest)); status = amduatd_space_manifest_put(store, pointer_store, space, payload, NULL, &ref, &manifest); if (status != AMDUATD_SPACE_MANIFEST_OK) { amduatd_space_manifest_free(&manifest); amduat_reference_free(&ref); return false; } amduatd_space_manifest_free(&manifest); amduat_reference_free(&ref); return true; } static bool amduatd_test_seed_cursor(amduat_asl_store_t *store, amduat_asl_pointer_store_t *pointer_store, const amduatd_space_t *space, const char *peer_key, const char *remote_space_id, uint64_t logseq, uint8_t fill) { amduatd_fed_cursor_record_t cursor; amduat_reference_t ref; amduatd_fed_cursor_status_t status; const char *space_id = NULL; bool ok = false; amduatd_fed_cursor_record_init(&cursor); cursor.peer_key = strdup(peer_key); if (space != NULL && space->enabled && space->space_id.data != NULL) { space_id = (const char *)space->space_id.data; } cursor.space_id = space_id != NULL ? strdup(space_id) : NULL; if (cursor.peer_key == NULL || cursor.space_id == NULL) { amduatd_fed_cursor_record_free(&cursor); return false; } cursor.has_logseq = true; cursor.last_logseq = logseq; if (!amduatd_make_test_ref(fill, &ref)) { amduatd_fed_cursor_record_free(&cursor); return false; } cursor.has_record_ref = true; cursor.last_record_ref = ref; status = amduatd_fed_cursor_cas_set_remote(store, pointer_store, space, peer_key, remote_space_id, NULL, &cursor, NULL); ok = (status == AMDUATD_FED_CURSOR_OK); amduatd_fed_cursor_record_free(&cursor); return ok; } static int amduatd_test_sync_missing_manifest(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; amduatd_test_sync_transport_t transport_state; amduatd_fed_pull_transport_t transport; amduatd_space_mounts_sync_report_t report; amduatd_space_mounts_sync_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_INDEX)) { 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); fed_cfg.enabled = true; memset(&transport_state, 0, sizeof(transport_state)); memset(&transport, 0, sizeof(transport)); transport.ctx = &transport_state; transport.get_records = amduatd_test_get_records; transport.free_records = amduatd_test_free_records; transport.get_artifact = amduatd_test_get_artifact; status = amduatd_space_mounts_sync_until(&store, &pointer_store, &space, &fed_cfg, &transport, 128u, 10u, 32u, &report); expect(status == AMDUATD_SPACE_MOUNTS_SYNC_ERR_NOT_FOUND, "missing manifest"); free(root); return failures == 0 ? 0 : 1; } static int amduatd_test_sync_no_track_mounts(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; amduatd_test_sync_transport_t transport_state; amduatd_fed_pull_transport_t transport; amduat_reference_t pinned_ref; char *pinned_hex = NULL; char json[512]; int n; amduatd_space_mounts_sync_report_t report; amduatd_space_mounts_sync_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_INDEX)) { 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); fed_cfg.enabled = true; if (!amduatd_make_test_ref(0x11, &pinned_ref) || !amduat_asl_ref_encode_hex(pinned_ref, &pinned_hex)) { fprintf(stderr, "failed to build pinned ref\n"); amduat_reference_free(&pinned_ref); free(root); return 1; } n = snprintf(json, sizeof(json), "{\"version\":1,\"mounts\":[{\"name\":\"p1\"," "\"peer_key\":\"1\",\"space_id\":\"beta\"," "\"mode\":\"pinned\",\"pinned_root_ref\":\"%s\"}]}", pinned_hex); free(pinned_hex); amduat_reference_free(&pinned_ref); if (n <= 0 || (size_t)n >= sizeof(json)) { fprintf(stderr, "failed to format manifest json\n"); free(root); return 1; } if (!amduatd_test_seed_manifest(&store, &pointer_store, &space, json)) { fprintf(stderr, "failed to seed manifest\n"); free(root); return 1; } memset(&transport_state, 0, sizeof(transport_state)); memset(&transport, 0, sizeof(transport)); transport.ctx = &transport_state; transport.get_records = amduatd_test_get_records; transport.free_records = amduatd_test_free_records; transport.get_artifact = amduatd_test_get_artifact; status = amduatd_space_mounts_sync_until(&store, &pointer_store, &space, &fed_cfg, &transport, 128u, 10u, 32u, &report); expect(status == AMDUATD_SPACE_MOUNTS_SYNC_OK, "sync no-track ok"); expect(report.mounts_total == 0u, "no-track total"); expect(report.mounts_synced == 0u, "no-track synced"); expect(report.ok, "no-track ok flag"); expect(report.results_json != NULL && strcmp(report.results_json, "[]") == 0, "no-track results empty"); amduatd_space_mounts_sync_report_free(&report); free(root); return failures == 0 ? 0 : 1; } static int amduatd_test_sync_two_mounts(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; amduatd_test_sync_transport_t transport_state; amduatd_fed_pull_transport_t transport; const char *json = "{\"version\":1,\"mounts\":[" "{\"name\":\"b\",\"peer_key\":\"2\",\"space_id\":\"beta\",\"mode\":\"track\"}," "{\"name\":\"a\",\"peer_key\":\"1\",\"space_id\":\"alpha\",\"mode\":\"track\"}" "]}"; amduatd_space_mounts_sync_report_t report; amduatd_space_mounts_sync_status_t status; const char *first_a = NULL; const char *second_b = 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_INDEX)) { 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); fed_cfg.enabled = true; if (!amduatd_test_seed_manifest(&store, &pointer_store, &space, json)) { fprintf(stderr, "failed to seed manifest\n"); free(root); return 1; } if (!amduatd_test_seed_cursor(&store, &pointer_store, &space, "1", "alpha", 5u, 0x22) || !amduatd_test_seed_cursor(&store, &pointer_store, &space, "2", "beta", 7u, 0x33)) { fprintf(stderr, "failed to seed cursors\n"); free(root); return 1; } memset(&transport_state, 0, sizeof(transport_state)); memset(&transport, 0, sizeof(transport)); transport.ctx = &transport_state; transport.get_records = amduatd_test_get_records; transport.free_records = amduatd_test_free_records; transport.get_artifact = amduatd_test_get_artifact; status = amduatd_space_mounts_sync_until(&store, &pointer_store, &space, &fed_cfg, &transport, 128u, 1u, 32u, &report); expect(status == AMDUATD_SPACE_MOUNTS_SYNC_OK, "sync two ok"); expect(report.mounts_total == 2u, "sync two total"); expect(report.mounts_synced == 2u, "sync two synced"); expect(report.ok, "sync two ok flag"); expect(transport_state.calls == 2u, "sync two transport calls"); expect(report.results_json != NULL, "sync two results present"); if (report.results_json != NULL) { first_a = strstr(report.results_json, "\"name\":\"a\""); second_b = strstr(report.results_json, "\"name\":\"b\""); } expect(first_a != NULL && second_b != NULL && first_a < second_b, "sync results canonical order"); expect(strstr(report.results_json, "\"last_logseq\":5") != NULL, "sync cursor logseq a"); expect(strstr(report.results_json, "\"last_logseq\":7") != NULL, "sync cursor logseq b"); amduatd_space_mounts_sync_report_free(&report); free(root); return failures == 0 ? 0 : 1; } static int amduatd_test_sync_partial_error(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; amduatd_test_sync_transport_t transport_state; amduatd_fed_pull_transport_t transport; const char *json = "{\"version\":1,\"mounts\":[" "{\"name\":\"a\",\"peer_key\":\"peer-a\",\"space_id\":\"alpha\"," "\"mode\":\"track\"}," "{\"name\":\"b\",\"peer_key\":\"2\",\"space_id\":\"beta\",\"mode\":\"track\"}" "]}"; amduatd_space_mounts_sync_report_t report; amduatd_space_mounts_sync_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_INDEX)) { 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); fed_cfg.enabled = true; if (!amduatd_test_seed_manifest(&store, &pointer_store, &space, json)) { fprintf(stderr, "failed to seed manifest\n"); free(root); return 1; } memset(&transport_state, 0, sizeof(transport_state)); memset(&transport, 0, sizeof(transport)); transport.ctx = &transport_state; transport.get_records = amduatd_test_get_records; transport.free_records = amduatd_test_free_records; transport.get_artifact = amduatd_test_get_artifact; status = amduatd_space_mounts_sync_until(&store, &pointer_store, &space, &fed_cfg, &transport, 128u, 1u, 32u, &report); expect(status == AMDUATD_SPACE_MOUNTS_SYNC_OK, "sync partial ok"); expect(!report.ok, "sync partial ok flag false"); expect(report.mounts_total == 2u, "sync partial total"); expect(report.mounts_synced == 2u, "sync partial synced"); expect(transport_state.calls == 1u, "sync partial transport calls"); expect(report.results_json != NULL && strstr(report.results_json, "\"code\":\"invalid_peer\"") != NULL, "sync partial invalid peer"); amduatd_space_mounts_sync_report_free(&report); free(root); return failures == 0 ? 0 : 1; } int main(void) { if (amduatd_test_sync_missing_manifest() != 0) { return 1; } if (amduatd_test_sync_no_track_mounts() != 0) { return 1; } if (amduatd_test_sync_two_mounts() != 0) { return 1; } if (amduatd_test_sync_partial_error() != 0) { return 1; } return failures == 0 ? 0 : 1; }