From 27dd9e7b607ab6415d693e0d76ffd4176ab6c605 Mon Sep 17 00:00:00 2001 From: evgeny Date: Sat, 24 Oct 2026 17:30:09 +0200 Subject: [PATCH] test(member-files): preserve unowned download temp on refusal --- tests/test_member_files_network.c | 33 +++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_member_files_network.c b/tests/test_member_files_network.c index f5c4b891..3087add0 100644 --- a/tests/test_member_files_network.c +++ b/tests/test_member_files_network.c @@ -49,6 +49,8 @@ struct network_test { int (*server_receive_ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t); int (*saved_receive_ready)(struct UTUN_INSTANCE*, uint64_t, uint64_t, const uint8_t*, size_t); unsigned restart_tick; + unsigned preservation_case; + char preserved_temp[864]; struct api_case api[8]; void* timer; }; @@ -124,6 +126,27 @@ static int check_file(const char* path, unsigned size) { fclose(file); return ok; } +/* Чужой temp и прежнее назначение должны пережить отказ до создания своего файла. */ +static void preservation_start(struct network_test* t) { + struct api_case* c = &t->api[7]; memset(c, 0, sizeof(*c)); c->id = 207 + t->preservation_case; + snprintf(c->local, sizeof(c->local), "%s/preserved.bin", t->dir); + snprintf(t->preserved_temp, sizeof(t->preserved_temp), "%s.utun-%016llx.part", c->local, (unsigned long long)c->id); + const char* paths[] = { c->local, t->preserved_temp }; + for (unsigned n = 0; n < 2; n++) { + FILE* file = fopen(paths[n], "wb"); + if (!file) { fail(t, "preservation fixture open"); return; } + int error = 0; + for (unsigned i = 0; i < 37; i++) if (fputc((i * 73 + 19) & 255, file) == EOF) error = 1; + if (fclose(file) || error) { fail(t, "preservation fixture write/close"); return; } + } + struct mf_transfer_request q = { .inst = t->nodes[READER], .id = c->id, .group = GROUP, + .server = t->ids[SERVER], .owner = t->ids[AUTHOR], .max_bytes = t->preservation_case == 1 ? BYTES - 1 : 0 }; + snprintf(q.remote, sizeof(q.remote), "file.bin"); snprintf(q.local, sizeof(q.local), "%s", c->local); + c->handle = member_files_transfer_start(&q, api_event, c); + if (!c->handle) { fail(t, "preservation transfer start"); return; } + if (t->preservation_case == 2) member_files_transfer_cancel(c->handle); +} + static int hold_receive(struct UTUN_INSTANCE* inst, uint64_t group, uint64_t peer, const uint8_t* payload, size_t bytes) { (void)inst; (void)group; (void)peer; (void)payload; (void)bytes; return 0; } @@ -298,6 +321,16 @@ static void tick(void* arg) { if (t->api[7].status != MF_STALE || !check_file(t->api[7].local, 0)) { fail(t, "stale download changed existing destination"); return; } + t->phase = 23; preservation_start(t); + } else if (t->phase == 23 && t->api[7].finished) { + const int expected[] = { MF_EXISTS, MF_INVALID_RANGE, MF_CANCELLED }; + int target_ok = check_file(t->api[7].local, 37), temp_ok = check_file(t->preserved_temp, 37); + DEBUG_INFO(DEBUG_CATEGORY_MEDIA, "member_files: preservation case=%u status=%s expected=%s target=%d foreign_temp=%d", + t->preservation_case, mf_status_text(t->api[7].status), mf_status_text(expected[t->preservation_case]), target_ok, temp_ok); + if (t->api[7].status != expected[t->preservation_case] || !target_ok || !temp_ok) { + fail(t, "download refusal/cancel removed unowned temp or changed target"); return; + } + if (++t->preservation_case < 3) { preservation_start(t); goto again; } t->phase = 10; t->answered = 0; struct mf_message q = { .command = MF_STAT, .owner = t->ids[AUTHOR] }; snprintf(q.path, sizeof(q.path), "cancelled.bin"); if (!member_files_request(t->nodes[READER], GROUP, t->ids[SERVER], &q, response, t)) { fail(t, "cancelled STAT"); return; }