1/*
2  2022-08-23
3
4  The author disclaims copyright to this source code.  In place of a
5  legal notice, here is a blessing:
6
7  *   May you do good and not evil.
8  *   May you find forgiveness for yourself and forgive others.
9  *   May you share freely, never taking more than you give.
10
11  ***********************************************************************
12
13  Demonstration of the sqlite3 Worker API #1 Promiser: a Promise-based
14  proxy for for the sqlite3 Worker #1 API.
15*/
16'use strict';
17(function(){
18  const T = self.SqliteTestUtil;
19  const eOutput = document.querySelector('#test-output');
20  const warn = console.warn.bind(console);
21  const error = console.error.bind(console);
22  const log = console.log.bind(console);
23  const logHtml = async function(cssClass,...args){
24    log.apply(this, args);
25    const ln = document.createElement('div');
26    if(cssClass) ln.classList.add(cssClass);
27    ln.append(document.createTextNode(args.join(' ')));
28    eOutput.append(ln);
29  };
30
31  let startTime;
32  const testCount = async ()=>{
33    logHtml("","Total test count:",T.counter+". Total time =",(performance.now() - startTime),"ms");
34  };
35
36  //why is this triggered even when we catch() a Promise?
37  //window.addEventListener('unhandledrejection', function(event) {
38  //  warn('unhandledrejection',event);
39  //});
40
41  const promiserConfig = {
42    worker: ()=>{
43      const w = new Worker("jswasm/sqlite3-worker1.js");
44      w.onerror = (event)=>error("worker.onerror",event);
45      return w;
46    },
47    debug: 1 ? undefined : (...args)=>console.debug('worker debug',...args),
48    onunhandled: function(ev){
49      error("Unhandled worker message:",ev.data);
50    },
51    onready: function(){
52      self.sqlite3TestModule.setStatus(null)/*hide the HTML-side is-loading spinner*/;
53      runTests();
54    },
55    onerror: function(ev){
56      error("worker1 error:",ev);
57    }
58  };
59  const workerPromise = self.sqlite3Worker1Promiser(promiserConfig);
60  delete self.sqlite3Worker1Promiser;
61
62  const wtest = async function(msgType, msgArgs, callback){
63    if(2===arguments.length && 'function'===typeof msgArgs){
64      callback = msgArgs;
65      msgArgs = undefined;
66    }
67    const p = workerPromise({type: msgType, args:msgArgs});
68    return callback ? p.then(callback).finally(testCount) : p;
69  };
70
71  const runTests = async function(){
72    const dbFilename = '/testing2.sqlite3';
73    startTime = performance.now();
74
75    let sqConfig;
76    await wtest('config-get', (ev)=>{
77      const r = ev.result;
78      log('sqlite3.config subset:', r);
79      T.assert('boolean' === typeof r.bigIntEnabled)
80        .assert('string'===typeof r.wasmfsOpfsDir)
81        .assert('boolean' === typeof r.wasmfsOpfsEnabled);
82      sqConfig = r;
83    });
84    logHtml('',
85            "Sending 'open' message and waiting for its response before continuing...");
86
87    await wtest('open', {
88      filename: dbFilename,
89      simulateError: 0 /* if true, fail the 'open' */,
90    }, function(ev){
91      const r = ev.result;
92      log("then open result",r);
93      T.assert(ev.dbId === r.dbId)
94        .assert(ev.messageId)
95        .assert('string' === typeof r.vfs);
96      promiserConfig.dbId = ev.dbId;
97    }).then(runTests2);
98  };
99
100  const runTests2 = async function(){
101    const mustNotReach = ()=>toss("This is not supposed to be reached.");
102
103    await wtest('exec',{
104      sql: ["create table t(a,b)",
105            "insert into t(a,b) values(1,2),(3,4),(5,6)"
106           ].join(';'),
107      multi: true,
108      resultRows: [], columnNames: []
109    }, function(ev){
110      ev = ev.result;
111      T.assert(0===ev.resultRows.length)
112        .assert(0===ev.columnNames.length);
113    });
114
115    await wtest('exec',{
116      sql: 'select a a, b b from t order by a',
117      resultRows: [], columnNames: [],
118    }, function(ev){
119      ev = ev.result;
120      T.assert(3===ev.resultRows.length)
121        .assert(1===ev.resultRows[0][0])
122        .assert(6===ev.resultRows[2][1])
123        .assert(2===ev.columnNames.length)
124        .assert('b'===ev.columnNames[1]);
125    });
126
127    await wtest('exec',{
128      sql: 'select a a, b b from t order by a',
129      resultRows: [], columnNames: [],
130      rowMode: 'object'
131    }, function(ev){
132      ev = ev.result;
133      T.assert(3===ev.resultRows.length)
134        .assert(1===ev.resultRows[0].a)
135        .assert(6===ev.resultRows[2].b)
136    });
137
138    await wtest(
139      'exec',
140      {sql:'intentional_error'},
141      mustNotReach
142    ).catch((e)=>{
143      warn("Intentional error:",e);
144    });
145
146    await wtest('exec',{
147      sql:'select 1 union all select 3',
148      resultRows: [],
149    }, function(ev){
150      ev = ev.result;
151      T.assert(2 === ev.resultRows.length)
152        .assert(1 === ev.resultRows[0][0])
153        .assert(3 === ev.resultRows[1][0]);
154    });
155
156    const resultRowTest1 = function f(ev){
157      if(undefined === f.counter) f.counter = 0;
158      if(null === ev.rowNumber){
159        /* End of result set. */
160        T.assert(undefined === ev.row)
161          .assert(2===ev.columnNames.length)
162          .assert('a'===ev.columnNames[0])
163          .assert('B'===ev.columnNames[1]);
164      }else{
165        T.assert(ev.rowNumber > 0);
166        ++f.counter;
167      }
168      log("exec() result row:",ev);
169      T.assert(null === ev.rowNumber || 'number' === typeof ev.row.B);
170    };
171    await wtest('exec',{
172      sql: 'select a a, b B from t order by a limit 3',
173      callback: resultRowTest1,
174      rowMode: 'object'
175    }, function(ev){
176      T.assert(3===resultRowTest1.counter);
177      resultRowTest1.counter = 0;
178    });
179
180    const resultRowTest2 = function f(ev){
181      if(null === ev.rowNumber){
182        /* End of result set. */
183        T.assert(undefined === ev.row)
184          .assert(1===ev.columnNames.length)
185          .assert('a'===ev.columnNames[0])
186      }else{
187        T.assert(ev.rowNumber > 0);
188        f.counter = ev.rowNumber;
189      }
190      log("exec() result row:",ev);
191      T.assert(null === ev.rowNumber || 'number' === typeof ev.row);
192    };
193    await wtest('exec',{
194      sql: 'select a a from t limit 3',
195      callback: resultRowTest2,
196      rowMode: 0
197    }, function(ev){
198      T.assert(3===resultRowTest2.counter);
199    });
200
201    const resultRowTest3 = function f(ev){
202      if(null === ev.rowNumber){
203        T.assert(3===ev.columnNames.length)
204          .assert('foo'===ev.columnNames[0])
205          .assert('bar'===ev.columnNames[1])
206          .assert('baz'===ev.columnNames[2]);
207      }else{
208        f.counter = ev.rowNumber;
209        T.assert('number' === typeof ev.row);
210      }
211    };
212    await wtest('exec',{
213      sql: "select 'foo' foo, a bar, 'baz' baz  from t limit 2",
214      callback: resultRowTest3,
215      columnNames: [],
216      rowMode: ':bar'
217    }, function(ev){
218      log("exec() result row:",ev);
219      T.assert(2===resultRowTest3.counter);
220    });
221
222    await wtest('exec',{
223      multi: true,
224      sql:[
225        'pragma foreign_keys=0;',
226        // ^^^ arbitrary query with no result columns
227        'select a, b from t order by a desc; select a from t;'
228        // multi-exec only honors results from the first
229        // statement with result columns (regardless of whether)
230        // it has any rows).
231      ],
232      rowMode: 1,
233      resultRows: []
234    },function(ev){
235      const rows = ev.result.resultRows;
236      T.assert(3===rows.length).
237        assert(6===rows[0]);
238    });
239
240    await wtest('exec',{sql: 'delete from t where a>3'});
241
242    await wtest('exec',{
243      sql: 'select count(a) from t',
244      resultRows: []
245    },function(ev){
246      ev = ev.result;
247      T.assert(1===ev.resultRows.length)
248        .assert(2===ev.resultRows[0][0]);
249    });
250
251    await wtest('export', function(ev){
252      ev = ev.result;
253      T.assert('string' === typeof ev.filename)
254        .assert(ev.byteArray instanceof Uint8Array)
255        .assert(ev.byteArray.length > 1024)
256        .assert('application/x-sqlite3' === ev.mimetype);
257    });
258
259    /***** close() tests must come last. *****/
260    await wtest('close',{},function(ev){
261      T.assert('string' === typeof ev.result.filename);
262    });
263
264    await wtest('close', (ev)=>{
265      T.assert(undefined === ev.result.filename);
266    }).finally(()=>logHtml('',"That's all, folks!"));
267  }/*runTests2()*/;
268
269  log("Init complete, but async init bits may still be running.");
270})();
271