diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6257276..3af2ef6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -18,7 +18,7 @@ jobs: env: POSTGRESQL_USER: root POSTGRESQL_PASSWORD: smartvm - POSTGRESQL_DATABASE: vmdb_production + POSTGRESQL_DATABASE: temp options: >- --name postgres --volume /tmp/postgresql-cfg/:/opt/app-root/src/postgresql-cfg/ @@ -28,6 +28,10 @@ jobs: --health-retries 5 ports: - 5432:5432 + env: + POSTGRESQL_HOST: localhost + POSTGRESQL_USER: root + POSTGRESQL_PASSWORD: smartvm steps: - uses: actions/checkout@v2 - name: Override postgres settings @@ -41,6 +45,8 @@ jobs: with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true + - name: Set up tests + run: bundle exec rake spec:setup - name: Run tests run: bundle exec rake env: diff --git a/Rakefile b/Rakefile index c6ab36c..9535c06 100644 --- a/Rakefile +++ b/Rakefile @@ -1,17 +1,17 @@ require "bundler/gem_tasks" require "rspec/core/rake_task" +require_relative "spec/support/connection_helper" + def create_database(dbname) - require "pg" - c = PG::Connection.new(:dbname => "postgres") + c = ConnectionHelper.connection_for("postgres") c.async_exec("CREATE DATABASE #{dbname}") rescue PG::DuplicateDatabase => err raise unless err.message =~ /already exists/ end def drop_database(dbname) - require "pg" - c = PG::Connection.new(:dbname => "postgres") + c = ConnectionHelper.connection_for("postgres") c.async_exec("DROP DATABASE #{dbname}") rescue PG::InvalidCatalogName => err raise unless err.message =~ /does not exist/ diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5748b6e..125809d 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,80 +4,4 @@ $LOAD_PATH.unshift File.expand_path("../../lib", __FILE__) require "pg-logical_replication" -module ConnectionHelper - def self.source_database_connection - @source_database_connection ||= PG::Connection.new(:dbname => "logical_test").tap do |c| - c.set_notice_receiver { |r| nil } - end - end - - def self.target_database_connection - @target_database_connection ||= PG::Connection.new(:dbname => "logical_test_target").tap do |c| - c.set_notice_receiver { |r| nil } - end - end - - def self.with_each_connection - [source_database_connection, target_database_connection].each do |conn| - yield conn - end - end -end - -module DatabaseHelper - def self.tables - %w(table1 table2 table3 table4) - end - - def self.create_tables - ConnectionHelper.with_each_connection do |conn| - tables.each do |t| - conn.async_exec(<<-SQL) - CREATE TABLE IF NOT EXISTS #{t} ( - id SERIAL PRIMARY KEY, - data VARCHAR(50) - ) - SQL - end - end - end - - def self.drop_tables - ConnectionHelper.with_each_connection do |conn| - tables.each { |t| conn.async_exec("DROP TABLE IF EXISTS #{t}") } - end - end - - def self.drop_subscriptions - conn = ConnectionHelper.target_database_connection - # Subscriptions are visible from all databases in the cluster so we need to specify only the subs from the target database. - conn.async_exec("SELECT subname::TEXT FROM pg_subscription AS sub JOIN pg_database ON sub.subdbid = pg_database.oid WHERE pg_database.datname = current_database()").values.flatten.each do |s| - conn.async_exec("ALTER subscription #{s} DISABLE") - conn.async_exec("ALTER subscription #{s} SET (slot_name = NONE)") - conn.async_exec("DROP SUBSCRIPTION #{s}") - end - end - - def self.drop_publications - conn = ConnectionHelper.source_database_connection - conn.async_exec("SELECT pubname::TEXT from pg_publication").values.flatten.each do |p| - conn.async_exec("DROP PUBLICATION #{p}") - end - end - - def self.drop_replication_slots - conn = ConnectionHelper.source_database_connection - # replication_slots are visible from all databases in the cluster so we need to specify only the slots from the source database. - conn.async_exec("SELECT slot_name::TEXT FROM pg_replication_slots WHERE slot_type = 'logical' AND NOT active AND database = current_database()").values.flatten.each do |slot| - conn.async_exec("SELECT pg_drop_replication_slot('#{slot}')") - end - end - - def self.with_clean_environment - yield - ensure - drop_subscriptions - drop_publications - drop_replication_slots - end -end +Dir[File.join(__dir__, "support/**/*.rb")].each { |f| require f } diff --git a/spec/support/connection_helper.rb b/spec/support/connection_helper.rb new file mode 100644 index 0000000..fcb1e99 --- /dev/null +++ b/spec/support/connection_helper.rb @@ -0,0 +1,32 @@ +module ConnectionHelper + def self.connection_for(dbname) + require "pg" + + options = { + :host => ENV["POSTGRESQL_HOST"], + :user => ENV["POSTGRESQL_USER"], + :password => ENV["POSTGRESQL_PASSWORD"], + :dbname => dbname + }.compact + + PG::Connection.new(options) + end + + def self.source_database_connection + @source_database_connection ||= connection_for("logical_test").tap do |c| + c.set_notice_receiver { |r| nil } + end + end + + def self.target_database_connection + @target_database_connection ||= connection_for("logical_test_target").tap do |c| + c.set_notice_receiver { |r| nil } + end + end + + def self.with_each_connection + [source_database_connection, target_database_connection].each do |conn| + yield conn + end + end +end diff --git a/spec/support/database_helper.rb b/spec/support/database_helper.rb new file mode 100644 index 0000000..62bb59f --- /dev/null +++ b/spec/support/database_helper.rb @@ -0,0 +1,59 @@ +require_relative "connection_helper" + +module DatabaseHelper + def self.tables + %w(table1 table2 table3 table4) + end + + def self.create_tables + ConnectionHelper.with_each_connection do |conn| + tables.each do |t| + conn.async_exec(<<-SQL) + CREATE TABLE IF NOT EXISTS #{t} ( + id SERIAL PRIMARY KEY, + data VARCHAR(50) + ) + SQL + end + end + end + + def self.drop_tables + ConnectionHelper.with_each_connection do |conn| + tables.each { |t| conn.async_exec("DROP TABLE IF EXISTS #{t}") } + end + end + + def self.drop_subscriptions + conn = ConnectionHelper.target_database_connection + # Subscriptions are visible from all databases in the cluster so we need to specify only the subs from the target database. + conn.async_exec("SELECT subname::TEXT FROM pg_subscription AS sub JOIN pg_database ON sub.subdbid = pg_database.oid WHERE pg_database.datname = current_database()").values.flatten.each do |s| + conn.async_exec("ALTER subscription #{s} DISABLE") + conn.async_exec("ALTER subscription #{s} SET (slot_name = NONE)") + conn.async_exec("DROP SUBSCRIPTION #{s}") + end + end + + def self.drop_publications + conn = ConnectionHelper.source_database_connection + conn.async_exec("SELECT pubname::TEXT from pg_publication").values.flatten.each do |p| + conn.async_exec("DROP PUBLICATION #{p}") + end + end + + def self.drop_replication_slots + conn = ConnectionHelper.source_database_connection + # replication_slots are visible from all databases in the cluster so we need to specify only the slots from the source database. + conn.async_exec("SELECT slot_name::TEXT FROM pg_replication_slots WHERE slot_type = 'logical' AND NOT active AND database = current_database()").values.flatten.each do |slot| + conn.async_exec("SELECT pg_drop_replication_slot('#{slot}')") + end + end + + def self.with_clean_environment + yield + ensure + drop_subscriptions + drop_publications + drop_replication_slots + end +end