AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3 4#include <stdlib.h>5#include <string.h>6 7/* The auto-generated wrapper for the static function under test */8extern void test_jsonValidFunc(sqlite3_context *ctx, int argc, sqlite3_value **argv);9 10/* Global database handle used by tests */11static sqlite3 *gDb = NULL;12 13/* SQL function that forwards directly to the target function wrapper */14static void sql_call_jsonValidFunc(sqlite3_context *ctx, int argc, sqlite3_value **argv){15 test_jsonValidFunc(ctx, argc, argv);16}17 18/* Helper: run a scalar SELECT and capture column-0 as integer or NULL */19static void runScalar(const char *zSql, int *pIsNull, int *pIntVal){20 sqlite3_stmt *pStmt = 0;21 int rc = sqlite3_prepare_v2(gDb, zSql, -1, &pStmt, 0);22 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");23 rc = sqlite3_step(pStmt);24 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ROW, rc, "step did not return a row");25 int ctype = sqlite3_column_type(pStmt, 0);26 if( pIsNull ) *pIsNull = (ctype == SQLITE_NULL);27 if( pIntVal ){28 if( ctype == SQLITE_NULL ){29 *pIntVal = 0;30 }else{31 *pIntVal = sqlite3_column_int(pStmt, 0);32 }33 }34 rc = sqlite3_finalize(pStmt);35 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "finalize failed");36}37 38/* Helper: run SELECT call_jsonValidFunc(?1, ?2) with bound blob and flags */39static void runScalarWithBlobAndFlags(const void *pBlob, int nBlob, int flags,40 int *pIsNull, int *pIntVal){41 sqlite3_stmt *pStmt = 0;42 const char *zSql = "SELECT call_jsonValidFunc(?1, ?2)";43 int rc = sqlite3_prepare_v2(gDb, zSql, -1, &pStmt, 0);44 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");45 rc = sqlite3_bind_blob(pStmt, 1, pBlob, nBlob, SQLITE_TRANSIENT);46 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "bind blob failed");47 rc = sqlite3_bind_int(pStmt, 2, flags);48 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "bind int failed");49 rc = sqlite3_step(pStmt);50 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ROW, rc, "step did not return a row");51 int ctype = sqlite3_column_type(pStmt, 0);52 if( pIsNull ) *pIsNull = (ctype == SQLITE_NULL);53 if( pIntVal ){54 if( ctype == SQLITE_NULL ){55 *pIntVal = 0;56 }else{57 *pIntVal = sqlite3_column_int(pStmt, 0);58 }59 }60 rc = sqlite3_finalize(pStmt);61 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "finalize failed");62}63 64/* Helper: run statement expecting an SQLITE_ERROR and check message substring */65static void runExpectError(const char *zSql, const char *zSubstr){66 sqlite3_stmt *pStmt = 0;67 int rc = sqlite3_prepare_v2(gDb, zSql, -1, &pStmt, 0);68 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "prepare failed");69 rc = sqlite3_step(pStmt);70 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_ERROR, rc, "expected SQLITE_ERROR");71 const char *zErr = sqlite3_errmsg(gDb);72 TEST_ASSERT_NOT_NULL_MESSAGE(zErr, "no error message");73 if( zSubstr ){74 TEST_ASSERT_NOT_NULL_MESSAGE(strstr(zErr, zSubstr), "error message mismatch");75 }76 sqlite3_finalize(pStmt);77}78 79void setUp(void) {80 int rc = sqlite3_open(":memory:", &gDb);81 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_open failed");82 rc = sqlite3_create_function(gDb, "call_jsonValidFunc", -1, SQLITE_UTF8, NULL,83 sql_call_jsonValidFunc, NULL, NULL);84 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "create_function failed");85}86 87void tearDown(void) {88 if( gDb ){89 int rc = sqlite3_close(gDb);90 TEST_ASSERT_EQUAL_INT_MESSAGE(SQLITE_OK, rc, "sqlite3_close failed");91 gDb = NULL;92 }93}94 95/* Tests */96 97/* NULL input returns NULL (default FLAGS) */98void test_jsonValidFunc_null_returns_null(void){99 int isNull = 0, val = -1;100 runScalar("SELECT call_jsonValidFunc(NULL)", &isNull, &val);101 TEST_ASSERT_TRUE(isNull);102}103 104/* FLAGS out of range (outside 1..15) raises an error */105void test_jsonValidFunc_flags_out_of_range_error(void){106 runExpectError("SELECT call_jsonValidFunc('null', 16)",107 "between 1 and 15");108 runExpectError("SELECT call_jsonValidFunc('null', 0)",109 "between 1 and 15");110}111 112/* Canonical JSON text accepted with default FLAGS (1) */113void test_jsonValidFunc_text_valid_canonical(void){114 int isNull = 0, val = 0;115 runScalar("SELECT call_jsonValidFunc('{\"a\":[1,2,3],\"b\":\"x\"}')", &isNull, &val);116 TEST_ASSERT_FALSE(isNull);117 TEST_ASSERT_EQUAL_INT(1, val);118}119 120/* Malformed JSON text rejected (returns 0) */121void test_jsonValidFunc_text_invalid(void){122 int isNull = 0, val = 1;123 runScalar("SELECT call_jsonValidFunc('{bad json}')", &isNull, &val);124 TEST_ASSERT_FALSE(isNull);125 TEST_ASSERT_EQUAL_INT(0, val);126}127 128/* JSON5 feature (hex integer) requires FLAGS include 0x02 */129void test_jsonValidFunc_json5_integer_requires_flag(void){130 int isNull = 0, val = -1;131 runScalar("SELECT call_jsonValidFunc('0x10')", &isNull, &val);132 TEST_ASSERT_FALSE(isNull);133 TEST_ASSERT_EQUAL_INT(0, val); /* default FLAGS=1 => reject JSON5 */134 runScalar("SELECT call_jsonValidFunc('0x10', 2)", &isNull, &val);135 TEST_ASSERT_FALSE(isNull);136 TEST_ASSERT_EQUAL_INT(1, val); /* FLAGS=2 => accept JSON5 */137}138 139/* BLOB containing text JSON falls through to text parsing if text flags set */140void test_jsonValidFunc_blob_text_fallthrough(void){141 int isNull = 0, val = -1;142 /* CAST('123' AS BLOB) with FLAGS=1 => treated as text JSON => valid */143 runScalar("SELECT call_jsonValidFunc(CAST('123' AS BLOB), 1)", &isNull, &val);144 TEST_ASSERT_FALSE(isNull);145 TEST_ASSERT_EQUAL_INT(1, val);146 147 /* Same input with FLAGS=4 (superficial JSONB only) => not JSONB => 0 */148 runScalar("SELECT call_jsonValidFunc(CAST('123' AS BLOB), 4)", &isNull, &val);149 TEST_ASSERT_FALSE(isNull);150 TEST_ASSERT_EQUAL_INT(0, val);151}152 153/* Valid JSONB BLOB: superficial and strict checks */154void test_jsonValidFunc_blob_jsonb_superficial_and_strict(void){155 int isNull = 0, val = -1;156 157 /* Superficial JSONB validation passes */158 runScalar("SELECT call_jsonValidFunc(jsonb('{\"x\":1}'), 4)", &isNull, &val);159 TEST_ASSERT_FALSE(isNull);160 TEST_ASSERT_EQUAL_INT(1, val);161 162 /* Strict JSONB validation passes */163 runScalar("SELECT call_jsonValidFunc(jsonb('{\"x\":1}'), 8)", &isNull, &val);164 TEST_ASSERT_FALSE(isNull);165 TEST_ASSERT_EQUAL_INT(1, val);166 167 /* With only text flags (1), JSONB BLOB is not accepted => 0 */168 runScalar("SELECT call_jsonValidFunc(jsonb('{\"x\":1}'), 1)", &isNull, &val);169 TEST_ASSERT_FALSE(isNull);170 TEST_ASSERT_EQUAL_INT(0, val);171 172 /* Combined flags including superficial (5 = 1|4) => accept */173 runScalar("SELECT call_jsonValidFunc(jsonb('{\"x\":1}'), 5)", &isNull, &val);174 TEST_ASSERT_FALSE(isNull);175 TEST_ASSERT_EQUAL_INT(1, val);176 177 /* Combined flags including strict (9 = 1|8) => accept */178 runScalar("SELECT call_jsonValidFunc(jsonb('{\"x\":1}'), 9)", &isNull, &val);179 TEST_ASSERT_FALSE(isNull);180 TEST_ASSERT_EQUAL_INT(1, val);181}182 183/* Superficially valid but strictly invalid JSONB BLOB */184void test_jsonValidFunc_blob_superficial_accept_strict_reject(void){185 /* Construct a JSONB blob:186 - Top-level: ARRAY with extended 1-byte size header (0xCB) and payload size 8187 (header bytes: 0xCB, 0x08)188 - Payload: 8 bytes beginning with 0xF0 (type=NULL with 8-byte size field).189 This makes inner element header require 9 bytes, but only 8 bytes of payload190 exist. Superficial check succeeds (outer size matches), strict check fails. */191 unsigned char blob[10];192 blob[0] = 0xCB; /* high nibble 12 => 1-byte size; low nibble 11 => ARRAY */193 blob[1] = 0x08; /* payload size = 8 */194 blob[2] = 0xF0; /* inner element: type NULL (0) with 8-byte size field indicated */195 /* Fill the remaining 7 bytes with zeros */196 for(int i=3; i<10; i++) blob[i] = 0x00;197 198 int isNull = 0, val = -1;199 200 /* Superficial JSONB validation: should return 1 */201 runScalarWithBlobAndFlags(blob, (int)sizeof(blob), 4, &isNull, &val);202 TEST_ASSERT_FALSE(isNull);203 TEST_ASSERT_EQUAL_INT(1, val);204 205 /* Strict JSONB validation: should return 0 */206 runScalarWithBlobAndFlags(blob, (int)sizeof(blob), 8, &isNull, &val);207 TEST_ASSERT_FALSE(isNull);208 TEST_ASSERT_EQUAL_INT(0, val);209 210 /* Combined strict or superficial flags behave accordingly */211 runScalarWithBlobAndFlags(blob, (int)sizeof(blob), 5, &isNull, &val); /* includes superficial */212 TEST_ASSERT_FALSE(isNull);213 TEST_ASSERT_EQUAL_INT(1, val);214 215 runScalarWithBlobAndFlags(blob, (int)sizeof(blob), 9, &isNull, &val); /* includes strict */216 TEST_ASSERT_FALSE(isNull);217 TEST_ASSERT_EQUAL_INT(0, val);218}219 220int main(void){221 UNITY_BEGIN();222 RUN_TEST(test_jsonValidFunc_null_returns_null);223 RUN_TEST(test_jsonValidFunc_flags_out_of_range_error);224 RUN_TEST(test_jsonValidFunc_text_valid_canonical);225 RUN_TEST(test_jsonValidFunc_text_invalid);226 RUN_TEST(test_jsonValidFunc_json5_integer_requires_flag);227 RUN_TEST(test_jsonValidFunc_blob_text_fallthrough);228 RUN_TEST(test_jsonValidFunc_blob_jsonb_superficial_and_strict);229 RUN_TEST(test_jsonValidFunc_blob_superficial_accept_strict_reject);230 return UNITY_END();231}