Skip to content

Commit a38449f

Browse files
committed
Merge branch 'main' into dev-to-sync
2 parents 68616fd + f9f7997 commit a38449f

File tree

3 files changed

+103
-23
lines changed

3 files changed

+103
-23
lines changed

.github/workflows/main.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ jobs:
8181

8282
env:
8383
INTEGRATION_TEST_DATABASE_ID: ${{ secrets.INTEGRATION_TEST_DATABASE_ID }}
84+
INTEGRATION_TEST_APIKEY: ${{ secrets.INTEGRATION_TEST_APIKEY }}
85+
INTEGRATION_TEST_CLOUDSYNC_ADDRESS: ${{ secrets.INTEGRATION_TEST_CLOUDSYNC_ADDRESS }}
8486
INTEGRATION_TEST_OFFLINE_DATABASE_ID: ${{ secrets.INTEGRATION_TEST_OFFLINE_DATABASE_ID }}
8587

8688
steps:
@@ -126,6 +128,8 @@ jobs:
126128
-v ${{ github.workspace }}:/workspace \
127129
-w /workspace \
128130
-e INTEGRATION_TEST_DATABASE_ID="${{ env.INTEGRATION_TEST_DATABASE_ID }}" \
131+
-e INTEGRATION_TEST_APIKEY="${{ env.INTEGRATION_TEST_APIKEY }}" \
132+
-e INTEGRATION_TEST_CLOUDSYNC_ADDRESS="${{ env.INTEGRATION_TEST_CLOUDSYNC_ADDRESS }}" \
129133
-e INTEGRATION_TEST_OFFLINE_DATABASE_ID="${{ env.INTEGRATION_TEST_OFFLINE_DATABASE_ID }}" \
130134
alpine:latest \
131135
tail -f /dev/null
@@ -194,9 +198,12 @@ jobs:
194198
echo "::group::prepare the test script"
195199
make test PLATFORM=$PLATFORM ARCH=$ARCH || echo "It should fail. Running remaining commands in the emulator"
196200
cat > commands.sh << EOF
201+
set -e
197202
mv -f /data/local/tmp/sqlite3 /system/xbin
198203
cd /data/local/tmp
199204
export INTEGRATION_TEST_DATABASE_ID="$INTEGRATION_TEST_DATABASE_ID"
205+
export INTEGRATION_TEST_APIKEY="$INTEGRATION_TEST_APIKEY"
206+
export INTEGRATION_TEST_CLOUDSYNC_ADDRESS="$INTEGRATION_TEST_CLOUDSYNC_ADDRESS"
200207
export INTEGRATION_TEST_OFFLINE_DATABASE_ID="$INTEGRATION_TEST_OFFLINE_DATABASE_ID"
201208
$(make test PLATFORM=$PLATFORM ARCH=$ARCH -n)
202209
EOF
@@ -212,7 +219,8 @@ jobs:
212219
adb root
213220
adb remount
214221
adb push ${{ github.workspace }}/. /data/local/tmp/
215-
adb shell "sh /data/local/tmp/commands.sh"
222+
adb shell "sh /data/local/tmp/commands.sh; echo EXIT_CODE=\$?" | tee /tmp/adb_output.log
223+
grep -q "EXIT_CODE=0" /tmp/adb_output.log
216224
217225
- name: test sqlite-sync
218226
if: contains(matrix.name, 'linux') || matrix.name == 'windows' || ( matrix.name == 'macos' && matrix.arch != 'x86_64' )

Makefile

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,8 @@ $(BUILD_TEST)/%.o: %.c
218218
$(CC) $(T_CFLAGS) -c $< -o $@
219219

220220
# Run code coverage (--css-file $(CUSTOM_CSS))
221-
test: $(TARGET) $(TEST_TARGET) unittest
222-
@if [ -f .env ]; then \
223-
export $$(grep -v '^#' .env | xargs); \
224-
fi; \
225-
set -e; $(SQLITE3) ":memory:" -cmd ".bail on" ".load ./$<" "SELECT cloudsync_version();" # && \
226-
#for t in $(TEST_TARGET); do ./$$t; done
221+
test: $(TARGET) $(TEST_TARGET) unittest e2e
222+
set -e; $(SQLITE3) ":memory:" -cmd ".bail on" ".load ./$<" "SELECT cloudsync_version();"
227223
ifneq ($(COVERAGE),false)
228224
mkdir -p $(COV_DIR)
229225
lcov --capture --directory . --output-file $(COV_DIR)/coverage.info $(subst src, --include src,${COV_FILES})
@@ -234,6 +230,13 @@ endif
234230
unittest: $(TARGET) $(DIST_DIR)/unit$(EXE)
235231
@./$(DIST_DIR)/unit$(EXE)
236232

233+
# Run end-to-end integration tests
234+
e2e: $(TARGET) $(DIST_DIR)/integration$(EXE)
235+
@if [ -f .env ]; then \
236+
export $$(grep -v '^#' .env | xargs); \
237+
fi; \
238+
./$(DIST_DIR)/integration$(EXE)
239+
237240
OPENSSL_TARBALL = $(OPENSSL_DIR)/$(OPENSSL_VERSION).tar.gz
238241

239242
$(OPENSSL_TARBALL):
@@ -448,4 +451,4 @@ help:
448451
# Include PostgreSQL extension targets
449452
include docker/Makefile.postgresql
450453

451-
.PHONY: all clean test unittest extension help version xcframework aar
454+
.PHONY: all clean test unittest e2e extension help version xcframework aar

test/integration.c

Lines changed: 84 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -224,17 +224,31 @@ int test_init (const char *db_path, int init) {
224224
rc = db_exec(db, "SELECT cloudsync_init('activities');"); RCHECK
225225
rc = db_exec(db, "SELECT cloudsync_init('workouts');"); RCHECK
226226

227-
// init network with JSON connection string
227+
// init network
228228
char network_init[1024];
229229
const char* test_db_id = getenv("INTEGRATION_TEST_DATABASE_ID");
230230
if (!test_db_id) {
231231
fprintf(stderr, "Error: INTEGRATION_TEST_DATABASE_ID not set.\n");
232232
exit(1);
233233
}
234-
snprintf(network_init, sizeof(network_init),
235-
"SELECT cloudsync_network_init('%s');", test_db_id);
234+
const char* custom_address = getenv("INTEGRATION_TEST_CLOUDSYNC_ADDRESS");
235+
if (custom_address) {
236+
snprintf(network_init, sizeof(network_init),
237+
"SELECT cloudsync_network_init_custom('%s', '%s');", custom_address, test_db_id);
238+
} else {
239+
snprintf(network_init, sizeof(network_init),
240+
"SELECT cloudsync_network_init('%s');", test_db_id);
241+
}
236242
rc = db_exec(db, network_init); RCHECK
237243

244+
const char* apikey = getenv("INTEGRATION_TEST_APIKEY");
245+
if (apikey) {
246+
char set_apikey[512];
247+
snprintf(set_apikey, sizeof(set_apikey),
248+
"SELECT cloudsync_network_set_apikey('%s');", apikey);
249+
rc = db_exec(db, set_apikey); RCHECK
250+
}
251+
238252
rc = db_expect_int(db, "SELECT COUNT(*) as count FROM activities;", 0); RCHECK
239253
rc = db_expect_int(db, "SELECT COUNT(*) as count FROM workouts;", 0); RCHECK
240254
char value[UUID_STR_MAXLEN];
@@ -294,17 +308,31 @@ int test_enable_disable(const char *db_path) {
294308
snprintf(sql, sizeof(sql), "INSERT INTO users (id, name) VALUES ('%s-should-sync', '%s-should-sync');", value, value);
295309
rc = db_exec(db, sql); RCHECK
296310

297-
// init network with JSON connection string
311+
// init network
298312
char network_init[1024];
299313
const char* test_db_id = getenv("INTEGRATION_TEST_DATABASE_ID");
300314
if (!test_db_id) {
301315
fprintf(stderr, "Error: INTEGRATION_TEST_DATABASE_ID not set.\n");
302316
exit(1);
303317
}
304-
snprintf(network_init, sizeof(network_init),
305-
"SELECT cloudsync_network_init('%s');", test_db_id);
318+
const char* custom_address = getenv("INTEGRATION_TEST_CLOUDSYNC_ADDRESS");
319+
if (custom_address) {
320+
snprintf(network_init, sizeof(network_init),
321+
"SELECT cloudsync_network_init_custom('%s', '%s');", custom_address, test_db_id);
322+
} else {
323+
snprintf(network_init, sizeof(network_init),
324+
"SELECT cloudsync_network_init('%s');", test_db_id);
325+
}
306326
rc = db_exec(db, network_init); RCHECK
307327

328+
const char* apikey = getenv("INTEGRATION_TEST_APIKEY");
329+
if (apikey) {
330+
char set_apikey[512];
331+
snprintf(set_apikey, sizeof(set_apikey),
332+
"SELECT cloudsync_network_set_apikey('%s');", apikey);
333+
rc = db_exec(db, set_apikey); RCHECK
334+
}
335+
308336
rc = db_exec(db, "SELECT cloudsync_network_send_changes();"); RCHECK
309337
rc = db_exec(db, "SELECT cloudsync_cleanup('users');"); RCHECK
310338
rc = db_exec(db, "SELECT cloudsync_cleanup('activities');"); RCHECK
@@ -324,6 +352,13 @@ int test_enable_disable(const char *db_path) {
324352
// init network with connection string + apikey
325353
rc = db_exec(db2, network_init); RCHECK
326354

355+
if (apikey) {
356+
char set_apikey2[512];
357+
snprintf(set_apikey2, sizeof(set_apikey2),
358+
"SELECT cloudsync_network_set_apikey('%s');", apikey);
359+
rc = db_exec(db2, set_apikey2); RCHECK
360+
}
361+
327362
rc = db_expect_gt0(db2, "SELECT cloudsync_network_sync(250,10) ->> '$.receive.rows';"); RCHECK
328363

329364
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM users WHERE name='%s';", value);
@@ -362,10 +397,26 @@ int test_offline_error(const char *db_path) {
362397
}
363398

364399
char network_init[512];
365-
snprintf(network_init, sizeof(network_init), "SELECT cloudsync_network_init('%s');", offline_db_id);
400+
const char* custom_address = getenv("INTEGRATION_TEST_CLOUDSYNC_ADDRESS");
401+
if (custom_address) {
402+
snprintf(network_init, sizeof(network_init),
403+
"SELECT cloudsync_network_init_custom('%s', '%s');", custom_address, offline_db_id);
404+
} else {
405+
snprintf(network_init, sizeof(network_init),
406+
"SELECT cloudsync_network_init('%s');", offline_db_id);
407+
}
366408
rc = db_exec(db, network_init);
367409
RCHECK
368410

411+
const char* apikey = getenv("INTEGRATION_TEST_APIKEY");
412+
if (apikey) {
413+
char set_apikey[512];
414+
snprintf(set_apikey, sizeof(set_apikey),
415+
"SELECT cloudsync_network_set_apikey('%s');", apikey);
416+
rc = db_exec(db, set_apikey);
417+
RCHECK
418+
}
419+
369420
// Try to sync - this should fail with the expected error
370421
char *errmsg = NULL;
371422
rc = sqlite3_exec(db, "SELECT cloudsync_network_sync();", NULL, NULL, &errmsg);
@@ -376,17 +427,35 @@ int test_offline_error(const char *db_path) {
376427
goto abort_test;
377428
}
378429

379-
// Verify the error message contains the expected text
380-
const char *expected_error = "cloudsync_network_send_changes unable to upload BLOB changes to remote host";
381-
if (!errmsg || strstr(errmsg, expected_error) == NULL) {
382-
printf("Error: Expected error message containing '%s', but got '%s'\n",
383-
expected_error, errmsg ? errmsg : "NULL");
384-
if (errmsg) sqlite3_free(errmsg);
430+
// Verify the error JSON contains expected fields using SQLite JSON extraction
431+
if (!errmsg) {
432+
printf("Error: Expected an error message, but got NULL\n");
385433
rc = SQLITE_ERROR;
386434
goto abort_test;
387435
}
388436

389-
if (errmsg) sqlite3_free(errmsg);
437+
char verify_sql[1024];
438+
snprintf(verify_sql, sizeof(verify_sql),
439+
"SELECT json_extract('%s', '$.errors[0].status');", errmsg);
440+
rc = db_expect_str(db, verify_sql, "500");
441+
if (rc != SQLITE_OK) { printf("Offline error: unexpected status in: %s\n", errmsg); sqlite3_free(errmsg); goto abort_test; }
442+
443+
snprintf(verify_sql, sizeof(verify_sql),
444+
"SELECT json_extract('%s', '$.errors[0].code');", errmsg);
445+
rc = db_expect_str(db, verify_sql, "internal_server_error");
446+
if (rc != SQLITE_OK) { printf("Offline error: unexpected code in: %s\n", errmsg); sqlite3_free(errmsg); goto abort_test; }
447+
448+
snprintf(verify_sql, sizeof(verify_sql),
449+
"SELECT json_extract('%s', '$.errors[0].title');", errmsg);
450+
rc = db_expect_str(db, verify_sql, "Internal Server Error");
451+
if (rc != SQLITE_OK) { printf("Offline error: unexpected title in: %s\n", errmsg); sqlite3_free(errmsg); goto abort_test; }
452+
453+
snprintf(verify_sql, sizeof(verify_sql),
454+
"SELECT json_extract('%s', '$.errors[0].detail');", errmsg);
455+
rc = db_expect_str(db, verify_sql, "failed to resolve token data: failed to resolve db user for api key: db: connect sqlitecloud failed after 3 attempts: Your free node has been paused due to inactivity. To resume usage, please restart your node from your dashboard: https://dashboard.sqlitecloud.io");
456+
if (rc != SQLITE_OK) { printf("Offline error: unexpected detail in: %s\n", errmsg); sqlite3_free(errmsg); goto abort_test; }
457+
458+
sqlite3_free(errmsg);
390459
rc = SQLITE_OK;
391460

392461
ABORT_TEST
@@ -588,4 +657,4 @@ int main (void) {
588657

589658
printf("\n");
590659
return rc;
591-
}
660+
}

0 commit comments

Comments
 (0)