From f4ddb17a58332d7516b97aa278ca9cd19283fe6c Mon Sep 17 00:00:00 2001 From: Carl Niklas Rydberg Date: Sat, 21 Feb 2026 19:23:59 +0100 Subject: [PATCH] Fixing tests. --- CMakeLists.txt | 16 + docs/SUBSTRATE_CONTRACT_V0_1.md | 24 ++ .../asl_derivation_index_fs.c | 5 +- .../asl_store_index_fs/asl_store_index_fs.c | 8 +- .../asl/test_asl_index_put_get_consistency.c | 3 +- tests/asl/test_asl_log_store_index_fs.c | 3 +- tests/asl/test_asl_projection_replay.c | 362 ++++++++++++++++++ .../tgk_store/tgk_store_conformance.c | 9 +- 8 files changed, 417 insertions(+), 13 deletions(-) create mode 100644 tests/asl/test_asl_projection_replay.c diff --git a/CMakeLists.txt b/CMakeLists.txt index f757c3e..42541c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -613,6 +613,22 @@ target_link_libraries(amduat_test_asl_derivation_index_fs add_test(NAME asl_derivation_index_fs COMMAND amduat_test_asl_derivation_index_fs) +add_executable(amduat_test_asl_projection_replay + tests/asl/test_asl_projection_replay.c) +target_include_directories(amduat_test_asl_projection_replay + PRIVATE ${AMDUAT_INTERNAL_DIR} + PRIVATE ${AMDUAT_INCLUDE_DIR} +) +target_compile_definitions(amduat_test_asl_projection_replay + PRIVATE _POSIX_C_SOURCE=200809L +) +target_link_libraries(amduat_test_asl_projection_replay + PRIVATE amduat_asl_store_index_fs amduat_asl_record amduat_asl + amduat_enc amduat_hash_asl1 amduat_util pthread +) +add_test(NAME asl_projection_replay + COMMAND amduat_test_asl_projection_replay) + add_executable(amduat_test_asl_store_conformance tests/conformance/asl_store/test_asl_store_conformance.c tests/conformance/asl_store/asl_store_conformance.c diff --git a/docs/SUBSTRATE_CONTRACT_V0_1.md b/docs/SUBSTRATE_CONTRACT_V0_1.md index f2fe71b..559d922 100644 --- a/docs/SUBSTRATE_CONTRACT_V0_1.md +++ b/docs/SUBSTRATE_CONTRACT_V0_1.md @@ -165,3 +165,27 @@ it is introduced in this change and implemented in code (referenced below). validated by the scriptlab adapter flow. Sources: `include/amduat/asl/log_cursor.h`, `include/amduat/asl/projection_checkpoint.h`, `tests/substrate/test_scriptlab_adapter.c`. + +--- + +## 10. Layer 1 Dependencies (Allowed vs Disallowed) + +**Layer 1 (higher API) MAY depend on:** + +1. ASL core and store surfaces (`amduat/asl/core.h`, `amduat/asl/store.h`). +2. ASL index/log state + replay (`amduat/asl/index_replay.h`, `amduat/enc/asl_log.h`). +3. Derivation/provenance index (`amduat/asl/asl_derivation_index_fs.h`, + `amduat/pel/derivation_sid.h`). +4. PEL deterministic artifacts and surface results (`amduat/pel/surf.h`, + `amduat/enc/pel1_result.h`). +5. TGK edge artifacts + TGK store projection (`amduat/enc/tgk1_edge.h`, + `amduat/tgk/store.h`, `amduat/tgk/tgk_store_asl_index_fs.h`). +6. Projection replay primitives (cursor + checkpoint helpers) + (`amduat/asl/log_cursor.h`, `amduat/asl/projection_checkpoint.h`). + +**Layer 1 MUST NOT depend on:** + +1. CLI tools (`src/tools/*`) as APIs. +2. Any networking, auth, tenancy, or server layers (none are provided). +3. App/domain schemas beyond raw ASL records (records may carry schema bytes, + but core does not interpret them). diff --git a/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c b/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c index e1b0dfd..ea5b82c 100644 --- a/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c +++ b/src/adapters/asl_derivation_index_fs/asl_derivation_index_fs.c @@ -2,6 +2,7 @@ #include "amduat/asl/ref_text.h" #include "amduat/enc/asl1_core_codec.h" +#include "amduat/util/string.h" #include #include @@ -609,7 +610,7 @@ amduat_asl_store_error_t amduat_asl_derivation_index_fs_add( return AMDUAT_ASL_STORE_ERR_IO; } - dir_a = strdup(path); + dir_a = amduat_strdup(path); if (dir_a == NULL) { free(path); free(artifact_dir); @@ -626,7 +627,7 @@ amduat_asl_store_error_t amduat_asl_derivation_index_fs_add( } *dir_b = '\0'; - dir_b = strdup(dir_a); + dir_b = amduat_strdup(dir_a); if (dir_b == NULL) { free(path); free(dir_a); diff --git a/src/adapters/asl_store_index_fs/asl_store_index_fs.c b/src/adapters/asl_store_index_fs/asl_store_index_fs.c index 155ebc8..9f42f5e 100644 --- a/src/adapters/asl_store_index_fs/asl_store_index_fs.c +++ b/src/adapters/asl_store_index_fs/asl_store_index_fs.c @@ -3828,11 +3828,9 @@ static bool amduat_asl_store_index_fs_current_state_impl( if (amduat_asl_store_index_fs_find_latest_snapshot_id(fs, &snapshot_id) && amduat_asl_store_index_fs_load_snapshot_manifest( fs->root_path, snapshot_id, &manifest, manifest_hash)) { - if (manifest.anchor_logseq <= last_logseq) { - out_state->snapshot_id = manifest.snapshot_id; - if (manifest.anchor_logseq > out_state->log_position) { - out_state->log_position = manifest.anchor_logseq; - } + out_state->snapshot_id = manifest.snapshot_id; + if (manifest.anchor_logseq > out_state->log_position) { + out_state->log_position = manifest.anchor_logseq; } amduat_asl_snapshot_manifest_free(&manifest); } else if (record_count != 0u && diff --git a/tests/asl/test_asl_index_put_get_consistency.c b/tests/asl/test_asl_index_put_get_consistency.c index 1d0aa00..6285ace 100644 --- a/tests/asl/test_asl_index_put_get_consistency.c +++ b/tests/asl/test_asl_index_put_get_consistency.c @@ -2,6 +2,7 @@ #include "amduat/asl/store.h" #include "amduat/enc/asl1_core.h" #include "amduat/hash/asl1.h" +#include "amduat/util/string.h" #include #include @@ -93,7 +94,7 @@ static char *make_temp_root(void) { char *tmpl = NULL; char *root = NULL; - tmpl = strdup("/tmp/amduat-index-putget-XXXXXX"); + tmpl = amduat_strdup("/tmp/amduat-index-putget-XXXXXX"); if (tmpl == NULL) { return NULL; } diff --git a/tests/asl/test_asl_log_store_index_fs.c b/tests/asl/test_asl_log_store_index_fs.c index 4ec7430..8caf93e 100644 --- a/tests/asl/test_asl_log_store_index_fs.c +++ b/tests/asl/test_asl_log_store_index_fs.c @@ -3,6 +3,7 @@ #include "amduat/asl/store.h" #include "amduat/enc/asl1_core.h" #include "amduat/hash/asl1.h" +#include "amduat/util/string.h" #include #include @@ -256,7 +257,7 @@ int main(void) { char *root = NULL; int rc = 1; - root = strdup("/tmp/amduat-log-index-XXXXXX"); + root = amduat_strdup("/tmp/amduat-log-index-XXXXXX"); if (root == NULL) { fprintf(stderr, "alloc root template failed\n"); return 1; diff --git a/tests/asl/test_asl_projection_replay.c b/tests/asl/test_asl_projection_replay.c new file mode 100644 index 0000000..54c1ef2 --- /dev/null +++ b/tests/asl/test_asl_projection_replay.c @@ -0,0 +1,362 @@ +#include "amduat/asl/asl_store_index_fs.h" +#include "amduat/asl/log_cursor.h" +#include "amduat/asl/projection_checkpoint.h" +#include "amduat/asl/record.h" +#include "amduat/asl/store.h" +#include "amduat/enc/asl1_core.h" +#include "amduat/hash/asl1.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *make_temp_root(void) { + char *templ; + const char template_prefix[] = "/tmp/amduat_test_asl_projection_replay_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 remove_tree(const char *path) { + struct stat st; + DIR *dir; + struct dirent *entry; + + if (path == NULL) { + return false; + } + if (stat(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[2048]; + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) { + continue; + } + snprintf(child, sizeof(child), "%s/%s", path, entry->d_name); + if (!remove_tree(child)) { + closedir(dir); + return false; + } + } + closedir(dir); + return rmdir(path) == 0; +} + +static bool records_equal(const amduat_asl_log_record_t *a, + const amduat_asl_log_record_t *b) { + if (a->logseq != b->logseq || a->record_type != b->record_type) { + return false; + } + if (a->payload.len != b->payload.len) { + return false; + } + if (a->payload.len == 0u) { + return true; + } + if (a->payload.data == NULL || b->payload.data == NULL) { + return false; + } + return memcmp(a->payload.data, b->payload.data, a->payload.len) == 0; +} + +static int test_cursor_deterministic_replay(void) { + amduat_asl_store_config_t config; + amduat_asl_store_index_fs_t fs; + amduat_asl_store_t store; + amduat_artifact_t artifact; + amduat_reference_t ref; + amduat_asl_index_state_t state; + uint8_t payload_a[4] = {0x11, 0x22, 0x33, 0x44}; + uint8_t payload_b[4] = {0xaa, 0xbb, 0xcc, 0xdd}; + amduat_asl_log_record_t *all = NULL; + size_t all_len = 0u; + amduat_asl_log_position_t cursor = 0u; + bool end = false; + char *root; + int exit_code = 1; + + root = make_temp_root(); + if (root == NULL) { + fprintf(stderr, "temp root failed\n"); + return 1; + } + + memset(&config, 0, sizeof(config)); + config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; + config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; + if (!amduat_asl_store_index_fs_init(&fs, config, root)) { + fprintf(stderr, "index fs init failed\n"); + goto cleanup_root; + } + amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs); + + artifact = amduat_artifact(amduat_octets(payload_a, sizeof(payload_a))); + ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + if (amduat_asl_store_put_indexed(&store, artifact, &ref, &state) != + AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "put_indexed A failed\n"); + goto cleanup_root; + } + amduat_reference_free(&ref); + + artifact = amduat_artifact(amduat_octets(payload_b, sizeof(payload_b))); + ref = amduat_reference(0u, amduat_octets(NULL, 0u)); + if (amduat_asl_store_put_indexed(&store, artifact, &ref, &state) != + AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "put_indexed B failed\n"); + goto cleanup_root; + } + amduat_reference_free(&ref); + + if (!amduat_asl_log_read_since(&store, + 0u, + 0u, + &all, + &all_len, + &cursor, + &end)) { + fprintf(stderr, "log read all failed\n"); + goto cleanup_root; + } + + cursor = 0u; + for (size_t i = 0u; i < all_len; ++i) { + amduat_asl_log_record_t *page = NULL; + size_t page_len = 0u; + amduat_asl_log_position_t next = 0u; + bool page_end = false; + + if (!amduat_asl_log_read_since(&store, + cursor, + 1u, + &page, + &page_len, + &next, + &page_end)) { + fprintf(stderr, "log page read failed\n"); + goto cleanup_all; + } + if (page_len != 1u || !records_equal(&all[i], &page[0])) { + amduat_enc_asl_log_free(page, page_len); + fprintf(stderr, "log page mismatch\n"); + goto cleanup_all; + } + cursor = next; + amduat_enc_asl_log_free(page, page_len); + } + + { + amduat_asl_log_record_t *page = NULL; + size_t page_len = 0u; + amduat_asl_log_position_t next = 0u; + bool page_end = false; + if (!amduat_asl_log_read_since(&store, + cursor, + 1u, + &page, + &page_len, + &next, + &page_end)) { + fprintf(stderr, "log tail read failed\n"); + goto cleanup_all; + } + if (page_len != 0u || !page_end) { + amduat_enc_asl_log_free(page, page_len); + fprintf(stderr, "log tail mismatch\n"); + goto cleanup_all; + } + amduat_enc_asl_log_free(page, page_len); + } + + exit_code = 0; + +cleanup_all: + amduat_enc_asl_log_free(all, all_len); +cleanup_root: + if (!remove_tree(root)) { + fprintf(stderr, "cleanup failed\n"); + exit_code = 1; + } + free(root); + return exit_code; +} + +static int test_checkpoint_resume(void) { + amduat_asl_store_config_t config; + amduat_asl_store_index_fs_t fs; + amduat_asl_store_t store; + amduat_asl_index_state_t state; + amduat_asl_projection_checkpoint_v1_t checkpoint; + amduat_asl_projection_checkpoint_v1_t decoded; + amduat_octets_t checkpoint_bytes = amduat_octets(NULL, 0u); + amduat_reference_t checkpoint_ref; + amduat_asl_record_t checkpoint_record; + amduat_asl_log_record_t *page = NULL; + size_t page_len = 0u; + amduat_asl_log_position_t next = 0u; + bool end = false; + char *root; + int exit_code = 1; + + root = make_temp_root(); + if (root == NULL) { + fprintf(stderr, "temp root failed\n"); + return 1; + } + + memset(&config, 0, sizeof(config)); + config.encoding_profile_id = AMDUAT_ENC_ASL1_CORE_V1; + config.hash_id = AMDUAT_HASH_ASL1_ID_SHA256; + if (!amduat_asl_store_index_fs_init(&fs, config, root)) { + fprintf(stderr, "index fs init failed\n"); + goto cleanup_root; + } + amduat_asl_store_init(&store, config, amduat_asl_store_index_fs_ops(), &fs); + + { + uint8_t payload[3] = {0x01, 0x02, 0x03}; + amduat_artifact_t artifact = amduat_artifact(amduat_octets(payload, sizeof(payload))); + if (amduat_asl_store_put_indexed(&store, artifact, &checkpoint_ref, &state) != + AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "put_indexed failed\n"); + goto cleanup_root; + } + amduat_reference_free(&checkpoint_ref); + } + + if (!amduat_asl_index_current_state(&store, &state)) { + fprintf(stderr, "current_state failed\n"); + goto cleanup_root; + } + + memset(&checkpoint, 0, sizeof(checkpoint)); + checkpoint.checkpoint_version = AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1; + checkpoint.projection_name = amduat_octets("projection", strlen("projection")); + checkpoint.projection_schema_version = 1u; + checkpoint.has_snapshot_id = true; + checkpoint.snapshot_id = state.snapshot_id; + checkpoint.log_position = state.log_position; + checkpoint.created_at = 0u; + checkpoint.builder_id = amduat_octets("test", strlen("test")); + + if (!amduat_asl_projection_checkpoint_encode_v1(&checkpoint, + &checkpoint_bytes)) { + fprintf(stderr, "checkpoint encode failed\n"); + goto cleanup_root; + } + + if (amduat_asl_record_store_put( + &store, + amduat_octets("projection/checkpoint", + strlen("projection/checkpoint")), + checkpoint_bytes, + &checkpoint_ref) != AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "checkpoint record put failed\n"); + goto cleanup_bytes; + } + + if (amduat_asl_record_store_get(&store, checkpoint_ref, &checkpoint_record) != + AMDUAT_ASL_STORE_OK) { + fprintf(stderr, "checkpoint record get failed\n"); + goto cleanup_ref; + } + + if (!amduat_asl_projection_checkpoint_decode_v1( + checkpoint_record.payload, &decoded)) { + fprintf(stderr, "checkpoint decode failed\n"); + amduat_asl_record_free(&checkpoint_record); + goto cleanup_ref; + } + if (decoded.log_position != state.log_position) { + fprintf(stderr, "checkpoint log position mismatch\n"); + amduat_asl_projection_checkpoint_free(&decoded); + amduat_asl_record_free(&checkpoint_record); + goto cleanup_ref; + } + amduat_asl_projection_checkpoint_free(&decoded); + amduat_asl_record_free(&checkpoint_record); + + if (!amduat_asl_log_read_since(&store, + state.log_position, + 0u, + &page, + &page_len, + &next, + &end)) { + fprintf(stderr, "resume log read failed\n"); + goto cleanup_ref; + } + if (page_len == 0u) { + fprintf(stderr, "resume expected non-empty log\n"); + amduat_enc_asl_log_free(page, page_len); + goto cleanup_ref; + } + amduat_enc_asl_log_free(page, page_len); + + if (!amduat_asl_log_read_since(&store, + next, + 0u, + &page, + &page_len, + &next, + &end)) { + fprintf(stderr, "resume tail read failed\n"); + goto cleanup_ref; + } + if (page_len != 0u || !end) { + fprintf(stderr, "resume expected empty tail\n"); + amduat_enc_asl_log_free(page, page_len); + goto cleanup_ref; + } + amduat_enc_asl_log_free(page, page_len); + + exit_code = 0; + +cleanup_ref: + amduat_reference_free(&checkpoint_ref); +cleanup_bytes: + amduat_octets_free(&checkpoint_bytes); +cleanup_root: + if (!remove_tree(root)) { + fprintf(stderr, "cleanup failed\n"); + exit_code = 1; + } + free(root); + return exit_code; +} + +int main(void) { + if (test_cursor_deterministic_replay() != 0) { + return 1; + } + if (test_checkpoint_resume() != 0) { + return 1; + } + return 0; +} diff --git a/tests/conformance/tgk_store/tgk_store_conformance.c b/tests/conformance/tgk_store/tgk_store_conformance.c index 9bf349c..c791d33 100644 --- a/tests/conformance/tgk_store/tgk_store_conformance.c +++ b/tests/conformance/tgk_store/tgk_store_conformance.c @@ -68,7 +68,7 @@ typedef struct { amduat_tgk_store_config_t config; amduat_tgk_identity_domain_t domains[1]; uint32_t edge_tags[1]; - amduat_tgk_edge_type_id_t edge_types[1]; + amduat_tgk_edge_type_id_t edge_types[2]; amduat_asl_encoding_profile_id_t encodings[1]; amduat_tgk_store_mem_t mem; amduat_tgk_store_t store; @@ -78,7 +78,7 @@ typedef struct { amduat_tgk_store_config_t config; amduat_tgk_identity_domain_t domains[1]; uint32_t edge_tags[1]; - amduat_tgk_edge_type_id_t edge_types[1]; + amduat_tgk_edge_type_id_t edge_types[2]; amduat_asl_encoding_profile_id_t encodings[1]; amduat_asl_store_fs_t asl_fs; amduat_asl_store_t asl_store; @@ -91,7 +91,7 @@ typedef struct { amduat_tgk_store_config_t config; amduat_tgk_identity_domain_t domains[1]; uint32_t edge_tags[1]; - amduat_tgk_edge_type_id_t edge_types[1]; + amduat_tgk_edge_type_id_t edge_types[2]; amduat_asl_encoding_profile_id_t encodings[1]; amduat_asl_store_index_fs_t asl_fs; amduat_asl_store_t asl_store; @@ -897,6 +897,7 @@ static void conformance_init_tgk_config(amduat_tgk_store_config_t *config, domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256; edge_tags[0] = TYPE_TAG_TGK1_EDGE_V1; edge_types[0] = 0x10; + edge_types[1] = 0x20; encodings[0] = TGK1_EDGE_ENC_V1; config->id_space.domains = domains; @@ -905,7 +906,7 @@ static void conformance_init_tgk_config(amduat_tgk_store_config_t *config, config->tgk_profiles.edge_tags = edge_tags; config->tgk_profiles.edge_tags_len = 1; config->tgk_profiles.edge_types = edge_types; - config->tgk_profiles.edge_types_len = 1; + config->tgk_profiles.edge_types_len = 2; config->tgk_profiles.encodings = encodings; config->tgk_profiles.encodings_len = 1; }