AryaWu/sqlite
0
1#include "sqliteInt.h"2#include "unity.h"3#include <stdlib.h>4#include <string.h>5#include <stdio.h>6 7/* Wrapper provided by the module under test */8extern int test_jsonMergePatch(JsonParse *pTarget, u32 iTarget,9 const JsonParse *pPatch, u32 iPatch);10 11/* Local constants for JSONB type codes (distinct names to avoid conflicts) */12enum {13 JT_NULL = 0,14 JT_TRUE = 1,15 JT_FALSE = 2,16 JT_INT = 3,17 JT_TEXT = 7,18 JT_TEXTRAW= 10,19 JT_ARRAY = 11,20 JT_OBJECT = 1221};22 23typedef struct Buf {24 u8 *a;25 int n;26} Buf;27 28static Buf buf_alloc(int n){29 Buf b;30 b.a = (u8*)sqlite3_malloc(n);31 TEST_ASSERT_NOT_NULL(b.a);32 b.n = n;33 return b;34}35 36static void buf_free(Buf *b){37 if( b && b->a ){38 sqlite3_free(b->a);39 b->a = NULL;40 b->n = 0;41 }42}43 44static Buf make_text_value(const char *s){45 int m = (int)strlen(s);46 TEST_ASSERT(m <= 11); /* keep simple single-byte header */47 Buf b = buf_alloc(1 + m);48 b.a[0] = (u8)((m<<4) | JT_TEXT);49 memcpy(&b.a[1], s, (size_t)m);50 return b;51}52 53static Buf make_int_value_from_str(const char *digits){54 int m = (int)strlen(digits);55 TEST_ASSERT(m <= 11);56 Buf b = buf_alloc(1 + m);57 b.a[0] = (u8)((m<<4) | JT_INT);58 memcpy(&b.a[1], digits, (size_t)m);59 return b;60}61 62static Buf make_int_value(int v){63 char tmp[32];64 snprintf(tmp, sizeof(tmp), "%d", v);65 return make_int_value_from_str(tmp);66}67 68static Buf make_null_value(void){69 Buf b = buf_alloc(1);70 b.a[0] = (u8)((0<<4) | JT_NULL);71 return b;72}73 74/* Create a label/value pair buffer (label is TEXT type) */75static Buf make_pair(const char *label, const Buf *val){76 int L = (int)strlen(label);77 TEST_ASSERT(L <= 11);78 Buf p = buf_alloc(1 + L + val->n);79 p.a[0] = (u8)((L<<4) | JT_TEXT);80 memcpy(&p.a[1], label, (size_t)L);81 memcpy(&p.a[1+L], val->a, (size_t)val->n);82 return p;83}84 85/* Create a label/value pair with an INT label (for malformed object tests) */86static Buf make_bad_pair_intlabel(const char *labelDigits, const Buf *val){87 int L = (int)strlen(labelDigits);88 TEST_ASSERT(L <= 11);89 Buf p = buf_alloc(1 + L + val->n);90 p.a[0] = (u8)((L<<4) | JT_INT);91 memcpy(&p.a[1], labelDigits, (size_t)L);92 memcpy(&p.a[1+L], val->a, (size_t)val->n);93 return p;94}95 96/* Build an OBJECT from an array of pair buffers. Copies pair contents. */97static Buf make_object_from_pairs(const Buf *pairs, int npair){98 int i, payload = 0;99 for(i=0; i<npair; i++){100 payload += pairs[i].n;101 }102 TEST_ASSERT(payload >= 0 && payload <= 255);103 int hdr = (payload <= 11) ? 1 : 2;104 Buf o = buf_alloc(hdr + payload);105 if( hdr==1 ){106 o.a[0] = (u8)((payload<<4) | JT_OBJECT);107 }else{108 o.a[0] = (u8)(((u8)12<<4) | JT_OBJECT); /* 0xCC */109 o.a[1] = (u8)payload;110 }111 int off = hdr;112 for(i=0; i<npair; i++){113 memcpy(&o.a[off], pairs[i].a, (size_t)pairs[i].n);114 off += pairs[i].n;115 }116 TEST_ASSERT_EQUAL_INT(hdr+payload, o.n);117 return o;118}119 120/* Initialize a JsonParse for target with its own writable copy and capacity */121static void initTargetParse(JsonParse *p, const Buf *content, int capacity){122 memset(p, 0, sizeof(*p));123 p->aBlob = (u8*)sqlite3_malloc(capacity);124 TEST_ASSERT_NOT_NULL(p->aBlob);125 memcpy(p->aBlob, content->a, (size_t)content->n);126 p->nBlob = content->n;127 p->nBlobAlloc = capacity;128 p->delta = 0;129 p->oom = 0;130 /* p->db may remain NULL; we pre-allocate enough to avoid growth */131}132 133/* Initialize a read-only patch JsonParse referencing existing bytes */134static void initPatchParse(JsonParse *p, const Buf *content){135 memset(p, 0, sizeof(*p));136 p->aBlob = content->a;137 p->nBlob = content->n;138 p->nBlobAlloc = content->n;139 p->delta = 0;140}141 142/* Common verification helper */143static void assertTargetEquals(const JsonParse *t, const Buf *expected){144 TEST_ASSERT_EQUAL_INT(expected->n, (int)t->nBlob);145 TEST_ASSERT_EQUAL_UINT8_ARRAY(expected->a, t->aBlob, expected->n);146}147 148/* Unity hooks */149void setUp(void) {}150void tearDown(void) {}151 152/* 1) Non-object patch replaces target entirely */153void test_jsonMergePatch_replace_with_non_object(void){154 /* Target: {"a":1} */155 Buf v1 = make_int_value(1);156 Buf pA1 = make_pair("a", &v1);157 Buf targetObj = make_object_from_pairs(&pA1, 1);158 159 /* Patch: TEXT "Z" */160 Buf patchTxt = make_text_value("Z");161 162 /* Expected: patchTxt */163 Buf expected = patchTxt; /* same content */164 165 JsonParse T, P;166 initTargetParse(&T, &targetObj, 128);167 initPatchParse(&P, &patchTxt);168 169 int rc = test_jsonMergePatch(&T, 0, &P, 0);170 TEST_ASSERT_EQUAL_INT(0, rc);171 assertTargetEquals(&T, &expected);172 173 sqlite3_free(T.aBlob);174 buf_free(&targetObj);175 buf_free(&pA1);176 buf_free(&v1);177 buf_free(&patchTxt);178}179 180/* 2) Patch is object; target is non-object -> becomes object with field */181void test_jsonMergePatch_target_non_object_patch_object_adds_field(void){182 /* Target: TEXT "X" */183 Buf targetTxt = make_text_value("X");184 /* Patch: {"a":1} */185 Buf v1 = make_int_value(1);186 Buf pA1 = make_pair("a", &v1);187 Buf patchObj = make_object_from_pairs(&pA1, 1);188 189 /* Expected: {"a":1} */190 Buf expected = patchObj; /* The merge of {} with patch yields patch */191 192 JsonParse T, P;193 initTargetParse(&T, &targetTxt, 128);194 initPatchParse(&P, &patchObj);195 196 int rc = test_jsonMergePatch(&T, 0, &P, 0);197 TEST_ASSERT_EQUAL_INT(0, rc);198 assertTargetEquals(&T, &expected);199 200 sqlite3_free(T.aBlob);201 buf_free(&targetTxt);202 buf_free(&patchObj);203 buf_free(&pA1);204 buf_free(&v1);205}206 207/* 3) Deleting an existing key via null patch value */208void test_jsonMergePatch_delete_existing_key_with_null(void){209 /* Target: {"x":1,"y":2} */210 Buf v1 = make_int_value(1);211 Buf v2 = make_int_value(2);212 Buf px1 = make_pair("x", &v1);213 Buf py2 = make_pair("y", &v2);214 Buf pairsXY[2] = { px1, py2 };215 Buf targetObj = make_object_from_pairs(pairsXY, 2);216 217 /* Patch: {"x": null} */218 Buf vNull = make_null_value();219 Buf pxNull = make_pair("x", &vNull);220 Buf patchObj = make_object_from_pairs(&pxNull, 1);221 222 /* Expected: {"y":2} */223 Buf expected = make_object_from_pairs(&py2, 1);224 225 JsonParse T, P;226 initTargetParse(&T, &targetObj, 128);227 initPatchParse(&P, &patchObj);228 229 int rc = test_jsonMergePatch(&T, 0, &P, 0);230 TEST_ASSERT_EQUAL_INT(0, rc);231 assertTargetEquals(&T, &expected);232 233 sqlite3_free(T.aBlob);234 buf_free(&expected);235 buf_free(&targetObj);236 buf_free(&px1);237 buf_free(&py2);238 buf_free(&v1);239 buf_free(&v2);240 buf_free(&vNull);241 buf_free(&pxNull);242 buf_free(&patchObj);243}244 245/* 4) Updating an existing scalar value */246void test_jsonMergePatch_update_existing_scalar(void){247 /* Target: {"y":2} */248 Buf v2 = make_int_value(2);249 Buf py2 = make_pair("y", &v2);250 Buf targetObj = make_object_from_pairs(&py2, 1);251 252 /* Patch: {"y":3} */253 Buf v3 = make_int_value(3);254 Buf py3 = make_pair("y", &v3);255 Buf patchObj = make_object_from_pairs(&py3, 1);256 257 /* Expected: {"y":3} */258 Buf expected = patchObj;259 260 JsonParse T, P;261 initTargetParse(&T, &targetObj, 128);262 initPatchParse(&P, &patchObj);263 264 int rc = test_jsonMergePatch(&T, 0, &P, 0);265 TEST_ASSERT_EQUAL_INT(0, rc);266 assertTargetEquals(&T, &expected);267 268 sqlite3_free(T.aBlob);269 buf_free(&targetObj);270 buf_free(&py2);271 buf_free(&v2);272 buf_free(&py3);273 buf_free(&v3);274 buf_free(&patchObj);275}276 277/* 5) Recursive merge on nested objects: remove 'b', add 'c' */278void test_jsonMergePatch_recursive_nested_merge(void){279 /* Target: {"nest": {"a":1,"b":2}} */280 Buf va1 = make_int_value(1);281 Buf vb2 = make_int_value(2);282 Buf pA1 = make_pair("a", &va1);283 Buf pB2 = make_pair("b", &vb2);284 Buf innerPairsAB[2] = { pA1, pB2 };285 Buf innerObjAB = make_object_from_pairs(innerPairsAB, 2);286 287 Buf pNestAB = make_pair("nest", &innerObjAB);288 Buf targetObj = make_object_from_pairs(&pNestAB, 1);289 290 /* Patch: {"nest": {"b": null, "c": 3}} */291 Buf vNull = make_null_value();292 Buf pBnull = make_pair("b", &vNull);293 Buf vc3 = make_int_value(3);294 Buf pC3 = make_pair("c", &vc3);295 Buf innerPatchPairs[2] = { pBnull, pC3 };296 Buf innerPatchObj = make_object_from_pairs(innerPatchPairs, 2);297 Buf pNestPatch = make_pair("nest", &innerPatchObj);298 Buf patchObj = make_object_from_pairs(&pNestPatch, 1);299 300 /* Expected: {"nest": {"a":1,"c":3}} (order preserved: 'a' then 'c') */301 Buf innerExpectedPairs[2] = { pA1, pC3 };302 Buf innerExpectedObj = make_object_from_pairs(innerExpectedPairs, 2);303 Buf pNestExpected = make_pair("nest", &innerExpectedObj);304 Buf expected = make_object_from_pairs(&pNestExpected, 1);305 306 JsonParse T, P;307 initTargetParse(&T, &targetObj, 256);308 initPatchParse(&P, &patchObj);309 310 int rc = test_jsonMergePatch(&T, 0, &P, 0);311 TEST_ASSERT_EQUAL_INT(0, rc);312 assertTargetEquals(&T, &expected);313 314 sqlite3_free(T.aBlob);315 buf_free(&expected);316 buf_free(&pNestExpected);317 buf_free(&innerExpectedObj);318 buf_free(&innerPatchObj);319 buf_free(&pNestPatch);320 buf_free(&patchObj);321 buf_free(&innerObjAB);322 buf_free(&pNestAB);323 buf_free(&targetObj);324 buf_free(&pA1);325 buf_free(&pB2);326 buf_free(&pBnull);327 buf_free(&pC3);328 buf_free(&va1);329 buf_free(&vb2);330 buf_free(&vNull);331 buf_free(&vc3);332}333 334/* 6) Add a new key whose value is an object (exercise placeholder + recursion) */335void test_jsonMergePatch_add_object_value_property(void){336 /* Target: {} */337 Buf emptyPairs[1]; /* unused */338 Buf targetEmpty = make_object_from_pairs(NULL, 0);339 340 /* Patch: {"obj": {"k":1}} */341 Buf vk1 = make_int_value(1);342 Buf pk1 = make_pair("k", &vk1);343 Buf innerObj = make_object_from_pairs(&pk1, 1);344 Buf pobj = make_pair("obj", &innerObj);345 Buf patchObj = make_object_from_pairs(&pobj, 1);346 347 /* Expected identical to patch */348 Buf expected = patchObj;349 350 JsonParse T, P;351 initTargetParse(&T, &targetEmpty, 128);352 initPatchParse(&P, &patchObj);353 354 int rc = test_jsonMergePatch(&T, 0, &P, 0);355 TEST_ASSERT_EQUAL_INT(0, rc);356 assertTargetEquals(&T, &expected);357 358 sqlite3_free(T.aBlob);359 buf_free(&targetEmpty);360 buf_free(&vk1);361 buf_free(&pk1);362 buf_free(&innerObj);363 buf_free(&pobj);364 buf_free(&patchObj);365}366 367/* 7) BADPATCH when patch object has non-text label */368void test_jsonMergePatch_badpatch_non_text_label(void){369 /* Target: {"a":1} (valid) */370 Buf v1 = make_int_value(1);371 Buf pA1 = make_pair("a", &v1);372 Buf targetObj = make_object_from_pairs(&pA1, 1);373 374 /* Patch: object with INT label '1' and value TEXT 'x' -> invalid label */375 Buf vx = make_text_value("x");376 Buf badPair = make_bad_pair_intlabel("1", &vx);377 Buf patchObj = make_object_from_pairs(&badPair, 1);378 379 JsonParse T, P;380 initTargetParse(&T, &targetObj, 128);381 initPatchParse(&P, &patchObj);382 383 int rc = test_jsonMergePatch(&T, 0, &P, 0);384 TEST_ASSERT(rc != 0); /* expect an error (BADPATCH) */385 386 sqlite3_free(T.aBlob);387 buf_free(&targetObj);388 buf_free(&pA1);389 buf_free(&v1);390 buf_free(&vx);391 buf_free(&badPair);392 buf_free(&patchObj);393}394 395/* 8) BADTARGET when target object has non-text label */396void test_jsonMergePatch_badtarget_non_text_label_in_target(void){397 /* Target: object with INT label '1' and value TEXT 'x' -> invalid target */398 Buf vx = make_text_value("x");399 Buf badPair = make_bad_pair_intlabel("1", &vx);400 Buf targetObj = make_object_from_pairs(&badPair, 1);401 402 /* Patch: {"a": null} to ensure a scan occurs */403 Buf vNull = make_null_value();404 Buf pAnull = make_pair("a", &vNull);405 Buf patchObj = make_object_from_pairs(&pAnull, 1);406 407 JsonParse T, P;408 initTargetParse(&T, &targetObj, 128);409 initPatchParse(&P, &patchObj);410 411 int rc = test_jsonMergePatch(&T, 0, &P, 0);412 TEST_ASSERT(rc != 0); /* expect an error (BADTARGET) */413 414 sqlite3_free(T.aBlob);415 buf_free(&targetObj);416 buf_free(&badPair);417 buf_free(&vx);418 buf_free(&patchObj);419 buf_free(&pAnull);420 buf_free(&vNull);421}422 423int main(void){424 UNITY_BEGIN();425 RUN_TEST(test_jsonMergePatch_replace_with_non_object);426 RUN_TEST(test_jsonMergePatch_target_non_object_patch_object_adds_field);427 RUN_TEST(test_jsonMergePatch_delete_existing_key_with_null);428 RUN_TEST(test_jsonMergePatch_update_existing_scalar);429 RUN_TEST(test_jsonMergePatch_recursive_nested_merge);430 RUN_TEST(test_jsonMergePatch_add_object_value_property);431 RUN_TEST(test_jsonMergePatch_badpatch_non_text_label);432 RUN_TEST(test_jsonMergePatch_badtarget_non_text_label_in_target);433 return UNITY_END();434}