amduat/tests/substrate/test_scriptlab_adapter.c
2026-02-21 18:22:41 +01:00

443 lines
14 KiB
C

#include "amduat/asl/asl_derivation_index_fs.h"
#include "amduat/asl/asl_store_index_fs.h"
#include "amduat/asl/collection.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/enc/tgk1_edge.h"
#include "amduat/hash/asl1.h"
#include "amduat/pel/derivation_sid.h"
#include "amduat/tgk/store.h"
#include "amduat/tgk/tgk_store_asl_index_fs.h"
#include <dirent.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
static char *make_temp_root(void) {
char *templ;
const char template_prefix[] = "/tmp/amduat_test_scriptlab_adapter_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 (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[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 ref_list_contains(amduat_reference_t needle,
const amduat_reference_t *list,
size_t len) {
for (size_t i = 0u; i < len; ++i) {
if (amduat_reference_eq(needle, list[i])) {
return true;
}
}
return false;
}
static int test_scriptlab_adapter_flow(void) {
amduat_asl_store_config_t config;
amduat_asl_store_index_fs_t asl_fs;
amduat_asl_store_t asl_store;
amduat_asl_collection_store_t collection_store;
amduat_asl_derivation_index_fs_t deriv_index;
amduat_reference_t node_a;
amduat_reference_t node_b;
amduat_reference_t edge_payload_ref;
amduat_reference_t edge_ref;
amduat_reference_t snapshot_ref;
amduat_reference_t program_ref;
amduat_reference_t checkpoint_ref;
amduat_octets_t edge_bytes = amduat_octets(NULL, 0u);
amduat_octets_t checkpoint_bytes = amduat_octets(NULL, 0u);
amduat_octets_t sid = amduat_octets(NULL, 0u);
amduat_asl_log_record_t *log_records = NULL;
size_t log_count = 0u;
amduat_asl_log_position_t next_logseq = 0u;
bool log_end = true;
amduat_asl_index_state_t index_state;
uint64_t collection_offset = 0u;
bool swapped = 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(&asl_fs, config, root)) {
fprintf(stderr, "index fs init failed\n");
goto cleanup_root;
}
amduat_asl_store_init(&asl_store, config, amduat_asl_store_index_fs_ops(), &asl_fs);
if (!amduat_asl_collection_store_init(&collection_store, root, &asl_store)) {
fprintf(stderr, "collection store init failed\n");
goto cleanup_root;
}
if (!amduat_asl_derivation_index_fs_init(&deriv_index, root)) {
fprintf(stderr, "derivation index init failed\n");
goto cleanup_root;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
amduat_octets("A", 1u),
&node_a) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "node A store failed\n");
goto cleanup_root;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/node", strlen("scriptlab/node")),
amduat_octets("B", 1u),
&node_b) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "node B store failed\n");
goto cleanup_nodes;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("tgk/edge_payload", strlen("tgk/edge_payload")),
amduat_octets("relates", strlen("relates")),
&edge_payload_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "edge payload store failed\n");
goto cleanup_nodes;
}
{
amduat_tgk_edge_body_t edge;
amduat_reference_t from_refs[1] = {node_a};
amduat_reference_t to_refs[1] = {node_b};
memset(&edge, 0, sizeof(edge));
edge.type = 1u;
edge.from = from_refs;
edge.from_len = 1u;
edge.to = to_refs;
edge.to_len = 1u;
edge.payload = edge_payload_ref;
if (!amduat_enc_tgk1_edge_encode_v1(&edge, &edge_bytes)) {
fprintf(stderr, "edge encode failed\n");
goto cleanup_nodes;
}
}
{
amduat_artifact_t edge_artifact = amduat_artifact_with_type(
edge_bytes, amduat_type_tag(AMDUAT_TYPE_TAG_TGK1_EDGE_V1));
if (amduat_asl_store_put(&asl_store, edge_artifact, &edge_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "edge store failed\n");
goto cleanup_edge_bytes;
}
}
amduat_octets_free(&edge_bytes);
if (amduat_asl_collection_append(&collection_store,
"tgk_edges",
edge_ref,
0u,
amduat_octets(NULL, 0u),
&collection_offset) != AMDUAT_ASL_COLLECTION_OK) {
fprintf(stderr, "collection append failed\n");
goto cleanup_edge_ref;
}
if (amduat_asl_collection_snapshot(&collection_store,
"tgk_edges",
UINT64_MAX,
&snapshot_ref,
&swapped) != AMDUAT_ASL_COLLECTION_OK) {
fprintf(stderr, "collection snapshot failed\n");
goto cleanup_edge_ref;
}
{
amduat_asl_record_t snapshot_record;
if (amduat_asl_record_store_get(&asl_store, snapshot_ref, &snapshot_record) !=
AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "snapshot record fetch failed\n");
goto cleanup_snapshot_ref;
}
{
amduat_asl_collection_snapshot_payload_t payload;
if (!amduat_asl_collection_snapshot_payload_decode_v1(
snapshot_record.payload, &payload)) {
fprintf(stderr, "snapshot payload decode failed\n");
amduat_asl_record_free(&snapshot_record);
goto cleanup_snapshot_ref;
}
if (!ref_list_contains(edge_ref, payload.refs, payload.refs_len)) {
fprintf(stderr, "snapshot missing edge ref\n");
amduat_asl_collection_snapshot_payload_free(&payload);
amduat_asl_record_free(&snapshot_record);
goto cleanup_snapshot_ref;
}
amduat_asl_collection_snapshot_payload_free(&payload);
}
amduat_asl_record_free(&snapshot_record);
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("scriptlab/transform", strlen("scriptlab/transform")),
amduat_octets("tgk_snapshot_v1", strlen("tgk_snapshot_v1")),
&program_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "program record store failed\n");
goto cleanup_snapshot_ref;
}
{
amduat_pel_derivation_sid_input_t sid_input;
amduat_asl_derivation_record_t record;
amduat_octets_t exec_profile =
amduat_octets("scriptlab/adapter", strlen("scriptlab/adapter"));
amduat_reference_t inputs[1] = {edge_ref};
memset(&sid_input, 0, sizeof(sid_input));
sid_input.program_ref = program_ref;
sid_input.input_refs = inputs;
sid_input.input_refs_len = 1u;
sid_input.has_params_ref = false;
sid_input.has_exec_profile = true;
sid_input.exec_profile = exec_profile;
if (!amduat_pel_derivation_sid_compute(&sid_input, &sid)) {
fprintf(stderr, "sid compute failed\n");
goto cleanup_program_ref;
}
memset(&record, 0, sizeof(record));
record.sid = sid;
record.program_ref = program_ref;
record.output_index = 0u;
record.input_refs = inputs;
record.input_refs_len = 1u;
record.has_params_ref = false;
record.has_exec_profile = true;
record.exec_profile = exec_profile;
if (amduat_asl_derivation_index_fs_add(&deriv_index,
snapshot_ref,
&record) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "derivation index add failed\n");
goto cleanup_sid;
}
}
if (!amduat_asl_index_current_state(&asl_store, &index_state)) {
fprintf(stderr, "index state read failed\n");
goto cleanup_sid;
}
if (!amduat_asl_log_read_since(&asl_store,
0u,
0u,
&log_records,
&log_count,
&next_logseq,
&log_end)) {
fprintf(stderr, "log read since failed\n");
goto cleanup_sid;
}
{
amduat_asl_projection_checkpoint_v1_t checkpoint;
amduat_asl_projection_checkpoint_v1_t decoded;
memset(&checkpoint, 0, sizeof(checkpoint));
checkpoint.checkpoint_version = AMDUAT_ASL_PROJECTION_CHECKPOINT_VERSION_V1;
checkpoint.projection_name =
amduat_octets("scriptlab", strlen("scriptlab"));
checkpoint.projection_schema_version = 1u;
checkpoint.has_snapshot_id = true;
checkpoint.snapshot_id = index_state.snapshot_id;
checkpoint.log_position = next_logseq;
checkpoint.created_at = 0u;
checkpoint.builder_id =
amduat_octets("scriptlab-adapter", strlen("scriptlab-adapter"));
checkpoint.has_artifact_count = true;
checkpoint.artifact_count = log_count;
if (!amduat_asl_projection_checkpoint_encode_v1(&checkpoint,
&checkpoint_bytes)) {
fprintf(stderr, "checkpoint encode failed\n");
goto cleanup_log_records;
}
if (amduat_asl_record_store_put(
&asl_store,
amduat_octets("projection/checkpoint",
strlen("projection/checkpoint")),
checkpoint_bytes,
&checkpoint_ref) != AMDUAT_ASL_STORE_OK) {
fprintf(stderr, "checkpoint store failed\n");
goto cleanup_checkpoint_bytes;
}
if (!amduat_asl_projection_checkpoint_decode_v1(checkpoint_bytes,
&decoded)) {
fprintf(stderr, "checkpoint decode failed\n");
goto cleanup_checkpoint_ref;
}
if (decoded.log_position != next_logseq) {
fprintf(stderr, "checkpoint log position mismatch\n");
amduat_asl_projection_checkpoint_free(&decoded);
goto cleanup_checkpoint_ref;
}
amduat_asl_projection_checkpoint_free(&decoded);
}
{
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_tgk_store_config_t tgk_config;
amduat_tgk_store_asl_index_fs_t tgk_fs;
amduat_tgk_store_t tgk_store;
amduat_tgk_edge_type_filter_t filter;
amduat_tgk_graph_edge_view_list_t edges;
bool found = false;
memset(&tgk_config, 0, sizeof(tgk_config));
domains[0].encoding_profile = AMDUAT_ENC_ASL1_CORE_V1;
domains[0].hash_id = AMDUAT_HASH_ASL1_ID_SHA256;
tgk_config.id_space.domains = domains;
tgk_config.id_space.domains_len = 1u;
tgk_config.artifact_scope.description = amduat_octets(NULL, 0u);
edge_tags[0] = AMDUAT_TYPE_TAG_TGK1_EDGE_V1;
edge_types[0] = 1u;
encodings[0] = TGK1_EDGE_ENC_V1;
tgk_config.tgk_profiles.edge_tags = edge_tags;
tgk_config.tgk_profiles.edge_tags_len = 1u;
tgk_config.tgk_profiles.edge_types = edge_types;
tgk_config.tgk_profiles.edge_types_len = 1u;
tgk_config.tgk_profiles.encodings = encodings;
tgk_config.tgk_profiles.encodings_len = 1u;
if (!amduat_tgk_store_asl_index_fs_init(&tgk_fs, tgk_config, &asl_fs)) {
fprintf(stderr, "tgk store init failed\n");
goto cleanup_checkpoint_ref;
}
amduat_tgk_store_init(&tgk_store,
tgk_config,
amduat_tgk_store_asl_index_fs_ops(),
&tgk_fs);
filter.types = edge_types;
filter.types_len = 1u;
if (!amduat_tgk_store_edges_from(&tgk_store, node_a, filter, &edges)) {
fprintf(stderr, "tgk edges_from failed\n");
goto cleanup_checkpoint_ref;
}
for (size_t i = 0u; i < edges.len; ++i) {
amduat_tgk_edge_body_t *body = &edges.edges[i].body;
if (ref_list_contains(node_b, body->to, body->to_len)) {
found = true;
break;
}
}
amduat_tgk_graph_edge_view_list_free(&edges);
if (!found) {
fprintf(stderr, "tgk traversal missing expected edge\n");
goto cleanup_checkpoint_ref;
}
}
exit_code = 0;
cleanup_checkpoint_ref:
amduat_reference_free(&checkpoint_ref);
cleanup_log_records:
amduat_enc_asl_log_free(log_records, log_count);
cleanup_checkpoint_bytes:
amduat_octets_free(&checkpoint_bytes);
cleanup_sid:
amduat_octets_free(&sid);
cleanup_program_ref:
amduat_reference_free(&program_ref);
cleanup_snapshot_ref:
amduat_reference_free(&snapshot_ref);
cleanup_edge_ref:
amduat_reference_free(&edge_ref);
cleanup_edge_bytes:
amduat_octets_free(&edge_bytes);
cleanup_nodes:
amduat_reference_free(&edge_payload_ref);
amduat_reference_free(&node_b);
amduat_reference_free(&node_a);
cleanup_root:
if (!remove_tree(root)) {
fprintf(stderr, "cleanup failed\n");
exit_code = 1;
}
free(root);
return exit_code;
}
int main(void) {
return test_scriptlab_adapter_flow();
}