tests: cover subgroup file receipts and endpoint storage policy across restart

This commit is contained in:
evgeny committed 2026-11-03 13:39:32 +01:00
1 parent 45f4ca0f31
commit 576236daeb
2 files changed
+108 -3

No files matched your search

+48
View File
@@ -8,6 +8,8 @@
#include "dm/dm_core.h"
#include "dm/dm_crypto.h"
#include "dm/dm_history.h"
#include "dm/dm_media_receipt.h"
#include "media_async/media_crypto.h"
#include "../lib/debug_config.h"
#include "test_utils.h"
#include <assert.h>
@@ -95,6 +97,51 @@ static size_t send_body(int from, const char* conv, const char* text, uint8_t bo
memcpy(body, sqlite3_column_blob(st, 0), len); sqlite3_finalize(st); return len;
}
/* Четыре участника раскрывают один файл; политика M считает только устройства получателя. */
static void group_file(const char* conv, const char* root) {
uint8_t data[1024] = {7}, key[32], secret[32], restored[32];
struct dm_party parties[2];
assert(!dm_conversation_access(nodes[0], conv, parties, NULL));
memcpy(data + DM_MEDIA_DESC_SIZE, "DMKEY001", 8);
uint8_t* keys = data + DM_MEDIA_DESC_SIZE + 8;
uint64_t id = strtoull(conv, NULL, 10);
assert(!dm_file_keys_create(id, nodes[0]->node_id, data, parties, keys, key));
for (unsigned i = 0; i < 4; i++) {
assert(!dm_party_private(nodes[i], parties + i / 2, secret));
assert(!dm_file_keys_open(id, nodes[0]->node_id, data, parties, i / 2, secret, keys, restored));
assert(!memcmp(key, restored, sizeof(key)));
}
char path[1024], cipher[1024];
snprintf(path, sizeof(path), "%s/0/dm_media/07000000000000000000000000000000.file", root);
snprintf(cipher, sizeof(cipher), "%s/0/dm_media/07000000000000000000000000000000.cipher", root);
FILE* plain = fopen(path, "wb+"); FILE* encrypted = fopen(cipher, "wb"); assert(plain && encrypted);
assert(fwrite("payload", 1, 7, plain) == 7); rewind(plain); uint64_t size;
assert(!media_encrypt_stream(key, nodes[0]->node_id, data, plain, encrypted, &size, data + 24));
assert(!fclose(plain) && !fclose(encrypted)); memcpy(data + 16, &size, 8);
struct attachment_info info = {.kind = ATTACHMENT_FILE}; snprintf(info.name, sizeof(info.name), "group.bin");
int n = attachment_encode(&info, data + DM_MEDIA_DESC_SIZE + DM_MEDIA_KEYS_HEADER_SIZE, ATTACHMENT_WIRE_MAX); assert(n > 0);
assert(!dm_send(nodes[0], conv, "file", data, DM_MEDIA_DESC_SIZE + DM_MEDIA_KEYS_HEADER_SIZE + n));
sqlite3_stmt* st = NULL;
assert(sqlite3_prepare_v2(nodes[0]->topo_sqlite_db, "SELECT body FROM dm_files", -1, &st, NULL) == SQLITE_OK);
assert(sqlite3_step(st) == SQLITE_ROW); uint8_t body[DM_BODY_MAX], r[DM_MEDIA_RECEIPT_SIZE], ack[DM_RECEIPT_SIZE];
size_t len = (size_t)sqlite3_column_bytes(st, 0); memcpy(body, sqlite3_column_blob(st, 0), len); sqlite3_finalize(st);
for (unsigned i = 1; i < 4; i++) assert(!dm_accept_message(nodes[i], body, len, ack));
struct dm_message_view view; assert(!dm_message_parse(body, len, &view));
assert(dm_media_user_action(nodes[2], conv, view.author, view.seq, 1) < 0); /* Само наличие сообщения — не открытие. */
assert(dm_media_receipt_make(nodes[1], body, len, 1, r) < 0); /* Свой узел не удаляет серверный файл. */
assert(dm_media_receipt_make(nodes[4], body, len, 0, r) < 0);
assert(!dm_media_retention_done(nodes[0], data, 0) && !dm_media_retention_done(nodes[0], data, 1));
assert(!dm_media_receipt_make(nodes[2], body, len, 0, r) && !dm_media_receipt_save(nodes[0], r, body, len));
assert(!dm_media_receipt_save(nodes[0], r, body, len)); /* Повтор не увеличивает число получателей. */
assert(!dm_media_retention_done(nodes[0], data, 0) && !dm_media_retention_done(nodes[0], data, 1));
assert(!dm_media_receipt_make(nodes[2], body, len, 1, r) && !dm_media_receipt_save(nodes[0], r, body, len));
assert(!dm_media_retention_done(nodes[0], data, 0) && dm_media_retention_done(nodes[0], data, 1) == 1);
assert(!dm_media_receipt_make(nodes[3], body, len, 0, r) && !dm_media_receipt_save(nodes[0], r, body, len));
assert(dm_media_retention_done(nodes[0], data, 0) == 1);
assert(scalar(0, "SELECT count(*) FROM dm_file_targets WHERE side=0 AND received IS NULL") == 1);
r[56] ^= 1; assert(dm_media_receipt_save(nodes[0], r, body, len) < 0);
}
int main(void) {
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); debug_set_category_level(DEBUG_CATEGORY_DM, DEBUG_LEVEL_DEBUG);
char root[512] = "/tmp/utun_dm_groups_XXXXXX"; assert(!test_mkdtemp(root));
@@ -131,6 +178,7 @@ int main(void) {
assert(view.mode == DM_MODE_DEVICE && view.seq == 2 && !dm_accept_message(nodes[4], reply, n, receipt));
assert(start(0, 1, DM_MODE_DEVICE, other) == dm_derive_conv_id(nodes[0]->node_id, nodes[1]->node_id));
n = send_body(0, other, "own device", reply); assert(!dm_accept_message(nodes[1], reply, n, receipt));
group_file(conv, root);
/* Выход атомарен: ошибка SQL не удаляет секреты и не закрывает беседу. */
assert(!sqlite3_exec(nodes[0]->topo_sqlite_db, "CREATE TEMP TRIGGER fail_close BEFORE UPDATE OF closed ON dm_conversations"
" BEGIN SELECT RAISE(ABORT,'injected closure failure'); END", NULL, NULL, NULL));