Skip to content

Commit

Permalink
qe: Allow to run query engine test suite against wasm engines
Browse files Browse the repository at this point in the history
Intoroduces new environment variable/test config option,
`EXTERNAL_TEST_EXECUTOR_ENGINE` that can be either `NAPI` (default) or
`WASM` that affects which engine external test executor will start with.

Adds a bunch of additional test configs for driver adapters that
duplicate existing ones, but run against WASM engine.

Close prisma/team-orm#551
  • Loading branch information
SevInf committed Nov 20, 2023
1 parent ebb702b commit 4b91480
Show file tree
Hide file tree
Showing 15 changed files with 162 additions and 28 deletions.
20 changes: 20 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ test-libsql-sqlite: dev-libsql-sqlite test-qe-st

test-driver-adapter-libsql: test-libsql-sqlite

dev-libsql-sqlite-wasm: build-qe-napi build-connector-kit-js
cp $(CONFIG_PATH)/libsql-sqlite-wasm $(CONFIG_FILE)

test-libsql-sqlite-wasm: dev-libsql-sqlite-wasm test-qe-st

start-postgres9:
docker compose -f docker-compose.yml up --wait -d --remove-orphans postgres9

Expand Down Expand Up @@ -128,6 +133,11 @@ dev-pg-postgres13: start-pg-postgres13

test-pg-postgres13: dev-pg-postgres13 test-qe-st

dev-pg-postgres13-wasm: start-pg-postgres13
cp $(CONFIG_PATH)/pg-postgres13-wasm $(CONFIG_FILE)

test-pg-postgres13-wasm: dev-pg-postgres13-wasm test-qe-st

test-driver-adapter-pg: test-pg-postgres13

start-neon-postgres13:
Expand All @@ -138,6 +148,11 @@ dev-neon-ws-postgres13: start-neon-postgres13 build-qe-napi build-connector-kit-

test-neon-ws-postgres13: dev-neon-ws-postgres13 test-qe-st

dev-neon-ws-postgres13-wasm: start-neon-postgres13 build-qe-napi build-connector-kit-js
cp $(CONFIG_PATH)/neon-ws-postgres13-wasm $(CONFIG_FILE)

test-neon-ws-postgres13-wasm: dev-neon-ws-postgres13-wasm test-qe-st

test-driver-adapter-neon: test-neon-ws-postgres13

start-postgres14:
Expand Down Expand Up @@ -270,6 +285,11 @@ dev-planetscale-vitess8: start-planetscale-vitess8 build-qe-napi build-connector

test-planetscale-vitess8: dev-planetscale-vitess8 test-qe-st

dev-planetscale-vitess8-wasm: start-planetscale-vitess8 build-qe-napi build-connector-kit-js
cp $(CONFIG_PATH)/planetscale-vitess8-wasm $(CONFIG_FILE)

test-planetscale-vitess8-wasm: dev-planetscale-vitess8-wasm test-qe-st

test-driver-adapter-planetscale: test-planetscale-vitess8

######################
Expand Down
42 changes: 40 additions & 2 deletions query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,26 @@ use crate::{
PostgresConnectorTag, SqlServerConnectorTag, SqliteConnectorTag, TestResult, VitessConnectorTag,
};
use serde::Deserialize;
use std::{convert::TryFrom, env, fs::File, io::Read, path::PathBuf};
use std::{convert::TryFrom, env, fmt::Display, fs::File, io::Read, path::PathBuf};

static TEST_CONFIG_FILE_NAME: &str = ".test_config";

#[derive(Debug, Default, Deserialize, Clone)]
enum TestExecutorEngine {
#[default]
NAPI,
WASM,
}

impl Display for TestExecutorEngine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TestExecutorEngine::NAPI => f.write_str("NAPI"),
TestExecutorEngine::WASM => f.write_str("WASM"),
}
}
}

/// The central test configuration.
#[derive(Debug, Default, Deserialize)]
pub struct TestConfig {
Expand All @@ -27,6 +43,11 @@ pub struct TestConfig {
/// Env key: `EXTERNAL_TEST_EXECUTOR`
external_test_executor: Option<String>,

/// Specifies which engine to use within external test executor.
/// If omitted, NAPI engine will be used.
/// Env key: `EXTERNAL_TEST_EXECUTOR_ENGINE`
external_test_executor_engine: Option<TestExecutorEngine>,

/// The driver adapter to use when running tests, will be forwarded to the external test
/// executor by setting the `DRIVER_ADAPTER` env var when spawning the executor process
driver_adapter: Option<String>,
Expand Down Expand Up @@ -109,6 +130,7 @@ impl TestConfig {
println!("* CI? {}", self.is_ci);
if self.external_test_executor.as_ref().is_some() {
println!("* External test executor: {}", self.external_test_executor().unwrap_or_default());
println!("* External test executor engine: {:?}", self.external_test_executor().unwrap_or_default());
println!("* Driver adapter: {}", self.driver_adapter().unwrap_or_default());
println!("* Driver adapter url override: {}", self.json_stringify_driver_adapter_config());
}
Expand All @@ -119,6 +141,10 @@ impl TestConfig {
let connector = std::env::var("TEST_CONNECTOR").ok();
let connector_version = std::env::var("TEST_CONNECTOR_VERSION").ok();
let external_test_executor = std::env::var("EXTERNAL_TEST_EXECUTOR").ok();
let external_test_executor_engine = std::env::var("EXTERNAL_TEST_EXECUTOR_ENGINE")
.map(|value| serde_json::from_str::<TestExecutorEngine>(&value).ok())
.unwrap_or_default();

let driver_adapter = std::env::var("DRIVER_ADAPTER").ok();
let driver_adapter_config = std::env::var("DRIVER_ADAPTER_CONFIG")
.map(|config| serde_json::from_str::<serde_json::Value>(config.as_str()).ok())
Expand All @@ -134,6 +160,7 @@ impl TestConfig {
external_test_executor,
driver_adapter,
driver_adapter_config,
external_test_executor_engine,
})
}

Expand Down Expand Up @@ -234,6 +261,12 @@ impl TestConfig {
);
}

if self.external_test_executor_engine.is_some() && self.external_test_executor.is_none() {
exit_with_message(
"External test executor engine can be used only if EXTERNAL_TEST_EXECUTOR env var is set.",
);
}

if self.driver_adapter.is_some() && self.external_test_executor.is_none() {
exit_with_message(
"When using a driver adapter, the external test executor (EXTERNAL_TEST_EXECUTOR env var) must be set.",
Expand Down Expand Up @@ -294,11 +327,16 @@ impl TestConfig {
vec!(
(
"DRIVER_ADAPTER".to_string(),
self.driver_adapter.clone().unwrap_or_default()),
self.driver_adapter.clone().unwrap_or_default()
),
(
"DRIVER_ADAPTER_CONFIG".to_string(),
self.json_stringify_driver_adapter_config()
),
(
"EXTERNAL_TEST_EXECUTOR_ENGINE".to_string(),
self.external_test_executor_engine.clone().unwrap_or_default().to_string(),
),
(
"PRISMA_DISABLE_QUAINT_EXECUTORS".to_string(),
"1".to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl ExecutorProcess {
};

self.task_handle.send((method_call, sender)).await?;
let raw_response = receiver.await?;
let raw_response = receiver.await??;
tracing::debug!(%raw_response);
let response = serde_json::from_value(raw_response)?;
Ok(response)
Expand All @@ -91,7 +91,10 @@ pub(super) static EXTERNAL_PROCESS: Lazy<ExecutorProcess> =
}
});

type ReqImpl = (jsonrpc_core::MethodCall, oneshot::Sender<serde_json::value::Value>);
type ReqImpl = (
jsonrpc_core::MethodCall,
oneshot::Sender<Result<serde_json::value::Value>>,
);

fn start_rpc_thread(mut receiver: mpsc::Receiver<ReqImpl>) -> Result<()> {
use std::process::Stdio;
Expand Down Expand Up @@ -119,7 +122,7 @@ fn start_rpc_thread(mut receiver: mpsc::Receiver<ReqImpl>) -> Result<()> {

let mut stdout = BufReader::new(process.stdout.unwrap()).lines();
let mut stdin = process.stdin.unwrap();
let mut pending_requests: HashMap<jsonrpc_core::Id, oneshot::Sender<serde_json::value::Value>> =
let mut pending_requests: HashMap<jsonrpc_core::Id, oneshot::Sender<Result<serde_json::value::Value>>> =
HashMap::new();

loop {
Expand All @@ -140,10 +143,11 @@ fn start_rpc_thread(mut receiver: mpsc::Receiver<ReqImpl>) -> Result<()> {
// The other end may be dropped if the whole
// request future was dropped and not polled to
// completion, so we ignore send errors here.
_ = sender.send(success.result);
_ = sender.send(Ok(success.result));
}
jsonrpc_core::Output::Failure(err) => {
panic!("error response from jsonrpc: {err:?}")
tracing::error!("error response from jsonrpc: {err:?}");
_ = sender.send(Err(Box::new(err.error)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"connector": "sqlite",
"driver_adapter": "libsql",
"external_test_executor": "default"
"external_test_executor": "default",
"external_test_executor_engine": "NAPI"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"connector": "sqlite",
"driver_adapter": "libsql",
"external_test_executor": "default",
"external_test_executor_engine": "WASM"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"version": "13",
"driver_adapter": "neon:ws",
"driver_adapter_config": { "proxyUrl": "127.0.0.1:5488/v1" },
"external_test_executor": "default"
"external_test_executor": "default",
"external_test_executor_engine": "NAPI"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"connector": "postgres",
"version": "13",
"driver_adapter": "neon:ws",
"driver_adapter_config": { "proxyUrl": "127.0.0.1:5488/v1" },
"external_test_executor": "default",
"external_test_executor_engine": "WASM"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"connector": "postgres",
"version": "13",
"driver_adapter": "pg",
"external_test_executor": "default"
"external_test_executor": "default",
"external_test_executor_engine": "NAPI"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"connector": "postgres",
"version": "13",
"driver_adapter": "pg",
"external_test_executor": "default",
"external_test_executor_engine": "WASM"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
"version": "8.0",
"driver_adapter": "planetscale",
"driver_adapter_config": { "proxyUrl": "http://root:[email protected]:8085" },
"external_test_executor": "default"
"external_test_executor": "default",
"external_test_executor_engine": "NAPI"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"connector": "vitess",
"version": "8.0",
"driver_adapter": "planetscale",
"driver_adapter_config": { "proxyUrl": "http://root:[email protected]:8085" },
"external_test_executor": "default",
"external_test_executor_engine": "WASM"
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as qe from './qe'
import * as engines from './engines/Library'
import * as readline from 'node:readline'
import * as jsonRpc from './jsonRpc'

Expand Down Expand Up @@ -76,7 +75,7 @@ async function main(): Promise<void> {
}

const state: Record<number, {
engine: engines.QueryEngineInstance,
engine: qe.QueryEngine,
adapter: ErrorCapturingDriverAdapter,
logs: string[]
}> = {}
Expand Down Expand Up @@ -215,7 +214,7 @@ function respondOk(requestId: number, payload: unknown) {
console.log(JSON.stringify(msg))
}

async function initQe(url: string, prismaSchema: string, logCallback: qe.QueryLogCallback): Promise<[engines.QueryEngineInstance, ErrorCapturingDriverAdapter]> {
async function initQe(url: string, prismaSchema: string, logCallback: qe.QueryLogCallback): Promise<[qe.QueryEngine, ErrorCapturingDriverAdapter]> {
const adapter = await adapterFromEnv(url) as DriverAdapter
const errorCapturingAdapter = bindAdapter(adapter)
const engineInstance = qe.initQueryEngine(errorCapturingAdapter, prismaSchema, logCallback, debug)
Expand Down
50 changes: 38 additions & 12 deletions query-engine/driver-adapters/connector-test-kit-executor/src/qe.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import type { ErrorCapturingDriverAdapter } from '@prisma/driver-adapter-utils'
import * as lib from './engines/Library'
import { WasmQueryEngine } from './wasm'
import * as napi from './engines/Library'
import * as os from 'node:os'
import * as path from 'node:path'
import { fileURLToPath } from 'node:url'

export type QueryLogCallback = (log: string) => void
const dirname = path.dirname(fileURLToPath(import.meta.url))

export function initQueryEngine(adapter: ErrorCapturingDriverAdapter, datamodel: string, queryLogCallback: QueryLogCallback, debug: (...args: any[]) => void): lib.QueryEngineInstance {
// I assume nobody will run this on Windows ¯\_(ツ)_/¯
const libExt = os.platform() === 'darwin' ? 'dylib' : 'so'
const dirname = path.dirname(new URL(import.meta.url).pathname)
export interface QueryEngine {
connect(trace: string): Promise<void>
disconnect(trace: string): Promise<void>;
query(body: string, trace: string, tx_id?: string): Promise<string>;
startTransaction(input: string, trace: string): Promise<string>;
commitTransaction(tx_id: string, trace: string): Promise<string>;
rollbackTransaction(tx_id: string, trace: string): Promise<string>;
}

const libQueryEnginePath = path.join(dirname, `../../../../target/debug/libquery_engine.${libExt}`)
export type QueryLogCallback = (log: string) => void

const libqueryEngine = { exports: {} as unknown as lib.Library }
// @ts-ignore
process.dlopen(libqueryEngine, libQueryEnginePath)

const QueryEngine = libqueryEngine.exports.QueryEngine
export function initQueryEngine(adapter: ErrorCapturingDriverAdapter, datamodel: string, queryLogCallback: QueryLogCallback, debug: (...args: any[]) => void): QueryEngine {

const queryEngineOptions = {
datamodel,
Expand All @@ -37,5 +40,28 @@ export function initQueryEngine(adapter: ErrorCapturingDriverAdapter, datamodel:
debug(parsed)
}

return new QueryEngine(queryEngineOptions, logCallback, adapter)
const engineFromEnv = process.env.EXTERNAL_TEST_EXECUTOR_ENGINE ?? 'napi'
if (engineFromEnv === 'WASM') {
return new WasmQueryEngine(queryEngineOptions, logCallback, adapter)
} else if (engineFromEnv === 'NAPI') {
const { QueryEngine } = loadNapiEngine()
return new QueryEngine(queryEngineOptions, logCallback, adapter)
} else {
throw new TypeError(`Invalid EXTERNAL_TEST_EXECUTOR_ENGINE value: ${engineFromEnv}. Expected NAPI or WASM`)
}


}

function loadNapiEngine(): napi.Library {
// I assume nobody will run this on Windows ¯\_(ツ)_/¯
const libExt = os.platform() === 'darwin' ? 'dylib' : 'so'

const libQueryEnginePath = path.join(dirname, `../../../../target/debug/libquery_engine.${libExt}`)

const libqueryEngine = { exports: {} as unknown as napi.Library }
// @ts-ignore
process.dlopen(libqueryEngine, libQueryEnginePath)

return libqueryEngine.exports
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as wasm from '../../../query-engine-wasm/pkg/query_engine_bg.js'
import fs from 'node:fs/promises'
import path from 'node:path'
import { fileURLToPath } from 'node:url'

const dirname = path.dirname(fileURLToPath(import.meta.url))

const bytes = await fs.readFile(path.resolve(dirname, '..', '..', '..', 'query-engine-wasm', 'pkg', 'query_engine_bg.wasm'))
const module = new WebAssembly.Module(bytes)
const instance = new WebAssembly.Instance(module, { './query_engine_bg.js': wasm })
wasm.__wbg_set_wasm(instance.exports);
wasm.init()

export const WasmQueryEngine = wasm.QueryEngine
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"lib": ["ES2022"],
"lib": ["ES2022", "DOM"],
"moduleResolution": "Bundler",
"esModuleInterop": false,
"isolatedModules": true,
Expand All @@ -17,7 +17,7 @@
"skipDefaultLibCheck": true,
"skipLibCheck": true,
"emitDeclarationOnly": true,
"resolveJsonModule": true
"resolveJsonModule": true,
},
"exclude": ["**/dist", "**/declaration", "**/node_modules", "**/src/__tests__"]
}

0 comments on commit 4b91480

Please sign in to comment.