1/************************************************************************************************** 2 * EJDB2 Node.js native API binding. 3 * 4 * MIT License 5 * 6 * Copyright (c) 2012-2022 Softmotions Ltd <info@softmotions.com> 7 * 8 * Permission is hereby granted, free of charge, to any person obtaining a copy 9 * of this software and associated documentation files (the "Software"), to deal 10 * in the Software without restriction, including without limitation the rights 11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 * copies of the Software, and to permit persons to whom the Software is 13 * furnished to do so, subject to the following conditions: 14 * 15 * The above copyright notice and this permission notice shall be included in all 16 * copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 * SOFTWARE. 25 *************************************************************************************************/ 26 27const test = require('ava'); 28const { assert } = require('chai'); 29const { EJDB2, JBE } = require('./index'); 30 31test('Main', async (t) => { 32 const db = await EJDB2.open('hello.db', { truncate: true }); 33 34 let q = db.createQuery('@mycoll/*'); 35 t.true(q != null); 36 t.is(q.collection, 'mycoll'); 37 t.true(q.db != null); 38 39 let id = await db.put('mycoll', { 'foo': 'bar' }); 40 t.is(id, 1); 41 t.throwsAsync(db.put('mycoll', '{"'), { 42 code: '@ejdb IWRC:76005 put', 43 message: 'Unquoted JSON string (JBL_ERROR_PARSE_UNQUOTED_STRING)' 44 }); 45 46 let doc = await db.get('mycoll', 1); 47 t.deepEqual(doc, { foo: 'bar' }); 48 49 await db.put('mycoll', { 'foo': 'baz' }); 50 51 const list = await db.createQuery('@mycoll/*').list({ limit: 1 }); 52 t.is(list.length, 1); 53 54 let first = await db.createQuery('@mycoll/*').first(); 55 t.true(first != null); 56 t.deepEqual(first.json, { foo: 'baz' }); 57 t.is(first._raw, null); 58 59 first = await db.createQuery('@mycoll/[zzz=bbb]').first(); 60 t.is(first, undefined); 61 62 let firstN = await db.createQuery('@mycoll/*').firstN(5); 63 t.true(firstN != null && firstN.length == 2); 64 t.deepEqual(firstN[0].json, { foo: 'baz' }); 65 t.deepEqual(firstN[1].json, { foo: 'bar' }); 66 67 firstN = await db.createQuery('@mycoll/*').firstN(1); 68 t.true(firstN != null && firstN.length == 1); 69 t.deepEqual(firstN[0].json, { foo: 'baz' }); 70 71 // Query 1 72 const rbuf = []; 73 for await (const doc of q.stream()) { 74 rbuf.push(doc.id); 75 rbuf.push(doc._raw); 76 } 77 t.is(rbuf.toString(), '2,{"foo":"baz"},1,{"foo":"bar"}'); 78 79 // Query 2 80 rbuf.length = 0; 81 for await (const doc of db.createQuery('@mycoll/[foo=zaz]').stream()) { 82 rbuf.push(doc.id); 83 rbuf.push(doc._raw); 84 } 85 t.is(rbuf.length, 0); 86 87 // Query 3 88 for await (const doc of db.createQuery('/[foo=bar]', 'mycoll').stream()) { 89 rbuf.push(doc.id); 90 rbuf.push(doc._raw); 91 } 92 t.is(rbuf.toString(), '1,{"foo":"bar"}'); 93 94 let error = null; 95 try { 96 await db.createQuery('@mycoll/[').stream(); 97 } catch (e) { 98 error = e; 99 t.true(JBE.isInvalidQuery(e)); 100 } 101 t.true(error != null); 102 error = null; 103 104 let count = await db.createQuery('@mycoll/* | count').scalarInt(); 105 t.is(count, 2); 106 107 // Del 108 await db.del('mycoll', 1); 109 110 error = null; 111 try { 112 await db.get('mycoll', 1); 113 } catch (e) { 114 error = e; 115 t.true(JBE.isNotFound(e)); 116 } 117 t.true(error != null); 118 error = null; 119 120 count = await db.createQuery('@mycoll/* | count').scalarInt(); 121 t.is(count, 1); 122 123 // Patch 124 await db.patch('mycoll', '[{"op":"add", "path":"/baz", "value":"qux"}]', 2); 125 doc = await db.get('mycoll', 2); 126 t.deepEqual(doc, { foo: 'baz', baz: 'qux' }); 127 128 // DB Info 129 doc = await db.info(); 130 assert.deepNestedInclude(doc, { 131 'collections[0]': { 132 name: 'mycoll', 133 rnum: 1, 134 dbid: 3, 135 indexes: [] 136 } 137 }); 138 139 // Indexes 140 await db.ensureStringIndex('mycoll', '/foo', true); 141 doc = await db.info(); 142 assert.deepNestedInclude(doc, { 143 'collections[0].indexes[0]': { 144 ptr: '/foo', 145 mode: 5, 146 idbf: 0, 147 dbid: 4, 148 rnum: 1 149 } 150 }); 151 152 // Test JQL set 153 doc = await db.createQuery('@mycoll/[foo=:?]').setString(0, 'baz').first(); 154 assert.deepInclude(doc.json, { 155 foo: 'baz', 156 baz: 'qux' 157 }); 158 159 let log = null; 160 // Test explain log 161 await db.createQuery('@mycoll/[foo=:?]').setString(0, 'baz').completionPromise({ 162 explainCallback: (l) => { 163 log = l; 164 } 165 }); 166 assert.isString(log); 167 t.true(log.indexOf('[INDEX] MATCHED UNIQUE|STR|1 /foo EXPR1: \'foo = :?\' INIT: IWKV_CURSOR_EQ') != -1); 168 169 170 doc = await db.createQuery('@mycoll/[foo=:?] and /[baz=:?]') 171 .setString(0, 'baz') 172 .setString(1, 'qux') 173 .first(); 174 assert.deepInclude(doc.json, { 175 foo: 'baz', 176 baz: 'qux' 177 }); 178 179 doc = await db.createQuery('@mycoll/[foo=:foo] and /[baz=:baz]') 180 .setString('foo', 'baz') 181 .setString('baz', 'qux') 182 .first(); 183 assert.deepInclude(doc.json, { 184 foo: 'baz', 185 baz: 'qux' 186 }); 187 188 doc = await db.createQuery('@mycoll/[foo re :?]').setRegexp(0, '.*').first(); 189 assert.deepInclude(doc.json, { 190 foo: 'baz', 191 baz: 'qux' 192 }); 193 194 doc = await db.createQuery('@mycoll/* | /foo').first(); 195 assert.deepEqual(doc.json, { foo: 'baz' }); 196 197 await db.removeStringIndex('mycoll', '/foo', true); 198 doc = await db.info(); 199 assert.deepNestedInclude(doc, { 200 'collections[0].indexes': [] 201 }); 202 203 await db.removeCollection('mycoll'); 204 doc = await db.info(); 205 assert.deepNestedInclude(doc, { 206 'collections': [] 207 }); 208 209 q = db.createQuery('@c1/* | limit 2'); 210 t.is(q.limit, 2); 211 212 q = db.createQuery('@c2/*'); 213 t.is(q.limit, 0); 214 215 // Rename collection 216 id = await db.put('cc1', { 'foo': 1 }); 217 doc = await db.get('cc1', id); 218 t.deepEqual(doc, { 'foo': 1 }); 219 220 await db.renameCollection('cc1', 'cc2'); 221 doc = await db.get('cc2', id); 222 t.deepEqual(doc, { 'foo': 1 }); 223 224 const ts0 = +new Date(); 225 const ts = await db.onlineBackup('hello-bkp.db'); 226 t.true(ts0 < ts); 227 228 await db.close(); 229 230 // Restore from backup 231 const db2 = await EJDB2.open('hello-bkp.db', { truncate: false }); 232 doc = await db2.get('cc2', id); 233 t.deepEqual(doc, { 'foo': 1 }); 234 await db2.close(); 235 236 //global.gc(); 237}); 238 239// test('GC', async () => { 240// global.gc(); 241// await new Promise((resolve) => { 242// global.gc(); 243// setTimeout(() => { 244// resolve(); 245// }, 500); 246// }); 247// }); 248 249