From 833b6147680e30767213dd9e17b38b8142d087f3 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Mon, 30 Sep 2024 16:05:24 +0200 Subject: [PATCH 1/3] test fix react native buffer --- bun.lockb | Bin 477612 -> 456642 bytes package-lock.json | 71 ++--- package.json | 12 +- test/core/built-in-commands.test.ts | 469 +++++++++++++++++++++++++--- test/core/reserved-commands.test.ts | 164 +++++++--- test/core/shared.ts | 10 +- 6 files changed, 585 insertions(+), 141 deletions(-) diff --git a/bun.lockb b/bun.lockb index 82fc869cc58c5f8aef497799caa0349cc47fc875..c48747cd5a3954bff462253d5372943f555e07e0 100755 GIT binary patch delta 76972 zcmeFacX$=$_Vz!MWJ87?5djeqlwtt|)BpiCT|#ft1TlmFp_71siaV&NQBdN58x<84 z#14vx9xEzpQ1oCq*p7-7`@xFG_WRuPtPRnx=e&Ne>wW+DT^9pu-)r5^dU}~>X3w77 z88&?4s|L$j9eeArvW1IJI;qzen`eL8{@OiVAAPJ{{@%t9y!6s#E9Xy`a!tI=50yL} zeac2OO1r63+5Ff1bHc%XqfzSv&#UHnB}D~!`Ezi15JPsI>YhhX$pDNKz}K-GgLySP zuPQj)usSm3s<*ed@>1P)ddER^BUcYYeW|rsGCeGi( zthBm`r?8acth_msTTU%l@POGHK&5;WNaiIg!7AWVP$if*xiEh+B`z#3m|p;1eW>S= zdC8=@>zH%fdMeq z?sU)FiY15E6~vvVcN_|Qw!(JS+F3l zXcGB*S0C+pv{%VukajOQ8&n0RfQsJ{RAanwj4Rlg#t$)kt*PhL^%{63J5W^8UKWrV zFB&J!6G00XeL?GXGsxAOkhfrpvj2(VsosBV`(N-^wk~=4@>o}#KlCkK>-@g8^qWy; z)ROy-_q^l43~aJ!Q_`@d=QV?P9&8Ri4yt~|1v4h7K*e+N=FBZFDE^_9<3A5a#TsSp z#!{uOImxk?ho!Mb!8&7Ch4;joCsGq1q&yd$u`#HN%b<=BUV?_ozd zpW=Gz9CBA4nWs8Fny4)ci|?}%-UcfCuxuCJkwj%z!zRmcM5X#!%iB5bQc!7s9xkfX zsPQ-LU5WOc;Cb{;$zvT{38r@SJPK2?KHOTVQDR7U&pQOG7pSF^>cIJ|4`EX8U3e% z9zly}F#kdUn8zjW_IDXR1!^R`1+N0`Uo0tQkbqc_H*H#B@f@%FK-UEw26-NfSIIh% zE-s-SdDHSH6tu)vlh+5el2r%QWlf34{4S{tsw@9D6-j6;*;2WGI^9A^wJLN6j|WSK zx{R`ix!H3VsG1a>?s-Rpok96kg;#p1rvI6E8Z+;J8XK>Is?bzW1!l}8q8j{hupLXr znk?BiR4a}4yw=z$J_Eij_Uta6cM|v#$Z}b-eT?Uw2(AW?15<RP) zgC;dt+~7DcHpZUmGUy7bFTWb=PMZy|n_;h^04m@DMx5r_P*D6lRu>Jr?u4^!luRyg zEmaqrX<71lBJPMMK$Uo<;hssZsZZ$WreSLE{NEZQGc2D(YIOE6;<(Ok32lJQs#~(G zldH+s@UlOh;q2|^caz~CNBud*XR{7y`VBEV9n=c^7U_bClKU-#si2y!mtkYWpJ@T{ zuYt#an?Vh_CTF`rdKsu`_bVf-Hh7hV&jOoZXE7o*m~-a4h3LG5^FIS@toh%*#MP*% zpm^rAvkSZdi`@L}0cy0K3MvCO7>l`%5oyW9CERX>@lcp@v{%!)l;1EGV8kr;wGh z{CwB7*MY6z=YVQ~fnZa7OPZmmX=l(>M}i)<2G6dAj^6~T=4GI2Gy_x%^a6FZJKF5M z=h-YEoulA)U@P8loMRe;gDm~k@GY?m;S(BkvmrEX9$)HtoI*J^jQLS!X5xB zV;}4fK1<)V2bX~=;JqDO7reaE;YsjXczRyzTA(4Qmihh~SCF^Cde{Rxx)$89%2lBC zs!1;3c^smp6VyJa)(tMfHP}k{PEdXQFL?QlzR``1fuO?QgVz|DHFZ+U357-8{_PKu z6rLZpuhG15lhtk;^cvn8S!dtmia%AG>zNb1N#UXzb#t$_JePp-nga&ljG3GQNPPWT zXMYN+OasH4P*jl5wvd#=-8D}17lm0h8)con-j$<02`bHB!mc$N`BTEYnsp0P zo3r2V=?-k^s?um0KA+||yaQD4Uq(V&J9n^<9Rc2Zr{hsY`^^rNJW)xYKlDab8hT-U9C)>}tfj`hHiI zIR!<d<^2t1Rvq2~RR$aMiIUW)Pop5}@QK9*XBW(vQ#_9g zA|=@65trbc>9eM(l8-&?7Nl(?q`{cFV(R&*^UE)6SvX_jOfR){O>I#-KIS@jE2s+J zM>?RkqHcdec`W5M*iIr)=+!&ZU*PDZNfL!f$P_S}NG1r+2qZ1vWdCmg>T zTO&JFfWNeHNf6;bZNb{m-5Qdq&3I}vo${ZihN2*+Kk58Zmo?3arid!y7i0jgmgBB;iJ;reHsy$V#qt)F#$c`>LO z-+zMRDd2=@g)<61#7T4NX~SE=Be5?B8-laI2H@#xe<`U8nbd{N{m;3TZMoqeYxL6& z*N1O|N-)*bs!_!X{jmafyy#jgRq$(e(Eh4HE*S}@%q*lqJ+I$RmqABR^Z5kBdZ1dU ziQx~=yO#I_)L1E)adtHLqEe7fG3jcx8Df~49TSV`hdD)g*N1tB)=i9f)ulRhTESdK zyq8neZ5W$@b%-7Q!1WiYF6)3A{$;5#XWV_piBH^nBg< z&$e`IlhLi@GwP(>##92rR5eoP!66^H>g@wH@lN~L6{KJ~ zcNq$b7JP2uspd((?V9W2JxY(I>oCT_bMt~Z213$Cj zGk;b=K5f<1!n47n2*3DqH({!ft`^aUIEkvCdQbt`&ERWjDe3&BOL)K+ZUnUlHG(er z%GK;lP#Mnx>w>APx59rpzwYpwkf|BpWUuS^16vHYlnf-r^YG8E*mr`e?jleVXnNk%Xje1*7nk~S_ue5*nA^9SjERJ-08 zeiUyxylVMAwlHtX*-`V&Eh=oOxjH@O{LchcubmlwBC6=N`&`DUv+JC^S$X596?m7B zp!&_7qe;Lr*jCw(D)ca@f~D*;30J}cKs8iHPz~7>RClE2a8>NXv8Pu__)$xatm0~v zQPm~f4O{(s8MX@02wRs89@q%n?Yo4_tNGDf?@-;3HqGC_s|PNq;YTBTDyWQy86E~6 zf!zkw<;W}bh@cF%g6h%@wVXXO9Cdi3#6(!tZ>WW)7W~x0p1OC}m2lPbBv2h*A5^v( zpgJnmp{b5cwNI)YQq}oG@e?Kgr}Ecj#3-XV3uYBen3?YtPcEEVNS~im&vnEUP#tk5 zs1eZz)O`5kK74v!(bR$oo;Pjoj3N!0Wh7k1OXuPsT+pC-F#ZTP@J5BVH5ll>9{$*% zQDRdg*H!;%>;0ejTZyMZ-VIdIuLIQsd9#YRJI6H;wu*dKW7qaGX3mR;v!Ym`?`s1^x~QMQyJvmgGal%7S2+r=Pkt6?E4TuWmLpIdI3Beq?Y-d zkzTR*PlK9W4;U8AAhdACq_)jm{8CVD_<2+LvHstRqe}W79~vd8dto=i%f15CXgJ^e zTDEZZ6l`@uejfk0tJ3aR*I7$JwM+|8>CS37Jx_HQbes#{2&w|>z^Vz9KW5y}5~v0$ z<1|n;-+R2P*%&fb4KFysWz?OfR7R6qx|+?&D{48BCftLqcsZ>c-wIU5v*u1$dAu%! zCzMeBWbP_3V9vA%kDurq?*cWO#-8LdEXr3j_5|;`g|l!*)N|gOgsaBU#k7KJNE)DY<)y>_ArP}4soH@0y!1LB(tC(e=YI`xL;uYo1YdL=AgaxUA zH-5n!ec|FQAY9e(!>x_#jtb%~wck3s!pw zJ#2kc-Nbj6>ey~B^`VAbh0IV>bjMa*CKMFR;*P&}9Cm%|Pl%^FJq0!e3n$H(Swvm+ z6_9Sdr#4e}5HA1gK&A6iPnQp0vgw?a@Ot-hjltJq?8YW|1;t#6F>h0+nu6k4MFk{$ zDG8|YTA$|nGIfec@#hh)0;TMN8MEgW78Mk|(%YrasyU~KtC&3ev=>^Nt1|?%d$5%( zRfBsvx%Fy&AD7Wr6jOs^|E9`n`o|2j`??zbk$;`|I>o&RYO)RM=X!U-Os>hR(Fpew zpe7m>?rhvVkr?Q*?Fed+eAnNd3A=(i<24440k!)quG9O4>Yn`>n| z22^?jKn>lShr0HxHO%GnD>l^}UUDspCd)bCQDE28okJ^79WdEBmShcgbD)Q1a22-V zy=vGJUh96I&n9ToFGqiN=%#(`MyA)=nZKdzQ~JMX_dJ$Q+I8gzD#%x`B?YHJ&DUI}~E$+vXXGTb|()`21?ISt| zJr8rGi|Xbt40A`ugJvrj{QJVvQSsmdxK?nn zuxvnfFpdE}2rFt`e?yo%I_`fHmX3}GZJ8jhyVUNpvG!LixXY}Vsu}x4R=6WKC)Spo zXAfz?JT$c}Ia9W~!rU?O*oSa!!v14&g1RgRClHzum1{^CjEx8P!7;FjQ8qj~*tg#r zJTg0W8XL>b;f_8z!D6(im%_ngvT54fGvmQtILnIx*gPzUo5^LW8kj~LknL{{bH~N~ zPr}l1@nGbUt|qkPxa`>VSRKNpy>o(HXlkqUDE~Tag;n3m;o#lvGBH%NsJg+OSn7X* zm>C~oxh7U~9?tYtqtUoD+DI%FjI5~AN-QPG2>kICkMwEi=|L=n|NN_MIM(OJc*@N@WR1oW(NV=_qJFyqaM8jYl!otc)wuv zaTc@Sv}5)UyK&k6(lECm9_)fsvFNE0*+Fg2EUFw8=$#$($5KJuP+w{mZJM7Qe1$cf zFgIv>Hggrvj2h)aEEi8r^dy#R292a2&C@Lk+09eqT46J>F z^+Q^?ax&2f>xZRLA2qapUYJ`L4>rQ7(-_%(vV+RUx?0kO_;)ueBU#qUZ(ljpFDk+_ z1gTolJdHIxj*5i+N9P1%(bTi7Ner^`{M$Lb#+4W)rO!BDh3eBEmK zHdaB(igjn)j|;00%<;R2<F#ER3ad;=$<@M(Jf~ddIH7>JsiaGe;{FF=`J#A`O&j5&!cp)=W_e*&QFibyRxiRB;G9kWUskkP ze+G)0KQ;mFER{Fyr~ct(=XQ<_WfyZs*neS8>~1tFwJ_%oy+4pCqIu{xKBZVTQaS59 z7M5Qe_XmZ+CGpq;gVd*5+rCA!9--1F40iJ{EzG(k+n*T*m&X0AVeX}jf+1SFl=mdG zUeWmYf7T2?w^Md%Z_Yj}x!;7jm&bz@L*1Ii6y-d4<}lAY+j%g(Ucs6YSz6teur|!t zANFso^HX8|1!4J;c<}geH%K`{%**z_4s(~rgVrOsHHu%AsQ;H^*%Ht%JN9?1GsFH& zidJFn74g{Qk)Ag(vR??xv73$J)DiBuA}8&SF+CFqAH4T zO_;kZ9y@%D=Up7xKZfPY;{Jj#D2vCgJX3S8{}nm@i(x5t&2h{gdBujJF?et3n1Mpq zU6B*}D5B)tA}qfu?#~N@Fdl3@D>YJSxmab+MHh$FmvziQ867Sy%ZYuAc8-b~%NtKK zMXA4tc2QWpEXThgEMFdv1rrn^@2k*8M%t%n=SSWlr?h!t|7AI`Dif8K%6Adk{7Cy5 z?d(X?R}D_9Iob1OMlsGun;2GK)iDEQLfHTE9KUy1o{R@q7kVC@$i^qDh2Jg{%e6jB zV9<=Nb%T^McS6zTJ7%Cvj-0c@V0ApUlIhSn8a)39OR<~I;|4)gmnCS?#v}F_TF>ZY z(r>JD!><|U z-W(5xUEoebZriW|tDAZ=S}%8n!P>arFwEt%BrIJUumAmplt}4w_(&xw9Pfv_*ESDk zU9^8MI6OP;mW#qFWu0S3EKZHTYtd#!&06K+RQsHb=DO=sv}mfvdS4QC+^romP+Z_~ zmqv|uL&pr1XadLnjizSom{F>JQxm^}HaT3%WV=k$XXzD@GG3!KcFg5UVd=V#87TQt z^X^4+ISyRndFMqXdk$@G6xwAe^$h#d;V4%rFzvD{!ajGzGp_Kw8>0NKzA`H4bsaO7 zMa5%G%s@LU8pJQ4xdGdwEE+n@yZg|hx#+hE%kPZGZn{eGb`Vl6^t|!mQU>b`G-fwr z=dG0H_X&f$$DpswlqXOR8F$3k{h?}gH3UNWth7uo|blFc-f}T z{)Di6Q#`i%MveXI{W@l%^q_8>I5;gFzk2@}kkdk5%1RC%z!HO{9huwJz4|+=HqB#a zMdI}}D@v0~r{Mm0Y%3f);bA$!UNoI^-EQspo3uQOzYtARfU5?Uux(hUVsQz>j`An0 zURX?)G1;-MHoVcQJT{w+U+^GTImU6- z%AVxMuoQ0OIyWpoM13oo2U>w&(4nBiFImJ z@b}P8f+vmT*});}_s{G8*+DNXg>eriKRZ^2)jM1|vtx$na4FY6!L80c$};GLrFQgH zq1aq37AO{wO=xZb(N^m#EVUq&qatm?@<-yaNw=v@`j5{EZbWNKXjYhYe|GQ^R#z-` z`NXV!yDLsC%v#H(9hNGT8C8BRmQr=s>VN$Lb_4OI5YLOQ7V6(&r>WRNv}m;Y{|tkz z@z_y!dfw1zDcy-SN?LH-UG5}C({ZVJCDzHL6x~*cJt-@?1@Z%0A9&h`eOcGA{E2u_ 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 E0}InJ1ONa4 diff --git a/package-lock.json b/package-lock.json index 63b660e..a1339a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.255", + "version": "1.0.273", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@sqlitecloud/drivers", - "version": "1.0.255", + "version": "1.0.273", "license": "MIT", "dependencies": { + "@craftzdog/react-native-buffer": "^6.0.5", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "jest-html-reporter": "^3.10.2", "lz4js": "^0.2.0", - "react-native-buffer": "^6.0.3", "react-native-url-polyfill": "^2.0.0", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", @@ -51,7 +51,7 @@ "node": ">=18.0" }, "peerDependencies": { - "react-native": "0.74.5", + "react-native-quick-base64": "*", "react-native-tcp-socket": "^6.2.0" } }, @@ -2444,6 +2444,30 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@craftzdog/react-native-buffer": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@craftzdog/react-native-buffer/-/react-native-buffer-6.0.5.tgz", + "integrity": "sha512-Av+YqfwA9e7jhgI9GFE/gTpwl/H+dRRLmZyJPOpKTy107j9Oj7oXlm3/YiMNz+C/CEGqcKAOqnXDLs4OL6AAFw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "ieee754": "^1.2.1", + "react-native-quick-base64": "^2.0.5" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -10829,42 +10853,13 @@ } } }, - "node_modules/react-native-buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/react-native-buffer/-/react-native-buffer-6.0.3.tgz", - "integrity": "sha512-zZ0+TytQukFkdq7l9VQ8eypY83qQLMYNNVA+1skHQWcjXWRxsJvR6TAW4/HwD1FTLIySQt7cyvbNYSO30RuNvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], + "node_modules/react-native-quick-base64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-native-quick-base64/-/react-native-quick-base64-2.1.2.tgz", + "integrity": "sha512-xghaXpWdB0ji8OwYyo0fWezRroNxiNFCNFpGUIyE7+qc4gA/IGWnysIG5L0MbdoORv8FkTKUvfd6yCUN5R2VFA==", "license": "MIT", "dependencies": { - "ieee754": "^1.2.1", - "react-native-fast-base64": "^0.1.1" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-fast-base64": "*" - } - }, - "node_modules/react-native-fast-base64": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/react-native-fast-base64/-/react-native-fast-base64-0.1.2.tgz", - "integrity": "sha512-LBwGGdAQirfU++1r4DX1ufkWKUhY6wwVEbVhyH247YXeubW0jxl7+yl8xlOchz6W/J1KFzXrGTcMiHlv+OpIig==", - "license": "MIT", - "engines": { - "node": ">= 16.0.0" + "base64-js": "^1.5.1" }, "peerDependencies": { "react": "*", diff --git a/package.json b/package.json index 5774a4c..91da70b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.255", + "version": "1.0.273", "description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -43,19 +43,19 @@ }, "homepage": "https://github.com/sqlitecloud/sqlitecloud-js#readme", "dependencies": { + "@craftzdog/react-native-buffer": "^6.0.5", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "jest-html-reporter": "^3.10.2", "lz4js": "^0.2.0", - "react-native-buffer": "^6.0.3", "react-native-url-polyfill": "^2.0.0", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", "whatwg-url": "^14.0.0" }, "peerDependencies": { - "react-native-tcp-socket": "^6.2.0", - "react-native-fast-base64": "*" + "react-native-quick-base64": "*", + "react-native-tcp-socket": "^6.2.0" }, "devDependencies": { "@types/bun": "^1.1.1", @@ -101,9 +101,9 @@ "react-native": { "whatwg-url": "react-native-url-polyfill", "tls": "react-native-tcp-socket", - "buffer": "react-native-buffer" + "buffer": "@craftzdog/react-native-buffer" }, "browser": { "tls": false } -} +} \ No newline at end of file diff --git a/test/core/built-in-commands.test.ts b/test/core/built-in-commands.test.ts index cbffa8a..8acef16 100644 --- a/test/core/built-in-commands.test.ts +++ b/test/core/built-in-commands.test.ts @@ -4,10 +4,6 @@ import { _, - SQLiteCloudError, - SQLiteCloudRowset, - SQLiteCloudConnection, - SQLiteCloudTlsConnection, CHINOOK_DATABASE_URL, parseconnectionstring, getConnection, @@ -1386,41 +1382,418 @@ describe.each([ }) describe.each([ - ['ID', 'autocheckpoint', true, true, false, true], - ['IP', 'backlog', false, true, false, true], - ['UUID', 'cluster_port', false, false, false, true], - ['MAXROWS', 'newcluster', true, false, true, true], - [undefined, undefined, false, false, false, false], - ['\0\0\\\\', '\0\0\\\\', true, true, true, false], - [99, 99, false, true, false, false] -])('settings', (client_key, cluster_key, detailed, no_read_only, client_editable, ok) => { - it(`should${ok ? '' : "n't"} get client key`, done => { - const chinook = getConnection() - chinook.sendCommands(`GET CLIENT KEY ${client_key}`, test(done, chinook, ok, regex_IP_UUID_N)) + ['COMPRESSION', 1, true], //tofix + ['ID', 10, true], + ['IP', 10, true], + ['MAXDATA', 0, true], //tofix + ['MAXROWS', 0, true], //tofix + ['MAXROWSET', 0, true], //tofix + ['NOBLOB', 0, true], //tofix + ['NONLINEARIZABLE', 0, true], //tofix + ['UUID', 10, true], + ['ZEROTEXT', 0, true], //tofix + [undefined, undefined, false], + ['\0\0\\\\', 10, false], + [99, 10, false], + + //['COMPRESSION', -1 * Number.MAX_VALUE, true], tofix + ['ID', -1 * Number.MAX_VALUE, true], + ['IP', -1 * Number.MAX_VALUE, true], + //['MAXDATA', -1 * Number.MAX_VALUE, true], tofix + //['MAXROWS', -1 * Number.MAX_VALUE, true], tofix + //['MAXROWSET', -1 * Number.MAX_VALUE, true], tofix + //['NOBLOB', -1 * Number.MAX_VALUE, true],/tofix + //['NONLINEARIZABLE', -1 * Number.MAX_VALUE, true], tofix + ['UUID', -1 * Number.MAX_VALUE, true], + //['ZEROTEXT', -1 * Number.MAX_VALUE, true], tofix + [undefined, undefined, false], + ['\0\0\\\\', -1 * Number.MAX_VALUE, false], + [99, -1 * Number.MAX_VALUE, false], + + //['COMPRESSION', Number.MAX_VALUE, true], tofix + ['ID', Number.MAX_VALUE, true], + ['IP', Number.MAX_VALUE, true], + //['MAXDATA', Number.MAX_VALUE, true], tofix + //['MAXROWS', Number.MAX_VALUE, true], tofix + //['MAXROWSET', Number.MAX_VALUE, true], tofix + //['NOBLOB', Number.MAX_VALUE, true], tofix + //['NONLINEARIZABLE', Number.MAX_VALUE, true], tofix + //['UUID', Number.MAX_VALUE, true], + //['ZEROTEXT', Number.MAX_VALUE, true], tofix + [undefined, undefined, false], + ['\0\0\\\\', Number.MAX_VALUE, false], + [99, Number.MAX_VALUE, false], + + //['COMPRESSION', 0, true], tofix + ['ID', 0, true], + ['IP', 0, true], + ['MAXDATA', 0, true], + ['MAXROWS', 0, true], + ['MAXROWSET', 0, true], + ['NOBLOB', 0, true], + ['NONLINEARIZABLE', 0, true], + ['UUID', 0, true], + ['ZEROTEXT', 0, true], + [undefined, undefined, false], + ['\0\0\\\\', 0, false], + [99, 0, false] +])('client settings', (key, value, ok) => { + it(`should${ok ? '' : "n't"} get key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET CLIENT KEY ${key}`, test(done, chinook, ok, regex_IP_UUID_N)) }) - it(`shouldn't get database key`, done => { + it(`should${ok ? '' : "n't"} list keys`, done => { const chinook = getConnection() - chinook.sendCommands(`GET DATABASE chinook.sqlite KEY ${client_key}`, test(done, chinook, false /* fails everytime in ci */)) + chinook.sendCommands( + `LIST CLIENT KEYS`, + test(done, chinook, ok, { + key: key, + value: expect.stringMatching(regex_IP_UUID_N) + }) + ) }) - it(`should${ok ? '' : "n't"} get cluster key`, done => { + let read_only = false + + it(`should${ok ? '' : "n't"} set key to ${value}`, done => { const chinook = getConnection() - chinook.sendCommands(`GET KEY ${cluster_key}`, test(done, chinook, ok, /[0-9]/)) + chinook.sendCommands(`SET CLIENT KEY ${key} TO ${value}`, (error: any, results: any) => { + if (ok) { + if (results) { + expect(error).toBeNull() + expect(results).toBe('OK') + } else { + expect(error.message).toMatch(/(is read-only|unable to set)/i) + read_only = true + } + done() + chinook.close() + } else { + test(done, chinook, ok)(error, results) + } + }) }) - it(`should${ok ? '' : "n't"} list client keys`, done => { + it(`should${ok ? '' : "n't"} check list keys`, done => { const chinook = getConnection() chinook.sendCommands( `LIST CLIENT KEYS`, test(done, chinook, ok, { - key: client_key, - value: expect.stringMatching(regex_IP_UUID_N) + key: key, + value: !read_only && ok ? value?.toString() : expect.stringMatching(regex_IP_UUID_N) + }) + ) + }) + + it(`should${ok ? '' : "n't"} check key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET CLIENT KEY ${key}`, test(done, chinook, ok, !read_only && ok ? value?.toString() : regex_IP_UUID_N)) + }) + + it(`should${ok ? '' : "n't"} remove key`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE CLIENT KEY ${key}`, test(done, chinook, ok)) + }) +}) + +describe.each([ + ['hello', 10, true], + [undefined, undefined, false], + ['\0\0\\\\', 10, false], + [99, 10, true], + + ['hello', -1 * Number.MAX_VALUE, true], + [undefined, undefined, false], + ['\0\0\\\\', -1 * Number.MAX_VALUE, false], + [99, -1 * Number.MAX_VALUE, true], + + ['hello', Number.MAX_VALUE, true], + [undefined, undefined, false], + ['\0\0\\\\', Number.MAX_VALUE, false], + [99, Number.MAX_VALUE, true], + + ['hello', 0, true], + [undefined, undefined, false], + ['\0\0\\\\', 0, false], + [99, 0, true] +])('database settings', (key, value, ok) => { + it(`shouldn't get key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET DATABASE chinook.sqlite KEY ${key}`, test(done, chinook, ok, null)) //key hasn't been set so it's right to receive null + }) + + it(`should${ok ? '' : "n't"} list keys`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST DATABASE chinook.sqlite KEYS`, + test(done, chinook, true, { + key: 'backup', + value: '1' }) ) }) - it(`should${ok ? '' : "n't"} list cluster keys`, done => { + it(`should${ok ? '' : "n't"} set key to ${value}`, done => { + const chinook = getConnection() + chinook.sendCommands(`SET DATABASE chinook.sqlite KEY ${key} TO ${value}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} check key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET DATABASE chinook.sqlite KEY ${key}`, test(done, chinook, ok, value?.toString())) + }) + + it(`should remove key`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE DATABASE chinook.sqlite KEY ${key}`, test(done, chinook, ok)) + }) +}) + +describe.each([ + ['autocheckpoint', 10, true, true, true], + ['autocheckpoint_full', 10, true, true, true], + ['backlog', 10, false, true, true], + ['backup_node_id', 10, false, true, true], + ['base_path', 10, false, false, true], + ['client_timeout', 10, false, true, true], + ['cluster_address', 10, false, false, true], + ['cluster_config', 10, false, false, true], + ['cluster_node_id', 10, false, false, true], + ['cluster_port', 10, false, false, true], + ['cluster_timeout', 10, false, true, true], + ['command_maxlen', 10, false, true, true], + ['dbbusy_timeout', 10, false, true, true], + ['dbdrop_timeout', 10, false, true, true], + ['dbpage_size', 10, false, true, true], + ['download_chunk_size', 10, false, true, true], + ['follower_client_timeout', 10, false, true, true], + ['insecure', 10, false, true, true], + ['listening_address', 10, false, false, true], + ['listening_port', 10, false, false, true], + ['log_format', 10, false, true, true], + ['log_level', 10, false, true, true], + ['max_chunk_size', 10, false, true, true], + ['max_connections', 10, false, true, true], + ['messages_path', 10, false, false, true], + ['min_compression_size', 10, false, true, true], + ['newcluster', 10, true, false, true], + ['nocluster', 10, false, true, true], + ['nthreads', 10, false, true, true], + ['pubsub_keep_history', 10, false, true, true], + ['pubsub_skip_blob', 10, false, true, true], + ['query_analyzer_enabled', 10, false, true, true], + ['query_analyzer_threshold', 10, false, true, true], + ['raft_election_tick', 10, false, true, true], + ['raft_election_timeout', 10, false, true, true], + ['raft_heartbeat_tick', 10, false, true, true], + ['raft_inc_vacuum_pages', 10, false, true, true], + ['raft_log_level', 10, false, true, true], + ['raft_max_db_size', 10, false, true, true], + ['raft_max_free_size', 10, false, true, true], + ['raft_max_log_entries', 10, false, true, true], + ['raft_tickms', 10, false, true, true], + ['raft_timeout', 10, false, true, true], + ['stats_interval', 10, false, true, true], + ['tcpkeepalive', 10, false, true, true], + ['tcpkeepalive_count', 10, false, true, true], + ['tls_certificate_path', 10, false, false, true], + ['tls_certificatekey_path', 10, false, false, true], + ['tls_cluster_certificate_path', 10, false, false, true], + ['tls_cluster_certificatekey_path', 10, false, false, true], + ['tls_root_certificate_path', 10, false, false, true], + ['tls_verify_client', 10, false, true, true], + ['use_concurrent_transactions', 10, false, true, true], + ['zombie_timeout', 10, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', 10, true, true, false], + [99, 10, false, true, false], + + ['autocheckpoint', -1 * Number.MAX_VALUE, true, true, true], + ['autocheckpoint_full', -1 * Number.MAX_VALUE, true, true, true], + ['backlog', -1 * Number.MAX_VALUE, false, true, true], + ['backup_node_id', -1 * Number.MAX_VALUE, false, true, true], + ['base_path', -1 * Number.MAX_VALUE, false, false, true], + ['client_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['cluster_address', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_config', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_node_id', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_port', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['command_maxlen', -1 * Number.MAX_VALUE, false, true, true], + ['dbbusy_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['dbdrop_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['dbpage_size', -1 * Number.MAX_VALUE, false, true, true], + ['download_chunk_size', -1 * Number.MAX_VALUE, false, true, true], + ['follower_client_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['insecure', -1 * Number.MAX_VALUE, false, true, true], + ['listening_address', -1 * Number.MAX_VALUE, false, false, true], + ['listening_port', -1 * Number.MAX_VALUE, false, false, true], + ['log_format', -1 * Number.MAX_VALUE, false, true, true], + ['log_level', -1 * Number.MAX_VALUE, false, true, true], + ['max_chunk_size', -1 * Number.MAX_VALUE, false, true, true], + ['max_connections', -1 * Number.MAX_VALUE, false, true, true], + ['messages_path', -1 * Number.MAX_VALUE, false, false, true], + ['min_compression_size', -1 * Number.MAX_VALUE, false, true, true], + ['newcluster', -1 * Number.MAX_VALUE, true, false, true], + ['nocluster', -1 * Number.MAX_VALUE, false, true, true], + ['nthreads', -1 * Number.MAX_VALUE, false, true, true], + ['pubsub_keep_history', -1 * Number.MAX_VALUE, false, true, true], + ['pubsub_skip_blob', -1 * Number.MAX_VALUE, false, true, true], + ['query_analyzer_enabled', -1 * Number.MAX_VALUE, false, true, true], + ['query_analyzer_threshold', -1 * Number.MAX_VALUE, false, true, true], + ['raft_election_tick', -1 * Number.MAX_VALUE, false, true, true], + ['raft_election_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['raft_heartbeat_tick', -1 * Number.MAX_VALUE, false, true, true], + ['raft_inc_vacuum_pages', -1 * Number.MAX_VALUE, false, true, true], + ['raft_log_level', -1 * Number.MAX_VALUE, false, true, true], + ['raft_max_db_size', -1 * Number.MAX_VALUE, false, true, true], + ['raft_max_free_size', -1 * Number.MAX_VALUE, false, true, true], + ['raft_max_log_entries', -1 * Number.MAX_VALUE, false, true, true], + ['raft_tickms', -1 * Number.MAX_VALUE, false, true, true], + ['raft_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['stats_interval', -1 * Number.MAX_VALUE, false, true, true], + ['tcpkeepalive', -1 * Number.MAX_VALUE, false, true, true], + ['tcpkeepalive_count', -1 * Number.MAX_VALUE, false, true, true], + ['tls_certificate_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_certificatekey_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificate_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificatekey_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_root_certificate_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_verify_client', -1 * Number.MAX_VALUE, false, true, true], + ['use_concurrent_transactions', -1 * Number.MAX_VALUE, false, true, true], + ['zombie_timeout', -1 * Number.MAX_VALUE, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', -1 * Number.MAX_VALUE, true, true, false], + [99, -1 * Number.MAX_VALUE, false, true, false], + + ['autocheckpoint', Number.MAX_VALUE, true, true, true], + ['autocheckpoint_full', Number.MAX_VALUE, true, true, true], + ['backlog', Number.MAX_VALUE, false, true, true], + ['backup_node_id', Number.MAX_VALUE, false, true, true], + ['base_path', Number.MAX_VALUE, false, false, true], + ['client_timeout', Number.MAX_VALUE, false, true, true], + ['cluster_address', Number.MAX_VALUE, false, false, true], + ['cluster_config', Number.MAX_VALUE, false, false, true], + ['cluster_node_id', Number.MAX_VALUE, false, false, true], + ['cluster_port', Number.MAX_VALUE, false, false, true], + ['cluster_timeout', Number.MAX_VALUE, false, true, true], + ['command_maxlen', Number.MAX_VALUE, false, true, true], + ['dbbusy_timeout', Number.MAX_VALUE, false, true, true], + ['dbdrop_timeout', Number.MAX_VALUE, false, true, true], + ['dbpage_size', Number.MAX_VALUE, false, true, true], + ['download_chunk_size', Number.MAX_VALUE, false, true, true], + ['follower_client_timeout', Number.MAX_VALUE, false, true, true], + ['insecure', Number.MAX_VALUE, false, true, true], + ['listening_address', Number.MAX_VALUE, false, false, true], + ['listening_port', Number.MAX_VALUE, false, false, true], + ['log_format', Number.MAX_VALUE, false, true, true], + ['log_level', Number.MAX_VALUE, false, true, true], + ['max_chunk_size', Number.MAX_VALUE, false, true, true], + //['max_connections', Number.MAX_VALUE, false, true, true], tofix + ['messages_path', Number.MAX_VALUE, false, false, true], + ['min_compression_size', Number.MAX_VALUE, false, true, true], + ['newcluster', Number.MAX_VALUE, true, false, true], + ['nocluster', Number.MAX_VALUE, false, true, true], + ['nthreads', Number.MAX_VALUE, false, true, true], + ['pubsub_keep_history', Number.MAX_VALUE, false, true, true], + ['pubsub_skip_blob', Number.MAX_VALUE, false, true, true], + ['query_analyzer_enabled', Number.MAX_VALUE, false, true, true], + ['query_analyzer_threshold', Number.MAX_VALUE, false, true, true], + ['raft_election_tick', Number.MAX_VALUE, false, true, true], + ['raft_election_timeout', Number.MAX_VALUE, false, true, true], + ['raft_heartbeat_tick', Number.MAX_VALUE, false, true, true], + ['raft_inc_vacuum_pages', Number.MAX_VALUE, false, true, true], + ['raft_log_level', Number.MAX_VALUE, false, true, true], + ['raft_max_db_size', Number.MAX_VALUE, false, true, true], + ['raft_max_free_size', Number.MAX_VALUE, false, true, true], + ['raft_max_log_entries', Number.MAX_VALUE, false, true, true], + ['raft_tickms', Number.MAX_VALUE, false, true, true], + ['raft_timeout', Number.MAX_VALUE, false, true, true], + ['stats_interval', Number.MAX_VALUE, false, true, true], + ['tcpkeepalive', Number.MAX_VALUE, false, true, true], + ['tcpkeepalive_count', Number.MAX_VALUE, false, true, true], + ['tls_certificate_path', Number.MAX_VALUE, false, false, true], + ['tls_certificatekey_path', Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificate_path', Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificatekey_path', Number.MAX_VALUE, false, false, true], + ['tls_root_certificate_path', Number.MAX_VALUE, false, false, true], + ['tls_verify_client', Number.MAX_VALUE, false, true, true], + ['use_concurrent_transactions', Number.MAX_VALUE, false, true, true], + ['zombie_timeout', Number.MAX_VALUE, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', Number.MAX_VALUE, true, true, false], + [99, Number.MAX_VALUE, false, true, false], + + ['autocheckpoint', 0, true, true, true], + ['autocheckpoint_full', 0, true, true, true], + ['backlog', 0, false, true, true], + ['backup_node_id', 0, false, true, true], + ['base_path', 0, false, false, true], + ['client_timeout', 0, false, true, true], + ['cluster_address', 0, false, false, true], + ['cluster_config', 0, false, false, true], + ['cluster_node_id', 0, false, false, true], + ['cluster_port', 0, false, false, true], + ['cluster_timeout', 0, false, true, true], + ['command_maxlen', 0, false, true, true], + ['dbbusy_timeout', 0, false, true, true], + ['dbdrop_timeout', 0, false, true, true], + ['dbpage_size', 0, false, true, true], + ['download_chunk_size', 0, false, true, true], + ['follower_client_timeout', 0, false, true, true], + ['insecure', 0, false, true, true], + ['listening_address', 0, false, false, true], + ['listening_port', 0, false, false, true], + ['log_format', 0, false, true, true], + ['log_level', 0, false, true, true], + ['max_chunk_size', 0, false, true, true], + ['max_connections', 0, false, true, true], + ['messages_path', 0, false, false, true], + ['min_compression_size', 0, false, true, true], + ['newcluster', 0, true, false, true], + ['nocluster', 0, false, true, true], + ['nthreads', 0, false, true, true], + ['pubsub_keep_history', 0, false, true, true], + ['pubsub_skip_blob', 0, false, true, true], + ['query_analyzer_enabled', 0, false, true, true], + ['query_analyzer_threshold', 0, false, true, true], + ['raft_election_tick', 0, false, true, true], + ['raft_election_timeout', 0, false, true, true], + ['raft_heartbeat_tick', 0, false, true, true], + ['raft_inc_vacuum_pages', 0, false, true, true], + ['raft_log_level', 0, false, true, true], + ['raft_max_db_size', 0, false, true, true], + ['raft_max_free_size', 0, false, true, true], + ['raft_max_log_entries', 0, false, true, true], + //['raft_tickms', 0, false, true, true], tofix + //['raft_timeout', 0, false, true, true], tofix + ['stats_interval', 0, false, true, true], + ['tcpkeepalive', 0, false, true, true], + ['tcpkeepalive_count', 0, false, true, true], + ['tls_certificate_path', 0, false, false, true], + ['tls_certificatekey_path', 0, false, false, true], + ['tls_cluster_certificate_path', 0, false, false, true], + ['tls_cluster_certificatekey_path', 0, false, false, true], + ['tls_root_certificate_path', 0, false, false, true], + ['tls_verify_client', 0, false, true, true], + ['use_concurrent_transactions', 0, false, true, true], + ['zombie_timeout', 0, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', 0, true, true, false], + [99, 0, false, true, false] +])('cluster settings', (key, value, detailed, no_read_only, ok) => { + let old_value = expect.stringMatching(/([0-9]|\/|\[)/) + + it(`should${ok ? '' : "n't"} get key`, done => { + const chinook = getConnection() + chinook.sendCommands( + `GET KEY ${key}`, + test(done, chinook, ok, /([0-9]|\/|\[|null)/, (res: any) => (res == null ? (old_value = null) : _)) + ) + }) + + it(`should${ok ? '' : "n't"} list keys`, done => { const chinook = getConnection() chinook.sendCommands( `LIST KEYS${detailed ? ' DETAILED' : ''}${no_read_only ? ' NOREADONLY' : ''}`, @@ -1430,47 +1803,51 @@ describe.each([ ok, detailed ? { - key: cluster_key, + key: key, value: expect.anything(), default_value: no_read_only ? expect.anything() : null, readonly: no_read_only ? 0 : expect.any(Number), description: expect.any(String) } : { - key: cluster_key, - value: expect.anything() + key: key, + value: old_value } ) ) }) - it(`should${ok ? '' : "n't"} set client key`, done => { - const chinook = getConnection() - chinook.sendCommands(`SET CLIENT KEY ${client_key} TO 10`, test(done, chinook, client_editable && ok)) - }) - - it(`should${ok ? '' : "n't"} set database key`, done => { - const chinook = getConnection() - chinook.sendCommands(`SET DATABASE chinook.sqlite KEY ${client_key} TO 10`, test(done, chinook, ok)) - }) - - it(`should${ok ? '' : "n't"} set cluster key`, done => { - const chinook = getConnection() - chinook.sendCommands(`SET KEY ${cluster_key} TO 1001`, test(done, chinook, no_read_only ? ok : false)) - }) + let read_only = false - it(`should${ok ? '' : "n't"} remove client key`, done => { + it(`should${ok ? '' : "n't"} set key to ${value}`, done => { const chinook = getConnection() - chinook.sendCommands(`REMOVE CLIENT KEY ${client_key}`, test(done, chinook, ok)) + chinook.sendCommands(`SET KEY ${key} TO ${value}`, (error: any, results: any) => { + if (ok) { + if (results) { + expect(error).toBeNull() + expect(results).toBe('OK') + } else { + expect(error.message).toMatch(/is read-only/i) + read_only = true + } + done() + chinook.close() + } else { + test(done, chinook, ok)(error, results) + } + }) }) - it(`should remove database key`, done => { + it(`should${ok ? '' : "n't"} check key`, done => { const chinook = getConnection() - chinook.sendCommands(`REMOVE DATABASE chinook.sqlite KEY ${client_key}`, test(done, chinook, ok)) + chinook.sendCommands( + `GET KEY ${key}`, + test(done, chinook, ok, /([0-9]|\/|\[|null)/, (res: any) => (!read_only && ok ? expect(res).toEqual(value?.toString()) : _)) + ) }) - it(`should${ok ? '' : "n't"} remove cluster key`, done => { + it(`should${ok ? '' : "n't"} remove key`, done => { const chinook = getConnection() - chinook.sendCommands(`REMOVE KEY ${cluster_key}`, test(done, chinook, no_read_only ? ok : false)) + chinook.sendCommands(`REMOVE KEY ${key}`, test(done, chinook, !read_only && ok)) }) }) diff --git a/test/core/reserved-commands.test.ts b/test/core/reserved-commands.test.ts index a7bf688..9739492 100644 --- a/test/core/reserved-commands.test.ts +++ b/test/core/reserved-commands.test.ts @@ -2,45 +2,32 @@ * reserved-commands.test.ts - test sqlitecloud reserved commands */ -import { - _, - SQLiteCloudError, - SQLiteCloudRowset, - SQLiteCloudConnection, - SQLiteCloudTlsConnection, - CHINOOK_DATABASE_URL, - parseconnectionstring, - getConnection, - connUsername, - randomName, - randomDate, - randomBool, - date, - ip, - uuid, - bool, - colseq, - screaming_snake_case, - regex_IP_UUID_N, - test -} from './shared' - -describe.skip.each([['example.com', 'chinook.sqlite', 'artists', 3, _, '', true]])('webhook', (url_or_code, database, table, mask, options, secret, ok) => { +import { _, getConnection, test } from './shared' + +describe.each([ + ['example.com', 'chinook.sqlite', 'artists', 3, _, 'example', true], + ['example2.com', 'chinook.sqlite', 'artists', 2, _, 'example2', true], + ['example1.com', 'chinook.sqlite', 'artists', 1, _, 'example1', true], + ['example0.com', 'chinook.sqlite', 'artists', 0, _, 'example0', false], + ['example_notexisting.com', _, _, 2, _, 'example_notexisting', false], + ['example.com', 'chinook', 'artist', 3, _, 'example', false], + [_, _, _, 2, _, _, false] +])('webhook', (url_or_code, database, table, mask, options, secret, ok) => { let generated_secret = '' it(`should${ok ? '' : "n't"} add`, done => { const chinook = getConnection() chinook.sendCommands( `ADD WEBHOOK ${url_or_code}`, - test(done, chinook, ok, (r: any) => (generated_secret = r)) + test(done, chinook, ok, /[A-Za-z0-9]{43}/i, (r: any) => (generated_secret = r)) ) }) it(`should${ok ? '' : "n't"} add with secret`, done => { const chinook = getConnection() chinook.sendCommands( - `ADD WEBHOOK ${url_or_code}${database ? `DATABASE ${database}` : ''}${table ? `TABLE ${table}` : ''}${mask ? `MASK ${mask}` : ''}${options ? `OPTIONS ${options}` : ''} SECRET ${secret}`, - test(done, chinook, ok) + `ADD WEBHOOK ${url_or_code}${database ? ` DATABASE ${database}` : ''}${table ? ` TABLE ${table}` : ''}${mask ? ` MASK ${mask}` : ''}${options ? ` OPTIONS ${options}` : ''} SECRET ${secret}`, + test(done, chinook, ok, secret) ) }) @@ -60,38 +47,123 @@ describe.skip.each([['example.com', 'chinook.sqlite', 'artists', 3, _, '', true] databasename: database, tablename: table, mask: mask, - options: options, + options: options ?? null, secret: secret }, - (r: any) => (id = r[1].id) + (r: any) => (id = r[r.length - 1].id) ) ) }) - it(`should${ok ? '' : "n't"} remove`, done => { + it(`should${ok ? '' : "n't"} remove with secret`, done => { const chinook = getConnection() chinook.sendCommands(`REMOVE WEBHOOK ${id}`, test(done, chinook, ok)) }) - it(`shouldn't list removed`, done => { + it(`shouldn't list ${ok ? 'removed' : ''}`, done => { const chinook = getConnection() chinook.sendCommands( `LIST WEBHOOKS`, - test( - done, - chinook, - false, - { - id: expect.any(Number), - action: url_or_code, - databasename: database, - tablename: table, - mask: mask, - options: options, - secret: secret - }, - (r: any) => (id = r.id) - ) + test(done, chinook, false, { + id: id, + action: url_or_code, + databasename: database, + tablename: table, + mask: mask, + options: options ?? null, + secret: secret + }) ) }) + + it(`should${ok ? '' : "n't"} set`, done => { + const chinook = getConnection() + chinook.sendCommands( + `SET WEBHOOK ${id - 1}${url_or_code ? ` ACTION ${url_or_code}` : ''}${database ? ` DATABASE ${database}` : ''}${table ? ` TABLE ${table}` : ''}${mask ? ` MASK ${mask}` : ''}${options ? ` OPTIONS ${options}` : ''}`, + test(done, chinook, ok) + ) + }) + + it(`should${ok ? '' : "n't"} list set`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST WEBHOOKS`, + test(done, chinook, ok, { + id: id - 1, + action: url_or_code, + databasename: database, + tablename: table, + mask: mask, + options: options ?? null, + secret: generated_secret + }) + ) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE WEBHOOK ${id - 1}`, test(done, chinook, ok)) + }) +}) + +describe.each([ + ['test', true], + [Number.MAX_VALUE, true], + [_, false] +])('debug mask', (mask, ok) => { + it(`should${ok ? '' : "n't"} set`, done => { + const chinook = getConnection() + chinook.sendCommands(`SET DEBUG MASK ${mask ?? ''}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE DEBUG MASK ${mask ?? ''}`, test(done, chinook, ok)) + }) +}) + +describe.skip.each([ + ['test', Number.MAX_VALUE, true], + ['//', '//', false] +])('env', (key, value, ok) => { + it(`should${ok ? '' : "n't"} set`, done => { + const chinook = getConnection() + chinook.sendCommands(`SET ENV ${key} TO ${value}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} get`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET ENV ${key}`, test(done, chinook, ok, value)) + }) + + it(`should${ok ? '' : "n't"} list`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST ENV`, + test(done, chinook, ok, { + key: key, + value: value + }) + ) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE ENV KEY ${key}`, test(done, chinook, ok)) + }) +}) + +describe.skip.each([ + [true, 2, '192.168.1.1:8860', '192.168.1.1:8860', true], + [false, 0, '//', '//', false] +])('node', (learner, id, address, cluster, ok) => { + it(`should${ok ? '' : "n't"} add`, done => { + const chinook = getConnection() + chinook.sendCommands(`ADD${learner ? ' LEARNER' : ''} NODE ${id} ADDRESS ${address}${cluster ? ` CLUSTER ${cluster}` : ''}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE NODE ${id}`, test(done, chinook, ok)) + }) }) diff --git a/test/core/shared.ts b/test/core/shared.ts index 0bd5101..2df75e2 100644 --- a/test/core/shared.ts +++ b/test/core/shared.ts @@ -79,7 +79,11 @@ const test = (done: jest.DoneCallback, chinook: SQLiteCloudConnection, ok: boole expect(results).toBe(expectedResult) } } else { - expect(results).toBe(expectedResult) + if (expectedResult && expectedResult.source.includes('null')) { + expect(results).toBeNull() + } else { + expect(results).toBe(expectedResult) + } } } else { try { @@ -113,10 +117,6 @@ const test = (done: jest.DoneCallback, chinook: SQLiteCloudConnection, ok: boole export { _, - SQLiteCloudError, - SQLiteCloudRowset, - SQLiteCloudConnection, - SQLiteCloudTlsConnection, CHINOOK_DATABASE_URL, parseconnectionstring, getConnection, From e7bfdd082c6d8e129a288ec368c6998cda89cd49 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Tue, 1 Oct 2024 20:28:10 +0200 Subject: [PATCH 2/3] removed test fix react native buffer --- bun.lockb | Bin 477612 -> 456649 bytes package-lock.json | 20 +- package.json | 4 +- test/core/built-in-commands.test.ts | 469 +++++++++++++++++++++++++--- test/core/reserved-commands.test.ts | 164 +++++++--- test/core/shared.ts | 10 +- 6 files changed, 565 insertions(+), 102 deletions(-) diff --git a/bun.lockb b/bun.lockb index 82fc869cc58c5f8aef497799caa0349cc47fc875..093ded85903c59efd68127ae8248dabfd4c579ce 100755 GIT binary patch delta 34268 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.0" }, "peerDependencies": { - "react-native": "0.74.5", + "react-native-quick-base64": "*", "react-native-tcp-socket": "^6.2.0" } }, @@ -10871,6 +10871,20 @@ "react-native": "*" } }, + "node_modules/react-native-quick-base64": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-native-quick-base64/-/react-native-quick-base64-2.1.2.tgz", + "integrity": "sha512-xghaXpWdB0ji8OwYyo0fWezRroNxiNFCNFpGUIyE7+qc4gA/IGWnysIG5L0MbdoORv8FkTKUvfd6yCUN5R2VFA==", + "license": "MIT", + "peer": true, + "dependencies": { + "base64-js": "^1.5.1" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-tcp-socket": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/react-native-tcp-socket/-/react-native-tcp-socket-6.2.0.tgz", diff --git a/package.json b/package.json index 5774a4c..e7c3f34 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.255", + "version": "1.0.274", "description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -106,4 +106,4 @@ "browser": { "tls": false } -} +} \ No newline at end of file diff --git a/test/core/built-in-commands.test.ts b/test/core/built-in-commands.test.ts index cbffa8a..8acef16 100644 --- a/test/core/built-in-commands.test.ts +++ b/test/core/built-in-commands.test.ts @@ -4,10 +4,6 @@ import { _, - SQLiteCloudError, - SQLiteCloudRowset, - SQLiteCloudConnection, - SQLiteCloudTlsConnection, CHINOOK_DATABASE_URL, parseconnectionstring, getConnection, @@ -1386,41 +1382,418 @@ describe.each([ }) describe.each([ - ['ID', 'autocheckpoint', true, true, false, true], - ['IP', 'backlog', false, true, false, true], - ['UUID', 'cluster_port', false, false, false, true], - ['MAXROWS', 'newcluster', true, false, true, true], - [undefined, undefined, false, false, false, false], - ['\0\0\\\\', '\0\0\\\\', true, true, true, false], - [99, 99, false, true, false, false] -])('settings', (client_key, cluster_key, detailed, no_read_only, client_editable, ok) => { - it(`should${ok ? '' : "n't"} get client key`, done => { - const chinook = getConnection() - chinook.sendCommands(`GET CLIENT KEY ${client_key}`, test(done, chinook, ok, regex_IP_UUID_N)) + ['COMPRESSION', 1, true], //tofix + ['ID', 10, true], + ['IP', 10, true], + ['MAXDATA', 0, true], //tofix + ['MAXROWS', 0, true], //tofix + ['MAXROWSET', 0, true], //tofix + ['NOBLOB', 0, true], //tofix + ['NONLINEARIZABLE', 0, true], //tofix + ['UUID', 10, true], + ['ZEROTEXT', 0, true], //tofix + [undefined, undefined, false], + ['\0\0\\\\', 10, false], + [99, 10, false], + + //['COMPRESSION', -1 * Number.MAX_VALUE, true], tofix + ['ID', -1 * Number.MAX_VALUE, true], + ['IP', -1 * Number.MAX_VALUE, true], + //['MAXDATA', -1 * Number.MAX_VALUE, true], tofix + //['MAXROWS', -1 * Number.MAX_VALUE, true], tofix + //['MAXROWSET', -1 * Number.MAX_VALUE, true], tofix + //['NOBLOB', -1 * Number.MAX_VALUE, true],/tofix + //['NONLINEARIZABLE', -1 * Number.MAX_VALUE, true], tofix + ['UUID', -1 * Number.MAX_VALUE, true], + //['ZEROTEXT', -1 * Number.MAX_VALUE, true], tofix + [undefined, undefined, false], + ['\0\0\\\\', -1 * Number.MAX_VALUE, false], + [99, -1 * Number.MAX_VALUE, false], + + //['COMPRESSION', Number.MAX_VALUE, true], tofix + ['ID', Number.MAX_VALUE, true], + ['IP', Number.MAX_VALUE, true], + //['MAXDATA', Number.MAX_VALUE, true], tofix + //['MAXROWS', Number.MAX_VALUE, true], tofix + //['MAXROWSET', Number.MAX_VALUE, true], tofix + //['NOBLOB', Number.MAX_VALUE, true], tofix + //['NONLINEARIZABLE', Number.MAX_VALUE, true], tofix + //['UUID', Number.MAX_VALUE, true], + //['ZEROTEXT', Number.MAX_VALUE, true], tofix + [undefined, undefined, false], + ['\0\0\\\\', Number.MAX_VALUE, false], + [99, Number.MAX_VALUE, false], + + //['COMPRESSION', 0, true], tofix + ['ID', 0, true], + ['IP', 0, true], + ['MAXDATA', 0, true], + ['MAXROWS', 0, true], + ['MAXROWSET', 0, true], + ['NOBLOB', 0, true], + ['NONLINEARIZABLE', 0, true], + ['UUID', 0, true], + ['ZEROTEXT', 0, true], + [undefined, undefined, false], + ['\0\0\\\\', 0, false], + [99, 0, false] +])('client settings', (key, value, ok) => { + it(`should${ok ? '' : "n't"} get key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET CLIENT KEY ${key}`, test(done, chinook, ok, regex_IP_UUID_N)) }) - it(`shouldn't get database key`, done => { + it(`should${ok ? '' : "n't"} list keys`, done => { const chinook = getConnection() - chinook.sendCommands(`GET DATABASE chinook.sqlite KEY ${client_key}`, test(done, chinook, false /* fails everytime in ci */)) + chinook.sendCommands( + `LIST CLIENT KEYS`, + test(done, chinook, ok, { + key: key, + value: expect.stringMatching(regex_IP_UUID_N) + }) + ) }) - it(`should${ok ? '' : "n't"} get cluster key`, done => { + let read_only = false + + it(`should${ok ? '' : "n't"} set key to ${value}`, done => { const chinook = getConnection() - chinook.sendCommands(`GET KEY ${cluster_key}`, test(done, chinook, ok, /[0-9]/)) + chinook.sendCommands(`SET CLIENT KEY ${key} TO ${value}`, (error: any, results: any) => { + if (ok) { + if (results) { + expect(error).toBeNull() + expect(results).toBe('OK') + } else { + expect(error.message).toMatch(/(is read-only|unable to set)/i) + read_only = true + } + done() + chinook.close() + } else { + test(done, chinook, ok)(error, results) + } + }) }) - it(`should${ok ? '' : "n't"} list client keys`, done => { + it(`should${ok ? '' : "n't"} check list keys`, done => { const chinook = getConnection() chinook.sendCommands( `LIST CLIENT KEYS`, test(done, chinook, ok, { - key: client_key, - value: expect.stringMatching(regex_IP_UUID_N) + key: key, + value: !read_only && ok ? value?.toString() : expect.stringMatching(regex_IP_UUID_N) + }) + ) + }) + + it(`should${ok ? '' : "n't"} check key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET CLIENT KEY ${key}`, test(done, chinook, ok, !read_only && ok ? value?.toString() : regex_IP_UUID_N)) + }) + + it(`should${ok ? '' : "n't"} remove key`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE CLIENT KEY ${key}`, test(done, chinook, ok)) + }) +}) + +describe.each([ + ['hello', 10, true], + [undefined, undefined, false], + ['\0\0\\\\', 10, false], + [99, 10, true], + + ['hello', -1 * Number.MAX_VALUE, true], + [undefined, undefined, false], + ['\0\0\\\\', -1 * Number.MAX_VALUE, false], + [99, -1 * Number.MAX_VALUE, true], + + ['hello', Number.MAX_VALUE, true], + [undefined, undefined, false], + ['\0\0\\\\', Number.MAX_VALUE, false], + [99, Number.MAX_VALUE, true], + + ['hello', 0, true], + [undefined, undefined, false], + ['\0\0\\\\', 0, false], + [99, 0, true] +])('database settings', (key, value, ok) => { + it(`shouldn't get key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET DATABASE chinook.sqlite KEY ${key}`, test(done, chinook, ok, null)) //key hasn't been set so it's right to receive null + }) + + it(`should${ok ? '' : "n't"} list keys`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST DATABASE chinook.sqlite KEYS`, + test(done, chinook, true, { + key: 'backup', + value: '1' }) ) }) - it(`should${ok ? '' : "n't"} list cluster keys`, done => { + it(`should${ok ? '' : "n't"} set key to ${value}`, done => { + const chinook = getConnection() + chinook.sendCommands(`SET DATABASE chinook.sqlite KEY ${key} TO ${value}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} check key`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET DATABASE chinook.sqlite KEY ${key}`, test(done, chinook, ok, value?.toString())) + }) + + it(`should remove key`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE DATABASE chinook.sqlite KEY ${key}`, test(done, chinook, ok)) + }) +}) + +describe.each([ + ['autocheckpoint', 10, true, true, true], + ['autocheckpoint_full', 10, true, true, true], + ['backlog', 10, false, true, true], + ['backup_node_id', 10, false, true, true], + ['base_path', 10, false, false, true], + ['client_timeout', 10, false, true, true], + ['cluster_address', 10, false, false, true], + ['cluster_config', 10, false, false, true], + ['cluster_node_id', 10, false, false, true], + ['cluster_port', 10, false, false, true], + ['cluster_timeout', 10, false, true, true], + ['command_maxlen', 10, false, true, true], + ['dbbusy_timeout', 10, false, true, true], + ['dbdrop_timeout', 10, false, true, true], + ['dbpage_size', 10, false, true, true], + ['download_chunk_size', 10, false, true, true], + ['follower_client_timeout', 10, false, true, true], + ['insecure', 10, false, true, true], + ['listening_address', 10, false, false, true], + ['listening_port', 10, false, false, true], + ['log_format', 10, false, true, true], + ['log_level', 10, false, true, true], + ['max_chunk_size', 10, false, true, true], + ['max_connections', 10, false, true, true], + ['messages_path', 10, false, false, true], + ['min_compression_size', 10, false, true, true], + ['newcluster', 10, true, false, true], + ['nocluster', 10, false, true, true], + ['nthreads', 10, false, true, true], + ['pubsub_keep_history', 10, false, true, true], + ['pubsub_skip_blob', 10, false, true, true], + ['query_analyzer_enabled', 10, false, true, true], + ['query_analyzer_threshold', 10, false, true, true], + ['raft_election_tick', 10, false, true, true], + ['raft_election_timeout', 10, false, true, true], + ['raft_heartbeat_tick', 10, false, true, true], + ['raft_inc_vacuum_pages', 10, false, true, true], + ['raft_log_level', 10, false, true, true], + ['raft_max_db_size', 10, false, true, true], + ['raft_max_free_size', 10, false, true, true], + ['raft_max_log_entries', 10, false, true, true], + ['raft_tickms', 10, false, true, true], + ['raft_timeout', 10, false, true, true], + ['stats_interval', 10, false, true, true], + ['tcpkeepalive', 10, false, true, true], + ['tcpkeepalive_count', 10, false, true, true], + ['tls_certificate_path', 10, false, false, true], + ['tls_certificatekey_path', 10, false, false, true], + ['tls_cluster_certificate_path', 10, false, false, true], + ['tls_cluster_certificatekey_path', 10, false, false, true], + ['tls_root_certificate_path', 10, false, false, true], + ['tls_verify_client', 10, false, true, true], + ['use_concurrent_transactions', 10, false, true, true], + ['zombie_timeout', 10, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', 10, true, true, false], + [99, 10, false, true, false], + + ['autocheckpoint', -1 * Number.MAX_VALUE, true, true, true], + ['autocheckpoint_full', -1 * Number.MAX_VALUE, true, true, true], + ['backlog', -1 * Number.MAX_VALUE, false, true, true], + ['backup_node_id', -1 * Number.MAX_VALUE, false, true, true], + ['base_path', -1 * Number.MAX_VALUE, false, false, true], + ['client_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['cluster_address', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_config', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_node_id', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_port', -1 * Number.MAX_VALUE, false, false, true], + ['cluster_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['command_maxlen', -1 * Number.MAX_VALUE, false, true, true], + ['dbbusy_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['dbdrop_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['dbpage_size', -1 * Number.MAX_VALUE, false, true, true], + ['download_chunk_size', -1 * Number.MAX_VALUE, false, true, true], + ['follower_client_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['insecure', -1 * Number.MAX_VALUE, false, true, true], + ['listening_address', -1 * Number.MAX_VALUE, false, false, true], + ['listening_port', -1 * Number.MAX_VALUE, false, false, true], + ['log_format', -1 * Number.MAX_VALUE, false, true, true], + ['log_level', -1 * Number.MAX_VALUE, false, true, true], + ['max_chunk_size', -1 * Number.MAX_VALUE, false, true, true], + ['max_connections', -1 * Number.MAX_VALUE, false, true, true], + ['messages_path', -1 * Number.MAX_VALUE, false, false, true], + ['min_compression_size', -1 * Number.MAX_VALUE, false, true, true], + ['newcluster', -1 * Number.MAX_VALUE, true, false, true], + ['nocluster', -1 * Number.MAX_VALUE, false, true, true], + ['nthreads', -1 * Number.MAX_VALUE, false, true, true], + ['pubsub_keep_history', -1 * Number.MAX_VALUE, false, true, true], + ['pubsub_skip_blob', -1 * Number.MAX_VALUE, false, true, true], + ['query_analyzer_enabled', -1 * Number.MAX_VALUE, false, true, true], + ['query_analyzer_threshold', -1 * Number.MAX_VALUE, false, true, true], + ['raft_election_tick', -1 * Number.MAX_VALUE, false, true, true], + ['raft_election_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['raft_heartbeat_tick', -1 * Number.MAX_VALUE, false, true, true], + ['raft_inc_vacuum_pages', -1 * Number.MAX_VALUE, false, true, true], + ['raft_log_level', -1 * Number.MAX_VALUE, false, true, true], + ['raft_max_db_size', -1 * Number.MAX_VALUE, false, true, true], + ['raft_max_free_size', -1 * Number.MAX_VALUE, false, true, true], + ['raft_max_log_entries', -1 * Number.MAX_VALUE, false, true, true], + ['raft_tickms', -1 * Number.MAX_VALUE, false, true, true], + ['raft_timeout', -1 * Number.MAX_VALUE, false, true, true], + ['stats_interval', -1 * Number.MAX_VALUE, false, true, true], + ['tcpkeepalive', -1 * Number.MAX_VALUE, false, true, true], + ['tcpkeepalive_count', -1 * Number.MAX_VALUE, false, true, true], + ['tls_certificate_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_certificatekey_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificate_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificatekey_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_root_certificate_path', -1 * Number.MAX_VALUE, false, false, true], + ['tls_verify_client', -1 * Number.MAX_VALUE, false, true, true], + ['use_concurrent_transactions', -1 * Number.MAX_VALUE, false, true, true], + ['zombie_timeout', -1 * Number.MAX_VALUE, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', -1 * Number.MAX_VALUE, true, true, false], + [99, -1 * Number.MAX_VALUE, false, true, false], + + ['autocheckpoint', Number.MAX_VALUE, true, true, true], + ['autocheckpoint_full', Number.MAX_VALUE, true, true, true], + ['backlog', Number.MAX_VALUE, false, true, true], + ['backup_node_id', Number.MAX_VALUE, false, true, true], + ['base_path', Number.MAX_VALUE, false, false, true], + ['client_timeout', Number.MAX_VALUE, false, true, true], + ['cluster_address', Number.MAX_VALUE, false, false, true], + ['cluster_config', Number.MAX_VALUE, false, false, true], + ['cluster_node_id', Number.MAX_VALUE, false, false, true], + ['cluster_port', Number.MAX_VALUE, false, false, true], + ['cluster_timeout', Number.MAX_VALUE, false, true, true], + ['command_maxlen', Number.MAX_VALUE, false, true, true], + ['dbbusy_timeout', Number.MAX_VALUE, false, true, true], + ['dbdrop_timeout', Number.MAX_VALUE, false, true, true], + ['dbpage_size', Number.MAX_VALUE, false, true, true], + ['download_chunk_size', Number.MAX_VALUE, false, true, true], + ['follower_client_timeout', Number.MAX_VALUE, false, true, true], + ['insecure', Number.MAX_VALUE, false, true, true], + ['listening_address', Number.MAX_VALUE, false, false, true], + ['listening_port', Number.MAX_VALUE, false, false, true], + ['log_format', Number.MAX_VALUE, false, true, true], + ['log_level', Number.MAX_VALUE, false, true, true], + ['max_chunk_size', Number.MAX_VALUE, false, true, true], + //['max_connections', Number.MAX_VALUE, false, true, true], tofix + ['messages_path', Number.MAX_VALUE, false, false, true], + ['min_compression_size', Number.MAX_VALUE, false, true, true], + ['newcluster', Number.MAX_VALUE, true, false, true], + ['nocluster', Number.MAX_VALUE, false, true, true], + ['nthreads', Number.MAX_VALUE, false, true, true], + ['pubsub_keep_history', Number.MAX_VALUE, false, true, true], + ['pubsub_skip_blob', Number.MAX_VALUE, false, true, true], + ['query_analyzer_enabled', Number.MAX_VALUE, false, true, true], + ['query_analyzer_threshold', Number.MAX_VALUE, false, true, true], + ['raft_election_tick', Number.MAX_VALUE, false, true, true], + ['raft_election_timeout', Number.MAX_VALUE, false, true, true], + ['raft_heartbeat_tick', Number.MAX_VALUE, false, true, true], + ['raft_inc_vacuum_pages', Number.MAX_VALUE, false, true, true], + ['raft_log_level', Number.MAX_VALUE, false, true, true], + ['raft_max_db_size', Number.MAX_VALUE, false, true, true], + ['raft_max_free_size', Number.MAX_VALUE, false, true, true], + ['raft_max_log_entries', Number.MAX_VALUE, false, true, true], + ['raft_tickms', Number.MAX_VALUE, false, true, true], + ['raft_timeout', Number.MAX_VALUE, false, true, true], + ['stats_interval', Number.MAX_VALUE, false, true, true], + ['tcpkeepalive', Number.MAX_VALUE, false, true, true], + ['tcpkeepalive_count', Number.MAX_VALUE, false, true, true], + ['tls_certificate_path', Number.MAX_VALUE, false, false, true], + ['tls_certificatekey_path', Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificate_path', Number.MAX_VALUE, false, false, true], + ['tls_cluster_certificatekey_path', Number.MAX_VALUE, false, false, true], + ['tls_root_certificate_path', Number.MAX_VALUE, false, false, true], + ['tls_verify_client', Number.MAX_VALUE, false, true, true], + ['use_concurrent_transactions', Number.MAX_VALUE, false, true, true], + ['zombie_timeout', Number.MAX_VALUE, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', Number.MAX_VALUE, true, true, false], + [99, Number.MAX_VALUE, false, true, false], + + ['autocheckpoint', 0, true, true, true], + ['autocheckpoint_full', 0, true, true, true], + ['backlog', 0, false, true, true], + ['backup_node_id', 0, false, true, true], + ['base_path', 0, false, false, true], + ['client_timeout', 0, false, true, true], + ['cluster_address', 0, false, false, true], + ['cluster_config', 0, false, false, true], + ['cluster_node_id', 0, false, false, true], + ['cluster_port', 0, false, false, true], + ['cluster_timeout', 0, false, true, true], + ['command_maxlen', 0, false, true, true], + ['dbbusy_timeout', 0, false, true, true], + ['dbdrop_timeout', 0, false, true, true], + ['dbpage_size', 0, false, true, true], + ['download_chunk_size', 0, false, true, true], + ['follower_client_timeout', 0, false, true, true], + ['insecure', 0, false, true, true], + ['listening_address', 0, false, false, true], + ['listening_port', 0, false, false, true], + ['log_format', 0, false, true, true], + ['log_level', 0, false, true, true], + ['max_chunk_size', 0, false, true, true], + ['max_connections', 0, false, true, true], + ['messages_path', 0, false, false, true], + ['min_compression_size', 0, false, true, true], + ['newcluster', 0, true, false, true], + ['nocluster', 0, false, true, true], + ['nthreads', 0, false, true, true], + ['pubsub_keep_history', 0, false, true, true], + ['pubsub_skip_blob', 0, false, true, true], + ['query_analyzer_enabled', 0, false, true, true], + ['query_analyzer_threshold', 0, false, true, true], + ['raft_election_tick', 0, false, true, true], + ['raft_election_timeout', 0, false, true, true], + ['raft_heartbeat_tick', 0, false, true, true], + ['raft_inc_vacuum_pages', 0, false, true, true], + ['raft_log_level', 0, false, true, true], + ['raft_max_db_size', 0, false, true, true], + ['raft_max_free_size', 0, false, true, true], + ['raft_max_log_entries', 0, false, true, true], + //['raft_tickms', 0, false, true, true], tofix + //['raft_timeout', 0, false, true, true], tofix + ['stats_interval', 0, false, true, true], + ['tcpkeepalive', 0, false, true, true], + ['tcpkeepalive_count', 0, false, true, true], + ['tls_certificate_path', 0, false, false, true], + ['tls_certificatekey_path', 0, false, false, true], + ['tls_cluster_certificate_path', 0, false, false, true], + ['tls_cluster_certificatekey_path', 0, false, false, true], + ['tls_root_certificate_path', 0, false, false, true], + ['tls_verify_client', 0, false, true, true], + ['use_concurrent_transactions', 0, false, true, true], + ['zombie_timeout', 0, false, true, true], + [undefined, undefined, false, false, false], + ['\0\0\\\\', 0, true, true, false], + [99, 0, false, true, false] +])('cluster settings', (key, value, detailed, no_read_only, ok) => { + let old_value = expect.stringMatching(/([0-9]|\/|\[)/) + + it(`should${ok ? '' : "n't"} get key`, done => { + const chinook = getConnection() + chinook.sendCommands( + `GET KEY ${key}`, + test(done, chinook, ok, /([0-9]|\/|\[|null)/, (res: any) => (res == null ? (old_value = null) : _)) + ) + }) + + it(`should${ok ? '' : "n't"} list keys`, done => { const chinook = getConnection() chinook.sendCommands( `LIST KEYS${detailed ? ' DETAILED' : ''}${no_read_only ? ' NOREADONLY' : ''}`, @@ -1430,47 +1803,51 @@ describe.each([ ok, detailed ? { - key: cluster_key, + key: key, value: expect.anything(), default_value: no_read_only ? expect.anything() : null, readonly: no_read_only ? 0 : expect.any(Number), description: expect.any(String) } : { - key: cluster_key, - value: expect.anything() + key: key, + value: old_value } ) ) }) - it(`should${ok ? '' : "n't"} set client key`, done => { - const chinook = getConnection() - chinook.sendCommands(`SET CLIENT KEY ${client_key} TO 10`, test(done, chinook, client_editable && ok)) - }) - - it(`should${ok ? '' : "n't"} set database key`, done => { - const chinook = getConnection() - chinook.sendCommands(`SET DATABASE chinook.sqlite KEY ${client_key} TO 10`, test(done, chinook, ok)) - }) - - it(`should${ok ? '' : "n't"} set cluster key`, done => { - const chinook = getConnection() - chinook.sendCommands(`SET KEY ${cluster_key} TO 1001`, test(done, chinook, no_read_only ? ok : false)) - }) + let read_only = false - it(`should${ok ? '' : "n't"} remove client key`, done => { + it(`should${ok ? '' : "n't"} set key to ${value}`, done => { const chinook = getConnection() - chinook.sendCommands(`REMOVE CLIENT KEY ${client_key}`, test(done, chinook, ok)) + chinook.sendCommands(`SET KEY ${key} TO ${value}`, (error: any, results: any) => { + if (ok) { + if (results) { + expect(error).toBeNull() + expect(results).toBe('OK') + } else { + expect(error.message).toMatch(/is read-only/i) + read_only = true + } + done() + chinook.close() + } else { + test(done, chinook, ok)(error, results) + } + }) }) - it(`should remove database key`, done => { + it(`should${ok ? '' : "n't"} check key`, done => { const chinook = getConnection() - chinook.sendCommands(`REMOVE DATABASE chinook.sqlite KEY ${client_key}`, test(done, chinook, ok)) + chinook.sendCommands( + `GET KEY ${key}`, + test(done, chinook, ok, /([0-9]|\/|\[|null)/, (res: any) => (!read_only && ok ? expect(res).toEqual(value?.toString()) : _)) + ) }) - it(`should${ok ? '' : "n't"} remove cluster key`, done => { + it(`should${ok ? '' : "n't"} remove key`, done => { const chinook = getConnection() - chinook.sendCommands(`REMOVE KEY ${cluster_key}`, test(done, chinook, no_read_only ? ok : false)) + chinook.sendCommands(`REMOVE KEY ${key}`, test(done, chinook, !read_only && ok)) }) }) diff --git a/test/core/reserved-commands.test.ts b/test/core/reserved-commands.test.ts index a7bf688..9739492 100644 --- a/test/core/reserved-commands.test.ts +++ b/test/core/reserved-commands.test.ts @@ -2,45 +2,32 @@ * reserved-commands.test.ts - test sqlitecloud reserved commands */ -import { - _, - SQLiteCloudError, - SQLiteCloudRowset, - SQLiteCloudConnection, - SQLiteCloudTlsConnection, - CHINOOK_DATABASE_URL, - parseconnectionstring, - getConnection, - connUsername, - randomName, - randomDate, - randomBool, - date, - ip, - uuid, - bool, - colseq, - screaming_snake_case, - regex_IP_UUID_N, - test -} from './shared' - -describe.skip.each([['example.com', 'chinook.sqlite', 'artists', 3, _, '', true]])('webhook', (url_or_code, database, table, mask, options, secret, ok) => { +import { _, getConnection, test } from './shared' + +describe.each([ + ['example.com', 'chinook.sqlite', 'artists', 3, _, 'example', true], + ['example2.com', 'chinook.sqlite', 'artists', 2, _, 'example2', true], + ['example1.com', 'chinook.sqlite', 'artists', 1, _, 'example1', true], + ['example0.com', 'chinook.sqlite', 'artists', 0, _, 'example0', false], + ['example_notexisting.com', _, _, 2, _, 'example_notexisting', false], + ['example.com', 'chinook', 'artist', 3, _, 'example', false], + [_, _, _, 2, _, _, false] +])('webhook', (url_or_code, database, table, mask, options, secret, ok) => { let generated_secret = '' it(`should${ok ? '' : "n't"} add`, done => { const chinook = getConnection() chinook.sendCommands( `ADD WEBHOOK ${url_or_code}`, - test(done, chinook, ok, (r: any) => (generated_secret = r)) + test(done, chinook, ok, /[A-Za-z0-9]{43}/i, (r: any) => (generated_secret = r)) ) }) it(`should${ok ? '' : "n't"} add with secret`, done => { const chinook = getConnection() chinook.sendCommands( - `ADD WEBHOOK ${url_or_code}${database ? `DATABASE ${database}` : ''}${table ? `TABLE ${table}` : ''}${mask ? `MASK ${mask}` : ''}${options ? `OPTIONS ${options}` : ''} SECRET ${secret}`, - test(done, chinook, ok) + `ADD WEBHOOK ${url_or_code}${database ? ` DATABASE ${database}` : ''}${table ? ` TABLE ${table}` : ''}${mask ? ` MASK ${mask}` : ''}${options ? ` OPTIONS ${options}` : ''} SECRET ${secret}`, + test(done, chinook, ok, secret) ) }) @@ -60,38 +47,123 @@ describe.skip.each([['example.com', 'chinook.sqlite', 'artists', 3, _, '', true] databasename: database, tablename: table, mask: mask, - options: options, + options: options ?? null, secret: secret }, - (r: any) => (id = r[1].id) + (r: any) => (id = r[r.length - 1].id) ) ) }) - it(`should${ok ? '' : "n't"} remove`, done => { + it(`should${ok ? '' : "n't"} remove with secret`, done => { const chinook = getConnection() chinook.sendCommands(`REMOVE WEBHOOK ${id}`, test(done, chinook, ok)) }) - it(`shouldn't list removed`, done => { + it(`shouldn't list ${ok ? 'removed' : ''}`, done => { const chinook = getConnection() chinook.sendCommands( `LIST WEBHOOKS`, - test( - done, - chinook, - false, - { - id: expect.any(Number), - action: url_or_code, - databasename: database, - tablename: table, - mask: mask, - options: options, - secret: secret - }, - (r: any) => (id = r.id) - ) + test(done, chinook, false, { + id: id, + action: url_or_code, + databasename: database, + tablename: table, + mask: mask, + options: options ?? null, + secret: secret + }) ) }) + + it(`should${ok ? '' : "n't"} set`, done => { + const chinook = getConnection() + chinook.sendCommands( + `SET WEBHOOK ${id - 1}${url_or_code ? ` ACTION ${url_or_code}` : ''}${database ? ` DATABASE ${database}` : ''}${table ? ` TABLE ${table}` : ''}${mask ? ` MASK ${mask}` : ''}${options ? ` OPTIONS ${options}` : ''}`, + test(done, chinook, ok) + ) + }) + + it(`should${ok ? '' : "n't"} list set`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST WEBHOOKS`, + test(done, chinook, ok, { + id: id - 1, + action: url_or_code, + databasename: database, + tablename: table, + mask: mask, + options: options ?? null, + secret: generated_secret + }) + ) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE WEBHOOK ${id - 1}`, test(done, chinook, ok)) + }) +}) + +describe.each([ + ['test', true], + [Number.MAX_VALUE, true], + [_, false] +])('debug mask', (mask, ok) => { + it(`should${ok ? '' : "n't"} set`, done => { + const chinook = getConnection() + chinook.sendCommands(`SET DEBUG MASK ${mask ?? ''}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE DEBUG MASK ${mask ?? ''}`, test(done, chinook, ok)) + }) +}) + +describe.skip.each([ + ['test', Number.MAX_VALUE, true], + ['//', '//', false] +])('env', (key, value, ok) => { + it(`should${ok ? '' : "n't"} set`, done => { + const chinook = getConnection() + chinook.sendCommands(`SET ENV ${key} TO ${value}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} get`, done => { + const chinook = getConnection() + chinook.sendCommands(`GET ENV ${key}`, test(done, chinook, ok, value)) + }) + + it(`should${ok ? '' : "n't"} list`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST ENV`, + test(done, chinook, ok, { + key: key, + value: value + }) + ) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE ENV KEY ${key}`, test(done, chinook, ok)) + }) +}) + +describe.skip.each([ + [true, 2, '192.168.1.1:8860', '192.168.1.1:8860', true], + [false, 0, '//', '//', false] +])('node', (learner, id, address, cluster, ok) => { + it(`should${ok ? '' : "n't"} add`, done => { + const chinook = getConnection() + chinook.sendCommands(`ADD${learner ? ' LEARNER' : ''} NODE ${id} ADDRESS ${address}${cluster ? ` CLUSTER ${cluster}` : ''}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} remove`, done => { + const chinook = getConnection() + chinook.sendCommands(`REMOVE NODE ${id}`, test(done, chinook, ok)) + }) }) diff --git a/test/core/shared.ts b/test/core/shared.ts index 0bd5101..2df75e2 100644 --- a/test/core/shared.ts +++ b/test/core/shared.ts @@ -79,7 +79,11 @@ const test = (done: jest.DoneCallback, chinook: SQLiteCloudConnection, ok: boole expect(results).toBe(expectedResult) } } else { - expect(results).toBe(expectedResult) + if (expectedResult && expectedResult.source.includes('null')) { + expect(results).toBeNull() + } else { + expect(results).toBe(expectedResult) + } } } else { try { @@ -113,10 +117,6 @@ const test = (done: jest.DoneCallback, chinook: SQLiteCloudConnection, ok: boole export { _, - SQLiteCloudError, - SQLiteCloudRowset, - SQLiteCloudConnection, - SQLiteCloudTlsConnection, CHINOOK_DATABASE_URL, parseconnectionstring, getConnection, From 8b99326938883abd131be2ea50859b824b771f89 Mon Sep 17 00:00:00 2001 From: Gioele Cantoni Date: Thu, 3 Oct 2024 14:32:48 +0200 Subject: [PATCH 3/3] new tests + replaced react-native-buffer --- README.md | 6 +- bun.lockb | Bin 456649 -> 456642 bytes package-lock.json | 65 +- package.json | 10 +- test/core/built-in-commands.test.ts | 93 ++- test/core/reserved-commands.test.ts | 1061 ++++++++++++++++++++++++++- test/core/shared.ts | 5 +- 7 files changed, 1166 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index b34d71c..4ede121 100644 --- a/README.md +++ b/README.md @@ -17,10 +17,10 @@ npm install @sqlitecloud/drivers You also have to install Peer Dependencies ```bash -npm install @sqlitecloud/drivers react-native-tcp-socket react-native-fast-base64 +npm install @sqlitecloud/drivers react-native-tcp-socket react-native-quick-base64 ``` -React Native run IOS +React Native run iOS ```bash cd ios && pod install && cd .. && npm run ios @@ -32,7 +32,7 @@ React Native run Android (without ./ in Windows) cd android && ./gradlew clean build && cd .. && npm run android ``` -Expo run IOS +Expo run iOS ```bash npx expo prebuild && npx expo run:ios diff --git a/bun.lockb b/bun.lockb index 093ded85903c59efd68127ae8248dabfd4c579ce..c48747cd5a3954bff462253d5372943f555e07e0 100755 GIT binary patch delta 55509 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 fY7wvs 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 delta 55709 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%0AxVSX9 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
  • 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 diff --git a/package-lock.json b/package-lock.json index 370d8c5..a65f22e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.273", + "version": "1.0.274", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@sqlitecloud/drivers", - "version": "1.0.273", + "version": "1.0.274", "license": "MIT", "dependencies": { + "@craftzdog/react-native-buffer": "^6.0.5", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "jest-html-reporter": "^3.10.2", "lz4js": "^0.2.0", - "react-native-buffer": "^6.0.3", "react-native-url-polyfill": "^2.0.0", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", @@ -51,7 +51,7 @@ "node": ">=18.0" }, "peerDependencies": { - "react-native-quick-base64": "*", + "react-native-fast-base64": "*", "react-native-tcp-socket": "^6.2.0" } }, @@ -2444,6 +2444,30 @@ "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==" }, + "node_modules/@craftzdog/react-native-buffer": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@craftzdog/react-native-buffer/-/react-native-buffer-6.0.5.tgz", + "integrity": "sha512-Av+YqfwA9e7jhgI9GFE/gTpwl/H+dRRLmZyJPOpKTy107j9Oj7oXlm3/YiMNz+C/CEGqcKAOqnXDLs4OL6AAFw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "ieee754": "^1.2.1", + "react-native-quick-base64": "^2.0.5" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -10829,40 +10853,12 @@ } } }, - "node_modules/react-native-buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/react-native-buffer/-/react-native-buffer-6.0.3.tgz", - "integrity": "sha512-zZ0+TytQukFkdq7l9VQ8eypY83qQLMYNNVA+1skHQWcjXWRxsJvR6TAW4/HwD1FTLIySQt7cyvbNYSO30RuNvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "ieee754": "^1.2.1", - "react-native-fast-base64": "^0.1.1" - }, - "peerDependencies": { - "react": "*", - "react-native": "*", - "react-native-fast-base64": "*" - } - }, "node_modules/react-native-fast-base64": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/react-native-fast-base64/-/react-native-fast-base64-0.1.2.tgz", "integrity": "sha512-LBwGGdAQirfU++1r4DX1ufkWKUhY6wwVEbVhyH247YXeubW0jxl7+yl8xlOchz6W/J1KFzXrGTcMiHlv+OpIig==", "license": "MIT", + "peer": true, "engines": { "node": ">= 16.0.0" }, @@ -10876,7 +10872,6 @@ "resolved": "https://registry.npmjs.org/react-native-quick-base64/-/react-native-quick-base64-2.1.2.tgz", "integrity": "sha512-xghaXpWdB0ji8OwYyo0fWezRroNxiNFCNFpGUIyE7+qc4gA/IGWnysIG5L0MbdoORv8FkTKUvfd6yCUN5R2VFA==", "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.5.1" }, @@ -13266,4 +13261,4 @@ } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index e7c3f34..b772178 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sqlitecloud/drivers", - "version": "1.0.274", + "version": "1.0.276", "description": "SQLiteCloud drivers for Typescript/Javascript in edge, web and node clients", "main": "./lib/index.js", "types": "./lib/index.d.ts", @@ -43,19 +43,19 @@ }, "homepage": "https://github.com/sqlitecloud/sqlitecloud-js#readme", "dependencies": { + "@craftzdog/react-native-buffer": "^6.0.5", "buffer": "^6.0.3", "eventemitter3": "^5.0.1", "jest-html-reporter": "^3.10.2", "lz4js": "^0.2.0", - "react-native-buffer": "^6.0.3", "react-native-url-polyfill": "^2.0.0", "socket.io": "^4.7.5", "socket.io-client": "^4.7.5", "whatwg-url": "^14.0.0" }, "peerDependencies": { - "react-native-tcp-socket": "^6.2.0", - "react-native-fast-base64": "*" + "react-native-quick-base64": "*", + "react-native-tcp-socket": "^6.2.0" }, "devDependencies": { "@types/bun": "^1.1.1", @@ -101,7 +101,7 @@ "react-native": { "whatwg-url": "react-native-url-polyfill", "tls": "react-native-tcp-socket", - "buffer": "react-native-buffer" + "buffer": "@craftzdog/react-native-buffer" }, "browser": { "tls": false diff --git a/test/core/built-in-commands.test.ts b/test/core/built-in-commands.test.ts index 8acef16..0fdddf0 100644 --- a/test/core/built-in-commands.test.ts +++ b/test/core/built-in-commands.test.ts @@ -2,6 +2,7 @@ * built-in-commands.test.ts - test sqlitecloud built-in commands */ +import { createHash } from 'crypto' import { _, CHINOOK_DATABASE_URL, @@ -18,7 +19,8 @@ import { colseq, screaming_snake_case, regex_IP_UUID_N, - test + test, + CHINOOK_API_KEY } from './shared' describe.each([ @@ -194,18 +196,19 @@ describe.each([ }) describe.each([ - ['admin', randomName(), randomDate(), true], - ['admin', randomName(), 'WRONG_DATE', false], - ['NOT_EXIST', randomName(), randomDate(), false], - ['admin', '', randomDate(), false] -])('api key', (username, keyName, expiration, ok) => { - let key: string + ['admin', randomName(), randomDate(), _, true], + ['admin', randomName(), randomDate(), randomName(), true], + ['admin', randomName(), 'WRONG_DATE', _, false], + ['NOT_EXIST', randomName(), randomDate(), randomName(), false], + ['admin', '', randomDate(), _, false] +])('api key', (username, keyName, expiration, key, ok) => { + let generated_key: string it(`should${ok ? '' : "n't"} create`, done => { const chinook = getConnection() chinook.sendCommands( - `CREATE APIKEY USER ${username} NAME ${keyName} EXPIRATION "${expiration}"`, - test(done, chinook, ok, /^[a-zA-Z0-9]{43}$/, (res: string) => (key = res)) + `CREATE APIKEY USER ${username} NAME ${keyName} EXPIRATION "${expiration}"${key ? ` KEY ${key}` : ''}`, + test(done, chinook, ok, key ? key : /^[a-zA-Z0-9]{43}$/, (res: string) => (generated_key = res)) ) }) @@ -213,7 +216,7 @@ describe.each([ const chinook = getConnection() chinook.sendCommands( `LIST APIKEYS USER ${username} ${username == 'admin' ? '; LIST MY APIKEYS' : ''}`, - test(done, chinook, ok, { creation_date: date(), expiration_date: expiration, key: key, name: keyName }) + test(done, chinook, ok, { creation_date: date(), expiration_date: expiration, key: generated_key, name: keyName }) ) }) @@ -224,8 +227,8 @@ describe.each([ expiration = randomDate() const chinook = getConnection() chinook.sendCommands( - `SET APIKEY ${key} NAME ${keyName} EXPIRATION "${expiration}"; LIST APIKEYS USER ${username}`, - test(done, chinook, false, { creation_date: date(), expiration_date: prevExpiration, key: key, name: prevKeyName }) + `SET APIKEY ${generated_key} NAME ${keyName} EXPIRATION "${expiration}"; LIST APIKEYS USER ${username}`, + test(done, chinook, false, { creation_date: date(), expiration_date: prevExpiration, key: generated_key, name: prevKeyName }) ) }) @@ -233,20 +236,20 @@ describe.each([ const chinook = getConnection() chinook.sendCommands( `LIST APIKEYS USER ${username} ${username == 'admin' ? '; LIST MY APIKEYS' : ''}`, - test(done, chinook, ok, { creation_date: date(), expiration_date: expiration, key: key, name: keyName }) + test(done, chinook, ok, { creation_date: date(), expiration_date: expiration, key: generated_key, name: keyName }) ) }) it(`should${ok ? '' : "n't"} remove`, done => { const chinook = getConnection() - chinook.sendCommands(`REMOVE APIKEY ${key}`, test(done, chinook, ok)) + chinook.sendCommands(`REMOVE APIKEY ${generated_key}`, test(done, chinook, ok)) }) it(`should${ok ? '' : "n't"} list empty`, done => { const chinook = getConnection() chinook.sendCommands( `LIST APIKEYS USER ${username} ${username == 'admin' ? '; LIST MY APIKEYS' : ''}`, - test(done, chinook, false, { creation_date: date(), expiration_date: expiration, key: key, name: keyName }) + test(done, chinook, false, { creation_date: date(), expiration_date: expiration, key: generated_key, name: keyName }) ) }) }) @@ -347,14 +350,14 @@ describe.each([ }) describe.each([ - [randomName(), randomName(), 'READ', 'chinook.sqlite', 'artists', true], - [randomName(), randomName(), '', '', '', true], - [randomName(), randomName(), 'READ', '', '', true], - [randomName(), randomName(), 'NOT_EXIST', '', '', false], - [randomName(), randomName(), '', 'chinook.sqlite', 'artists', true] + [randomName(), randomName(), 'READ', 'chinook.sqlite', 'artists', CHINOOK_API_KEY, true], + [randomName(), randomName(), '', '', '', CHINOOK_API_KEY, true], + [randomName(), randomName(), 'READ', '', '', CHINOOK_API_KEY, true], + [randomName(), randomName(), 'NOT_EXIST', '', '', randomName(), false], + [randomName(), randomName(), '', 'chinook.sqlite', 'artists', CHINOOK_API_KEY, true] //[randomName(), randomName(), 'READ', 'NOT_EXIST', '', false], //[randomName(), randomName(), 'READ', '', 'NOT_EXIST', false] core not checking if database or table exists -])('user', (username, password, role, database, table, ok) => { +])('user', (username, password, role, database, table, key, ok) => { it(`should${ok ? '' : "n't"} create`, done => { const chinook = getConnection() chinook.sendCommands( @@ -384,6 +387,16 @@ describe.each([ chinook.sendCommands(`AUTH USER ${username} PASSWORD ${password}`, test(done, chinook, ok)) }) + it(`should${ok ? '' : "n't"} auth with apikey`, done => { + const chinook = getConnection() + chinook.sendCommands(`AUTH APIKEY ${key}`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} auth with hash`, done => { + const chinook = getConnection() + chinook.sendCommands(`AUTH USER ${username} HASH ${createHash('sha256').update(password).digest('base64')}`, test(done, chinook, ok)) + }) + it(`should${ok ? '' : "n't"} revoke role`, done => { const gOk = role != '' && ok const chinook = getConnection() @@ -603,14 +616,44 @@ describe.each([ chinook.sendCommands(`CREATE CHANNEL ${name} IF NOT EXISTS`, test(done, chinook, ok)) }) - /* it(`should${ok ? '' : "n't"} listen`, done => { //ERROR Data type: | is not defined in SCSP, it isn't supported yet + it(`should${ok ? '' : "n't"} listen`, done => { + //ERROR Data type: | is not defined in SCSP, it isn't supported yet const chinook = getConnection() - chinook.sendCommands(`LISTEN ${name}`, test(done, chinook, ok)) + chinook.sendCommands( + `LISTEN ${name}`, + test( + done, + chinook, + ok, + /^PAUTH\s([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})\s([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/ + ) + ) }) it(`should${ok ? '' : "n't"} listen table`, done => { const chinook = getConnection() - chinook.sendCommands(`LISTEN TABLE ${table} ${database ? `DATABASE ${database}` : ''}`, test(done, chinook, ok)) - }) */ + chinook.sendCommands( + `LISTEN TABLE ${table} ${database ? `DATABASE ${database}` : ''}`, + test( + done, + chinook, + ok, + /^PAUTH\s([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})\s([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/ + ) + ) + }) + + it.skip(`should${ok ? '' : "n't"} list pubsub connections`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST PUBSUB CONNECTIONS`, + test(done, chinook, ok, { + id: expect.any(Number), + dbname: database, + chname: table, + client_uuid: uuid() + }) + ) + }) it(`should${ok ? '' : "n't"} notify`, done => { const chinook = getConnection() diff --git a/test/core/reserved-commands.test.ts b/test/core/reserved-commands.test.ts index 9739492..dc3b74b 100644 --- a/test/core/reserved-commands.test.ts +++ b/test/core/reserved-commands.test.ts @@ -2,7 +2,7 @@ * reserved-commands.test.ts - test sqlitecloud reserved commands */ -import { _, getConnection, test } from './shared' +import { _, getConnection, test, CHINOOK_API_KEY, date, parseconnectionstring, CHINOOK_DATABASE_URL, uuid, ip } from './shared' describe.each([ ['example.com', 'chinook.sqlite', 'artists', 3, _, 'example', true], @@ -122,6 +122,45 @@ describe.each([ }) }) +describe('command debug', () => { + for (let i = -5; i < 10; i++) { + let ok = false + if (i > 0 && i < 5) ok = true + it(`should${ok ? '' : "n't"} command debug`, done => { + const chinook = getConnection() + chinook.sendCommands( + `COMMAND DEBUG ${i}`, + test( + done, + chinook, + true, + ok + ? { + id: expect.any(Number), + address: ip(), + port: expect.any(Number), + nodeid: expect.any(Number), + username: parseconnectionstring(CHINOOK_DATABASE_URL).username, + database: parseconnectionstring(CHINOOK_DATABASE_URL).database, + uuid: uuid(), + ptr: expect.stringMatching(/^0x[a-z0-9]{12}/), + connection_date: date(), + last_activity: date(), + forward: expect.any(Number), + userid: expect.any(Number), + exp: i != 2 ? undefined : expect.any(Number), + dnow: i == 2 ? undefined : i == 4 ? expect.any(String) : expect.any(Number), + last: i == 2 || i == 4 ? undefined : expect.any(Number), + dlast: i != 4 ? undefined : expect.any(String), + diff: i == 2 ? undefined : expect.any(Number) + } + : 'OK' + ) + ) + }) + } +}) + describe.skip.each([ ['test', Number.MAX_VALUE, true], ['//', '//', false] @@ -153,17 +192,1031 @@ describe.skip.each([ }) }) -describe.skip.each([ - [true, 2, '192.168.1.1:8860', '192.168.1.1:8860', true], - [false, 0, '//', '//', false] +describe.each([ + [true, 2, '192.168.1.1:8860', '192.168.1.1:8860', true] + //[false, 0, '//', '//', false] ])('node', (learner, id, address, cluster, ok) => { it(`should${ok ? '' : "n't"} add`, done => { const chinook = getConnection() chinook.sendCommands(`ADD${learner ? ' LEARNER' : ''} NODE ${id} ADDRESS ${address}${cluster ? ` CLUSTER ${cluster}` : ''}`, test(done, chinook, ok)) }) + it(`should${ok ? '' : "n't"} list`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST NODES`, + test(done, chinook, ok, { + id: id, + node: address, + cluster: cluster, + status: learner ? 'Learner' : 'Leader', + progress: expect.stringMatching(/(replicate|probe)/i), + match: expect.any(Number), + last_activity: expect.any(String) //date() + }) + ) + }) + + it.skip(`should${ok ? '' : "n't"} auth`, done => { + const chinook = getConnection() + chinook.sendCommands(`AUTH CLUSTER ${id} ${CHINOOK_API_KEY}`, test(done, chinook, ok)) + }) + + it.skip(`should${ok ? '' : "n't"} promote`, done => { + const chinook = getConnection() + chinook.sendCommands(`PROMOTE NODE ${id}`, test(done, chinook, ok)) + }) + it(`should${ok ? '' : "n't"} remove`, done => { const chinook = getConnection() chinook.sendCommands(`REMOVE NODE ${id}`, test(done, chinook, ok)) }) }) + +describe('list', () => { + it(`should list compile options`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST COMPILE OPTIONS`, + test(done, chinook, true, [ + { compile_options: 'ATOMIC_INTRINSICS=1' }, + { compile_options: 'CODEC=see-aes256-ofb' }, + { compile_options: 'COMPILER=gcc-12.2.0' }, + { compile_options: 'DEFAULT_AUTOVACUUM' }, + { compile_options: 'DEFAULT_CACHE_SIZE=-2000' }, + { compile_options: 'DEFAULT_FILE_FORMAT=4' }, + { compile_options: 'DEFAULT_FOREIGN_KEYS' }, + { compile_options: 'DEFAULT_JOURNAL_SIZE_LIMIT=-1' }, + { compile_options: 'DEFAULT_MMAP_SIZE=0' }, + { compile_options: 'DEFAULT_PAGE_SIZE=4096' }, + { compile_options: 'DEFAULT_PCACHE_INITSZ=20' }, + { compile_options: 'DEFAULT_RECURSIVE_TRIGGERS' }, + { compile_options: 'DEFAULT_SECTOR_SIZE=4096' }, + { compile_options: 'DEFAULT_SYNCHRONOUS=2' }, + { compile_options: 'DEFAULT_WAL_AUTOCHECKPOINT=1000' }, + { compile_options: 'DEFAULT_WAL_SYNCHRONOUS=2' }, + { compile_options: 'DEFAULT_WORKER_THREADS=0' }, + { compile_options: 'DIRECT_OVERFLOW_READ' }, + { compile_options: 'ENABLE_COLUMN_METADATA' }, + { compile_options: 'ENABLE_DBPAGE_VTAB' }, + { compile_options: 'ENABLE_FTS3' }, + { compile_options: 'ENABLE_FTS3_PARENTHESIS' }, + { compile_options: 'ENABLE_FTS3_TOKENIZER' }, + { compile_options: 'ENABLE_FTS4' }, + { compile_options: 'ENABLE_FTS5' }, + { compile_options: 'ENABLE_GEOPOLY' }, + { compile_options: 'ENABLE_LOAD_EXTENSION' }, + { compile_options: 'ENABLE_MATH_FUNCTIONS' }, + { compile_options: 'ENABLE_NORMALIZE' }, + { compile_options: 'ENABLE_PREUPDATE_HOOK' }, + { compile_options: 'ENABLE_RTREE' }, + { compile_options: 'ENABLE_SESSION' }, + { compile_options: 'ENABLE_SNAPSHOT' }, + { compile_options: 'ENABLE_STMT_SCANSTATUS' }, + { compile_options: 'ENABLE_ZIPVFS' }, + { compile_options: 'HAS_CODEC' }, + { compile_options: 'MALLOC_SOFT_LIMIT=1024' }, + { compile_options: 'MAX_ATTACHED=10' }, + { compile_options: 'MAX_COLUMN=2000' }, + { compile_options: 'MAX_COMPOUND_SELECT=500' }, + { compile_options: 'MAX_DEFAULT_PAGE_SIZE=8192' }, + { compile_options: 'MAX_EXPR_DEPTH=1000' }, + { compile_options: 'MAX_FUNCTION_ARG=127' }, + { compile_options: 'MAX_LENGTH=1000000000' }, + { compile_options: 'MAX_LIKE_PATTERN_LENGTH=50000' }, + { compile_options: 'MAX_MMAP_SIZE=0x7fff0000' }, + { compile_options: 'MAX_PAGE_COUNT=0xfffffffe' }, + { compile_options: 'MAX_PAGE_SIZE=65536' }, + { compile_options: 'MAX_SQL_LENGTH=1000000000' }, + { compile_options: 'MAX_TRIGGER_DEPTH=1000' }, + { compile_options: 'MAX_VARIABLE_NUMBER=32766' }, + { compile_options: 'MAX_VDBE_OP=250000000' }, + { compile_options: 'MAX_WORKER_THREADS=8' }, + { compile_options: 'MUTEX_PTHREADS' }, + { compile_options: 'OMIT_AUTOINIT' }, + { compile_options: 'OMIT_DEPRECATED' }, + { compile_options: 'OMIT_TCL_VARIABLE' }, + { compile_options: 'SYSTEM_MALLOC' }, + { compile_options: 'TEMP_STORE=1' }, + { compile_options: 'THREADSAFE=1' } + ]) + ) + }) + + it(`should list only reserved commands`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST ONLY RESERVED COMMANDS`, + test(done, chinook, true, [ + { + command: 'ADD WEBHOOK [DATABASE ] [TABLE ] [MASK ] [OPTIONS ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'ADD WEBHOOK [DATABASE ] [TABLE ] [MASK ] [OPTIONS ] SECRET ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'ADD [LEARNER] NODE ADDRESS [CLUSTER ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'AUTH APIKEY ', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'AUTH CLUSTER ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'AUTH USER HASH ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BACKUP FINISH ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BACKUP INIT [] [SOURCE ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BACKUP PAGECOUNT ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BACKUP REMAINING ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BACKUP STEP PAGES ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BLOB BYTES ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BLOB CLOSE ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BLOB OPEN TABLE COLUMN ROWID RWFLAG ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BLOB READ SIZE OFFSET ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BLOB REOPEN ROWID ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'BLOB WRITE OFFSET DATA ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'COMMAND DEBUG ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'COUNT LOG [FROM ] [TO ] [LEVEL ] [TYPE ] [ID] [NODE ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'CREATE APIKEY USER NAME [EXPIRATION ] KEY ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE CACHEFLUSH', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'DATABASE ERRNO', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'DATABASE FILENAME ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE GET CHANGES', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE GET ROWID', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE GET TOTAL CHANGES', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE LIMIT [VALUE ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE NAME ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE READONLY ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE STATUS RESET ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'DATABASE TXNSTATE []', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'DOWNLOAD ABORT', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'DOWNLOAD DATABASE [IF EXISTS]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'DOWNLOAD STEP', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'GET CONNECTION STATUS', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'HELP <>', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'LIST COMPILE OPTIONS', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'LIST LATENCY KEY [NODE ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'LIST LATENCY [NODE ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'LIST WEBHOOKS', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'LIST [ONLY] RESERVED COMMANDS [DETAILED]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'LOAD DATABASE [KEY ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'PAUTH ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'PROMOTE NODE ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'PUBSUB ONLY', count: expect.any(Number), avgtime: expect.any(Number) }, + { command: 'QUIT SERVER', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'REMOVE DEBUG MASK ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'REMOVE NODE ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'REMOVE WEBHOOK ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'RESERVE DATABASE UUID ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SET DEBUG MASK ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SET USER ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SET WEBHOOK [ACTION ] [DATABASE ] [TABLE ] [MASK ] [OPTIONS ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SQLITE RANDOMNESS [RESET]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SQLITE STATUS RESET ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SWITCH APIKEY ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SWITCH DATABASE ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'SWITCH USER ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'TRANSFER DATABASE [KEY ] [SNAPSHOT ] [INTERNAL]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'UNRESERVE DATABASE [UUID]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'UPLOAD ABORT', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'UPLOAD DATABASE [KEY ] [REPLACE]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VERIFY USER PASSWORD [IP ]', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VM BIND TYPE COLUMN VALUE ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VM CLEAR ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'VM COMPILE ', count: expect.any(Number), avgtime: expect.any(Number) }, + { command: 'VM EXECUTE ', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'VM FINALIZE ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'VM LIST ', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'VM PARAMETER INDEX ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VM PARAMETER NAME ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VM RESET ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VM SCAN STATUS INDEX OP ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VM SCAN STATUS RESET ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { command: 'VM SQL ', count: expect.any(Number), avgtime: expect.any(Number) }, + { + command: 'VM STATUS OP RESET ', + count: expect.any(Number), + avgtime: expect.any(Number) + }, + { + command: 'VM STEP []', + count: expect.any(Number), + avgtime: expect.any(Number) + } + ]) + ) + }) + + it(`should list only reserved commands detailed`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST ONLY RESERVED COMMANDS DETAILED`, + test(done, chinook, true, [ + { + command: 'ADD WEBHOOK [DATABASE ] [TABLE ] [MASK ] [OPTIONS ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'ADD WEBHOOK [DATABASE ] [TABLE ] [MASK ] [OPTIONS ] SECRET ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'ADD [LEARNER] NODE ADDRESS [CLUSTER ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'AUTH APIKEY ', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'AUTH CLUSTER ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'AUTH USER HASH ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BACKUP FINISH ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BACKUP INIT [] [SOURCE ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BACKUP PAGECOUNT ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BACKUP REMAINING ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BACKUP STEP PAGES ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BLOB BYTES ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BLOB CLOSE ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BLOB OPEN TABLE COLUMN ROWID RWFLAG ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BLOB READ SIZE OFFSET ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BLOB REOPEN ROWID ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'BLOB WRITE OFFSET DATA ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'COMMAND DEBUG ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'COUNT LOG [FROM ] [TO ] [LEVEL ] [TYPE ] [ID] [NODE ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'CREATE APIKEY USER NAME [EXPIRATION ] KEY ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE CACHEFLUSH', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'DATABASE ERRNO', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'DATABASE FILENAME ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE GET CHANGES', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE GET ROWID', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE GET TOTAL CHANGES', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE LIMIT [VALUE ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE NAME ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE READONLY ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE STATUS RESET ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'DATABASE TXNSTATE []', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'DOWNLOAD ABORT', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'DOWNLOAD DATABASE [IF EXISTS]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'DOWNLOAD STEP', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'GET CONNECTION STATUS', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'HELP <>', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'LIST COMPILE OPTIONS', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'LIST LATENCY KEY [NODE ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'LIST LATENCY [NODE ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'LIST WEBHOOKS', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'LIST [ONLY] RESERVED COMMANDS [DETAILED]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'LOAD DATABASE [KEY ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'PAUTH ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'PROMOTE NODE ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'PUBSUB ONLY', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { command: 'QUIT SERVER', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'REMOVE DEBUG MASK ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'REMOVE NODE ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'REMOVE WEBHOOK ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'RESERVE DATABASE UUID ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SET DEBUG MASK ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SET USER ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SET WEBHOOK [ACTION ] [DATABASE ] [TABLE ] [MASK ] [OPTIONS ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SQLITE RANDOMNESS [RESET]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SQLITE STATUS RESET ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SWITCH APIKEY ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SWITCH DATABASE ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'SWITCH USER ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'TRANSFER DATABASE [KEY ] [SNAPSHOT ] [INTERNAL]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'UNRESERVE DATABASE [UUID]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'UPLOAD ABORT', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'UPLOAD DATABASE [KEY ] [REPLACE]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VERIFY USER PASSWORD [IP ]', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VM BIND TYPE COLUMN VALUE ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VM CLEAR ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'VM COMPILE ', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { command: 'VM EXECUTE ', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'VM FINALIZE ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'VM LIST ', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'VM PARAMETER INDEX ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VM PARAMETER NAME ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VM RESET ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VM SCAN STATUS INDEX OP ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VM SCAN STATUS RESET ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { command: 'VM SQL ', count: expect.any(Number), avgtime: expect.any(Number), privileges: expect.anything() }, + { + command: 'VM STATUS OP RESET ', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + }, + { + command: 'VM STEP []', + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.anything() + } + ]) + ) + }) + + it(`should list reserved commands`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST RESERVED COMMANDS`, + test(done, chinook, true, { + command: expect.any(String), + count: expect.any(Number), + avgtime: expect.any(Number) + }) + ) + }) + + it(`should list reserved commands detailed`, done => { + const chinook = getConnection() + chinook.sendCommands( + `LIST RESERVED COMMANDS DETAILED`, + test(done, chinook, true, { + command: expect.any(String), + count: expect.any(Number), + avgtime: expect.any(Number), + privileges: expect.any(String) + }) + ) + }) + + it(`should get help`, done => { + //to be implemented + const chinook = getConnection() + chinook.sendCommands(`HELP <>`, test(done, chinook, true)) + }) +}) + +describe.each([[true]])('connection', ok => { + it(`should${ok ? '' : "n't"} get connection status`, done => { + const chinook = getConnection() + chinook.sendCommands( + `GET CONNECTION STATUS`, + test(done, chinook, ok, [parseconnectionstring(CHINOOK_DATABASE_URL).username, parseconnectionstring(CHINOOK_DATABASE_URL).database, 0, 0]) + ) + }) +}) + +describe.each([ + ['chinook.sqlite', true], + [_, false] +])('download database', (database, ok) => { + it(`should${ok ? '' : "n't"} download database`, done => { + const chinook = getConnection() + chinook.sendCommands( + `DOWNLOAD DATABASE ${database}`, + ok + ? (e, r) => { + expect(e).toBeNull() + expect(r).toEqual([expect.any(Number), expect.any(Number), expect.any(Number)]) //could fail?? + r.forEach((v: number) => expect(v).toBeGreaterThan(0)) + for (let i = 0; i < r.length - 1; i++) { + chinook.sendCommands( + `DOWNLOAD STEP`, + i != r.length - 2 + ? (e, r) => { + expect(e).toBeNull() + expect(r).toBeInstanceOf(Buffer) + } + : test(done, chinook, ok, expect.any(Buffer)) + ) + } + } + : test(done, chinook, ok) + ) + }) + + it(`should${ok ? '' : "n't"} abort download database`, done => { + const chinook = getConnection() + chinook.sendCommands( + `DOWNLOAD DATABASE ${database}`, + ok + ? (e, r) => { + expect(e).toBeNull() + expect(r).toEqual([expect.any(Number), expect.any(Number), expect.any(Number)]) + r.forEach((v: number) => expect(v).toBeGreaterThan(0)) + + chinook.sendCommands(`DOWNLOAD STEP`, (e, r) => { + expect(e).toBeNull() + expect(r).toBeInstanceOf(Buffer) + chinook.sendCommands(`DOWNLOAD ABORT`, (e, r) => { + expect(e).toBeNull() + expect(r).toEqual('OK') + chinook.sendCommands(`DOWNLOAD STEP`, test(done, chinook, ok)) + }) + }) + } + : test(done, chinook, ok) + ) + }) + + it(`should download database if exists${ok ? '' : " (it doesn't exist)"}`, done => { + const chinook = getConnection() + chinook.sendCommands( + `DOWNLOAD DATABASE ${database} IF EXISTS`, + ok + ? (e, r) => { + expect(e).toBeNull() + expect(r).toEqual([expect.any(Number), expect.any(Number), expect.any(Number)]) + r.forEach((v: number) => expect(v).toBeGreaterThan(0)) + for (let i = 0; i < r.length - 1; i++) { + chinook.sendCommands( + `DOWNLOAD STEP`, + i != r.length - 2 + ? (e, r) => { + expect(e).toBeNull() + expect(r).toBeInstanceOf(Buffer) + } + : test(done, chinook, ok, expect.any(Buffer)) + ) + } + } + : (e, r) => { + expect(e).toBeNull() + expect(r).toEqual([0, 0, expect.any(Number)]) + chinook.sendCommands(`DOWNLOAD STEP`, test(done, chinook, ok)) + } + ) + }) +}) + +describe.each([[true]])('database commands', ok => { + it(`should${ok ? '' : "n't"} do a cache flush`, done => { + const chinook = getConnection() + chinook.sendCommands(`DATABASE CACHEFLUSH`, test(done, chinook, ok)) + }) + + it(`should${ok ? '' : "n't"} get error number`, done => { + const chinook = getConnection() + chinook.sendCommands(`DATABASE ERRNO`, test(done, chinook, ok, 0)) + }) + + it(`should${ok ? '' : "n't"} get changes`, done => { + const chinook = getConnection() + chinook.sendCommands(`DATABASE GET CHANGES`, test(done, chinook, ok, 0)) + }) + + it(`should${ok ? '' : "n't"} get rowid`, done => { + const chinook = getConnection() + chinook.sendCommands(`DATABASE GET ROWID`, test(done, chinook, ok, 0)) + }) + + it(`should${ok ? '' : "n't"} get total changes`, done => { + const chinook = getConnection() + chinook.sendCommands(`DATABASE GET TOTAL CHANGES`, test(done, chinook, ok, 0)) + }) +}) + +describe('pubsub', () => { + it(`should do pubsub only`, done => { + const chinook = getConnection() + chinook.sendCommands(`PUBSUB ONLY`, (e, r) => { + expect(e).toBeNull() + expect(r).toEqual('OK') + chinook.sendCommands(`LIST DATABASES`, test(done, chinook, false)) + }) + }) +}) + +/* describe.only('sqlite commands', () => { + it(`should set sqlite randomness`, done => { + const chinook = getConnection() + chinook.sendCommands(``, ) + }) +}) */ diff --git a/test/core/shared.ts b/test/core/shared.ts index 2df75e2..839a982 100644 --- a/test/core/shared.ts +++ b/test/core/shared.ts @@ -1,7 +1,7 @@ import { SQLiteCloudError, SQLiteCloudRowset } from '../../src/index' import { SQLiteCloudConnection } from '../../src/drivers/connection' import { SQLiteCloudTlsConnection } from '../../src/drivers/connection-tls' -import { CHINOOK_DATABASE_URL } from '../shared' +import { CHINOOK_DATABASE_URL, CHINOOK_API_KEY } from '../shared' import { parseconnectionstring } from '../../src/drivers/utilities' const _ = undefined // to use undefined as empty argument @@ -95,7 +95,7 @@ const test = (done: jest.DoneCallback, chinook: SQLiteCloudConnection, ok: boole try { expect(error).toBeInstanceOf(SQLiteCloudError) expect((error as SQLiteCloudError).message).toMatch( - /(not found|doesn\'t exist|does not exist|invalid|unable|fail|cannot|must be unique|unknown|undefined|error|no such|not available|try again later|wrong|has no|is read-only)/i + /(not found|doesn\'t exist|does not exist|invalid|unable|fail|cannot|must be unique|unknown|undefined|error|no such|not available|try again later|wrong|has no|is read-only|ended the connection)/i ) expect(results).toBeUndefined() } catch { @@ -118,6 +118,7 @@ const test = (done: jest.DoneCallback, chinook: SQLiteCloudConnection, ok: boole export { _, CHINOOK_DATABASE_URL, + CHINOOK_API_KEY, parseconnectionstring, getConnection, connUsername,