Tighten result regression snapshots and expose SQL transition diagnostics

This commit is contained in:
evgeny committed 2026-10-26 23:07:50 +01:00
1 parent 935bd704ae
commit 03d1afbd13
2 files changed
+27 -11

No files matched your search

+15 -3
View File
@@ -426,6 +426,8 @@ static int ak_apply_result(struct UTUN_INSTANCE* inst, const char* channel, uint
int incoming = sqlite3_column_int(st, 0), state = sqlite3_column_int(st, 1), queued = sqlite3_column_int(st, 2);
stage = "read finalize"; rc = sqlite3_finalize(st); st = NULL;
if (rc != SQLITE_OK) goto fail;
DEBUG_DEBUG(DEBUG_CATEGORY_MEDIA, "attachment keys: result before transaction writes channel=%s peer=%llu command=%u incoming=%d state=%d queued=%d",
channel, (unsigned long long)peer, cmd, incoming, state, queued);
if ((incoming != 0 && incoming != 1) || state < 0 || state > 4 ||
(cmd == AK_RESULT_ACK ? !incoming || (state != 2 && state != 3 && state != 4) : incoming || state > 3) ||
((cmd == AK_GRANT || cmd == AK_DENY) && ((state == 2 && cmd != AK_GRANT) || (state == 3 && cmd != AK_DENY)))) {
@@ -454,7 +456,10 @@ static int ak_apply_result(struct UTUN_INSTANCE* inst, const char* channel, uint
changed = sqlite3_changes(db);
stage = "key finalize"; rc = sqlite3_finalize(st); st = NULL;
if (rc != SQLITE_OK) goto fail;
if (changed != 1) { stage = "key row count"; rc = SQLITE_CONSTRAINT; goto fail; }
if (changed != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment keys: result key rows=%d expected=1 channel=%s", changed, channel);
stage = "key row count"; rc = SQLITE_CONSTRAINT; goto fail;
}
}
int next = cmd == AK_REQUEST_ACK ? (state == 0 ? 1 : state) : cmd == AK_RESULT_ACK ? (state == 3 ? 3 : 4) : cmd == AK_GRANT ? 2 : 3;
stage = "state prepare";
@@ -473,7 +478,10 @@ static int ak_apply_result(struct UTUN_INSTANCE* inst, const char* channel, uint
changed = sqlite3_changes(db);
stage = "state finalize"; rc = sqlite3_finalize(st); st = NULL;
if (rc != SQLITE_OK) goto fail;
if (changed != 1) { stage = "state row count"; rc = SQLITE_CONSTRAINT; goto fail; }
if (changed != 1) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment keys: result state rows=%d expected=1 channel=%s", changed, channel);
stage = "state row count"; rc = SQLITE_CONSTRAINT; goto fail;
}
int command = cmd == AK_RESULT_ACK ? (state == 3 ? AK_DENY : AK_GRANT) : AK_REQUEST;
stage = "outbox prepare";
rc = sqlite3_prepare_v2(db, "DELETE FROM attachment_key_outbox WHERE channel=? AND message_id=? AND peer=? AND command=?", -1, &st, NULL);
@@ -492,7 +500,11 @@ static int ak_apply_result(struct UTUN_INSTANCE* inst, const char* channel, uint
stage = "outbox finalize"; rc = sqlite3_finalize(st); st = NULL;
if (rc != SQLITE_OK) goto fail;
/* REQUEST_ACK мог удалить REQUEST раньше GRANT; дубликат результата также не требует строки outbox. */
if (changed != (queued == command)) { stage = "outbox row count"; rc = SQLITE_CONSTRAINT; goto fail; }
if (changed != (queued == command)) {
DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "attachment keys: result outbox rows=%d expected=%d channel=%s command=%d",
changed, queued == command, channel, command);
stage = "outbox row count"; rc = SQLITE_CONSTRAINT; goto fail;
}
stage = "commit"; rc = sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
if (rc != SQLITE_OK) goto fail;
*next_out = next;
+12 -8
View File
@@ -41,10 +41,10 @@ static struct fixture* active;
static enum operation operation(const char* sql) {
if (!sql) return NO_OPERATION;
if (!strncmp(sql, "SELECT r.incoming,r.state,o.command", 34)) return READ;
if (!strncmp(sql, "UPDATE attachment_keys SET key=?,error=0 WHERE", 45)) return KEY;
if (!strncmp(sql, "UPDATE attachment_key_requests SET state=? WHERE", 47)) return STATE;
if (!strncmp(sql, "DELETE FROM attachment_key_outbox WHERE", 38)) return OUTBOX;
static const char* prefixes[] = {NULL, "SELECT r.incoming,r.state,o.command", "UPDATE attachment_keys SET key=?,error=0 WHERE",
"UPDATE attachment_key_requests SET state=? WHERE", "DELETE FROM attachment_key_outbox WHERE"};
for (enum operation op = READ; op <= OUTBOX; op++)
if (!strncmp(sql, prefixes[op], strlen(prefixes[op]))) return op;
return NO_OPERATION;
}
@@ -56,7 +56,7 @@ static int take(enum failure point, enum operation op, int index) {
extern int __real_sqlite3_exec(sqlite3*, const char*, int (*)(void*, int, char**, char**), void*, char**);
int __wrap_sqlite3_exec(sqlite3* db, const char* sql, int (*cb)(void*, int, char**, char**), void* arg, char** error) {
if (active && db == active->inst->topo_sqlite_db) {
if (active && active->inst && db == active->inst->topo_sqlite_db) {
if (!strcmp(sql, "BEGIN IMMEDIATE") && take(BEGIN, NO_OPERATION, 0)) return SQLITE_BUSY;
if (!strcmp(sql, "COMMIT") && take(COMMIT, NO_OPERATION, 0)) return SQLITE_BUSY;
if (!strcmp(sql, "ROLLBACK") && fail_rollback) {
@@ -147,7 +147,10 @@ static struct snapshot snapshot(struct fixture* f) {
/* Reader проверяет обе целевые строки и не затронутую соседнюю запись без чтения секретов в лог. */
static void equal(struct fixture* f, struct snapshot expected) {
struct snapshot current = snapshot(f); assert(!memcmp(&current, &expected, sizeof(current)));
struct snapshot current = snapshot(f);
assert(current.key_bytes == expected.key_bytes && current.error == expected.error && current.incoming == expected.incoming);
assert(current.state == expected.state && current.queued == expected.queued);
assert(!current.key_bytes || !memcmp(current.key, expected.key, 32));
sqlite3_stmt* st = NULL;
assert(sqlite3_prepare_v2(f->reader, "SELECT key,error FROM attachment_keys WHERE channel='900'", -1, &st, NULL) == SQLITE_OK);
assert(sqlite3_step(st) == SQLITE_ROW && sqlite3_column_bytes(st, 0) == 32 && sqlite3_column_int(st, 1) == 7);
@@ -213,9 +216,10 @@ static void reset(struct fixture* f, int command) {
f->room = 1; memset(f->key, 0, sizeof(f->key)); f->key[0] = 55;
int author = command == RESULT_ACK, state = author ? 2 : command == REQUEST_ACK ? 0 : 1;
sqlite3_stmt* st = NULL;
assert(sqlite3_prepare_v2(f->inst->topo_sqlite_db, "UPDATE attachment_keys SET key=?,error=1 WHERE channel=?", -1, &st, NULL) == SQLITE_OK);
assert(sqlite3_prepare_v2(f->inst->topo_sqlite_db, "UPDATE attachment_keys SET key=?,error=1,author=? WHERE channel=?", -1, &st, NULL) == SQLITE_OK);
assert((author ? sqlite3_bind_blob(st, 1, f->key, 32, SQLITE_STATIC) : sqlite3_bind_null(st, 1)) == SQLITE_OK);
assert(sqlite3_bind_text(st, 2, f->channel, -1, SQLITE_STATIC) == SQLITE_OK && sqlite3_step(st) == SQLITE_DONE);
assert(sqlite3_bind_int64(st, 2, (sqlite3_int64)(author ? f->inst->node_id : 123)) == SQLITE_OK);
assert(sqlite3_bind_text(st, 3, f->channel, -1, SQLITE_STATIC) == SQLITE_OK && sqlite3_step(st) == SQLITE_DONE);
assert(sqlite3_finalize(st) == SQLITE_OK);
char query[512];
snprintf(query, sizeof(query), "UPDATE attachment_key_requests SET incoming=%d,state=%d WHERE channel='%s';"