UNPKG

@linked-db/linked-ql

Version:

A query client that extends standard SQL with new syntax sugars and enables auto-versioning capabilities on any database

139 lines (117 loc) 5.17 kB
import { expect } from 'chai'; import { registry } from '../src/lang/registry.js'; import '../src/lang/index.js'; import chalk from 'chalk'; // --- Test Helpers --- let activeIndentation = 0; export async function $describe(text, callback) { activeIndentation++; describe(text, () => callback()); activeIndentation--; } let log; const baseIndentation = ' '; export function $it(text, callback) { const indentation = activeIndentation; it(text, (done) => { log = new Set; Promise.resolve(callback()).then(() => { const $log = log; done(); for (const { entryPoint, nodeName, sql, options, resultNode, desugaredResultNode } of $log) { const formattingOptions = { prettyPrint: options.prettyPrint, indentation }; console.log( baseIndentation + (' '.repeat(indentation)) + chalk.green('∟'), chalk.gray(entryPoint) + ' '.repeat(Math.max(0, 15 - entryPoint.length - 2)) + chalk.gray(`.parse(\``) + chalk.green(formatSql(sql.replace(/`/g, '\\`'), formattingOptions)) + chalk.gray(`\`)`), chalk.gray(formatSql(`--> ${nodeName}:`, formattingOptions)), chalk.green(formatSql(resultNode?.stringify(options), formattingOptions)), ...(desugaredResultNode !== resultNode ? [ chalk.gray(formatSql(`--> ${desugaredResultNode.constructor.name}:`, formattingOptions)), chalk.blue(formatSql(desugaredResultNode?.stringify(options), formattingOptions)), ] : []) ); } }).catch(done); }); } export async function testParseAndStringify(entryPoint, sql, options = {}, schemaInference = null, transformTarget = null) { let nodeName = entryPoint; if (Array.isArray(entryPoint)) { [entryPoint, nodeName] = entryPoint; } let expectedSql = sql; if (Array.isArray(sql)) { [sql, expectedSql] = sql; } const resultNode = await registry[entryPoint].parse(sql, options); expect(resultNode).to.be.instanceOf(registry[nodeName]); // ------------------------- deSugaring const deSugarSpec = { tableQualifiers: true, tableAliases: -1, columnQualifiers: true, selectAliases: -1, normalizeCasing: true, // ---- expansions expandStarRefs: true, flattenUnaliasedRootObjects: false, // ---- exclusions dropVersionSpecs: true, // ---- edges rowConstructorSchemas: false, resultSchemas: true, }; let desugaredResultNode = resultNode; if (options.deSugar) { desugaredResultNode = resultNode.deSugar(deSugarSpec, { dialect: options.dialect }, null, schemaInference); } else if (options.deSugar || options.toDialect) { desugaredResultNode = resultNode.toDialect(options.toDialect, { dialect: options.dialect }, null, schemaInference); } log?.add({ entryPoint, nodeName, sql, options, resultNode, desugaredResultNode }); const normalizerOptions = { stripSpaces: options.stripSpaces, stripQuotes: options.stripQuotes }; expect( normalizeSql(desugaredResultNode.stringify(options)) ).to.equal(normalizeSql(expectedSql, normalizerOptions)); // ------------------------- cloning const resultClone = registry[entryPoint].fromJSON(resultNode.jsonfy(), resultNode.options); expect(resultClone).to.be.instanceOf(registry[nodeName]); let desugaredResultClone = resultClone; if (options.deSugar) { desugaredResultClone = desugaredResultClone.deSugar(deSugarSpec, { dialect: options.dialect }, null, schemaInference); } else if (options.deSugar || options.toDialect) { desugaredResultClone = desugaredResultClone.toDialect(options.toDialect, { dialect: options.dialect }, null, schemaInference); } expect( normalizeSql(desugaredResultClone.stringify(options)) ).to.equal(normalizeSql(expectedSql, normalizerOptions)); return resultNode; } export async function testExprAndNodeEntryPoints(nodeName, sql, options = {}) { //return await testParseAndStringify(['Expr', nodeName], sql, options); const entryPoints = ['Expr', nodeName]; for (const entryPoint of entryPoints) { await testParseAndStringify([entryPoint, nodeName], sql, options); } } export function normalizeSql(sql, normalizerOptions = {}) { if (normalizerOptions.stripSpaces) { sql = sql.replace(/\s/g, ''); } if (normalizerOptions.stripQuotes) { sql = sql.replace(/['"]/g, ''); } if (normalizerOptions.stripSpaces || normalizerOptions.stripQuotes) { return sql; } return sql.replace(/\s+/g, ' ').replace(/\(\s+?/g, '(').replace(/\s+?\)/g, ')'); } export function formatSql(sql, formattingOptions) { if (formattingOptions.prettyPrint) { const ln = (depth = 2) => `\n${'\t'.repeat(depth + formattingOptions.indentation)}`; return `\n${sql}\n`.replace(/\n/g, ln()); } return sql; }