From 05f6a1394de68b5dbe7100304a3a8bef5e5fa48e Mon Sep 17 00:00:00 2001 From: Alexander Bayandin Date: Tue, 5 Jul 2022 12:22:58 +0100 Subject: [PATCH] Add tests for different Postgres client libraries (#2008) * Add tests for different postgres clients * test/fixtures: sanitize test name for test_output_dir * test/fixtures: do not look for etcd before runtime * Add workflow for testing Postgres client libraries --- .github/workflows/pg_clients.yml | 74 +++++ setup.cfg | 4 + test_runner/fixtures/neon_fixtures.py | 7 +- .../pg_clients/csharp/npgsql/.dockerignore | 2 + .../pg_clients/csharp/npgsql/.gitignore | 2 + .../pg_clients/csharp/npgsql/Dockerfile | 14 + .../pg_clients/csharp/npgsql/Program.cs | 19 ++ .../csharp/npgsql/csharp-npgsql.csproj | 14 + test_runner/pg_clients/java/jdbc/.gitignore | 1 + test_runner/pg_clients/java/jdbc/Dockerfile | 10 + test_runner/pg_clients/java/jdbc/Example.java | 31 +++ .../pg_clients/python/asyncpg/Dockerfile | 8 + .../python/asyncpg/asyncpg_example.py | 30 ++ .../python/asyncpg/requirements.txt | 1 + .../pg_clients/python/pg8000/Dockerfile | 8 + .../pg_clients/python/pg8000/README.md | 0 .../python/pg8000/pg8000_example.py | 23 ++ .../pg_clients/python/pg8000/requirements.txt | 1 + .../PostgresClientKitExample/.dockerignore | 1 + .../swift/PostgresClientKitExample/.gitignore | 1 + .../swift/PostgresClientKitExample/Dockerfile | 11 + .../PostgresClientKitExample/Package.resolved | 41 +++ .../PostgresClientKitExample/Package.swift | 17 ++ .../PostgresClientKitExample/main.swift | 38 +++ test_runner/pg_clients/test_pg_clients.py | 54 ++++ .../postgresql-client/.dockerignore | 1 + .../typescript/postgresql-client/.gitignore | 1 + .../typescript/postgresql-client/Dockerfile | 7 + .../typescript/postgresql-client/index.js | 25 ++ .../postgresql-client/package-lock.json | 262 ++++++++++++++++++ .../typescript/postgresql-client/package.json | 6 + 31 files changed, 712 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/pg_clients.yml create mode 100644 test_runner/pg_clients/csharp/npgsql/.dockerignore create mode 100644 test_runner/pg_clients/csharp/npgsql/.gitignore create mode 100644 test_runner/pg_clients/csharp/npgsql/Dockerfile create mode 100644 test_runner/pg_clients/csharp/npgsql/Program.cs create mode 100644 test_runner/pg_clients/csharp/npgsql/csharp-npgsql.csproj create mode 100644 test_runner/pg_clients/java/jdbc/.gitignore create mode 100644 test_runner/pg_clients/java/jdbc/Dockerfile create mode 100644 test_runner/pg_clients/java/jdbc/Example.java create mode 100644 test_runner/pg_clients/python/asyncpg/Dockerfile create mode 100755 test_runner/pg_clients/python/asyncpg/asyncpg_example.py create mode 100644 test_runner/pg_clients/python/asyncpg/requirements.txt create mode 100644 test_runner/pg_clients/python/pg8000/Dockerfile create mode 100644 test_runner/pg_clients/python/pg8000/README.md create mode 100755 test_runner/pg_clients/python/pg8000/pg8000_example.py create mode 100644 test_runner/pg_clients/python/pg8000/requirements.txt create mode 100644 test_runner/pg_clients/swift/PostgresClientKitExample/.dockerignore create mode 100644 test_runner/pg_clients/swift/PostgresClientKitExample/.gitignore create mode 100644 test_runner/pg_clients/swift/PostgresClientKitExample/Dockerfile create mode 100644 test_runner/pg_clients/swift/PostgresClientKitExample/Package.resolved create mode 100644 test_runner/pg_clients/swift/PostgresClientKitExample/Package.swift create mode 100644 test_runner/pg_clients/swift/PostgresClientKitExample/Sources/PostgresClientKitExample/main.swift create mode 100644 test_runner/pg_clients/test_pg_clients.py create mode 100644 test_runner/pg_clients/typescript/postgresql-client/.dockerignore create mode 100644 test_runner/pg_clients/typescript/postgresql-client/.gitignore create mode 100644 test_runner/pg_clients/typescript/postgresql-client/Dockerfile create mode 100755 test_runner/pg_clients/typescript/postgresql-client/index.js create mode 100644 test_runner/pg_clients/typescript/postgresql-client/package-lock.json create mode 100644 test_runner/pg_clients/typescript/postgresql-client/package.json diff --git a/.github/workflows/pg_clients.yml b/.github/workflows/pg_clients.yml new file mode 100644 index 0000000000..66f259d012 --- /dev/null +++ b/.github/workflows/pg_clients.yml @@ -0,0 +1,74 @@ +name: Test Postgres client libraries + +on: + schedule: + # * is a special character in YAML so you have to quote this string + # ┌───────────── minute (0 - 59) + # │ ┌───────────── hour (0 - 23) + # │ │ ┌───────────── day of the month (1 - 31) + # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) + # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) + - cron: '23 02 * * *' # run once a day, timezone is utc + + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test-postgres-client-libs: + runs-on: [ ubuntu-latest ] + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - uses: actions/setup-python@v4 + with: + python-version: 3.9 + + - name: Install Poetry + uses: snok/install-poetry@v1 + + - name: Cache poetry deps + id: cache_poetry + uses: actions/cache@v3 + with: + path: ~/.cache/pypoetry/virtualenvs + key: v1-${{ runner.os }}-python-deps-${{ hashFiles('poetry.lock') }} + + - name: Install Python deps + shell: bash -ex {0} + run: ./scripts/pysync + + - name: Run pytest + env: + REMOTE_ENV: 1 + BENCHMARK_CONNSTR: "${{ secrets.BENCHMARK_STAGING_CONNSTR }}" + TEST_OUTPUT: /tmp/test_output + POSTGRES_DISTRIB_DIR: /tmp/neon/pg_install + # this variable will be embedded in perf test report + # and is needed to distinguish different environments + PLATFORM: github-actions-selfhosted + shell: bash -ex {0} + run: | + # Test framework expects we have psql binary; + # but since we don't really need it in this test, let's mock it + mkdir -p "$POSTGRES_DISTRIB_DIR/bin" && touch "$POSTGRES_DISTRIB_DIR/bin/psql"; + ./scripts/pytest \ + --junitxml=$TEST_OUTPUT/junit.xml \ + --tb=short \ + --verbose \ + -m "remote_cluster" \ + -rA "test_runner/pg_clients" + + - name: Post to a Slack channel + if: failure() + id: slack + uses: slackapi/slack-github-action@v1 + with: + channel-id: "C033QLM5P7D" # dev-staging-stream + slack-message: "Testing Postgres clients: ${{ job.status }}\n${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} diff --git a/setup.cfg b/setup.cfg index b3b39fadd7..d1a2f9a359 100644 --- a/setup.cfg +++ b/setup.cfg @@ -28,6 +28,10 @@ strict = true # There is some work in progress, though: https://github.com/MagicStack/asyncpg/pull/577 ignore_missing_imports = true +[mypy-pg8000.*] +# Used only in testing clients +ignore_missing_imports = true + [mypy-cached_property.*] ignore_missing_imports = true diff --git a/test_runner/fixtures/neon_fixtures.py b/test_runner/fixtures/neon_fixtures.py index 8df4878039..9eb02b50d0 100644 --- a/test_runner/fixtures/neon_fixtures.py +++ b/test_runner/fixtures/neon_fixtures.py @@ -1925,9 +1925,12 @@ class Etcd: datadir: str port: int peer_port: int - binary_path: Path = etcd_path() + binary_path: Path = field(init=False) handle: Optional[subprocess.Popen[Any]] = None # handle of running daemon + def __post_init__(self): + self.binary_path = etcd_path() + def client_url(self): return f'http://127.0.0.1:{self.port}' @@ -1984,7 +1987,7 @@ class Etcd: def get_test_output_dir(request: Any) -> pathlib.Path: """ Compute the working directory for an individual test. """ test_name = request.node.name - test_dir = pathlib.Path(top_output_dir) / test_name + test_dir = pathlib.Path(top_output_dir) / test_name.replace("/", "-") log.info(f'get_test_output_dir is {test_dir}') # make mypy happy assert isinstance(test_dir, pathlib.Path) diff --git a/test_runner/pg_clients/csharp/npgsql/.dockerignore b/test_runner/pg_clients/csharp/npgsql/.dockerignore new file mode 100644 index 0000000000..cd42ee34e8 --- /dev/null +++ b/test_runner/pg_clients/csharp/npgsql/.dockerignore @@ -0,0 +1,2 @@ +bin/ +obj/ diff --git a/test_runner/pg_clients/csharp/npgsql/.gitignore b/test_runner/pg_clients/csharp/npgsql/.gitignore new file mode 100644 index 0000000000..cd42ee34e8 --- /dev/null +++ b/test_runner/pg_clients/csharp/npgsql/.gitignore @@ -0,0 +1,2 @@ +bin/ +obj/ diff --git a/test_runner/pg_clients/csharp/npgsql/Dockerfile b/test_runner/pg_clients/csharp/npgsql/Dockerfile new file mode 100644 index 0000000000..a78bc2f3bc --- /dev/null +++ b/test_runner/pg_clients/csharp/npgsql/Dockerfile @@ -0,0 +1,14 @@ +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build +WORKDIR /source + +COPY *.csproj . +RUN dotnet restore + +COPY . . +RUN dotnet publish -c release -o /app --no-restore + +FROM mcr.microsoft.com/dotnet/runtime:6.0 +WORKDIR /app +COPY --from=build /app . + +ENTRYPOINT ["dotnet", "csharp-npgsql.dll"] diff --git a/test_runner/pg_clients/csharp/npgsql/Program.cs b/test_runner/pg_clients/csharp/npgsql/Program.cs new file mode 100644 index 0000000000..17c2d5b81d --- /dev/null +++ b/test_runner/pg_clients/csharp/npgsql/Program.cs @@ -0,0 +1,19 @@ +using Npgsql; + +var host = Environment.GetEnvironmentVariable("NEON_HOST"); +var database = Environment.GetEnvironmentVariable("NEON_DATABASE"); +var user = Environment.GetEnvironmentVariable("NEON_USER"); +var password = Environment.GetEnvironmentVariable("NEON_PASSWORD"); + +var connString = $"Host={host};Username={user};Password={password};Database={database}"; + +await using var conn = new NpgsqlConnection(connString); +await conn.OpenAsync(); + +await using (var cmd = new NpgsqlCommand("SELECT 1", conn)) +await using (var reader = await cmd.ExecuteReaderAsync()) +{ + while (await reader.ReadAsync()) + Console.WriteLine(reader.GetInt32(0)); +} +await conn.CloseAsync(); diff --git a/test_runner/pg_clients/csharp/npgsql/csharp-npgsql.csproj b/test_runner/pg_clients/csharp/npgsql/csharp-npgsql.csproj new file mode 100644 index 0000000000..7c1f90c1fc --- /dev/null +++ b/test_runner/pg_clients/csharp/npgsql/csharp-npgsql.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/test_runner/pg_clients/java/jdbc/.gitignore b/test_runner/pg_clients/java/jdbc/.gitignore new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/test_runner/pg_clients/java/jdbc/.gitignore @@ -0,0 +1 @@ + diff --git a/test_runner/pg_clients/java/jdbc/Dockerfile b/test_runner/pg_clients/java/jdbc/Dockerfile new file mode 100644 index 0000000000..daad99c3a1 --- /dev/null +++ b/test_runner/pg_clients/java/jdbc/Dockerfile @@ -0,0 +1,10 @@ +FROM openjdk:17 +WORKDIR /source + +COPY . . + +WORKDIR /app +RUN curl --output postgresql.jar https://jdbc.postgresql.org/download/postgresql-42.4.0.jar && \ + javac -d /app /source/Example.java + +CMD ["java", "-cp", "/app/postgresql.jar:.", "Example"] diff --git a/test_runner/pg_clients/java/jdbc/Example.java b/test_runner/pg_clients/java/jdbc/Example.java new file mode 100644 index 0000000000..410a971649 --- /dev/null +++ b/test_runner/pg_clients/java/jdbc/Example.java @@ -0,0 +1,31 @@ +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; +import java.util.Properties; + +public class Example +{ + public static void main( String[] args ) throws Exception + { + String host = System.getenv("NEON_HOST"); + String database = System.getenv("NEON_DATABASE"); + String user = System.getenv("NEON_USER"); + String password = System.getenv("NEON_PASSWORD"); + + String url = "jdbc:postgresql://%s/%s".formatted(host, database); + Properties props = new Properties(); + props.setProperty("user", user); + props.setProperty("password", password); + + Connection conn = DriverManager.getConnection(url, props); + Statement st = conn.createStatement(); + ResultSet rs = st.executeQuery("SELECT 1"); + while (rs.next()) + { + System.out.println(rs.getString(1)); + } + rs.close(); + st.close(); + } +} diff --git a/test_runner/pg_clients/python/asyncpg/Dockerfile b/test_runner/pg_clients/python/asyncpg/Dockerfile new file mode 100644 index 0000000000..10662f92d5 --- /dev/null +++ b/test_runner/pg_clients/python/asyncpg/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.10 +WORKDIR /source + +COPY . . + +RUN python3 -m pip install --no-cache-dir -r requirements.txt + +CMD ["python3", "asyncpg_example.py"] diff --git a/test_runner/pg_clients/python/asyncpg/asyncpg_example.py b/test_runner/pg_clients/python/asyncpg/asyncpg_example.py new file mode 100755 index 0000000000..7f579ce672 --- /dev/null +++ b/test_runner/pg_clients/python/asyncpg/asyncpg_example.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python3 + +import asyncio +import os + +import asyncpg + + +async def run(**kwargs) -> asyncpg.Record: + conn = await asyncpg.connect( + **kwargs, + statement_cache_size=0, # Prepared statements doesn't work pgbouncer + ) + rv = await conn.fetchrow("SELECT 1") + await conn.close() + + return rv + + +if __name__ == "__main__": + kwargs = { + k.lstrip("NEON_").lower(): v + for k in ("NEON_HOST", "NEON_DATABASE", "NEON_USER", "NEON_PASSWORD") + if (v := os.environ.get(k, None)) is not None + } + + loop = asyncio.new_event_loop() + row = loop.run_until_complete(run(**kwargs)) + + print(row[0]) diff --git a/test_runner/pg_clients/python/asyncpg/requirements.txt b/test_runner/pg_clients/python/asyncpg/requirements.txt new file mode 100644 index 0000000000..edc57ecc81 --- /dev/null +++ b/test_runner/pg_clients/python/asyncpg/requirements.txt @@ -0,0 +1 @@ +asyncpg==0.25.0 diff --git a/test_runner/pg_clients/python/pg8000/Dockerfile b/test_runner/pg_clients/python/pg8000/Dockerfile new file mode 100644 index 0000000000..eddf64df5b --- /dev/null +++ b/test_runner/pg_clients/python/pg8000/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.10 +WORKDIR /source + +COPY . . + +RUN python3 -m pip install --no-cache-dir -r requirements.txt + +CMD ["python3", "pg8000_example.py"] diff --git a/test_runner/pg_clients/python/pg8000/README.md b/test_runner/pg_clients/python/pg8000/README.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test_runner/pg_clients/python/pg8000/pg8000_example.py b/test_runner/pg_clients/python/pg8000/pg8000_example.py new file mode 100755 index 0000000000..f463867f88 --- /dev/null +++ b/test_runner/pg_clients/python/pg8000/pg8000_example.py @@ -0,0 +1,23 @@ +#! /usr/bin/env python3 + +import os +import ssl + +import pg8000.dbapi + +if __name__ == "__main__": + kwargs = { + k.lstrip("NEON_").lower(): v + for k in ("NEON_HOST", "NEON_DATABASE", "NEON_USER", "NEON_PASSWORD") + if (v := os.environ.get(k, None)) is not None + } + conn = pg8000.dbapi.connect( + **kwargs, + ssl_context=True, + ) + + cursor = conn.cursor() + cursor.execute("SELECT 1") + row = cursor.fetchone() + print(row[0]) + conn.close() diff --git a/test_runner/pg_clients/python/pg8000/requirements.txt b/test_runner/pg_clients/python/pg8000/requirements.txt new file mode 100644 index 0000000000..1577712150 --- /dev/null +++ b/test_runner/pg_clients/python/pg8000/requirements.txt @@ -0,0 +1 @@ +pg8000==1.29.1 diff --git a/test_runner/pg_clients/swift/PostgresClientKitExample/.dockerignore b/test_runner/pg_clients/swift/PostgresClientKitExample/.dockerignore new file mode 100644 index 0000000000..30bcfa4ed5 --- /dev/null +++ b/test_runner/pg_clients/swift/PostgresClientKitExample/.dockerignore @@ -0,0 +1 @@ +.build/ diff --git a/test_runner/pg_clients/swift/PostgresClientKitExample/.gitignore b/test_runner/pg_clients/swift/PostgresClientKitExample/.gitignore new file mode 100644 index 0000000000..30bcfa4ed5 --- /dev/null +++ b/test_runner/pg_clients/swift/PostgresClientKitExample/.gitignore @@ -0,0 +1 @@ +.build/ diff --git a/test_runner/pg_clients/swift/PostgresClientKitExample/Dockerfile b/test_runner/pg_clients/swift/PostgresClientKitExample/Dockerfile new file mode 100644 index 0000000000..8f9477bd6a --- /dev/null +++ b/test_runner/pg_clients/swift/PostgresClientKitExample/Dockerfile @@ -0,0 +1,11 @@ +FROM swift:5.6 AS build +RUN apt-get -q update && apt-get -q install -y libssl-dev +WORKDIR /source + +COPY . . +RUN swift build --configuration release + +FROM swift:5.6 +WORKDIR /app +COPY --from=build /source/.build/release/release . +CMD ["/app/PostgresClientKitExample"] diff --git a/test_runner/pg_clients/swift/PostgresClientKitExample/Package.resolved b/test_runner/pg_clients/swift/PostgresClientKitExample/Package.resolved new file mode 100644 index 0000000000..478e31000e --- /dev/null +++ b/test_runner/pg_clients/swift/PostgresClientKitExample/Package.resolved @@ -0,0 +1,41 @@ +{ + "pins" : [ + { + "identity" : "bluesocket", + "kind" : "remoteSourceControl", + "location" : "https://github.com/IBM-Swift/BlueSocket.git", + "state" : { + "revision" : "dd924c3bc2c1c144c42b8dda3896f1a03115ded4", + "version" : "2.0.2" + } + }, + { + "identity" : "bluesslservice", + "kind" : "remoteSourceControl", + "location" : "https://github.com/IBM-Swift/BlueSSLService", + "state" : { + "revision" : "c249988fb748749739144e7f554710552acdc0bd", + "version" : "2.0.1" + } + }, + { + "identity" : "postgresclientkit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/codewinsdotcom/PostgresClientKit.git", + "state" : { + "branch" : "v1.4.3", + "revision" : "beafedaea6dc9f04712e9a8547b77f47c406a47e" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser", + "state" : { + "revision" : "6b2aa2748a7881eebb9f84fb10c01293e15b52ca", + "version" : "0.5.0" + } + } + ], + "version" : 2 +} diff --git a/test_runner/pg_clients/swift/PostgresClientKitExample/Package.swift b/test_runner/pg_clients/swift/PostgresClientKitExample/Package.swift new file mode 100644 index 0000000000..0d40b28572 --- /dev/null +++ b/test_runner/pg_clients/swift/PostgresClientKitExample/Package.swift @@ -0,0 +1,17 @@ +// swift-tools-version:5.6 +import PackageDescription + +let package = Package( + name: "PostgresClientKitExample", + dependencies: [ + .package( + url: "https://github.com/codewinsdotcom/PostgresClientKit.git", + revision: "v1.4.3" + ) + ], + targets: [ + .target( + name: "PostgresClientKitExample", + dependencies: [ "PostgresClientKit" ]) + ] +) diff --git a/test_runner/pg_clients/swift/PostgresClientKitExample/Sources/PostgresClientKitExample/main.swift b/test_runner/pg_clients/swift/PostgresClientKitExample/Sources/PostgresClientKitExample/main.swift new file mode 100644 index 0000000000..c7518dd88c --- /dev/null +++ b/test_runner/pg_clients/swift/PostgresClientKitExample/Sources/PostgresClientKitExample/main.swift @@ -0,0 +1,38 @@ +import Foundation + +import PostgresClientKit + +do { + var configuration = PostgresClientKit.ConnectionConfiguration() + + let env = ProcessInfo.processInfo.environment + if let host = env["NEON_HOST"] { + configuration.host = host + } + if let database = env["NEON_DATABASE"] { + configuration.database = database + } + if let user = env["NEON_USER"] { + configuration.user = user + } + if let password = env["NEON_PASSWORD"] { + configuration.credential = .scramSHA256(password: password) + } + + let connection = try PostgresClientKit.Connection(configuration: configuration) + defer { connection.close() } + + let text = "SELECT 1;" + let statement = try connection.prepareStatement(text: text) + defer { statement.close() } + + let cursor = try statement.execute(parameterValues: [ ]) + defer { cursor.close() } + + for row in cursor { + let columns = try row.get().columns + print(columns[0]) + } +} catch { + print(error) +} diff --git a/test_runner/pg_clients/test_pg_clients.py b/test_runner/pg_clients/test_pg_clients.py new file mode 100644 index 0000000000..7dc7299791 --- /dev/null +++ b/test_runner/pg_clients/test_pg_clients.py @@ -0,0 +1,54 @@ +import os +import shutil +import subprocess +from pathlib import Path +from tempfile import NamedTemporaryFile +from urllib.parse import urlparse + +import pytest +from fixtures.neon_fixtures import RemotePostgres + + +@pytest.mark.remote_cluster +@pytest.mark.parametrize( + "client", + [ + "csharp/npgsql", + "java/jdbc", + "python/asyncpg", + pytest.param( + "python/pg8000", # See https://github.com/neondatabase/neon/pull/2008#discussion_r912264281 + marks=pytest.mark.xfail(reason="Handles SSL in incompatible with Neon way")), + pytest.param( + "swift/PostgresClientKit", # See https://github.com/neondatabase/neon/pull/2008#discussion_r911896592 + marks=pytest.mark.xfail(reason="Neither SNI nor parameters is supported")), + "typescript/postgresql-client", + ], +) +def test_pg_clients(remote_pg: RemotePostgres, client: str): + conn_options = remote_pg.conn_options() + + env_file = None + with NamedTemporaryFile(mode="w", delete=False) as f: + env_file = f.name + f.write(f""" + NEON_HOST={conn_options["host"]} + NEON_DATABASE={conn_options["dbname"]} + NEON_USER={conn_options["user"]} + NEON_PASSWORD={conn_options["password"]} + """) + + image_tag = client.lower() + docker_bin = shutil.which("docker") + if docker_bin is None: + raise RuntimeError("docker is required for running this test") + + build_cmd = [ + docker_bin, "build", "--quiet", "--tag", image_tag, f"{Path(__file__).parent / client}" + ] + run_cmd = [docker_bin, "run", "--rm", "--env-file", env_file, image_tag] + + subprocess.run(build_cmd, check=True) + result = subprocess.run(run_cmd, check=True, capture_output=True, text=True) + + assert result.stdout.strip() == "1" diff --git a/test_runner/pg_clients/typescript/postgresql-client/.dockerignore b/test_runner/pg_clients/typescript/postgresql-client/.dockerignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/test_runner/pg_clients/typescript/postgresql-client/.dockerignore @@ -0,0 +1 @@ +node_modules/ diff --git a/test_runner/pg_clients/typescript/postgresql-client/.gitignore b/test_runner/pg_clients/typescript/postgresql-client/.gitignore new file mode 100644 index 0000000000..c2658d7d1b --- /dev/null +++ b/test_runner/pg_clients/typescript/postgresql-client/.gitignore @@ -0,0 +1 @@ +node_modules/ diff --git a/test_runner/pg_clients/typescript/postgresql-client/Dockerfile b/test_runner/pg_clients/typescript/postgresql-client/Dockerfile new file mode 100644 index 0000000000..b57147503f --- /dev/null +++ b/test_runner/pg_clients/typescript/postgresql-client/Dockerfile @@ -0,0 +1,7 @@ +FROM node:16 +WORKDIR /source + +COPY . . +RUN npm clean-install + +CMD ["/source/index.js"] \ No newline at end of file diff --git a/test_runner/pg_clients/typescript/postgresql-client/index.js b/test_runner/pg_clients/typescript/postgresql-client/index.js new file mode 100755 index 0000000000..af4899baab --- /dev/null +++ b/test_runner/pg_clients/typescript/postgresql-client/index.js @@ -0,0 +1,25 @@ +#! /usr/bin/env node + +import {Connection} from 'postgresql-client'; + +const params = { + "host": process.env.NEON_HOST, + "database": process.env.NEON_DATABASE, + "user": process.env.NEON_USER, + "password": process.env.NEON_PASSWORD, + "ssl": true, +} +for (const key in params) { + if (params[key] === undefined) { + delete params[key]; + } +} + +const connection = new Connection(params); +await connection.connect(); +const result = await connection.query( + 'select 1' +); +const rows = result.rows; +await connection.close(); +console.log(rows[0][0]); diff --git a/test_runner/pg_clients/typescript/postgresql-client/package-lock.json b/test_runner/pg_clients/typescript/postgresql-client/package-lock.json new file mode 100644 index 0000000000..bb5b4a1378 --- /dev/null +++ b/test_runner/pg_clients/typescript/postgresql-client/package-lock.json @@ -0,0 +1,262 @@ +{ + "name": "typescript", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "postgresql-client": "^2.1.3" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/doublylinked": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/doublylinked/-/doublylinked-2.5.1.tgz", + "integrity": "sha512-Lpqb+qyHpR5Bew8xfKsxVYdjXEYAQ7HLp1IX47kHKmVCZeXErInytonjkL+kE+L4yaKSYEmDNR9MJYr5zwuAKA==", + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/lightning-pool": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lightning-pool/-/lightning-pool-3.1.3.tgz", + "integrity": "sha512-OgWuoh0BBrikWx/mc/XwIKwC9HHTe/GU3XODLMBPibv7jv8u0o2gQFS7KVEg5U8Oufg6N7mkm8Y1RoiLER0zeQ==", + "dependencies": { + "doublylinked": "^2.4.3", + "putil-promisify": "^1.8.2" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postgresql-client": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/postgresql-client/-/postgresql-client-2.1.3.tgz", + "integrity": "sha512-36Ga6JzhydsRzcCRcA/Y2hrX9C9sI0wS6sgRNBlOGkOwACXQVybmhDM7mAUbi9cT00N39Ee7btR0eMCyD//5Xg==", + "dependencies": { + "debug": "^4.3.4", + "doublylinked": "^2.5.1", + "lightning-pool": "^3.1.3", + "postgres-bytea": "^3.0.0", + "power-tasks": "^0.8.0", + "putil-merge": "^3.8.0", + "putil-promisify": "^1.8.5", + "putil-varhelpers": "^1.6.4" + }, + "engines": { + "node": ">=14.0", + "npm": ">=7.0.0" + } + }, + "node_modules/power-tasks": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/power-tasks/-/power-tasks-0.8.0.tgz", + "integrity": "sha512-HhMcx+y5UkzlEmKslruz8uAU2Yq8CODJsFEMFsYMrGp5EzKpkNHGu0RNvBqyewKJDZHPNKtBSULsEAxMqQIBVQ==", + "dependencies": { + "debug": "^4.3.4", + "doublylinked": "^2.5.1", + "strict-typed-events": "^2.2.0" + }, + "engines": { + "node": ">=14.0", + "npm": ">=7.0.0" + } + }, + "node_modules/putil-merge": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/putil-merge/-/putil-merge-3.8.0.tgz", + "integrity": "sha512-5tXPafJawWFoYZWLhkYXZ7IC/qkI45HgJsgv36lJBeq3qjFZfUITZE01CmWUBIlIn9f1yDiikqgYERARhVmgrg==", + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/putil-promisify": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/putil-promisify/-/putil-promisify-1.8.5.tgz", + "integrity": "sha512-DItclasWWZokvpq3Aiaq0iV7WC8isP/0o/8mhC0yV6CQ781N/7NQHA1VyOm6hfpeFEwIQoo1C4Yjc5eH0q6Jbw==", + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/putil-varhelpers": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/putil-varhelpers/-/putil-varhelpers-1.6.4.tgz", + "integrity": "sha512-nM2nO1HS2yJUyPgz0grd2XZAM0Spr6/tt6F4xXeNDjByV00BV2mq6lZ+sDff8WIfQBI9Hn1Czh93H1xBvKESxw==", + "engines": { + "node": ">= 6.0" + } + }, + "node_modules/strict-typed-events": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strict-typed-events/-/strict-typed-events-2.2.0.tgz", + "integrity": "sha512-yvHRtEfRRV7TJWi9cLhMt4Mb12JtAwXXONltUlLCA3fRB0LRy94B4E4e2gIlXzT5nZHTZVpOjJNOshri3LZ5bw==", + "dependencies": { + "putil-promisify": "^1.8.5", + "ts-gems": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/ts-gems": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-gems/-/ts-gems-2.1.0.tgz", + "integrity": "sha512-5IqiG4nq1tsOhYPc4CwxA6bsE+TfU6uAABzf6bH4sdElgXpt/mlStvIYedvvtU7BM1+RRJxCaTLaaVFcCqNaiA==", + "peerDependencies": { + "typescript": ">=4.0.0" + } + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "doublylinked": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/doublylinked/-/doublylinked-2.5.1.tgz", + "integrity": "sha512-Lpqb+qyHpR5Bew8xfKsxVYdjXEYAQ7HLp1IX47kHKmVCZeXErInytonjkL+kE+L4yaKSYEmDNR9MJYr5zwuAKA==" + }, + "lightning-pool": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lightning-pool/-/lightning-pool-3.1.3.tgz", + "integrity": "sha512-OgWuoh0BBrikWx/mc/XwIKwC9HHTe/GU3XODLMBPibv7jv8u0o2gQFS7KVEg5U8Oufg6N7mkm8Y1RoiLER0zeQ==", + "requires": { + "doublylinked": "^2.4.3", + "putil-promisify": "^1.8.2" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==" + }, + "postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "requires": { + "obuf": "~1.1.2" + } + }, + "postgresql-client": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/postgresql-client/-/postgresql-client-2.1.3.tgz", + "integrity": "sha512-36Ga6JzhydsRzcCRcA/Y2hrX9C9sI0wS6sgRNBlOGkOwACXQVybmhDM7mAUbi9cT00N39Ee7btR0eMCyD//5Xg==", + "requires": { + "debug": "^4.3.4", + "doublylinked": "^2.5.1", + "lightning-pool": "^3.1.3", + "postgres-bytea": "^3.0.0", + "power-tasks": "^0.8.0", + "putil-merge": "^3.8.0", + "putil-promisify": "^1.8.5", + "putil-varhelpers": "^1.6.4" + } + }, + "power-tasks": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/power-tasks/-/power-tasks-0.8.0.tgz", + "integrity": "sha512-HhMcx+y5UkzlEmKslruz8uAU2Yq8CODJsFEMFsYMrGp5EzKpkNHGu0RNvBqyewKJDZHPNKtBSULsEAxMqQIBVQ==", + "requires": { + "debug": "^4.3.4", + "doublylinked": "^2.5.1", + "strict-typed-events": "^2.2.0" + } + }, + "putil-merge": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/putil-merge/-/putil-merge-3.8.0.tgz", + "integrity": "sha512-5tXPafJawWFoYZWLhkYXZ7IC/qkI45HgJsgv36lJBeq3qjFZfUITZE01CmWUBIlIn9f1yDiikqgYERARhVmgrg==" + }, + "putil-promisify": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/putil-promisify/-/putil-promisify-1.8.5.tgz", + "integrity": "sha512-DItclasWWZokvpq3Aiaq0iV7WC8isP/0o/8mhC0yV6CQ781N/7NQHA1VyOm6hfpeFEwIQoo1C4Yjc5eH0q6Jbw==" + }, + "putil-varhelpers": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/putil-varhelpers/-/putil-varhelpers-1.6.4.tgz", + "integrity": "sha512-nM2nO1HS2yJUyPgz0grd2XZAM0Spr6/tt6F4xXeNDjByV00BV2mq6lZ+sDff8WIfQBI9Hn1Czh93H1xBvKESxw==" + }, + "strict-typed-events": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/strict-typed-events/-/strict-typed-events-2.2.0.tgz", + "integrity": "sha512-yvHRtEfRRV7TJWi9cLhMt4Mb12JtAwXXONltUlLCA3fRB0LRy94B4E4e2gIlXzT5nZHTZVpOjJNOshri3LZ5bw==", + "requires": { + "putil-promisify": "^1.8.5", + "ts-gems": "^2.0.0" + } + }, + "ts-gems": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-gems/-/ts-gems-2.1.0.tgz", + "integrity": "sha512-5IqiG4nq1tsOhYPc4CwxA6bsE+TfU6uAABzf6bH4sdElgXpt/mlStvIYedvvtU7BM1+RRJxCaTLaaVFcCqNaiA==", + "requires": {} + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "peer": true + } + } +} diff --git a/test_runner/pg_clients/typescript/postgresql-client/package.json b/test_runner/pg_clients/typescript/postgresql-client/package.json new file mode 100644 index 0000000000..5d8ca23a7f --- /dev/null +++ b/test_runner/pg_clients/typescript/postgresql-client/package.json @@ -0,0 +1,6 @@ +{ + "type": "module", + "dependencies": { + "postgresql-client": "^2.1.3" + } +}