/* test_store.c — M1 gate for the engram paged store (engram_store.{c,h}). * * Pure C. Build: gcc -O2 test_store.c ../../lang/runtime/engram_store.c -o test_store * Writes ONLY under a throwaway /tmp dir. Never touches ~/.neuron or live ports. * * Covers §7 M1 gates: round-trip (5k nodes / 20k edges, all fields, emb bit-exact, * hebb, >page content), TLV forward-compat, overflow chains, B+-tree indexes * across splits, free-list reuse, and corruption/superblock recovery. */ #include "../../lang/runtime/engram_store.h" #include #include #include #include #include #include #include static int g_pass = 0, g_fail = 0; static void ok(const char* name, int cond){ printf(" [%s] %s\n", cond ? "PASS" : "FAIL", name); if (cond) g_pass++; else g_fail++; } static char g_dir[512]; static void mk_dir(void){ snprintf(g_dir, sizeof g_dir, "/tmp/engram-store-test-%d", (int)getpid()); mkdir(g_dir, 0700); } static void path_in(char* out, size_t cap, const char* name){ snprintf(out, cap, "%s/%s", g_dir, name); } static long file_size(const char* p){ struct stat st; return stat(p,&st)==0 ? (long)st.st_size : -1; } /* ── deterministic RNG so oracle nodes/edges regenerate bit-exact ─────────── */ static uint64_t xs(uint64_t* s){ uint64_t x=*s; x^=x<<13; x^=x>>7; x^=x<<17; *s=x; return x; } static uint64_t node_seed(int i){ return 0x9E3779B97F4A7C15ULL ^ ((uint64_t)(i+1)*0xD1B54A32D192ED03ULL); } static uint64_t edge_seed(int i){ return 0xC2B2AE3D27D4EB4FULL ^ ((uint64_t)(i+1)*0x165667B19E3779F9ULL); } static char* rnd_str(uint64_t* st, size_t len){ char* s = (char*)malloc(len + 1); for (size_t i=0;ipage content to force overflow chains. */ #define NODE_COUNT 5000 #define EDGE_COUNT 20000 #define EMB_DIM 768 static void gen_node(int i, StoreNode* n){ memset(n, 0, sizeof *n); uint64_t st = node_seed(i); char id[32]; snprintf(id, sizeof id, "node-%d", i); n->id = strdup(id); size_t clen = (i % 500 == 0) ? (size_t)(17000 + (xs(&st) % 6000)) : (size_t)(xs(&st) % 300); n->content = rnd_str(&st, clen); n->node_type = rnd_str(&st, 4 + (xs(&st) % 8)); n->label = (i % 2) ? rnd_str(&st, 3 + (xs(&st) % 10)) : NULL; n->tier = rnd_str(&st, 4 + (xs(&st) % 6)); n->tags = rnd_str(&st, xs(&st) % 40); n->metadata = (i % 3) ? rnd_str(&st, xs(&st) % 60) : NULL; n->salience = (double)(xs(&st) % 1000000) / 997.0; n->importance = (double)(xs(&st) % 1000000) / 131.0; n->confidence = (double)(xs(&st) % 1000000) / 733.0; n->temporal_decay_rate = (double)(xs(&st) % 1000000) / 101.0; n->activation_count = (int64_t)(xs(&st) % 100000); n->last_activated = (int64_t)xs(&st); n->created_at = (int64_t)(1600000000000LL + i); n->updated_at = (int64_t)xs(&st); n->background_activation = (double)(xs(&st) % 1000000) / 17.0; n->working_memory_weight = (double)(xs(&st) % 1000000) / 29.0; n->suppression_count = (int32_t)(xs(&st) % 50); n->layer_id = (uint32_t)(xs(&st) % 5); for (int k=0;kaccess_ts[k] = (int64_t)xs(&st); n->access_head = (int32_t)(xs(&st) % STORE_BLL_K); n->access_filled = (int32_t)(xs(&st) % (STORE_BLL_K + 1)); n->wm_anchor = (double)(xs(&st) % 1000000) / 3.0; n->emb = (float*)malloc(EMB_DIM * sizeof(float)); for (int k=0;kemb[k], &u, 4); } n->emb_dim = EMB_DIM; } static void gen_edge(int i, StoreEdge* e){ memset(e, 0, sizeof *e); uint64_t st = edge_seed(i); char id[32], from[32], to[32]; snprintf(id, sizeof id, "edge-%d", i); snprintf(from, sizeof from, "node-%d", (int)(xs(&st) % NODE_COUNT)); snprintf(to, sizeof to, "node-%d", (int)(xs(&st) % NODE_COUNT)); e->id = strdup(id); e->from_id = strdup(from); e->to_id = strdup(to); e->relation = rnd_str(&st, 3 + (xs(&st) % 12)); e->metadata = (i % 4) ? rnd_str(&st, xs(&st) % 40) : NULL; e->weight = (double)(xs(&st) % 1000000) / 111.0; e->hebb = (double)(xs(&st) % 1000000) / 1000000.0; /* the learned field */ e->confidence = (double)(xs(&st) % 1000000) / 777.0; e->created_at = (int64_t)(1600000000000LL + i); e->updated_at = (int64_t)xs(&st); e->last_fired = (int64_t)xs(&st); e->inhibitory = (int32_t)(xs(&st) % 2); e->layer_id = (uint32_t)(xs(&st) % 5); } static int streq(const char* a, const char* b){ if (!a && !b) return 1; if (!a || !b) return 0; return strcmp(a,b)==0; } static int cmp_node(const StoreNode* a, const StoreNode* b){ if (!streq(a->id,b->id) || !streq(a->content,b->content) || !streq(a->node_type,b->node_type) || !streq(a->label,b->label) || !streq(a->tier,b->tier) || !streq(a->tags,b->tags) || !streq(a->metadata,b->metadata)) return 0; if (a->salience!=b->salience || a->importance!=b->importance || a->confidence!=b->confidence || a->temporal_decay_rate!=b->temporal_decay_rate || a->activation_count!=b->activation_count || a->last_activated!=b->last_activated || a->created_at!=b->created_at || a->updated_at!=b->updated_at || a->background_activation!=b->background_activation || a->working_memory_weight!=b->working_memory_weight || a->suppression_count!=b->suppression_count || a->layer_id!=b->layer_id || a->access_head!=b->access_head || a->access_filled!=b->access_filled || a->wm_anchor!=b->wm_anchor || a->emb_dim!=b->emb_dim) return 0; for (int k=0;kaccess_ts[k]!=b->access_ts[k]) return 0; if ((a->emb==NULL) != (b->emb==NULL)) return 0; if (a->emb && memcmp(a->emb, b->emb, (size_t)a->emb_dim*4)!=0) return 0; return 1; } static int cmp_edge(const StoreEdge* a, const StoreEdge* b){ if (!streq(a->id,b->id) || !streq(a->from_id,b->from_id) || !streq(a->to_id,b->to_id) || !streq(a->relation,b->relation) || !streq(a->metadata,b->metadata)) return 0; if (a->weight!=b->weight || a->hebb!=b->hebb || a->confidence!=b->confidence || a->created_at!=b->created_at || a->updated_at!=b->updated_at || a->last_fired!=b->last_fired || a->inhibitory!=b->inhibitory || a->layer_id!=b->layer_id) return 0; return 1; } static void free_node_fields(StoreNode* n){ free(n->id); free(n->content); free(n->node_type); free(n->label); free(n->tier); free(n->tags); free(n->metadata); free(n->emb); free(n->unknown); } static void free_edge_fields(StoreEdge* e){ free(e->id); free(e->from_id); free(e->to_id); free(e->relation); free(e->metadata); free(e->unknown); } /* Flip one byte in the store file at (page*PAGE_SIZE + off). */ static void flip_byte(const char* path, uint64_t page, size_t off){ int fd = open(path, O_RDWR); uint8_t b; off_t at = (off_t)page*STORE_PAGE_SIZE + off; pread(fd, &b, 1, at); b ^= 0xFF; pwrite(fd, &b, 1, at); close(fd); } /* ════════════════════════════════════════════════════════════════════════ */ static void test_roundtrip(void){ printf("\n== round-trip: %d nodes + %d edges, all fields, emb bit-exact ==\n", NODE_COUNT, EDGE_COUNT); char path[600]; path_in(path, sizeof path, "roundtrip.store"); unlink(path); EngramPagedStore* s = store_create(path); ok("store_create", s != NULL); if (!s) return; for (int i=0;i 0); for (int i=0;i= 1); printf(" store_check reported %d corrupt page(s)\n", bad); store_close(s); /* fresh store, corrupt superblock 0, must recover via mirror superblock 1 */ char p2[600]; path_in(p2, sizeof p2, "sbrec.store"); unlink(p2); s = store_create(p2); StoreNode n; gen_node(42,&n); store_put_node(s,&n); store_close(s); /* trash magic + crc region of page 0 */ flip_byte(p2, 0, 0); flip_byte(p2, 0, 1); flip_byte(p2, 0, 90); s = store_open(p2); ok("open recovers via mirror superblock (page 1)", s != NULL); if (s){ StoreNode g; int r = store_get_node(s, "node-42", &g); ok("data intact after superblock recovery", r==1 && cmp_node(&n,&g)); if (r==1) store_node_free(&g); store_close(s); } free_node_fields(&n); } int main(void){ mk_dir(); printf("engram_store M1 test harness — dir=%s\n", g_dir); test_roundtrip(); test_forward_compat(); test_overflow(); test_index_splits(); test_freelist(); test_corruption(); printf("\n================ %d passed, %d failed ================\n", g_pass, g_fail); return g_fail ? 1 : 0; }