Team Ai
Modelpublic

AryaWu/sqlite

sourceHugging Faceupdated 10mo agoView on Hugging Face
0likes
tests_json_jsonPatchFunc.c221 linesDownload Raw Back to tests
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdio.h>6 7/* External wrapper provided in the module for the static function */8void test_jsonPatchFunc(sqlite3_context *ctx, int argc, sqlite3_value **argv);9 10/* We'll expose it to SQL via a tiny shim that calls the wrapper directly */11static void sql_json_mergepatch_text(sqlite3_context *ctx, int argc, sqlite3_value **argv){12  /* Call the provided wrapper directly. User data (sqlite3_user_data(ctx))13     is configured by sqlite3_create_function in setUp(). */14  test_jsonPatchFunc(ctx, argc, argv);15}16 17static sqlite3 *gDb = NULL;18 19/* Helper: execute a scalar SQL and return a heap-allocated copy of result text.20   On success, returns non-NULL and sets *pRc to SQLITE_ROW.21   On error (including no row), returns NULL and *pRc is the SQLite rc (prepare/step). */22static char *execScalarText(sqlite3 *db, const char *sql, int *pRc){23  sqlite3_stmt *pStmt = NULL;24  int rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);25  if( rc!=SQLITE_OK ){26    if( pRc ) *pRc = rc;27    return NULL;28  }29  rc = sqlite3_step(pStmt);30  if( rc==SQLITE_ROW ){31    const unsigned char *z = sqlite3_column_text(pStmt, 0);32    int n = sqlite3_column_bytes(pStmt, 0);33    char *out = (char*)sqlite3_malloc(n+1);34    if( out ){35      memcpy(out, z ? z : (const unsigned char*)"", n);36      out[n] = 0;37    }38    sqlite3_finalize(pStmt);39    if( pRc ) *pRc = SQLITE_ROW;40    return out;41  }else{42    if( pRc ) *pRc = rc;43    sqlite3_finalize(pStmt);44    return NULL;45  }46}47 48/* Helper: expect the SQL to fail at prepare or step time */49static int execExpectError(sqlite3 *db, const char *sql, char **pzErrMsgOut){50  sqlite3_stmt *pStmt = NULL;51  int rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);52  if( rc!=SQLITE_OK ){53    if( pzErrMsgOut ){54      const char *msg = sqlite3_errmsg(db);55      if( msg ){56        *pzErrMsgOut = sqlite3_mprintf("%s", msg);57      }else{58        *pzErrMsgOut = NULL;59      }60    }61    if( pStmt ) sqlite3_finalize(pStmt);62    return rc;63  }64  rc = sqlite3_step(pStmt);65  if( rc==SQLITE_ROW ){66    /* Unexpected success */67    sqlite3_finalize(pStmt);68    if( pzErrMsgOut ) *pzErrMsgOut = NULL;69    return SQLITE_OK;70  }71  /* rc should be SQLITE_ERROR or another error code */72  if( pzErrMsgOut ){73    const char *msg = sqlite3_errmsg(db);74    if( msg ){75      *pzErrMsgOut = sqlite3_mprintf("%s", msg);76    }else{77      *pzErrMsgOut = NULL;78    }79  }80  sqlite3_finalize(pStmt);81  return rc;82}83 84void setUp(void) {85  int rc = sqlite3_open(":memory:", &gDb);86  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to open in-memory SQLite DB");87 88  /* Register our function that directly calls the target wrapper.89     Set user-data to 0 so jsonPatchFunc returns JSON text, not JSONB blob. */90  rc = sqlite3_create_function(gDb,91                               "t_json_mergepatch_text",92                               2,               /* nargs */93                               SQLITE_UTF8,94                               (void*)0,        /* user-data => flags = 0 */95                               sql_json_mergepatch_text,96                               0, 0);97  TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to create t_json_mergepatch_text function");98}99 100void tearDown(void) {101  if( gDb ){102    sqlite3_close(gDb);103    gDb = NULL;104  }105}106 107/* If the patch is not an object, it replaces the entire target (lines 02-03) */108void test_jsonPatchFunc_replace_with_non_object_patch(void){109  int rc;110  /* target is an object, patch is a string => result is the patch itself */111  const char *sql =112    "SELECT t_json_mergepatch_text('{\"a\":1}', '\"x\"');";113  char *res = execScalarText(gDb, sql, &rc);114  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);115  TEST_ASSERT_NOT_NULL(res);116  TEST_ASSERT_EQUAL_STRING("\"x\"", res);117  sqlite3_free(res);118}119 120/* If the patch is an object and the target is not an object, target becomes {} before merge */121void test_jsonPatchFunc_patch_on_non_object_target(void){122  int rc;123  const char *sql =124    "SELECT t_json_mergepatch_text('5', '{\"a\":1}');";125  char *res = execScalarText(gDb, sql, &rc);126  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);127  TEST_ASSERT_NOT_NULL(res);128  TEST_ASSERT_EQUAL_STRING("{\"a\":1}", res);129  sqlite3_free(res);130}131 132/* Deleting a property by setting it to null in the patch (lines 09-10) */133void test_jsonPatchFunc_delete_property_with_null(void){134  int rc;135  const char *sql =136    "SELECT t_json_mergepatch_text('{\"a\":1,\"b\":2}', '{\"a\":null}');";137  char *res = execScalarText(gDb, sql, &rc);138  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);139  TEST_ASSERT_NOT_NULL(res);140  TEST_ASSERT_EQUAL_STRING("{\"b\":2}", res);141  sqlite3_free(res);142}143 144/* No-op deletion when key does not exist (lines 09-10) */145void test_jsonPatchFunc_delete_nonexistent_key_noop(void){146  int rc;147  const char *sql =148    "SELECT t_json_mergepatch_text('{}', '{\"z\":null}');";149  char *res = execScalarText(gDb, sql, &rc);150  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);151  TEST_ASSERT_NOT_NULL(res);152  TEST_ASSERT_EQUAL_STRING("{}", res);153  sqlite3_free(res);154}155 156/* Recursive merge into objects (lines 12 and recursion) */157void test_jsonPatchFunc_recursive_merge(void){158  int rc;159  const char *sql =160    "SELECT t_json_mergepatch_text('{\"a\":{\"b\":1,\"c\":2}}', '{\"a\":{\"b\":null,\"d\":3}}');";161  char *res = execScalarText(gDb, sql, &rc);162  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);163  TEST_ASSERT_NOT_NULL(res);164  /* Expect: remove a.b, keep a.c, add a.d; canonical order likely c then d */165  TEST_ASSERT_EQUAL_STRING("{\"a\":{\"c\":2,\"d\":3}}", res);166  sqlite3_free(res);167}168 169/* Add new key where patch value is object: equivalent to MergePatch('{}', value) (lines 16-17) */170void test_jsonPatchFunc_add_object_value_new_key(void){171  int rc;172  const char *sql =173    "SELECT t_json_mergepatch_text('{}', '{\"a\":{\"b\":2}}');";174  char *res = execScalarText(gDb, sql, &rc);175  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);176  TEST_ASSERT_NOT_NULL(res);177  TEST_ASSERT_EQUAL_STRING("{\"a\":{\"b\":2}}", res);178  sqlite3_free(res);179}180 181/* Accept JSONB inputs: feed jsonb() blobs, still return text (user-data flags=0) */182void test_jsonPatchFunc_accepts_jsonb_inputs(void){183  int rc;184  const char *sql =185    "SELECT t_json_mergepatch_text(jsonb('{\"a\":1}'), jsonb('{\"b\":2}'));";186  char *res = execScalarText(gDb, sql, &rc);187  TEST_ASSERT_EQUAL_INT(SQLITE_ROW, rc);188  TEST_ASSERT_NOT_NULL(res);189  TEST_ASSERT_EQUAL_STRING("{\"a\":1,\"b\":2}", res);190  sqlite3_free(res);191}192 193/* Malformed JSON input should raise an error */194void test_jsonPatchFunc_error_on_malformed_json(void){195  char *zErr = NULL;196  /* Pass a malformed JSON string as the patch argument */197  const char *sql =198    "SELECT t_json_mergepatch_text('{\"a\":1}', '{invalid}');";199  int rc = execExpectError(gDb, sql, &zErr);200  TEST_ASSERT_NOT_EQUAL_MESSAGE(SQLITE_ROW, rc, "Malformed JSON unexpectedly returned a row");201  /* We expect an error of some kind: SQLITE_ERROR is most likely */202  TEST_ASSERT_NOT_EQUAL(SQLITE_OK, rc);203  if( zErr ){204    /* Just ensure we got some error message back */205    TEST_ASSERT_TRUE_MESSAGE(strlen(zErr) > 0, "Expected an error message for malformed JSON");206    sqlite3_free(zErr);207  }208}209 210int main(void) {211  UNITY_BEGIN();212  RUN_TEST(test_jsonPatchFunc_replace_with_non_object_patch);213  RUN_TEST(test_jsonPatchFunc_patch_on_non_object_target);214  RUN_TEST(test_jsonPatchFunc_delete_property_with_null);215  RUN_TEST(test_jsonPatchFunc_delete_nonexistent_key_noop);216  RUN_TEST(test_jsonPatchFunc_recursive_merge);217  RUN_TEST(test_jsonPatchFunc_add_object_value_new_key);218  RUN_TEST(test_jsonPatchFunc_accepts_jsonb_inputs);219  RUN_TEST(test_jsonPatchFunc_error_on_malformed_json);220  return UNITY_END();221}