|
| 1 | +// |
| 2 | +// network_unit.c |
| 3 | +// cloudsync |
| 4 | +// |
| 5 | +// Unit tests for the network layer's pure response-handling logic. Built with |
| 6 | +// networking ENABLED (unlike dist/unit, which is -DCLOUDSYNC_OMIT_NETWORK), so it |
| 7 | +// can call the internal functions directly on crafted in-memory NETWORK_RESULT |
| 8 | +// buffers — no server, no sockets. |
| 9 | +// |
| 10 | + |
| 11 | +#include <stdio.h> |
| 12 | +#include <string.h> |
| 13 | +#include <stdbool.h> |
| 14 | +#include <stdint.h> |
| 15 | +#include <inttypes.h> |
| 16 | +#include "network_private.h" |
| 17 | + |
| 18 | +static int failures = 0; |
| 19 | + |
| 20 | +static void check(const char *name, bool ok) { |
| 21 | + printf("%-64s %s\n", name, ok ? "OK" : "FAIL"); |
| 22 | + if (!ok) failures++; |
| 23 | +} |
| 24 | + |
| 25 | +static NETWORK_RESULT json_buffer(char *json) { |
| 26 | + NETWORK_RESULT r = {0}; |
| 27 | + r.code = CLOUDSYNC_NETWORK_BUFFER; |
| 28 | + r.buffer = json; |
| 29 | + r.blen = strlen(json); |
| 30 | + return r; |
| 31 | +} |
| 32 | + |
| 33 | +// Regression: lastOptimisticVersion must track the LATEST valid value, including a |
| 34 | +// decrease. The server can roll the optimistic version back when a later send chunk |
| 35 | +// fails; since it becomes the durable send checkpoint, a monotonic "max" would mask |
| 36 | +// the rollback and skip the rolled-back changes on the next send. |
| 37 | +static bool test_optimistic_version_rollback(void) { |
| 38 | + int64_t optimistic = -1, confirmed = -1; |
| 39 | + int gaps = -1; |
| 40 | + char *apply = NULL, *check_fail = NULL; |
| 41 | + bool ok = true; |
| 42 | + |
| 43 | + char j1[] = "{\"lastOptimisticVersion\":50,\"lastConfirmedVersion\":10}"; |
| 44 | + NETWORK_RESULT r1 = json_buffer(j1); |
| 45 | + network_sync_state_update_from_response(&r1, &optimistic, &confirmed, &gaps, &apply, &check_fail); |
| 46 | + ok = ok && optimistic == 50 && confirmed == 10; |
| 47 | + |
| 48 | + char j2[] = "{\"lastOptimisticVersion\":100,\"lastConfirmedVersion\":20}"; |
| 49 | + NETWORK_RESULT r2 = json_buffer(j2); |
| 50 | + network_sync_state_update_from_response(&r2, &optimistic, &confirmed, &gaps, &apply, &check_fail); |
| 51 | + ok = ok && optimistic == 100 && confirmed == 20; |
| 52 | + |
| 53 | + // Server rolls back on a later chunk error: the value must DECREASE to 50. |
| 54 | + char j3[] = "{\"lastOptimisticVersion\":50,\"lastConfirmedVersion\":20}"; |
| 55 | + NETWORK_RESULT r3 = json_buffer(j3); |
| 56 | + network_sync_state_update_from_response(&r3, &optimistic, &confirmed, &gaps, &apply, &check_fail); |
| 57 | + ok = ok && optimistic == 50; |
| 58 | + |
| 59 | + // A response missing the field (parsed -1) must NOT clobber the current value. |
| 60 | + char j4[] = "{\"lastConfirmedVersion\":20}"; |
| 61 | + NETWORK_RESULT r4 = json_buffer(j4); |
| 62 | + network_sync_state_update_from_response(&r4, &optimistic, &confirmed, &gaps, &apply, &check_fail); |
| 63 | + ok = ok && optimistic == 50; |
| 64 | + |
| 65 | + ok = ok && apply == NULL && check_fail == NULL; // no failures object in these responses |
| 66 | + return ok; |
| 67 | +} |
| 68 | + |
| 69 | +// A non-BUFFER result (or NULL buffer) must leave the accumulators untouched. |
| 70 | +static bool test_non_buffer_is_noop(void) { |
| 71 | + int64_t optimistic = 7, confirmed = 3; |
| 72 | + int gaps = 0; |
| 73 | + char *apply = NULL, *check_fail = NULL; |
| 74 | + |
| 75 | + NETWORK_RESULT err = {0}; |
| 76 | + err.code = CLOUDSYNC_NETWORK_ERROR; |
| 77 | + network_sync_state_update_from_response(&err, &optimistic, &confirmed, &gaps, &apply, &check_fail); |
| 78 | + return optimistic == 7 && confirmed == 3 && gaps == 0; |
| 79 | +} |
| 80 | + |
| 81 | +static bool test_compute_status(void) { |
| 82 | + bool ok = true; |
| 83 | + ok = ok && strcmp(network_compute_status(100, 100, 0, 100), "synced") == 0; |
| 84 | + ok = ok && strcmp(network_compute_status(100, 50, 0, 100), "syncing") == 0; |
| 85 | + ok = ok && strcmp(network_compute_status(100, 100, 1, 100), "out-of-sync") == 0; // gaps |
| 86 | + ok = ok && strcmp(network_compute_status(90, 90, 0, 100), "out-of-sync") == 0; // behind local |
| 87 | + ok = ok && strcmp(network_compute_status(-1, 100, 0, 100), "error") == 0; // unparsed |
| 88 | + return ok; |
| 89 | +} |
| 90 | + |
| 91 | +int main(void) { |
| 92 | + printf("\nNetwork unit tests\n"); |
| 93 | + check("optimistic/confirmed version folds latest-valid (allows rollback):", test_optimistic_version_rollback()); |
| 94 | + check("non-buffer response is a no-op:", test_non_buffer_is_noop()); |
| 95 | + check("network_compute_status:", test_compute_status()); |
| 96 | + if (failures) { printf("\n%d test(s) FAILED\n", failures); return 1; } |
| 97 | + printf("\nAll network unit tests passed\n"); |
| 98 | + return 0; |
| 99 | +} |
0 commit comments