diff --git a/CMakeLists.txt b/CMakeLists.txt index 30b71ab..94b3162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,6 +251,16 @@ target_link_libraries(amduat_test_tgk_store_mem ) add_test(NAME tgk_store_mem COMMAND amduat_test_tgk_store_mem) +add_executable(amduat_test_tgk_store_fs tests/tgk/test_tgk_store_fs.c) +target_include_directories(amduat_test_tgk_store_fs + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_link_libraries(amduat_test_tgk_store_fs + PRIVATE amduat_tgk_store_fs +) +add_test(NAME tgk_store_fs COMMAND amduat_test_tgk_store_fs) + add_executable(amduat_test_tgk_prov tests/tgk/test_tgk_prov.c) target_include_directories(amduat_test_tgk_prov PRIVATE ${AMDUAT_INTERNAL_DIR} diff --git a/include/amduat/tgk/tgk_store_fs.h b/include/amduat/tgk/tgk_store_fs.h index 387b020..9a0596b 100644 --- a/include/amduat/tgk/tgk_store_fs.h +++ b/include/amduat/tgk/tgk_store_fs.h @@ -3,4 +3,50 @@ /* Filesystem-backed TGK store adapter public API. */ +#include "amduat/asl/asl_store_fs.h" +#include "amduat/asl/store.h" +#include "amduat/format/ref.h" +#include "amduat/tgk/store.h" +#include "amduat/tgk/tgk_store_mem.h" + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* Manifest contract: + * - AMDUAT_FORMAT_REF_HEX: ASCII hex tokens of ASL1 ReferenceBytes, + * separated by whitespace. + * - AMDUAT_FORMAT_REF_BYTES: raw concatenation of ASL1 ReferenceBytes + * (hash_id big-endian + digest). */ +typedef struct { + amduat_tgk_store_mem_t mem; + amduat_tgk_store_mem_artifact_t *artifacts; + size_t artifacts_len; +} amduat_tgk_store_fs_t; + +bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs, + amduat_tgk_store_config_t config, + const char *manifest_path, + amduat_format_ref_format_t manifest_format, + amduat_asl_store_t *asl_store); + +bool amduat_tgk_store_fs_init_with_asl_fs( + amduat_tgk_store_fs_t *fs, + amduat_tgk_store_config_t config, + const char *manifest_path, + amduat_format_ref_format_t manifest_format, + amduat_asl_store_config_t asl_config, + const char *asl_root_path); + +void amduat_tgk_store_fs_free(amduat_tgk_store_fs_t *fs); + +amduat_tgk_store_ops_t amduat_tgk_store_fs_ops(void); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + #endif /* AMDUAT_TGK_TGK_STORE_FS_H */ diff --git a/src/adapters/tgk_store_fs/tgk_store_fs.c b/src/adapters/tgk_store_fs/tgk_store_fs.c index a38803e..8be7897 100644 --- a/src/adapters/tgk_store_fs/tgk_store_fs.c +++ b/src/adapters/tgk_store_fs/tgk_store_fs.c @@ -1,3 +1,268 @@ #include "amduat/tgk/tgk_store_fs.h" -/* TODO: implement filesystem-backed TGK store adapter. */ +#include "amduat/asl/io.h" +#include "amduat/asl/ref_io.h" + +#include +#include + +static void amduat_tgk_store_fs_reference_free(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0; +} + +static void amduat_tgk_store_fs_artifact_bytes_free( + amduat_tgk_store_mem_artifact_t *artifact) { + if (artifact == NULL) { + return; + } + free((void *)artifact->artifact.bytes.data); + artifact->artifact.bytes.data = NULL; + artifact->artifact.bytes.len = 0; + artifact->artifact.has_type_tag = false; + artifact->artifact.type_tag.tag_id = 0; +} + +static void amduat_tgk_store_fs_artifact_full_free( + amduat_tgk_store_mem_artifact_t *artifact) { + if (artifact == NULL) { + return; + } + amduat_tgk_store_fs_artifact_bytes_free(artifact); + amduat_tgk_store_fs_reference_free(&artifact->ref); +} + +static bool amduat_tgk_store_fs_load_manifest( + const char *manifest_path, + amduat_format_ref_format_t manifest_format, + amduat_reference_t **out_refs, + size_t *out_len) { + uint8_t *bytes = NULL; + size_t len = 0; + bool ok; + + if (out_refs == NULL || out_len == NULL || manifest_path == NULL) { + return false; + } + *out_refs = NULL; + *out_len = 0; + + if (!amduat_asl_read_path(manifest_path, &bytes, &len)) { + free(bytes); + return false; + } + ok = amduat_asl_ref_list_parse(amduat_octets(bytes, len), manifest_format, + out_refs, out_len); + free(bytes); + return ok; +} + +static bool amduat_tgk_store_fs_get_config(void *ctx, + amduat_tgk_store_config_t *out_config) { + amduat_tgk_store_fs_t *fs = (amduat_tgk_store_fs_t *)ctx; + amduat_tgk_store_ops_t mem_ops = amduat_tgk_store_mem_ops(); + + if (fs == NULL || mem_ops.get_config == NULL) { + return false; + } + return mem_ops.get_config(&fs->mem, out_config); +} + +static amduat_tgk_graph_error_t amduat_tgk_store_fs_resolve_edge( + void *ctx, + amduat_reference_t ref, + amduat_tgk_edge_body_t *out_body) { + amduat_tgk_store_fs_t *fs = (amduat_tgk_store_fs_t *)ctx; + amduat_tgk_store_ops_t mem_ops = amduat_tgk_store_mem_ops(); + + if (fs == NULL || mem_ops.resolve_edge == NULL) { + return GS_ERR_UNSUPPORTED; + } + return mem_ops.resolve_edge(&fs->mem, ref, out_body); +} + +static bool amduat_tgk_store_fs_edges_from( + void *ctx, + amduat_reference_t node, + amduat_tgk_edge_type_filter_t type_filter, + amduat_tgk_graph_edge_view_list_t *out_edges) { + amduat_tgk_store_fs_t *fs = (amduat_tgk_store_fs_t *)ctx; + amduat_tgk_store_ops_t mem_ops = amduat_tgk_store_mem_ops(); + + if (fs == NULL || mem_ops.edges_from == NULL) { + return false; + } + return mem_ops.edges_from(&fs->mem, node, type_filter, out_edges); +} + +static bool amduat_tgk_store_fs_edges_to( + void *ctx, + amduat_reference_t node, + amduat_tgk_edge_type_filter_t type_filter, + amduat_tgk_graph_edge_view_list_t *out_edges) { + amduat_tgk_store_fs_t *fs = (amduat_tgk_store_fs_t *)ctx; + amduat_tgk_store_ops_t mem_ops = amduat_tgk_store_mem_ops(); + + if (fs == NULL || mem_ops.edges_to == NULL) { + return false; + } + return mem_ops.edges_to(&fs->mem, node, type_filter, out_edges); +} + +static bool amduat_tgk_store_fs_edges_incident( + void *ctx, + amduat_reference_t node, + amduat_tgk_edge_type_filter_t type_filter, + amduat_tgk_graph_edge_view_list_t *out_edges) { + amduat_tgk_store_fs_t *fs = (amduat_tgk_store_fs_t *)ctx; + amduat_tgk_store_ops_t mem_ops = amduat_tgk_store_mem_ops(); + + if (fs == NULL || mem_ops.edges_incident == NULL) { + return false; + } + return mem_ops.edges_incident(&fs->mem, node, type_filter, out_edges); +} + +static bool amduat_tgk_store_fs_scan_edges( + void *ctx, + amduat_tgk_edge_type_filter_t type_filter, + amduat_octets_t page_token, + bool has_page_token, + amduat_tgk_graph_scan_result_t *out_scan) { + amduat_tgk_store_fs_t *fs = (amduat_tgk_store_fs_t *)ctx; + amduat_tgk_store_ops_t mem_ops = amduat_tgk_store_mem_ops(); + + if (fs == NULL || mem_ops.scan_edges == NULL) { + return false; + } + return mem_ops.scan_edges(&fs->mem, type_filter, page_token, has_page_token, + out_scan); +} + +static bool amduat_tgk_store_fs_neighbors( + void *ctx, + amduat_reference_t node, + amduat_tgk_edge_type_filter_t type_filter, + amduat_tgk_graph_direction_t direction, + amduat_tgk_node_list_t *out_nodes) { + amduat_tgk_store_fs_t *fs = (amduat_tgk_store_fs_t *)ctx; + amduat_tgk_store_ops_t mem_ops = amduat_tgk_store_mem_ops(); + + if (fs == NULL || mem_ops.neighbors == NULL) { + return false; + } + return mem_ops.neighbors(&fs->mem, node, type_filter, direction, out_nodes); +} + +bool amduat_tgk_store_fs_init(amduat_tgk_store_fs_t *fs, + amduat_tgk_store_config_t config, + const char *manifest_path, + amduat_format_ref_format_t manifest_format, + amduat_asl_store_t *asl_store) { + amduat_reference_t *refs = NULL; + size_t refs_len = 0; + amduat_tgk_store_mem_artifact_t *artifacts = NULL; + size_t i; + + if (fs == NULL || manifest_path == NULL || asl_store == NULL) { + return false; + } + memset(fs, 0, sizeof(*fs)); + + if (!amduat_tgk_store_fs_load_manifest(manifest_path, + manifest_format, + &refs, + &refs_len)) { + return false; + } + + if (refs_len != 0u) { + artifacts = (amduat_tgk_store_mem_artifact_t *)calloc( + refs_len, sizeof(*artifacts)); + if (artifacts == NULL) { + amduat_asl_ref_list_free(refs, refs_len); + return false; + } + } + + for (i = 0; i < refs_len; ++i) { + amduat_asl_store_error_t err; + artifacts[i].ref = refs[i]; + artifacts[i].artifact = amduat_artifact(amduat_octets(NULL, 0)); + err = amduat_asl_store_get(asl_store, refs[i], &artifacts[i].artifact); + if (err != AMDUAT_ASL_STORE_OK) { + size_t j; + for (j = 0; j < i; ++j) { + amduat_tgk_store_fs_artifact_bytes_free(&artifacts[j]); + } + amduat_asl_ref_list_free(refs, refs_len); + free(artifacts); + return false; + } + } + + free(refs); + fs->artifacts = artifacts; + fs->artifacts_len = refs_len; + + if (!amduat_tgk_store_mem_init(&fs->mem, config, fs->artifacts, + fs->artifacts_len)) { + amduat_tgk_store_fs_free(fs); + return false; + } + return true; +} + +bool amduat_tgk_store_fs_init_with_asl_fs( + amduat_tgk_store_fs_t *fs, + amduat_tgk_store_config_t config, + const char *manifest_path, + amduat_format_ref_format_t manifest_format, + amduat_asl_store_config_t asl_config, + const char *asl_root_path) { + amduat_asl_store_fs_t asl_fs; + amduat_asl_store_t store; + + if (fs == NULL || manifest_path == NULL || asl_root_path == NULL) { + return false; + } + if (!amduat_asl_store_fs_init(&asl_fs, asl_config, asl_root_path)) { + return false; + } + amduat_asl_store_init(&store, asl_config, amduat_asl_store_fs_ops(), &asl_fs); + return amduat_tgk_store_fs_init(fs, config, manifest_path, manifest_format, + &store); +} + +void amduat_tgk_store_fs_free(amduat_tgk_store_fs_t *fs) { + size_t i; + + if (fs == NULL) { + return; + } + amduat_tgk_store_mem_free(&fs->mem); + for (i = 0; i < fs->artifacts_len; ++i) { + amduat_tgk_store_fs_artifact_full_free(&fs->artifacts[i]); + } + free(fs->artifacts); + fs->artifacts = NULL; + fs->artifacts_len = 0; +} + +amduat_tgk_store_ops_t amduat_tgk_store_fs_ops(void) { + amduat_tgk_store_ops_t ops; + + memset(&ops, 0, sizeof(ops)); + ops.get_config = amduat_tgk_store_fs_get_config; + ops.resolve_edge = amduat_tgk_store_fs_resolve_edge; + ops.edges_from = amduat_tgk_store_fs_edges_from; + ops.edges_to = amduat_tgk_store_fs_edges_to; + ops.edges_incident = amduat_tgk_store_fs_edges_incident; + ops.scan_edges = amduat_tgk_store_fs_scan_edges; + ops.neighbors = amduat_tgk_store_fs_neighbors; + return ops; +} diff --git a/tests/tgk/test_tgk_store_fs.c b/tests/tgk/test_tgk_store_fs.c new file mode 100644 index 0000000..d9fd81a --- /dev/null +++ b/tests/tgk/test_tgk_store_fs.c @@ -0,0 +1,392 @@ +#include "amduat/asl/asl_store_fs.h" +#include "amduat/asl/ref_text.h" +#include "amduat/enc/asl1_core.h" +#include "amduat/enc/tgk1_edge.h" +#include "amduat/hash/asl1.h" +#include "amduat/tgk/store.h" +#include "amduat/tgk/tgk_store_fs.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void fill_digest(uint8_t *out, uint8_t value) { + memset(out, value, 32); +} + +static amduat_reference_t make_ref(uint8_t value, uint8_t *storage) { + fill_digest(storage, value); + return amduat_reference(AMDUAT_HASH_ASL1_ID_SHA256, + amduat_octets(storage, 32)); +} + +static void free_ref(amduat_reference_t *ref) { + if (ref == NULL) { + return; + } + free((void *)ref->digest.data); + ref->digest.data = NULL; + ref->digest.len = 0; +} + +static bool join_path(const char *base, const char *segment, char **out_path) { + size_t base_len; + size_t seg_len; + bool needs_sep; + size_t total_len; + char *buffer; + size_t offset; + + if (base == NULL || segment == NULL || out_path == NULL) { + return false; + } + if (base[0] == '\0' || segment[0] == '\0') { + return false; + } + + base_len = strlen(base); + seg_len = strlen(segment); + needs_sep = base[base_len - 1u] != '/'; + total_len = base_len + (needs_sep ? 1u : 0u) + seg_len + 1u; + + buffer = (char *)malloc(total_len); + if (buffer == NULL) { + return false; + } + + offset = 0u; + memcpy(buffer + offset, base, base_len); + offset += base_len; + if (needs_sep) { + buffer[offset++] = '/'; + } + memcpy(buffer + offset, segment, seg_len); + offset += seg_len; + buffer[offset] = '\0'; + + *out_path = buffer; + return true; +} + +static bool remove_tree(const char *path) { + struct stat st; + DIR *dir; + struct dirent *entry; + + if (path == NULL) { + return false; + } + if (lstat(path, &st) != 0) { + return errno == ENOENT; + } + + if (!S_ISDIR(st.st_mode)) { + return unlink(path) == 0; + } + + dir = opendir(path); + if (dir == NULL) { + return false; + } + + while ((entry = readdir(dir)) != NULL) { + char *child = NULL; + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) { + continue; + } + if (!join_path(path, entry->d_name, &child)) { + closedir(dir); + return false; + } + if (!remove_tree(child)) { + free(child); + closedir(dir); + return false; + } + free(child); + } + + if (closedir(dir) != 0) { + return false; + } + return rmdir(path) == 0; +} + +static char *make_temp_root(void) { + char *templ; + const char template_prefix[] = "/tmp/amduat_test_tgk_store_fs_XXXXXX"; + + templ = (char *)malloc(sizeof(template_prefix)); + if (templ == NULL) { + return NULL; + } + memcpy(templ, template_prefix, sizeof(template_prefix)); + if (mkdtemp(templ) == NULL) { + free(templ); + return NULL; + } + return templ; +} + +static bool write_manifest(const char *path, + const amduat_reference_t *refs, + size_t refs_len) { + FILE *stream; + size_t i; + + if (path == NULL || (refs_len != 0 && refs == NULL)) { + return false; + } + + stream = fopen(path, "wb"); + if (stream == NULL) { + return false; + } + + for (i = 0; i < refs_len; ++i) { + char *hex = NULL; + if (!amduat_asl_ref_encode_hex(refs[i], &hex)) { + fclose(stream); + return false; + } + if (fprintf(stream, "%s\n", hex) < 0) { + free(hex); + fclose(stream); + return false; + } + free(hex); + } + + return fclose(stream) == 0; +} + +static int test_manifest_load(void) { + amduat_asl_store_config_t asl_config; + amduat_asl_store_fs_t asl_fs; + amduat_asl_store_t asl_store; + amduat_tgk_store_config_t tgk_config; + amduat_tgk_store_t store; + amduat_tgk_store_fs_t fs; + amduat_tgk_identity_domain_t domains[1]; + uint32_t edge_tags[1]; + amduat_tgk_edge_type_id_t edge_types[1]; + amduat_asl_encoding_profile_id_t encodings[1]; + amduat_octets_t edge_bytes[2]; + amduat_reference_t ref_edge1; + amduat_reference_t ref_edge2; + amduat_reference_t node_a; + amduat_reference_t node_b; + amduat_reference_t node_c; + amduat_reference_t payload1; + amduat_reference_t payload2; + uint8_t digest_node_a[32]; + uint8_t digest_node_b[32]; + uint8_t digest_node_c[32]; + uint8_t digest_payload1[32]; + uint8_t digest_payload2[32]; + amduat_tgk_graph_edge_view_list_t edges; + amduat_tgk_edge_body_t body; + amduat_tgk_edge_body_t edge1; + amduat_tgk_edge_body_t edge2; + amduat_reference_t edge1_from[1]; + amduat_reference_t edge1_to[1]; + amduat_reference_t edge2_from[1]; + amduat_reference_t edge2_to[1]; + char *root = NULL; + char *manifest_path = NULL; + int exit_code = 1; + bool fs_ready = false; + + memset(&edges, 0, sizeof(edges)); + memset(&body, 0, sizeof(body)); + edge_bytes[0] = amduat_octets(NULL, 0); + edge_bytes[1] = amduat_octets(NULL, 0); + memset(&ref_edge1, 0, sizeof(ref_edge1)); + memset(&ref_edge2, 0, sizeof(ref_edge2)); + memset(&fs, 0, sizeof(fs)); + + root = make_temp_root(); + if (root == NULL) { + fprintf(stderr, "temp root failed\n"); + goto cleanup; + } + if (!join_path(root, "manifest.txt", &manifest_path)) { + fprintf(stderr, "manifest path failed\n"); + goto cleanup; + } + + asl_config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; + asl_config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; + if (!amduat_asl_store_fs_init(&asl_fs, asl_config, root)) { + fprintf(stderr, "asl store fs init failed\n"); + goto cleanup; + } + amduat_asl_store_init(&asl_store, asl_config, amduat_asl_store_fs_ops(), + &asl_fs); + + domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1; + domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256; + edge_tags[0] = TYPE_TAG_TGK1_EDGE_V1; + edge_types[0] = 0x10; + encodings[0] = TGK1_EDGE_ENC_V1; + + memset(&tgk_config, 0, sizeof(tgk_config)); + tgk_config.id_space.domains = domains; + tgk_config.id_space.domains_len = 1; + tgk_config.artifact_scope.description = amduat_octets(NULL, 0); + tgk_config.tgk_profiles.edge_tags = edge_tags; + tgk_config.tgk_profiles.edge_tags_len = 1; + tgk_config.tgk_profiles.edge_types = edge_types; + tgk_config.tgk_profiles.edge_types_len = 1; + tgk_config.tgk_profiles.encodings = encodings; + tgk_config.tgk_profiles.encodings_len = 1; + + node_a = make_ref(0xa1, digest_node_a); + node_b = make_ref(0xb1, digest_node_b); + node_c = make_ref(0xc1, digest_node_c); + payload1 = make_ref(0xe1, digest_payload1); + payload2 = make_ref(0xe2, digest_payload2); + + memset(&edge1, 0, sizeof(edge1)); + edge1.type = 0x10; + edge1_from[0] = node_a; + edge1.from = edge1_from; + edge1.from_len = 1; + edge1_to[0] = node_b; + edge1.to = edge1_to; + edge1.to_len = 1; + edge1.payload = payload1; + + memset(&edge2, 0, sizeof(edge2)); + edge2.type = 0x10; + edge2_from[0] = node_a; + edge2.from = edge2_from; + edge2.from_len = 1; + edge2_to[0] = node_c; + edge2.to = edge2_to; + edge2.to_len = 1; + edge2.payload = payload2; + + if (!amduat_enc_tgk1_edge_encode_v1(&edge1, &edge_bytes[0]) || + !amduat_enc_tgk1_edge_encode_v1(&edge2, &edge_bytes[1])) { + fprintf(stderr, "edge encode failed\n"); + goto cleanup; + } + + if (amduat_asl_store_put( + &asl_store, + amduat_artifact_with_type(edge_bytes[0], + amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)), + &ref_edge1) != AMDUAT_ASL_STORE_OK || + amduat_asl_store_put( + &asl_store, + amduat_artifact_with_type(edge_bytes[1], + amduat_type_tag(TYPE_TAG_TGK1_EDGE_V1)), + &ref_edge2) != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "asl store put failed\n"); + goto cleanup; + } + + { + amduat_reference_t refs[2]; + refs[0] = ref_edge1; + refs[1] = ref_edge2; + if (!write_manifest(manifest_path, refs, 2)) { + fprintf(stderr, "manifest write failed\n"); + goto cleanup; + } + } + + if (!amduat_tgk_store_fs_init(&fs, tgk_config, manifest_path, + AMDUAT_FORMAT_REF_HEX, &asl_store)) { + fprintf(stderr, "tgk store fs init failed\n"); + goto cleanup; + } + fs_ready = true; + amduat_tgk_store_init(&store, tgk_config, amduat_tgk_store_fs_ops(), &fs); + + if (amduat_tgk_store_resolve_edge(&store, ref_edge1, &body) != 0) { + fprintf(stderr, "resolve_edge failed\n"); + goto cleanup; + } + if (body.type != 0x10 || body.from_len != 1 || body.to_len != 1 || + !amduat_reference_eq(body.from[0], node_a) || + !amduat_reference_eq(body.to[0], node_b)) { + fprintf(stderr, "resolve_edge mismatch\n"); + amduat_tgk_edge_body_free(&body); + goto cleanup; + } + amduat_tgk_edge_body_free(&body); + memset(&body, 0, sizeof(body)); + + { + amduat_tgk_edge_type_filter_t filter; + amduat_tgk_edge_type_id_t types[1] = {0x10}; + size_t seen_b = 0; + size_t seen_c = 0; + size_t i; + + filter.types = types; + filter.types_len = 1; + if (!amduat_tgk_store_edges_from(&store, node_a, filter, &edges)) { + fprintf(stderr, "edges_from failed\n"); + goto cleanup; + } + if (edges.len != 2) { + fprintf(stderr, "edges_from len mismatch\n"); + goto cleanup; + } + for (i = 0; i < edges.len; ++i) { + const amduat_tgk_graph_edge_view_t *edge = &edges.edges[i]; + if (edge->body.type != 0x10 || edge->body.from_len != 1 || + edge->body.to_len != 1 || + !amduat_reference_eq(edge->body.from[0], node_a)) { + fprintf(stderr, "edges_from body mismatch\n"); + goto cleanup; + } + if (amduat_reference_eq(edge->body.to[0], node_b)) { + seen_b++; + } else if (amduat_reference_eq(edge->body.to[0], node_c)) { + seen_c++; + } else { + fprintf(stderr, "edges_from unexpected endpoint\n"); + goto cleanup; + } + } + if (seen_b != 1 || seen_c != 1) { + fprintf(stderr, "edges_from endpoints mismatch\n"); + goto cleanup; + } + } + + exit_code = 0; + +cleanup: + amduat_tgk_graph_edge_view_list_free(&edges); + amduat_tgk_edge_body_free(&body); + if (fs_ready) { + amduat_tgk_store_fs_free(&fs); + } + free((void *)edge_bytes[0].data); + free((void *)edge_bytes[1].data); + free_ref(&ref_edge1); + free_ref(&ref_edge2); + free(manifest_path); + if (root != NULL) { + remove_tree(root); + } + free(root); + return exit_code; +} + +int main(void) { + return test_manifest_load(); +} diff --git a/tests/tgk/test_tgk_store_mem.c b/tests/tgk/test_tgk_store_mem.c index 6834142..ed02cf8 100644 --- a/tests/tgk/test_tgk_store_mem.c +++ b/tests/tgk/test_tgk_store_mem.c @@ -21,6 +21,7 @@ typedef struct { amduat_reference_t ref_edge3; amduat_reference_t ref_edge_bad; amduat_reference_t ref_non_edge; + amduat_reference_t ref_missing; amduat_reference_t node_a; amduat_reference_t node_b; amduat_reference_t node_c; @@ -33,6 +34,7 @@ typedef struct { uint8_t digest_edge3[32]; uint8_t digest_edge_bad[32]; uint8_t digest_non_edge[32]; + uint8_t digest_missing[32]; uint8_t digest_node_a[32]; uint8_t digest_node_b[32]; uint8_t digest_node_c[32]; @@ -105,6 +107,7 @@ static bool init_env(test_env_t *env) { env->ref_edge3 = make_ref(0x30, env->digest_edge3); env->ref_edge_bad = make_ref(0x40, env->digest_edge_bad); env->ref_non_edge = make_ref(0x50, env->digest_non_edge); + env->ref_missing = make_ref(0x60, env->digest_missing); env->node_a = make_ref(0xa1, env->digest_node_a); env->node_b = make_ref(0xb1, env->digest_node_b); @@ -236,6 +239,65 @@ static int test_resolve_edge_not_edge(const test_env_t *env) { return 0; } +static int test_resolve_edge_missing(const test_env_t *env) { + amduat_tgk_edge_body_t body; + amduat_tgk_graph_error_t err; + + err = amduat_tgk_store_resolve_edge((amduat_tgk_store_t *)&env->store, + env->ref_missing, &body); + if (err != GS_ERR_ARTIFACT_ERROR) { + fprintf(stderr, "resolve_edge missing mismatch: %d\n", err); + return 1; + } + return 0; +} + +static bool make_ref_for_hash(amduat_hash_id_t hash_id, + uint8_t value, + amduat_reference_t *out_ref) { + const amduat_hash_asl1_desc_t *desc; + uint8_t *digest; + + if (out_ref == NULL) { + return false; + } + desc = amduat_hash_asl1_desc_lookup(hash_id); + if (desc == NULL || desc->digest_len == 0) { + return false; + } + digest = (uint8_t *)malloc(desc->digest_len); + if (digest == NULL) { + return false; + } + memset(digest, value, desc->digest_len); + *out_ref = amduat_reference(hash_id, amduat_octets(digest, desc->digest_len)); + return true; +} + +static int test_resolve_edge_unsupported(const test_env_t *env) { + amduat_reference_t ref; + amduat_tgk_edge_body_t body; + amduat_tgk_graph_error_t err; + int exit_code = 1; + + if (!make_ref_for_hash(0x0002, 0x6au, &ref)) { + fprintf(stderr, "unsupported ref alloc failed\n"); + return 1; + } + + err = amduat_tgk_store_resolve_edge((amduat_tgk_store_t *)&env->store, ref, + &body); + if (err != GS_ERR_UNSUPPORTED) { + fprintf(stderr, "resolve_edge unsupported mismatch: %d\n", err); + goto cleanup; + } + + exit_code = 0; +cleanup: + free_ref(&ref); + return exit_code; +} + static int test_type_filter(const test_env_t *env) { amduat_tgk_edge_type_id_t types[1] = {0x10}; amduat_tgk_edge_type_filter_t filter; @@ -419,6 +481,8 @@ int main(void) { if (test_resolve_edge_ok(&env) != 0 || test_resolve_edge_not_edge(&env) != 0 || + test_resolve_edge_missing(&env) != 0 || + test_resolve_edge_unsupported(&env) != 0 || test_type_filter(&env) != 0 || test_ordering(&env) != 0 || test_adjacency(&env) != 0 ||