#include "amduat/fed/view.h" #include #include #include typedef struct { amduat_reference_t ref; amduat_artifact_t artifact; } stub_store_entry_t; typedef struct { amduat_asl_store_config_t config; stub_store_entry_t entries[4]; size_t entries_len; } stub_store_t; static void stub_store_init(stub_store_t *store) { memset(store, 0, sizeof(*store)); store->config.hash_id = 1; } static amduat_asl_store_error_t stub_store_get(void *ctx, amduat_reference_t ref, amduat_artifact_t *out) { stub_store_t *store = (stub_store_t *)ctx; size_t i; for (i = 0; i < store->entries_len; ++i) { if (amduat_reference_eq(store->entries[i].ref, ref)) { amduat_artifact_t src = store->entries[i].artifact; uint8_t *payload = NULL; if (src.bytes.len != 0u) { payload = (uint8_t *)malloc(src.bytes.len); if (payload == NULL) { return AMDUAT_ASL_STORE_ERR_IO; } memcpy(payload, src.bytes.data, src.bytes.len); } out->bytes = amduat_octets(payload, src.bytes.len); out->has_type_tag = src.has_type_tag; out->type_tag = src.type_tag; return AMDUAT_ASL_STORE_OK; } } return AMDUAT_ASL_STORE_ERR_NOT_FOUND; } static amduat_reference_t make_ref(amduat_hash_id_t hash_id, const uint8_t *bytes, size_t len) { return amduat_reference(hash_id, amduat_octets(bytes, len)); } static amduat_fed_record_t make_record(uint32_t domain_id, uint8_t visibility, uint64_t logseq, amduat_reference_t ref) { amduat_fed_record_t record; memset(&record, 0, sizeof(record)); record.meta.domain_id = domain_id; record.meta.visibility = visibility; record.meta.has_source = 0; record.id.type = AMDUAT_FED_REC_ARTIFACT; record.id.ref = ref; record.logseq = logseq; record.snapshot_id = 1; record.log_prefix = 10; return record; } static int test_view_and_resolve(void) { uint8_t a_bytes[] = {0x01}; uint8_t b_bytes[] = {0x02}; uint8_t c_bytes[] = {0x03}; uint8_t d_bytes[] = {0x04}; amduat_reference_t ref_a = make_ref(1, a_bytes, sizeof(a_bytes)); amduat_reference_t ref_b = make_ref(1, b_bytes, sizeof(b_bytes)); amduat_reference_t ref_c = make_ref(1, c_bytes, sizeof(c_bytes)); amduat_reference_t ref_d = make_ref(1, d_bytes, sizeof(d_bytes)); amduat_fed_record_t records[4]; amduat_fed_view_bounds_t bounds[2]; amduat_fed_view_t view; amduat_fed_policy_deny_t denies[1]; stub_store_t stub; amduat_asl_store_ops_t ops; amduat_asl_store_t store; amduat_artifact_t artifact; records[0] = make_record(1, 0, 1, ref_a); records[1] = make_record(1, 1, 2, ref_b); records[2] = make_record(2, 1, 1, ref_c); records[3] = make_record(2, 0, 1, ref_d); bounds[0].domain_id = 1; bounds[0].snapshot_id = 1; bounds[0].log_prefix = 10; bounds[1].domain_id = 2; bounds[1].snapshot_id = 1; bounds[1].log_prefix = 10; denies[0].id.type = AMDUAT_FED_REC_ARTIFACT; denies[0].id.ref = ref_d; denies[0].reason_code = 0; if (!amduat_fed_view_build(records, 4, 1, bounds, 2, denies, 1, &view)) { fprintf(stderr, "view build failed\n"); return 1; } if (view.len != 3) { fprintf(stderr, "view size mismatch\n"); amduat_fed_view_free(&view); return 1; } stub_store_init(&stub); stub.entries[0].ref = ref_a; stub.entries[0].artifact = amduat_artifact(amduat_octets(a_bytes, sizeof(a_bytes))); stub.entries_len = 1; amduat_asl_store_ops_init(&ops); ops.get = stub_store_get; amduat_asl_store_init(&store, stub.config, ops, &stub); if (amduat_fed_resolve(&view, &store, ref_a, &artifact) != AMDUAT_FED_RESOLVE_OK) { fprintf(stderr, "resolve local failed\n"); amduat_fed_view_free(&view); return 1; } amduat_artifact_free(&artifact); if (amduat_fed_resolve(&view, &store, ref_c, &artifact) != AMDUAT_FED_RESOLVE_FOUND_REMOTE_NO_BYTES) { fprintf(stderr, "resolve remote mismatch\n"); amduat_fed_view_free(&view); return 1; } if (amduat_fed_resolve(&view, &store, ref_d, &artifact) != AMDUAT_FED_RESOLVE_POLICY_DENIED) { fprintf(stderr, "resolve policy denied mismatch\n"); amduat_fed_view_free(&view); return 1; } amduat_fed_view_free(&view); return 0; } static int test_view_conflict(void) { uint8_t key[] = {0x09}; amduat_reference_t ref = make_ref(1, key, sizeof(key)); amduat_fed_record_t records[2]; amduat_fed_view_bounds_t bounds[2]; amduat_fed_view_t view; records[0] = make_record(1, 1, 1, ref); records[1] = make_record(2, 1, 1, ref); bounds[0].domain_id = 1; bounds[0].snapshot_id = 1; bounds[0].log_prefix = 10; bounds[1].domain_id = 2; bounds[1].snapshot_id = 1; bounds[1].log_prefix = 10; if (amduat_fed_view_build(records, 2, 1, bounds, 2, NULL, 0, &view)) { fprintf(stderr, "expected conflict\n"); amduat_fed_view_free(&view); return 1; } return 0; } static int test_view_rebuild_metadata(void) { uint8_t key[] = {0x08}; amduat_reference_t ref = make_ref(1, key, sizeof(key)); amduat_fed_record_t records[1]; amduat_fed_view_bounds_t bounds[1]; amduat_fed_view_t view_a; amduat_fed_view_t view_b; records[0] = make_record(1, 1, 1, ref); records[0].meta.has_source = 1; records[0].meta.source_domain = 5; bounds[0].domain_id = 1; bounds[0].snapshot_id = 1; bounds[0].log_prefix = 10; if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_a)) { fprintf(stderr, "view rebuild build A failed\n"); return 1; } if (!amduat_fed_view_build(records, 1, 1, bounds, 1, NULL, 0, &view_b)) { fprintf(stderr, "view rebuild build B failed\n"); amduat_fed_view_free(&view_a); return 1; } if (view_a.len != 1 || view_b.len != 1) { fprintf(stderr, "view rebuild length mismatch\n"); amduat_fed_view_free(&view_a); amduat_fed_view_free(&view_b); return 1; } if (view_a.records[0].meta.has_source != view_b.records[0].meta.has_source || view_a.records[0].meta.source_domain != view_b.records[0].meta.source_domain || view_a.records[0].meta.visibility != view_b.records[0].meta.visibility) { fprintf(stderr, "view rebuild metadata mismatch\n"); amduat_fed_view_free(&view_a); amduat_fed_view_free(&view_b); return 1; } amduat_fed_view_free(&view_a); amduat_fed_view_free(&view_b); return 0; } int main(void) { if (test_view_and_resolve() != 0) { return 1; } if (test_view_conflict() != 0) { return 1; } if (test_view_rebuild_metadata() != 0) { return 1; } return 0; }