Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: add integration tests against Postgres.js #479

Merged
merged 1 commit into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,54 @@ jobs:
- name: Run tests
run: mix test

integration:
name: Run integration tests
runs-on: u22-arm-runner
needs: [deps]

steps:
- uses: actions/checkout@v4
- name: Setup Elixir
id: beam
uses: erlef/setup-beam@v1
with:
otp-version: '25.3.2.7'
elixir-version: '1.14.5'
Comment on lines +141 to +142
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wondering if we want to put these versions in the env vars

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could be useful, but as a separate PR. There I simply copied what we have in other steps.

- uses: actions/setup-node@v4
with:
node-version: 'lts/*'
- name: Set up Rust
uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
- name: Cache Mix
uses: actions/cache@v4
with:
path: deps
key: ${{ runner.os }}-mix-${{ hashFiles(format('{0}{1}', github.workspace, '/mix.lock')) }}
- name: Cache native
uses: actions/cache@v4
with:
path: |
_build/${{ env.MIX_ENV }}/lib/supavisor/native
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-build-native-${{ hashFiles(format('{0}{1}', github.workspace, '/native/**/Cargo.lock')) }}
restore-keys: |
${{ runner.os }}-build-native-
- name: Compile deps
run: mix deps.compile
- name: Compile
run: mix compile
- name: Set up Postgres
run: docker-compose -f ./docker-compose.db.yml up -d
- name: Start epmd
run: epmd -daemon
- name: Run tests
run: mix test --only integration --trace

dialyzer:
name: Dialyze
runs-on: u22-arm-runner
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.db.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ services:
- ./dev/postgres:/docker-entrypoint-initdb.d/
# Uncomment to set MD5 authentication method on uninitialized databases
# - ./dev/postgres/md5/etc/postgresql/pg_hba.conf:/etc/postgresql/pg_hba.conf
command: postgres -c config_file=/etc/postgresql/postgresql.conf
command: postgres -c config_file=/etc/postgresql/postgresql.conf -c max_prepared_transactions=2000
environment:
POSTGRES_HOST: /var/run/postgresql
POSTGRES_PASSWORD: postgres
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ services:
- ./dev/postgres:/docker-entrypoint-initdb.d/
# Uncomment to set MD5 authentication method on uninitialized databases
# - ./dev/postgres/md5/etc/postgresql/pg_hba.conf:/etc/postgresql/pg_hba.conf
command: postgres -c config_file=/etc/postgresql/postgresql.conf
command: postgres -c config_file=/etc/postgresql/postgresql.conf -c max_prepared_transactions=2000
environment:
POSTGRES_HOST: /var/run/postgresql
POSTGRES_PASSWORD: postgres
Expand Down
18 changes: 17 additions & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,12 @@
{
pre-commit.hooks = {
alejandra.enable = true;
typos.enable = true;
typos = {
enable = true;
excludes = [
"test/integration/"
];
};
};
}
{
Expand All @@ -78,20 +83,31 @@

services.postgres = {
enable = true;
package = pkgs.postgresql_15;
initialScript = ''
${builtins.readFile ./dev/postgres/00-setup.sql}

CREATE USER postgres SUPERUSER PASSWORD 'postgres';
'';
listen_addresses = "127.0.0.1";
port = 6432;
settings = {
max_prepared_transactions = 262143;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is maximal possible value, in tests I have set it to 2000 as it doesn't matter really as long as it is greater than 0 I think.

};
};

process.implementation = "honcho";

# Force connection through TCP instead of Unix socket
env.PGHOST = lib.mkForce "";
}
{
languages.javascript = {
enable = true;
bun.enable = true;
yarn.enable = true;
};
}
({
pkgs,
lib,
Expand Down
125 changes: 125 additions & 0 deletions test/integration/external_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
defmodule Supavisor.Integration.ExternalTest do
use ExUnit.Case, async: false

@moduletag integration: true

setup_all do
npm =
get_tool("yarn") || get_tool("npm") || get_tool("bun") ||
raise "Cannot find neither Yarn nor NPM"

assert {_, 0} = System.cmd(npm, ~w[install], cd: suite("js"))

{:ok, npm: npm}
end

setup :external_id

setup ctx do
if get_tool(ctx.runtime) do
:ok
else
raise "Runtime not available"
end
end

describe "Postgres.js" do
@describetag library: "postgres.js", suite: "js"

@tag runtime: "node", mode: "session"
test "Node session", ctx do
assert_run(ctx, ~w[postgres/index.js])
end

@tag runtime: "node", mode: "transaction"
test "Node transaction", ctx do
assert_run(ctx, ~w[postgres/index.js])
end

# These currently do not pass
# @tag runtime: "bun", mode: "session"
# test "Bun session", ctx do
# assert_run ctx, ~w[postgres/index.js], suite: "js"
# end
#
# @tag runtime: "bun", mode: "transaction"
# test "Bun transaction", ctx do
# assert_run ctx, ~w[postgres/index.js], suite: "js"
# end
#
# @tag runtime: "deno", mode: "session"
# test "Deno session", ctx do
# assert_run ctx, ~w[run --allow-all postgres/index.js], suite: "js"
# end
#
# @tag runtime: "deno", mode: "transaction"
# test "Deno transaction", ctx do
# assert_run ctx, ~w[run --allow-all postgres/index.js], suite: "js"
# end
end

defp assert_run(ctx, args, opts \\ []) do
suite = suite(ctx.suite)

env =
[
{"PGMODE", ctx.mode},
{"PGDATABASE", ctx.db},
{"PGHOST", "localhost"},
{"PGPORT", to_string(port(ctx.mode))},
{"PGUSER", ctx.user},
{"PGPASS", "postgres"}
] ++ (opts[:env] || [])

assert {output, code} =
System.cmd(ctx.runtime, args,
env: env,
cd: suite,
stderr_to_stdout: true
)

assert code == 0, output
end

## UTILS

defp suite(name), do: Path.join(__DIR__, name)

defp get_tool(name), do: System.find_executable(name)

defp port("session"), do: Application.fetch_env!(:supavisor, :proxy_port_session)
defp port("transaction"), do: Application.fetch_env!(:supavisor, :proxy_port_transaction)

defp external_id(ctx) do
external_id =
[ctx.runtime, ctx.library, ctx.mode]
|> Enum.map_join("_", &String.replace(&1, ~r/\W/, ""))

# Ensure that there are no leftovers
_ = Supavisor.Tenants.delete_tenant_by_external_id(external_id)

_ = Supavisor.Repo.query("DROP DATABASE IF EXISTS #{external_id}")
assert {:ok, _} = Supavisor.Repo.query("CREATE DATABASE #{external_id}")

assert {:ok, tenant} =
Supavisor.Tenants.create_tenant(%{
default_parameter_status: %{},
db_host: "localhost",
db_port: 6432,
db_database: external_id,
auth_query: "SELECT rolname, rolpassword FROM pg_authid WHERE rolname=$1;",
external_id: external_id,
users: [
%{
"pool_size" => 3,
"db_user" => "postgres",
"db_password" => "postgres",
"is_manager" => true,
"mode_type" => "session"
}
]
})

{:ok, user: "postgres.#{external_id}", db: tenant.db_database, external_id: external_id}
end
end
1 change: 1 addition & 0 deletions test/integration/js/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/node_modules
13 changes: 13 additions & 0 deletions test/integration/js/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "supavisor-integration",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"license": "MIT",
"scripts": {
"test:postgres": "node ./postgres/index.js"
},
"dependencies": {
"postgres": "^3.4.5"
}
}
2 changes: 2 additions & 0 deletions test/integration/js/postgres/copy.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
1 2 3
4 5 6
Comment on lines +1 to +2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does it do?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No idea, it is copied as is from postgres.js test folder. I haven't really checked if that is used anywhere in test suite.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's try to run it without this file :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have found that it is used in one test:

t('Copy from file', async() => {
  await sql`create table test (x int, y int, z int)`
  await new Promise(async r => fs
    .createReadStream(rel('copy.csv'))
    .pipe(await sql`copy test from stdin`.writable())
    .on('finish', r)
  )

  return [
    JSON.stringify(await sql`select * from test`),
    '[{"x":1,"y":2,"z":3},{"x":4,"y":5,"z":6}]',
    await sql`drop table test`
  ]
})

Loading
Loading