AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdio.h>5#include <stdlib.h>6#include <string.h>7 8/* Forward declarations of the wrapper provided in the module */9void test_jsonInsertIntoBlob(sqlite3_context *ctx, int argc, sqlite3_value **argv, int eEdit);10 11/* Global database handle for tests */12static sqlite3 *gDb = NULL;13 14/* Helper: create SQL scalar functions that wrap test_jsonInsertIntoBlob */15static void t_json_insert_func(sqlite3_context *ctx, int argc, sqlite3_value **argv){16 test_jsonInsertIntoBlob(ctx, argc, argv, JEDIT_INS);17}18static void t_json_replace_func(sqlite3_context *ctx, int argc, sqlite3_value **argv){19 test_jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL);20}21static void t_json_set_func(sqlite3_context *ctx, int argc, sqlite3_value **argv){22 test_jsonInsertIntoBlob(ctx, argc, argv, JEDIT_SET);23}24 25/* Register wrappers with the current database connection. */26static void register_json_edit_wrappers(sqlite3 *db){27 int rc;28 rc = sqlite3_create_function(db, "t_json_insert", -1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0,29 t_json_insert_func, 0, 0);30 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to create t_json_insert");31 rc = sqlite3_create_function(db, "t_json_replace", -1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0,32 t_json_replace_func, 0, 0);33 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to create t_json_replace");34 rc = sqlite3_create_function(db, "t_json_set", -1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0,35 t_json_set_func, 0, 0);36 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "Failed to create t_json_set");37}38 39/* Helper: evaluate a scalar SQL and return a newly-allocated copy of column text */40static char *eval_scalar_text(sqlite3 *db, const char *sql){41 sqlite3_stmt *pStmt = NULL;42 int rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);43 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");44 rc = sqlite3_step(pStmt);45 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ROW, rc, "expected one row");46 const unsigned char *z = sqlite3_column_text(pStmt, 0);47 char *ret = sqlite3_mprintf("%s", z ? (const char*)z : "");48 sqlite3_finalize(pStmt);49 return ret;50}51 52/* Helper: execute a scalar SQL expected to error; return a copy of the error message */53static char *eval_expect_error(sqlite3 *db, const char *sql){54 sqlite3_stmt *pStmt = NULL;55 int rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);56 if( rc==SQLITE_OK ){57 rc = sqlite3_step(pStmt);58 /* Expecting SQLITE_ERROR */59 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ERROR, rc, "expected SQLITE_ERROR");60 sqlite3_finalize(pStmt);61 }else{62 /* Prepare-time error is also acceptable for some malformed cases */63 }64 const char *zErr = sqlite3_errmsg(db);65 return sqlite3_mprintf("%s", zErr ? zErr : "");66}67 68void setUp(void) {69 int rc = sqlite3_open(":memory:", &gDb);70 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");71 register_json_edit_wrappers(gDb);72}73 74void tearDown(void) {75 if( gDb ){76 sqlite3_close(gDb);77 gDb = NULL;78 }79}80 81/* Tests */82 83/* Root-level insert on "$" is a no-op */84void test_jsonInsertIntoBlob_root_insert_noop(void){85 const char *sql = "SELECT t_json_insert(jsonb('[1]'), '$', 77)";86 char *z = eval_scalar_text(gDb, sql);87 TEST_ASSERT_NOT_NULL(z);88 TEST_ASSERT_EQUAL_STRING("[1]", z);89 sqlite3_free(z);90}91 92/* Root-level replace on "$" replaces entire document */93void test_jsonInsertIntoBlob_root_replace(void){94 const char *sql = "SELECT t_json_replace(jsonb('[1,2,3]'), '$', 99)";95 char *z = eval_scalar_text(gDb, sql);96 TEST_ASSERT_NOT_NULL(z);97 TEST_ASSERT_EQUAL_STRING("99", z);98 sqlite3_free(z);99}100 101/* Insert object key when missing */102void test_jsonInsertIntoBlob_object_insert_key(void){103 const char *sql = "SELECT t_json_insert(jsonb('{}'), '$.a', 1)";104 char *z = eval_scalar_text(gDb, sql);105 TEST_ASSERT_NOT_NULL(z);106 TEST_ASSERT_EQUAL_STRING("{\"a\":1}", z);107 sqlite3_free(z);108}109 110/* Set object key when missing (create it) */111void test_jsonInsertIntoBlob_object_set_inserts_when_missing(void){112 const char *sql = "SELECT t_json_set(jsonb('{}'), '$.a', 1)";113 char *z = eval_scalar_text(gDb, sql);114 TEST_ASSERT_NOT_NULL(z);115 TEST_ASSERT_EQUAL_STRING("{\"a\":1}", z);116 sqlite3_free(z);117}118 119/* Replace on missing key does nothing */120void test_jsonInsertIntoBlob_object_replace_missing_noop(void){121 const char *sql = "SELECT t_json_replace(jsonb('{}'), '$.a', 1)";122 char *z = eval_scalar_text(gDb, sql);123 TEST_ASSERT_NOT_NULL(z);124 TEST_ASSERT_EQUAL_STRING("{}", z);125 sqlite3_free(z);126}127 128/* Replace on existing key updates value */129void test_jsonInsertIntoBlob_object_replace_existing(void){130 const char *sql = "SELECT t_json_replace(jsonb('{\"a\":1}'), '$.a', 2)";131 char *z = eval_scalar_text(gDb, sql);132 TEST_ASSERT_NOT_NULL(z);133 TEST_ASSERT_EQUAL_STRING("{\"a\":2}", z);134 sqlite3_free(z);135}136 137/* Insert into array at index 0 on empty array */138void test_jsonInsertIntoBlob_array_insert_index0(void){139 const char *sql = "SELECT t_json_insert(jsonb('[]'), '$[0]', 5)";140 char *z = eval_scalar_text(gDb, sql);141 TEST_ASSERT_NOT_NULL(z);142 TEST_ASSERT_EQUAL_STRING("[5]", z);143 sqlite3_free(z);144}145 146/* Set into array at index 1 appends */147void test_jsonInsertIntoBlob_array_set_appends(void){148 const char *sql = "SELECT t_json_set(jsonb('[0]'), '$[1]', 9)";149 char *z = eval_scalar_text(gDb, sql);150 TEST_ASSERT_NOT_NULL(z);151 TEST_ASSERT_EQUAL_STRING("[0,9]", z);152 sqlite3_free(z);153}154 155/* Multiple path/value edits in one call */156void test_jsonInsertIntoBlob_multiple_pairs(void){157 const char *sql = "SELECT t_json_set(jsonb('{}'), '$.a', 1, '$.b', 2)";158 char *z = eval_scalar_text(gDb, sql);159 TEST_ASSERT_NOT_NULL(z);160 /* Expect insertion order a then b */161 TEST_ASSERT_EQUAL_STRING("{\"a\":1,\"b\":2}", z);162 sqlite3_free(z);163}164 165/* NULL path arguments are ignored */166void test_jsonInsertIntoBlob_null_path_is_ignored(void){167 const char *sql = "SELECT t_json_set(jsonb('{}'), NULL, 99, '$.x', 1)";168 char *z = eval_scalar_text(gDb, sql);169 TEST_ASSERT_NOT_NULL(z);170 TEST_ASSERT_EQUAL_STRING("{\"x\":1}", z);171 sqlite3_free(z);172}173 174/* Malformed path (e.g., '$.') yields bad JSON path error */175void test_jsonInsertIntoBlob_malformed_path_error(void){176 const char *sql = "SELECT t_json_set(jsonb('{}'), '$.', 1)";177 char *zErr = eval_expect_error(gDb, sql);178 TEST_ASSERT_NOT_NULL(zErr);179 TEST_ASSERT_EQUAL_STRING("bad JSON path: $.", zErr);180 sqlite3_free(zErr);181}182 183/* Path not starting with '$' yields bad JSON path error */184void test_jsonInsertIntoBlob_path_missing_dollar_error(void){185 const char *sql = "SELECT t_json_set(jsonb('{}'), 'a', 1)";186 char *zErr = eval_expect_error(gDb, sql);187 TEST_ASSERT_NOT_NULL(zErr);188 TEST_ASSERT_EQUAL_STRING("bad JSON path: a", zErr);189 sqlite3_free(zErr);190}191 192/* Content as non-JSONB BLOB is rejected */193void test_jsonInsertIntoBlob_blob_content_error(void){194 const char *sql = "SELECT t_json_set(jsonb('{}'), '$.a', x'1234')";195 char *zErr = eval_expect_error(gDb, sql);196 TEST_ASSERT_NOT_NULL(zErr);197 TEST_ASSERT_EQUAL_STRING("JSON cannot hold BLOB values", zErr);198 sqlite3_free(zErr);199}200 201/* Single-argument call returns original unchanged */202void test_jsonInsertIntoBlob_single_arg_passthrough(void){203 const char *sql = "SELECT t_json_set(jsonb('{\"a\":1}'))";204 char *z = eval_scalar_text(gDb, sql);205 TEST_ASSERT_NOT_NULL(z);206 TEST_ASSERT_EQUAL_STRING("{\"a\":1}", z);207 sqlite3_free(z);208}209 210/* Nested path creation with json_set */211void test_jsonInsertIntoBlob_nested_set_creates_structure(void){212 const char *sql = "SELECT t_json_set(jsonb('{}'), '$.a.b', 1)";213 char *z = eval_scalar_text(gDb, sql);214 TEST_ASSERT_NOT_NULL(z);215 TEST_ASSERT_EQUAL_STRING("{\"a\":{\"b\":1}}", z);216 sqlite3_free(z);217}218 219int main(void){220 UNITY_BEGIN();221 RUN_TEST(test_jsonInsertIntoBlob_root_insert_noop);222 RUN_TEST(test_jsonInsertIntoBlob_root_replace);223 RUN_TEST(test_jsonInsertIntoBlob_object_insert_key);224 RUN_TEST(test_jsonInsertIntoBlob_object_set_inserts_when_missing);225 RUN_TEST(test_jsonInsertIntoBlob_object_replace_missing_noop);226 RUN_TEST(test_jsonInsertIntoBlob_object_replace_existing);227 RUN_TEST(test_jsonInsertIntoBlob_array_insert_index0);228 RUN_TEST(test_jsonInsertIntoBlob_array_set_appends);229 RUN_TEST(test_jsonInsertIntoBlob_multiple_pairs);230 RUN_TEST(test_jsonInsertIntoBlob_null_path_is_ignored);231 RUN_TEST(test_jsonInsertIntoBlob_malformed_path_error);232 RUN_TEST(test_jsonInsertIntoBlob_path_missing_dollar_error);233 RUN_TEST(test_jsonInsertIntoBlob_blob_content_error);234 RUN_TEST(test_jsonInsertIntoBlob_single_arg_passthrough);235 RUN_TEST(test_jsonInsertIntoBlob_nested_set_creates_structure);236 return UNITY_END();237}