sqlite persistence: the store moves from db.json to dist.db (P5.2)
src/repo/db.sx persists the whole Repo to <store>/dist.db through the vendored SQLite bindings, keeping the load-whole/save-whole call shape. One table per entity; enums as lowercase variant names; list order round-trips via rowid. Enforced uniqueness: apps.slug, channels(app_id, name), tokens.token_hash; lookup indexes on releases(app_id) and artifacts(sha256) (non-unique - identical bytes may ship in several releases). save is DELETE-all + INSERT-all inside BEGIN IMMEDIATE...COMMIT with rollback on failure; every connection sets busy_timeout so the CLI and a running distd interleave safely. A store holding only a pre-SQLite db.json imports once on first load, then the file is renamed db.json.imported; a store with neither starts empty. Consumers gate on db.store_exists instead of probing db.json. The JSON read-back stays for the import path; the entity->json writers stay for distd's /api responses. Tests that parsed db.json directly now assert by querying dist.db through the SQLite bindings; tests/db_import.sx pins the import path; tests/repo_roundtrip.sx pins the SQLite round-trip. make test 22/22.
This commit is contained in:
@@ -16,12 +16,13 @@
|
||||
// * any other path → JSON error http.not_found
|
||||
//
|
||||
// FRESHNESS is asserted by publishing a SECOND version while the server
|
||||
// is running: the next /api/apps/<slug> must list both releases (db.json
|
||||
// is reloaded per request — no stale cache).
|
||||
// is running: the next /api/apps/<slug> must list both releases (the
|
||||
// store database is reloaded per request — no stale cache).
|
||||
#import "modules/std.sx";
|
||||
#import "modules/std/json.sx";
|
||||
process :: #import "modules/std/process.sx";
|
||||
fs :: #import "modules/std/fs.sx";
|
||||
sq :: #import "../src/db/sqlite.sx";
|
||||
|
||||
STORE :: ".sx-tmp/server_http";
|
||||
MDIR :: ".sx-tmp/server_http_m";
|
||||
@@ -162,10 +163,17 @@ main :: () -> i32 {
|
||||
print(" api routes ok\n");
|
||||
|
||||
// ── download: bytes identical to the source fixture ───────────────
|
||||
db_bytes := fs.read_file(path_join(STORE, "db.json"));
|
||||
process.assert(db_bytes != null, "db.json must exist");
|
||||
dbo := parse_body(db_bytes!, "db.json", xx arena);
|
||||
sha := get_str(get_arr(dbo, "artifacts").items[0].object, "sha256");
|
||||
// The published artifact's digest, read from the store database.
|
||||
conn, oe := sq.Sqlite.open_v2(path_join(STORE, "dist.db"), sq.SQLITE_OPEN_READONLY);
|
||||
process.assert(!oe, "dist.db must open as a SQLite database");
|
||||
conn.busy_timeout(2000);
|
||||
stq, pe := conn.prepare("SELECT sha256 FROM artifacts ORDER BY rowid");
|
||||
process.assert(!pe, "artifact digest query must prepare");
|
||||
src, se := stq.step();
|
||||
process.assert(!se and src == sq.SQLITE_ROW, "store must record the artifact");
|
||||
sha := stq.column_text(0);
|
||||
stq.finalize();
|
||||
conn.close();
|
||||
|
||||
dl := process.run(concat(concat(concat(concat("curl -s -m 2 -o .sx-tmp/server_http_dl.bin ", BASE), "/download/"), sha), " && cmp -s .sx-tmp/server_http_dl.bin examples/fixtures/acme-1.2.3-android.apk && echo SAME"));
|
||||
process.assert(dl != null, "download curl spawn failed");
|
||||
|
||||
Reference in New Issue
Block a user