# frozen_string_literal: true RSpec.describe "DB / Gateways", :app_integration do before do @env = ENV.to_h allow(Hanami::Env).to receive(:loaded?).and_return(false) end after do ENV.replace(@env) end it "configures gateways by detecting ENV vars" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App end end RUBY write "db/.keep", "" write "app/relations/.keep", "" write "slices/admin/relations/.keep", "" ENV["DATABASE_URL"] = "sqlite://db/default.sqlite3" ENV["DATABASE_URL__EXTRA"] = "sqlite://db/extra.sqlite3" ENV["ADMIN__DATABASE_URL__DEFAULT"] = "sqlite://db/admin.sqlite3" ENV["ADMIN__DATABASE_URL__SPECIAL"] = "sqlite://db/admin_special.sqlite3" require "hanami/prepare" expect(Hanami.app["db.rom"].gateways[:default]).to be expect(Hanami.app["db.rom"].gateways[:extra]).to be expect(Hanami.app["db.gateway"]).to be Hanami.app["db.rom"].gateways[:default] expect(Hanami.app["db.gateways.default"]).to be Hanami.app["db.rom"].gateways[:default] expect(Hanami.app["db.gateways.extra"]).to be Hanami.app["db.rom"].gateways[:extra] expect(Admin::Slice["db.rom"].gateways[:default]).to be expect(Admin::Slice["db.rom"].gateways[:special]).to be expect(Admin::Slice["db.gateway"]).to be Admin::Slice["db.rom"].gateways[:default] expect(Admin::Slice["db.gateways.default"]).to be Admin::Slice["db.rom"].gateways[:default] expect(Admin::Slice["db.gateways.special"]).to be Admin::Slice["db.rom"].gateways[:special] end end it "configures gateways from explicit config in the provider" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App end end RUBY write "db/.keep", "" write "config/providers/db.rb", <<~RUBY Hanami.app.configure_provider :db do config.gateway :default do |gw| gw.database_url = "sqlite://db/default.sqlite3" end config.gateway :extra do |gw| gw.database_url = "sqlite://db/extra.sqlite3" end end RUBY require "hanami/prepare" expect(Hanami.app["db.rom"].gateways[:default]).to be expect(Hanami.app["db.rom"].gateways[:extra]).to be expect(Hanami.app["db.gateway"]).to be Hanami.app["db.rom"].gateways[:default] expect(Hanami.app["db.gateways.default"]).to be Hanami.app["db.rom"].gateways[:default] expect(Hanami.app["db.gateways.extra"]).to be Hanami.app["db.rom"].gateways[:extra] end end it "configures connection options on their respective gateways" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App end end RUBY write "db/.keep", "" write "config/providers/db.rb", <<~RUBY Hanami.app.configure_provider :db do config.gateway :default do |gw| gw.connection_options timeout: 10_000 gw.adapter :sql do |a| a.skip_defaults :extensions a.extension :error_sql end end config.gateway :extra do |gw| gw.connection_options readonly: true end end RUBY ENV["DATABASE_URL"] = "sqlite::memory" ENV["DATABASE_URL__EXTRA"] = "sqlite::memory" require "hanami/prepare" default = Hanami.app["db.gateways.default"] extra = Hanami.app["db.gateways.extra"] expect(default.options).to eq({timeout: 10_000, extensions: [:error_sql]}) expect(extra.options).to eq({readonly: true, extensions: %i[caller_logging error_sql sql_comments]}) end end it "exposes all database URLs as #database_urls on the provider source (for CLI commands)" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App end end RUBY write "config/providers/db.rb", <<~RUBY Hanami.app.configure_provider :db do config.gateway :special do |gw| gw.database_url = "sqlite://db/special.sqlite3" end end RUBY ENV["DATABASE_URL"] = "sqlite://db/default.sqlite3" ENV["DATABASE_URL__EXTRA"] = "sqlite://db/extra.sqlite3" require "hanami/prepare" database_urls = Hanami.app.container.providers[:db].source.finalize_config.database_urls expect(database_urls).to eq( default: "sqlite://db/default.sqlite3", extra: "sqlite://db/extra.sqlite3", special: "sqlite://db/special.sqlite3" ) end end it "applies extensions from the default adapter to explicitly configured gateway adapters" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App end end RUBY write "config/providers/db.rb", <<~RUBY Hanami.app.configure_provider :db do config.adapter :sql do |a| a.extension :is_distinct_from end config.gateway :default do |gw| gw.adapter :sql do |a| a.extension :exclude_or_null end end end RUBY ENV["DATABASE_URL"] = "sqlite::memory" ENV["DATABASE_URL__SPECIAL"] = "sqlite::memory" require "hanami/prepare" expect(Hanami.app["db.gateways.default"].options[:extensions]) .to eq [:exclude_or_null, :is_distinct_from, :caller_logging, :error_sql, :sql_comments] expect(Hanami.app["db.gateways.special"].options[:extensions]) .to eq [:is_distinct_from, :caller_logging, :error_sql, :sql_comments] end end it "combines ROM plugins from both default adapter and gateway-configured adapters" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App end end RUBY write "config/providers/db.rb", <<~RUBY Hanami.app.configure_provider :db do config.adapter :sql do |a| a.plugin command: :associates end config.gateway :default do |gw| gw.database_url = "sqlite::memory" gw.adapter :sql do |a| a.plugin relation: :nullify end end config.gateway :special do |gw| gw.adapter :sql do |a| a.plugin relation: :pagination end end end RUBY ENV["DATABASE_URL"] = "sqlite::memory" ENV["DATABASE_URL__SPECIAL"] = "sqlite::memory" require "hanami/prepare" expect(Hanami.app["db.config"].setup.plugins.length).to eq 5 expect(Hanami.app["db.config"].setup.plugins).to include( satisfying { |plugin| plugin.type == :command && plugin.name == :associates }, satisfying { |plugin| plugin.type == :relation && plugin.name == :nullify }, satisfying { |plugin| plugin.type == :relation && plugin.name == :pagination }, satisfying { |plugin| plugin.type == :relation && plugin.name == :auto_restrictions }, satisfying { |plugin| plugin.type == :relation && plugin.name == :instrumentation } ) end end it "configures gateway adapters for their specific database types" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App end end RUBY write "config/providers/db.rb", <<~RUBY Hanami.app.configure_provider :db do config.gateway :default do |gw| gw.database_url = "sqlite::memory" end end RUBY ENV["DATABASE_URL"] = "sqlite::memory" ENV["DATABASE_URL__SPECIAL"] = "postgres://localhost/database" require "hanami/prepare" # Get the provider source and finalize config, because the tests here aren't set up to handle # connections to a running postgres database allow(Hanami).to receive(:bundled?).and_call_original allow(Hanami).to receive(:bundled?).with("pg").and_return true provider_source = Hanami.app.container.providers[:db].source provider_source.finalize_config expect(provider_source.config.gateways[:default].config.adapter.extensions) .to eq [:caller_logging, :error_sql, :sql_comments] expect(provider_source.config.gateways[:special].config.adapter.extensions) .to eq [ :caller_logging, :error_sql, :sql_comments, :pg_array, :pg_enum, :pg_json, :pg_range ] end end it "makes the gateways available to relations" do with_tmp_directory(@dir = Dir.mktmpdir) do write "config/app.rb", <<~RUBY require "hanami" module TestApp class App < Hanami::App config.logger.stream = File::NULL end end RUBY write "app/relations/posts.rb", <<~RUBY module TestApp module Relations class Posts < Hanami::DB::Relation schema :posts, infer: true end end end RUBY write "app/relations/users.rb", <<~RUBY module TestApp module Relations class Users < Hanami::DB::Relation gateway :extra schema :users, infer: true end end end RUBY write "db/.keep", "" ENV["DATABASE_URL"] = "sqlite://db/default.sqlite3" ENV["DATABASE_URL__EXTRA"] = "sqlite://db/extra.sqlite3" require "hanami/prepare" Hanami.app.prepare :db # Manually run a migration and add a test record default_gateway = Hanami.app["db.gateways.default"] migration = default_gateway.migration do change do create_table :posts do primary_key :id column :title, :text, null: false end end end migration.apply(default_gateway, :up) default_gateway.connection.execute("INSERT INTO posts (title) VALUES ('Together breakfast')") extra_gateway = Hanami.app["db.gateways.extra"] migration = extra_gateway.migration do change do create_table :users do primary_key :id column :name, :text, null: false end end end migration.apply(extra_gateway, :up) extra_gateway.connection.execute("INSERT INTO users (name) VALUES ('Jane Doe')") Hanami.app.boot expect(Hanami.app["relations.posts"].to_a).to eq [{id: 1, title: "Together breakfast"}] expect(Hanami.app["relations.users"].to_a).to eq [{id: 1, name: "Jane Doe"}] end end end