From 4ca969109bff73f4dc6d634dcfb7cb3ae18dac15 Mon Sep 17 00:00:00 2001 From: Nikita Shilnikov Date: Mon, 4 May 2026 11:19:09 +0200 Subject: [PATCH 1/2] Fix tests by introducing DSL separating DDL, relations, and seeding Before that, emerging dependencies between spec parts were hard to navigate: when you call `container` it materializes the container meaning you won't be able to add new tables/relations to the container. It's a mystery to me how we've managed to continue without proper separation the stages so far. Finally, I've hit it pretty hard when fixing failing specs. What this introduces are three DSL methods accepting blocks: - setup_tables - setup_relations - seed No setup_relations blocks are yielded before all setup_tables are run. seed blocks follow setup_relations. These methods have an additional optional parameter for sequencing blocks within one stage: ```ruby setup_tables(:base) {} # called first setup_tables(dep: :base) {} # depends on base, called second setup_tables {} # no dependency provided, will be called last ``` Also, in a given spec you can use metadata to prevent seeding or declaring relations from a shared context: ```ruby include 'users and tasks' describe 'a block without seeding and relations', relations: false, seeds: false do .... end describe 'a block without seeding', seeds: false do .... end ``` --- .rubocop.yml | 8 + lib/rom/sql/plugin/associates.rb | 2 +- lib/rom/sql/schema/inferrer.rb | 2 +- .../postgres/attribute/array_spec.rb | 6 +- .../postgres/attribute/range_spec.rb | 48 ++- spec/extensions/postgres/attribute_spec.rb | 28 +- spec/extensions/postgres/integration_spec.rb | 10 +- .../many_to_many/custom_fks_spec.rb | 6 +- .../many_to_many/from_view_spec.rb | 6 +- .../many_to_many/self_ref_spec.rb | 16 +- .../associations/many_to_many_spec.rb | 24 +- .../many_to_one/custom_fks_spec.rb | 7 +- .../many_to_one/from_view_spec.rb | 10 +- .../associations/many_to_one/self_ref_spec.rb | 6 +- .../associations/many_to_one_spec.rb | 4 +- .../one_to_many/custom_fks_spec.rb | 6 +- .../one_to_many/from_view_spec.rb | 6 +- .../associations/one_to_many/self_ref_spec.rb | 6 +- .../associations/one_to_many_spec.rb | 2 +- .../associations/one_to_one_spec.rb | 8 +- .../associations/one_to_one_through_spec.rb | 2 +- .../auto_migrations/errors_spec.rb | 8 +- .../file_based_migrations_spec.rb | 12 +- .../auto_migrations/foreign_keys_spec.rb | 16 +- .../auto_migrations/indexes_spec.rb | 239 +++++----- .../auto_migrations/managing_columns_spec.rb | 28 +- .../postgres/column_types_spec.rb | 4 +- spec/integration/combine_with_spec.rb | 4 +- spec/integration/commands/create_spec.rb | 46 +- spec/integration/commands/delete_spec.rb | 19 +- spec/integration/commands/update_spec.rb | 6 +- spec/integration/commands/upsert_spec.rb | 16 +- spec/integration/gateway_spec.rb | 5 +- spec/integration/migration_spec.rb | 14 +- .../plugins/associates/many_to_many_spec.rb | 27 +- spec/integration/plugins/associates_spec.rb | 46 +- .../plugins/auto_restrictions_spec.rb | 46 +- spec/integration/plugins/explain_spec.rb | 2 +- .../plugins/full_text_search_spec.rb | 18 +- spec/integration/plugins/pg_streaming_spec.rb | 8 +- .../relation/default_views_spec.rb | 4 +- spec/integration/relation_schema_spec.rb | 408 +++++++++++------- spec/integration/schema/call_spec.rb | 6 - .../integration/schema/inferrer/mysql_spec.rb | 4 +- .../schema/inferrer/postgres_spec.rb | 21 +- .../schema/inferrer/sqlite_spec.rb | 4 +- spec/integration/schema/inferrer_spec.rb | 91 ++-- spec/integration/schema/prefix_spec.rb | 8 +- spec/integration/schema/qualified_spec.rb | 8 +- spec/integration/schema/rename_spec.rb | 8 +- spec/integration/schema/view_spec.rb | 4 +- .../active_support_notifications_spec.rb | 2 +- .../support/rails_log_subscriber_spec.rb | 2 +- spec/integration/wrap_spec.rb | 8 +- spec/shared/accounts.rb | 8 +- spec/shared/articles.rb | 8 +- spec/shared/database_setup.rb | 5 +- spec/shared/json_notes.rb | 5 +- spec/shared/notes.rb | 8 +- spec/shared/posts.rb | 38 +- spec/shared/puppies.rb | 4 +- spec/shared/relations.rb | 5 - spec/shared/users.rb | 14 +- spec/shared/users_and_tasks.rb | 54 ++- spec/spec_helper.rb | 2 + spec/support/container_helper.rb | 110 +++++ spec/unit/commands/create_spec.rb | 4 +- spec/unit/plugin/nullify_spec.rb | 2 +- spec/unit/plugin/pagination_spec.rb | 4 +- spec/unit/plugin/timestamp_spec.rb | 2 +- spec/unit/relation/as_hash_spec.rb | 8 +- spec/unit/relation/assoc_spec.rb | 41 +- spec/unit/relation/associations_spec.rb | 10 +- spec/unit/relation/batch_spec.rb | 2 +- spec/unit/relation/by_pk_spec.rb | 4 +- spec/unit/relation/dataset_spec.rb | 10 +- spec/unit/relation/distinct_spec.rb | 2 +- spec/unit/relation/exists_spec.rb | 20 +- spec/unit/relation/having_spec.rb | 2 +- spec/unit/relation/import_spec.rb | 14 +- spec/unit/relation/inner_join_spec.rb | 16 +- spec/unit/relation/instrument_spec.rb | 8 +- spec/unit/relation/join_dsl_spec.rb | 20 +- spec/unit/relation/left_join_spec.rb | 14 +- spec/unit/relation/lock_spec.rb | 2 +- spec/unit/relation/map_spec.rb | 2 +- spec/unit/relation/order_spec.rb | 2 +- spec/unit/relation/prefix_spec.rb | 2 +- spec/unit/relation/primary_key_spec.rb | 8 +- spec/unit/relation/project_spec.rb | 2 +- spec/unit/relation/qualified_columns_spec.rb | 8 +- spec/unit/relation/rename_spec.rb | 4 +- spec/unit/relation/right_join_spec.rb | 14 - spec/unit/relation/select_spec.rb | 4 - spec/unit/relation/union_spec.rb | 10 +- spec/unit/relation/unique_predicate_spec.rb | 2 +- spec/unit/relation/where_spec.rb | 2 +- 97 files changed, 1064 insertions(+), 797 deletions(-) create mode 100644 spec/support/container_helper.rb diff --git a/.rubocop.yml b/.rubocop.yml index 9aed9fee..a9f07401 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -51,6 +51,10 @@ Lint/EmptyClass: Exclude: - "spec/**/*.rb" +Lint/NestedMethodDefinition: + Exclude: + - "spec/**/*.rb" + Lint/RaiseException: Enabled: false @@ -63,6 +67,8 @@ Lint/SuppressedException: Metrics/AbcSize: Max: 20 + Exclude: + - "spec/**/*.rb" Metrics/BlockLength: Enabled: false @@ -76,6 +82,8 @@ Metrics/CyclomaticComplexity: Metrics/MethodLength: Max: 22 + Exclude: + - "spec/**/*.rb" Naming/FileName: Exclude: diff --git a/lib/rom/sql/plugin/associates.rb b/lib/rom/sql/plugin/associates.rb index ad8fa96c..1e258290 100644 --- a/lib/rom/sql/plugin/associates.rb +++ b/lib/rom/sql/plugin/associates.rb @@ -162,7 +162,7 @@ def associate( def with_association(name, opts = EMPTY_HASH) self.class.build( relation, - **options, associations: associations.merge(name => opts) + **options, associations: { **associations, name => opts } ) end end diff --git a/lib/rom/sql/schema/inferrer.rb b/lib/rom/sql/schema/inferrer.rb index 3e276212..3e7dd7c9 100644 --- a/lib/rom/sql/schema/inferrer.rb +++ b/lib/rom/sql/schema/inferrer.rb @@ -36,7 +36,7 @@ def call(schema, gateway) else infer_from_attributes(gateway, schema, **super) end - rescue Sequel::Error => e + rescue ::Sequel::Error => e on_error(schema.name, e) { **FALLBACK_SCHEMA, indexes: schema.indexes } end diff --git a/spec/extensions/postgres/attribute/array_spec.rb b/spec/extensions/postgres/attribute/array_spec.rb index 3f15237c..d169b8af 100644 --- a/spec/extensions/postgres/attribute/array_spec.rb +++ b/spec/extensions/postgres/attribute/array_spec.rb @@ -5,7 +5,7 @@ include_context 'database setup' - before do + setup_relations do conf.relation(:pg_arrays) do schema(infer: true) end @@ -16,7 +16,7 @@ end context 'with a primitive type' do - before do + setup_tables do conn.create_table :pg_arrays do column :numbers, 'int[]' end @@ -34,7 +34,7 @@ end context 'with a custom json type' do - before do + setup_tables do conn.create_table :pg_arrays do column :meta, 'json[]' end diff --git a/spec/extensions/postgres/attribute/range_spec.rb b/spec/extensions/postgres/attribute/range_spec.rb index 937532e9..25c49888 100644 --- a/spec/extensions/postgres/attribute/range_spec.rb +++ b/spec/extensions/postgres/attribute/range_spec.rb @@ -3,27 +3,6 @@ RSpec.describe 'ROM::SQL::Attribute', :postgres do include_context 'database setup' - def create_ranges_table(db_type, values) - conn.create_table :pg_ranges do - primary_key :id - text :name - - send(db_type, :range) - end - - conf.relation(:pg_ranges) do - schema(:pg_ranges, infer: true) - end - - conf.commands(:pg_ranges) do - define(:create) - end - - values.each do |key, value| - commands[:pg_ranges].create.(name: key.to_s, range: value) - end - end - shared_examples 'range type' do let(:rel) { pg_ranges.select { [name] } } @@ -106,10 +85,33 @@ def create_ranges_table(db_type, values) let(:pg_ranges) { relations[:pg_ranges] } let(:range_value) { ROM::SQL::Postgres::Values::Range } - before do + setup_tables do conn.extension(:pg_range) conn.drop_table?(:pg_ranges) - create_ranges_table(db_type, values) + + ctx = self + conn.create_table :pg_ranges do + primary_key :id + text :name + + send(ctx.db_type, :range) + end + end + + setup_relations do + conf.relation(:pg_ranges) do + schema(:pg_ranges, infer: true) + end + + conf.commands(:pg_ranges) do + define(:create) + end + end + + seed do + values.each do |key, value| + commands[:pg_ranges].create.(name: key.to_s, range: value) + end end describe 'numrange' do diff --git a/spec/extensions/postgres/attribute_spec.rb b/spec/extensions/postgres/attribute_spec.rb index 6f1f8f65..0ac81917 100644 --- a/spec/extensions/postgres/attribute_spec.rb +++ b/spec/extensions/postgres/attribute_spec.rb @@ -3,10 +3,12 @@ RSpec.describe 'ROM::SQL::Attribute', :postgres do include_context 'database setup' - before do + setup_tables do conn.drop_table?(:pg_people) conn.drop_table?(:people) + end + setup_relations do conf.relation(:people) do schema(:pg_people, infer: true) end @@ -17,18 +19,22 @@ %i[json jsonb].each do |type| describe "using arrays in #{type}" do - before do + setup_tables do conn.create_table :pg_people do primary_key :id String :name column :fields, type end + end + setup_relations do conf.commands(:people) do define(:create) define(:update) end + end + seed do create_person.( name: 'John Doe', fields: [ @@ -100,18 +106,22 @@ next unless type == :jsonb describe "using maps in #{type}" do - before do + setup_tables do conn.create_table :pg_people do primary_key :id String :name column :data, type end + end + setup_relations do conf.commands(:people) do define(:create) define(:update) end + end + seed do create_person.(name: 'John Doe', data: { age: 30, height: 180 }) create_person.(name: 'Jade Doe', data: { age: 25 }) end @@ -160,19 +170,23 @@ end describe 'using array types' do - before do + setup_tables do conn.create_table :pg_people do primary_key :id String :name column :emails, 'text[]' column :bigids, 'bigint[]' end + end + setup_relations do conf.commands(:people) do define(:create) define(:update) end + end + seed do create_person.(name: 'John Doe', emails: %w[john@doe.com john@example.com], bigids: [84]) create_person.(name: 'Jade Doe', emails: %w[jade@hotmail.com], bigids: [42]) end @@ -253,7 +267,7 @@ end describe 'using ltree types' do - before do + setup_tables do conn.execute('create extension if not exists ltree') conn.create_table :pg_people do @@ -262,12 +276,16 @@ column :ltree_tags, :ltree column :parents_tags, 'ltree[]', default: [] end + end + setup_relations do conf.commands(:people) do define(:create) define(:update) end + end + seed do create_person.(name: 'John Wilkson', ltree_tags: ltree('Bottom'), parents_tags: [ltree('Top'), ltree('Top.Building')]) create_person.(name: 'John Wayne', ltree_tags: ltree('Bottom.Countries'), parents_tags: [ltree('Left'), ltree('Left.Parks')]) create_person.(name: 'John Fake', ltree_tags: ltree('Bottom.Cities'), parents_tags: [ltree('Top.Building.EmpireState'), ltree('Top.Building.EmpireState.381')]) diff --git a/spec/extensions/postgres/integration_spec.rb b/spec/extensions/postgres/integration_spec.rb index 3e5fa259..cb348e87 100644 --- a/spec/extensions/postgres/integration_spec.rb +++ b/spec/extensions/postgres/integration_spec.rb @@ -3,20 +3,22 @@ RSpec.describe 'PostgreSQL extension', :postgres do include_context 'database setup' - before do + setup_tables do conn.drop_table?(:pg_people) conn.drop_table?(:people) end context 'with arrays' do - before do + setup_tables do conn.create_table :pg_people do primary_key :id String :name column :tags, 'text[]' column :allowed_subnets, 'cidr[]' end + end + setup_relations do conf.relation(:people) do schema(:pg_people, infer: true) end @@ -85,13 +87,15 @@ end context 'with jsonb' do - before do + setup_tables do conn.create_table :pg_people do primary_key :id String :name column :attributes, 'jsonb' end + end + setup_relations do conf.relation(:people) do schema(:pg_people, infer: true) end diff --git a/spec/integration/associations/many_to_many/custom_fks_spec.rb b/spec/integration/associations/many_to_many/custom_fks_spec.rb index d99bda0b..4e3a3ade 100644 --- a/spec/integration/associations/many_to_many/custom_fks_spec.rb +++ b/spec/integration/associations/many_to_many/custom_fks_spec.rb @@ -15,7 +15,7 @@ let(:puzzle_solvers) { relations[:puzzle_solvers] } with_adapters do - before do + setup_tables do conn.create_table(:puzzles) do primary_key :id column :text, String, null: false @@ -26,7 +26,9 @@ foreign_key :puzzle_id, :puzzles, null: false primary_key [:solver_id, :puzzle_id] end + end + setup_relations do conf.relation(:puzzles) { schema(infer: true) } conf.relation(:puzzle_solvers) do @@ -46,7 +48,9 @@ end end end + end + seed do p1_id = relations[:puzzles].insert(text: 'P1') p2_id = relations[:puzzles].insert(text: 'P2') p3_id = relations[:puzzles].insert(text: 'P3') diff --git a/spec/integration/associations/many_to_many/from_view_spec.rb b/spec/integration/associations/many_to_many/from_view_spec.rb index f334cd15..226a7838 100644 --- a/spec/integration/associations/many_to_many/from_view_spec.rb +++ b/spec/integration/associations/many_to_many/from_view_spec.rb @@ -15,7 +15,7 @@ let(:puzzle_solvers) { relations[:puzzle_solvers] } with_adapters do - before do + setup_tables do conn.create_table(:puzzles) do primary_key :id column :text, String, null: false @@ -27,7 +27,9 @@ foreign_key :puzzle_id, :puzzles, null: false primary_key [:user_id, :puzzle_id] end + end + setup_relations do conf.relation(:puzzles) do schema(infer: true) @@ -54,7 +56,9 @@ end end end + end + seed do p1_id = relations[:puzzles].insert(text: 'P1') p2_id = relations[:puzzles].insert(text: 'P2', solved: true) p3_id = relations[:puzzles].insert(text: 'P3') diff --git a/spec/integration/associations/many_to_many/self_ref_spec.rb b/spec/integration/associations/many_to_many/self_ref_spec.rb index 463be73e..3c560dd9 100644 --- a/spec/integration/associations/many_to_many/self_ref_spec.rb +++ b/spec/integration/associations/many_to_many/self_ref_spec.rb @@ -16,7 +16,7 @@ end with_adapters do - before do + setup_tables(hr: :db) do conn.create_table :employees do primary_key :id, Integer column :name, String @@ -27,7 +27,9 @@ foreign_key :manager_id, :employees foreign_key :participant_id, :employees end + end + setup_relations do conf.relation(:employees) do schema(:employees, infer: true) do associations do @@ -46,17 +48,19 @@ end end + seed do + jane = employees.insert(name: 'Jane') + fred = employees.insert(name: 'Fred') + + positions.insert(manager_id: jane, participant_id: fred) + end + after do conn.drop_table?(:positions) conn.drop_table?(:employees) end it 'preloads self-referenced tuples' do - jane = employees.insert(name: 'Jane') - fred = employees.insert(name: 'Fred') - - positions.insert(manager_id: jane, participant_id: fred) - expect(assoc.().to_a).to eql([{ id: 1, name: 'Jane', participant_id: 2 }]) end end diff --git a/spec/integration/associations/many_to_many_spec.rb b/spec/integration/associations/many_to_many_spec.rb index 3f41f98f..025edc4b 100644 --- a/spec/integration/associations/many_to_many_spec.rb +++ b/spec/integration/associations/many_to_many_spec.rb @@ -11,26 +11,6 @@ let(:tags) { relations[:tags] } - before do - conf.relation(:task_tags) do - schema(infer: true) do - associations do - belongs_to :task - belongs_to :tag - end - end - end - - conf.relation(:tasks) do - schema(infer: true) do - associations do - has_many :task_tags - has_many :tags, through: :task_tags - end - end - end - end - describe '#result' do specify { expect(assoc.result).to be(:many) } end @@ -105,13 +85,15 @@ inferrable_relations.push(:users_tasks) end - before do + setup_tables do conn.create_table(:users_tasks) do foreign_key :user_id, :users foreign_key :task_id, :tasks primary_key [:user_id, :task_id] end + end + setup_relations do conf.relation(:users) do schema(infer: true) do associations do diff --git a/spec/integration/associations/many_to_one/custom_fks_spec.rb b/spec/integration/associations/many_to_one/custom_fks_spec.rb index da7f2b62..02fb9310 100644 --- a/spec/integration/associations/many_to_one/custom_fks_spec.rb +++ b/spec/integration/associations/many_to_one/custom_fks_spec.rb @@ -13,7 +13,7 @@ let(:assoc_to) { relations[:flights].associations[:to] } with_adapters do - before do + setup_tables do conn.create_table(:destinations) do primary_key :id column :name, String, null: false @@ -25,7 +25,9 @@ foreign_key :to_id, :destinations, null: false column :code, String, null: false end + end + setup_relations do conf.relation(:destinations) { schema(infer: true) } conf.relation(:flights) do @@ -36,10 +38,11 @@ end end end + end + seed do from_id = relations[:destinations].insert(name: 'FROM') to_id = relations[:destinations].insert(name: 'TO') - relations[:flights].insert(code: 'F1', from_id: from_id, to_id: to_id) end diff --git a/spec/integration/associations/many_to_one/from_view_spec.rb b/spec/integration/associations/many_to_one/from_view_spec.rb index de7cf4eb..2e6da0eb 100644 --- a/spec/integration/associations/many_to_one/from_view_spec.rb +++ b/spec/integration/associations/many_to_one/from_view_spec.rb @@ -13,7 +13,7 @@ let(:assoc_final) { relations[:flights].associations[:final_destination] } with_adapters do - before do + setup_tables do conn.create_table(:destinations) do primary_key :id column :name, String, null: false @@ -25,7 +25,9 @@ foreign_key :destination_id, :destinations, null: false column :code, String, null: false end + end + setup_relations do conf.relation(:destinations) do schema(infer: true) @@ -46,7 +48,9 @@ end end end + end + seed do final_id = relations[:destinations].insert(name: 'Final') inter_id = relations[:destinations].insert(name: 'Intermediate', intermediate: true) @@ -55,8 +59,8 @@ end after do - conn.drop_table(:flights) - conn.drop_table(:destinations) + conn.drop_table?(:flights) + conn.drop_table?(:destinations) end it 'prepares joined relations using custom view in target relation' do diff --git a/spec/integration/associations/many_to_one/self_ref_spec.rb b/spec/integration/associations/many_to_one/self_ref_spec.rb index bc4f3b01..f768a982 100644 --- a/spec/integration/associations/many_to_one/self_ref_spec.rb +++ b/spec/integration/associations/many_to_one/self_ref_spec.rb @@ -10,13 +10,15 @@ include_context 'database setup' with_adapters do - before do + setup_tables do conn.create_table(:categories) do primary_key :id foreign_key :parent_id, :categories, null: true column :name, String, null: false end + end + setup_relations do conf.relation(:categories) do schema(infer: true) do associations do @@ -24,7 +26,9 @@ end end end + end + seed do p1_id = relations[:categories].insert(name: 'P1') p2_id = relations[:categories].insert(name: 'P2') relations[:categories].insert(name: 'C3', parent_id: p2_id) diff --git a/spec/integration/associations/many_to_one_spec.rb b/spec/integration/associations/many_to_one_spec.rb index a691bcbc..d3b635f0 100644 --- a/spec/integration/associations/many_to_one_spec.rb +++ b/spec/integration/associations/many_to_one_spec.rb @@ -10,7 +10,7 @@ build_assoc(:many_to_one, :tasks, :users) end - before do + setup_relations do conf.relation(:tasks) do schema(infer: true) end @@ -86,7 +86,7 @@ build_assoc(:many_to_one, articles_name, :users) end - before do + setup_relations do conf.relation(:articles) do schema(:posts, infer: true) end diff --git a/spec/integration/associations/one_to_many/custom_fks_spec.rb b/spec/integration/associations/one_to_many/custom_fks_spec.rb index ef14b8e0..becd9a5f 100644 --- a/spec/integration/associations/one_to_many/custom_fks_spec.rb +++ b/spec/integration/associations/one_to_many/custom_fks_spec.rb @@ -14,14 +14,16 @@ end with_adapters do - before do + setup_tables do conn.create_table(:puzzles) do primary_key :id foreign_key :author_id, :users, null: false foreign_key :solver_id, :users, null: true column :text, String, null: false end + end + setup_relations do conf.relation(:puzzles) { schema(infer: true) } conf.relation(:users) do @@ -32,7 +34,9 @@ end end end + end + seed do relations[:puzzles].insert(author_id: joe_id, text: 'P1') relations[:puzzles].insert(author_id: joe_id, solver_id: jane_id, text: 'P2') end diff --git a/spec/integration/associations/one_to_many/from_view_spec.rb b/spec/integration/associations/one_to_many/from_view_spec.rb index 459989bb..1e41b83c 100644 --- a/spec/integration/associations/one_to_many/from_view_spec.rb +++ b/spec/integration/associations/one_to_many/from_view_spec.rb @@ -14,14 +14,16 @@ end with_adapters do - before do + setup_tables do conn.create_table(:puzzles) do primary_key :id foreign_key :user_id, :users, null: false column :text, String, null: false column :solved, TrueClass, null: false, default: false end + end + setup_relations do conf.relation(:users) do schema(infer: true) do associations do @@ -38,7 +40,9 @@ where(solved: true) end end + end + seed do relations[:puzzles].insert(user_id: joe_id, text: 'P1') relations[:puzzles].insert(user_id: joe_id, solved: true, text: 'P2') end diff --git a/spec/integration/associations/one_to_many/self_ref_spec.rb b/spec/integration/associations/one_to_many/self_ref_spec.rb index b55e3d39..40451a2e 100644 --- a/spec/integration/associations/one_to_many/self_ref_spec.rb +++ b/spec/integration/associations/one_to_many/self_ref_spec.rb @@ -14,13 +14,15 @@ end with_adapters do - before do + setup_tables do conn.create_table(:categories) do primary_key :id foreign_key :parent_id, :categories, null: true column :name, String, null: false end + end + setup_relations do conf.relation(:categories) do schema(infer: true) do associations do @@ -29,7 +31,9 @@ end end end + end + seed do p1_id = relations[:categories].insert(name: 'P1') p2_id = relations[:categories].insert(name: 'P2') relations[:categories].insert(name: 'C3', parent_id: p2_id) diff --git a/spec/integration/associations/one_to_many_spec.rb b/spec/integration/associations/one_to_many_spec.rb index 12c447e0..450b7f2e 100644 --- a/spec/integration/associations/one_to_many_spec.rb +++ b/spec/integration/associations/one_to_many_spec.rb @@ -8,7 +8,7 @@ end with_adapters do - before do + setup_relations do conf.relation(:tasks) do schema do attribute :id, ROM::SQL::Types::Serial diff --git a/spec/integration/associations/one_to_one_spec.rb b/spec/integration/associations/one_to_one_spec.rb index d8bf750a..6d3b9bde 100644 --- a/spec/integration/associations/one_to_one_spec.rb +++ b/spec/integration/associations/one_to_one_spec.rb @@ -9,9 +9,7 @@ end with_adapters do - before do - conn[:accounts].insert user_id: 1, number: '43', balance: -273.15.to_d - + setup_relations do conf.relation(:accounts) do schema do attribute :id, ROM::SQL::Types::Serial @@ -22,6 +20,10 @@ end end + seed do + conn[:accounts].insert user_id: 1, number: '43', balance: -273.15.to_d + end + describe '#result' do specify { expect(assoc.result).to be(:one) } end diff --git a/spec/integration/associations/one_to_one_through_spec.rb b/spec/integration/associations/one_to_one_through_spec.rb index 36eba30a..63055f0e 100644 --- a/spec/integration/associations/one_to_one_through_spec.rb +++ b/spec/integration/associations/one_to_one_through_spec.rb @@ -9,7 +9,7 @@ end with_adapters do - before do + setup_relations do conf.relation(:accounts) do schema do attribute :id, ROM::SQL::Types::Serial diff --git a/spec/integration/auto_migrations/errors_spec.rb b/spec/integration/auto_migrations/errors_spec.rb index cd76071d..f067203d 100644 --- a/spec/integration/auto_migrations/errors_spec.rb +++ b/spec/integration/auto_migrations/errors_spec.rb @@ -5,12 +5,12 @@ subject(:gateway) { container.gateways[:default] } - before do + setup_tables do conn.drop_table?(:users) end describe 'unsupported conversions' do - before do + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial @@ -19,12 +19,14 @@ end end - it 'raises an error' do + setup_tables do conn.create_table :users do primary_key :id column :name, Integer, null: false end + end + it 'raises an error' do expect { gateway.auto_migrate!(conf) }.to raise_error(ROM::SQL::UnsupportedConversion, /Don't know how to convert/) diff --git a/spec/integration/auto_migrations/file_based_migrations_spec.rb b/spec/integration/auto_migrations/file_based_migrations_spec.rb index 30a6833b..6aaa42e2 100644 --- a/spec/integration/auto_migrations/file_based_migrations_spec.rb +++ b/spec/integration/auto_migrations/file_based_migrations_spec.rb @@ -12,7 +12,7 @@ let(:options) { { path: path } } - before do + setup_tables do conn.drop_table?(:posts) conn.drop_table?(:users) conn.drop_table?(:schema_migrations) @@ -25,7 +25,7 @@ def migrations end context 'creating from scratch' do - before do + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial @@ -61,13 +61,15 @@ def migrations context 'alter table' do context 'changing columns' do - before do + setup_tables do conn.create_table(:users) do primary_key :id column :name, String column :age, Integer end + end + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial @@ -105,12 +107,14 @@ def migrations end context 'managing foreign keys' do - before do + setup_tables do conn.create_table(:users) do primary_key :id column :name, String, null: false end + end + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial diff --git a/spec/integration/auto_migrations/foreign_keys_spec.rb b/spec/integration/auto_migrations/foreign_keys_spec.rb index e61ec0f2..5178c413 100644 --- a/spec/integration/auto_migrations/foreign_keys_spec.rb +++ b/spec/integration/auto_migrations/foreign_keys_spec.rb @@ -3,7 +3,7 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do include_context 'database setup' - before do + setup_tables do conn.drop_table?(:posts) conn.drop_table?(:users) end @@ -24,13 +24,17 @@ let(:attributes) { migrated_schema.to_a } + before { container } + describe 'create table' do - before do + setup_tables do conn.create_table(:users) do primary_key :id column :name, String, null: false end + end + setup_relations do conf.relation(:posts) do schema do attribute :id, ROM::SQL::Types::Serial @@ -58,7 +62,7 @@ describe 'alter table' do context 'adding' do - before do + setup_tables do conn.create_table(:users) do primary_key :id column :name, String, null: false @@ -68,7 +72,9 @@ primary_key :id column :user_id, Integer, null: false end + end + setup_relations do conf.relation(:posts) do schema do attribute :id, ROM::SQL::Types::Serial @@ -96,7 +102,7 @@ end context 'removing' do - before do + setup_tables do conn.create_table(:users) do primary_key :id column :name, String, null: false @@ -106,7 +112,9 @@ primary_key :id foreign_key :user_id, :users end + end + setup_relations do conf.relation(:posts) do schema do attribute :id, ROM::SQL::Types::Serial diff --git a/spec/integration/auto_migrations/indexes_spec.rb b/spec/integration/auto_migrations/indexes_spec.rb index 9100d227..3367c70c 100644 --- a/spec/integration/auto_migrations/indexes_spec.rb +++ b/spec/integration/auto_migrations/indexes_spec.rb @@ -3,7 +3,7 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do include_context 'database setup' - before do + setup_tables do conn.drop_table?(:users) end @@ -31,7 +31,7 @@ def indexdef(index) describe 'create table' do describe 'one-column indexes' do - before do + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial @@ -50,11 +50,11 @@ def indexdef(index) expect(attributes.map(&:to_ast)).to eql([ [:attribute, [:id, - [:nominal, [Integer, {}]], + [:nominal, [Integer, {}, {}]], primary_key: true, source: :users, alias: nil]], [:attribute, [:name, - [:nominal, [String, {}]], + [:nominal, [String, {}, {}]], index: true, source: :users, alias: nil]] ]) @@ -73,170 +73,195 @@ def indexdef(index) describe 'alter table' do describe 'one-column indexes' do context 'adding' do - before do - end - - it 'adds indexed column' do - conn.create_table :users do - primary_key :id + context 'adding indexed column' do + setup_tables do + conn.create_table :users do + primary_key :id + end end - conf.relation(:users) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :name, ROM::SQL::Types::String.meta(index: true) + setup_relations do + conf.relation(:users) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :name, ROM::SQL::Types::String.meta(index: true) + end end end - gateway.auto_migrate!(conf, inline: true) + it 'adds indexed column' do + gateway.auto_migrate!(conf, inline: true) - name_index = migrated_schema.indexes.first + name_index = migrated_schema.indexes.first - expect(migrated_schema.attributes[1].name).to eql(:name) - expect(migrated_schema.indexes.size).to eql(1) - expect(name_index.name).to eql(:users_name_index) - expect(name_index.attributes.map(&:name)).to eql(%i[name]) + expect(migrated_schema.attributes[1].name).to eql(:name) + expect(migrated_schema.indexes.size).to eql(1) + expect(name_index.name).to eql(:users_name_index) + expect(name_index.attributes.map(&:name)).to eql(%i[name]) + end end - it 'supports custom names' do - conn.create_table :users do - primary_key :id + context 'adding index with custom name' do + setup_tables do + conn.create_table :users do + primary_key :id + end end - conf.relation(:users) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :name, ROM::SQL::Types::String + setup_relations do + conf.relation(:users) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :name, ROM::SQL::Types::String - indexes do - index :name, name: :custom_idx + indexes do + index :name, name: :custom_idx + end end end end - gateway.auto_migrate!(conf, inline: true) + it 'supports custom names' do + gateway.auto_migrate!(conf, inline: true) - name_index = migrated_schema.indexes.first + name_index = migrated_schema.indexes.first - expect(migrated_schema.attributes[1].name).to eql(:name) - expect(migrated_schema.indexes.size).to eql(1) - expect(name_index.name).to eql(:custom_idx) - expect(name_index.attributes.map(&:name)).to eql(%i[name]) + expect(migrated_schema.attributes[1].name).to eql(:name) + expect(migrated_schema.indexes.size).to eql(1) + expect(name_index.name).to eql(:custom_idx) + expect(name_index.attributes.map(&:name)).to eql(%i[name]) + end end - it 'adds index to existing column' do - conn.create_table :users do - primary_key :id - column :name, String + context 'adding index to existing column' do + setup_tables do + conn.create_table :users do + primary_key :id + column :name, String + end end - conf.relation(:users) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :name, ROM::SQL::Types::String + setup_relations do + conf.relation(:users) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :name, ROM::SQL::Types::String - indexes do - index :name + indexes do + index :name + end end end end - gateway.auto_migrate!(conf, inline: true) + it 'adds index to existing column' do + gateway.auto_migrate!(conf, inline: true) - name_index = migrated_schema.indexes.first + name_index = migrated_schema.indexes.first - expect(name_index.name).to eql(:users_name_index) - expect(name_index.attributes.map(&:name)).to eql(%i[name]) - expect(name_index).not_to be_unique + expect(name_index.name).to eql(:users_name_index) + expect(name_index.attributes.map(&:name)).to eql(%i[name]) + expect(name_index).not_to be_unique + end end - it 'supports unique indexes' do - conn.create_table :users do - primary_key :id - column :name, String + context 'adding unique index' do + setup_tables do + conn.create_table :users do + primary_key :id + column :name, String + end end - conf.relation(:users) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :name, ROM::SQL::Types::String + setup_relations do + conf.relation(:users) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :name, ROM::SQL::Types::String - indexes do - index :name, unique: true + indexes do + index :name, unique: true + end end end end - gateway.auto_migrate!(conf, inline: true) + it 'supports unique indexes' do + gateway.auto_migrate!(conf, inline: true) - name_index = migrated_schema.indexes.first + name_index = migrated_schema.indexes.first - expect(name_index.name).to eql(:users_name_index) - expect(name_index.attributes.map(&:name)).to eql(%i[name]) - expect(name_index).to be_unique + expect(name_index.name).to eql(:users_name_index) + expect(name_index.attributes.map(&:name)).to eql(%i[name]) + expect(name_index).to be_unique + end end if metadata[:postgres] - it 'uses index method' do - conn.create_table :users do - primary_key :id - column :props, :jsonb, null: false + context 'using index method' do + setup_tables do + conn.create_table :users do + primary_key :id + column :props, :jsonb, null: false + end end - conf.relation(:users) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :props, ROM::SQL::Types::PG::JSONB + setup_relations do + conf.relation(:users) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :props, ROM::SQL::Types::PG::JSONB - indexes do - index :props, type: :gin + indexes do + index :props, type: :gin + end end end end - gateway.auto_migrate!(conf, inline: true) + it 'uses index method' do + gateway.auto_migrate!(conf, inline: true) - expect(indexdef('users_props_index')).to eql( - 'CREATE INDEX users_props_index ON public.users USING gin (props)' - ) + expect(indexdef('users_props_index')).to eql( + 'CREATE INDEX users_props_index ON public.users USING gin (props)' + ) + end end - it 'supports partial indexes' do - conn.create_table :users do - primary_key :id - column :name, String + context 'supports partial indexes' do + setup_tables do + conn.create_table :users do + primary_key :id + column :name, String + end end - conf.relation(:users) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :name, ROM::SQL::Types::String + setup_relations do + conf.relation(:users) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :name, ROM::SQL::Types::String - indexes do - index :name, name: :long_names_only, predicate: 'length(name) > 10' + indexes do + index :name, name: :long_names_only, predicate: 'length(name) > 10' + end end end end - gateway.auto_migrate!(conf, inline: true) + it 'supports partial indexes' do + gateway.auto_migrate!(conf, inline: true) - expect(indexdef('long_names_only')).to eql( - 'CREATE INDEX long_names_only ON public.users USING btree (name) WHERE (length(name) > 10)' - ) + expect(indexdef('long_names_only')).to eql( + 'CREATE INDEX long_names_only ON public.users USING btree (name) WHERE (length(name) > 10)' + ) + end end end end context 'removing' do - before do - conf.relation(:users) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :name, ROM::SQL::Types::String - attribute :email, ROM::SQL::Types::String - end - end - + setup_tables do conn.create_table :users do primary_key :id column :name, String @@ -247,6 +272,16 @@ def indexdef(index) end end + setup_relations do + conf.relation(:users) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :name, ROM::SQL::Types::String + attribute :email, ROM::SQL::Types::String + end + end + end + it 'removes index' do gateway.auto_migrate!(conf, inline: true) diff --git a/spec/integration/auto_migrations/managing_columns_spec.rb b/spec/integration/auto_migrations/managing_columns_spec.rb index be92e53e..abfc195c 100644 --- a/spec/integration/auto_migrations/managing_columns_spec.rb +++ b/spec/integration/auto_migrations/managing_columns_spec.rb @@ -3,11 +3,11 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do include_context 'database setup' - before do + setup_tables do conn.drop_table?(:users) end - before do + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial @@ -37,16 +37,16 @@ expect(attributes.map(&:to_ast)).to eql([ [:attribute, [:id, - [:nominal, [Integer, {}]], + [:nominal, [Integer, {}, {}]], primary_key: true, source: :users, alias: nil]], - [:attribute, [:name, [:nominal, [String, {}]], source: :users, alias: nil]], + [:attribute, [:name, [:nominal, [String, {}, {}]], source: :users, alias: nil]], [:attribute, [:email, [:sum, [[:constrained, - [[:nominal, [NilClass, {}]], + [[:nominal, [NilClass, {}, {}]], [:predicate, [:type?, [[:type, NilClass], [:input, ROM::Undefined]]]]]], - [:nominal, [String, {}]], + [:nominal, [String, {}, {}]], {}]], source: :users, alias: nil]] ]) @@ -54,7 +54,7 @@ end describe 'adding columns' do - before do + setup_tables do conn.create_table :users do primary_key :id end @@ -64,16 +64,16 @@ gateway.auto_migrate!(conf, inline: true) expect(attributes[1].to_ast).to eql( - [:attribute, [:name, [:nominal, [String, {}]], source: :users, alias: nil]] + [:attribute, [:name, [:nominal, [String, {}, {}]], source: :users, alias: nil]] ) expect(attributes[2].to_ast).to eql( [:attribute, [:email, [:sum, [[:constrained, - [[:nominal, [NilClass, {}]], + [[:nominal, [NilClass, {}, {}]], [:predicate, [:type?, [[:type, NilClass], [:input, ROM::Undefined]]]]]], - [:nominal, [String, {}]], + [:nominal, [String, {}, {}]], {}]], source: :users, alias: nil]] ) @@ -81,7 +81,7 @@ end describe 'removing columns' do - before do + setup_tables do conn.create_table :users do primary_key :id column :name, String, null: false @@ -98,7 +98,7 @@ end describe 'empty diff' do - before do + setup_tables do conn.create_table :users do primary_key :id column :name, String, null: false @@ -117,7 +117,7 @@ describe 'changing NOTNULL' do describe 'adding' do - before do + setup_tables do conn.create_table :users do primary_key :id column :name, String @@ -134,7 +134,7 @@ end describe 'removing' do - before do + setup_tables do conn.create_table :users do primary_key :id column :name, String, null: false diff --git a/spec/integration/auto_migrations/postgres/column_types_spec.rb b/spec/integration/auto_migrations/postgres/column_types_spec.rb index c91e64d0..cdb729f7 100644 --- a/spec/integration/auto_migrations/postgres/column_types_spec.rb +++ b/spec/integration/auto_migrations/postgres/column_types_spec.rb @@ -3,7 +3,7 @@ RSpec.describe ROM::SQL::Gateway, :postgres, :helpers do include_context 'database setup' - before do + setup_tables do conn.drop_table?(:test_pg_types) conn.execute('create extension if not exists hstore') @@ -24,7 +24,7 @@ end describe 'common types' do - before do + setup_relations do conf.relation(:test_pg_types) do schema do attribute :id, ROM::SQL::Types::Serial diff --git a/spec/integration/combine_with_spec.rb b/spec/integration/combine_with_spec.rb index 75b90ac6..d59b3453 100644 --- a/spec/integration/combine_with_spec.rb +++ b/spec/integration/combine_with_spec.rb @@ -4,7 +4,7 @@ include_context 'users and tasks' with_adapters do - before do + setup_relations do conf.relation(:users) do auto_map false @@ -47,7 +47,7 @@ def for_tasks(tasks) include_context 'articles' with_adapters do - before do + setup_relations do conf.relation(:users) do schema infer: true do associations do diff --git a/spec/integration/commands/create_spec.rb b/spec/integration/commands/create_spec.rb index 0660f83f..e8bc45f5 100644 --- a/spec/integration/commands/create_spec.rb +++ b/spec/integration/commands/create_spec.rb @@ -12,7 +12,7 @@ let(:create_task) { task_commands.create } let(:create_profile) { profile_commands.create } - before do |ex| + setup_relations do module Test class Params < Dry::Struct attribute :name, Types::Strict::String.optional @@ -22,7 +22,9 @@ def self.[](input) end end end + end + setup_tables do |ex| conn.add_index :users, :name, unique: true if sqlite?(ex) @@ -30,7 +32,9 @@ def self.[](input) else conn.execute 'ALTER TABLE tasks add CONSTRAINT tasks_title_key UNIQUE (title)' end + end + setup_relations do conf.relation(:profiles) do schema(:users, infer: true) do attribute :name, Types::String, alias: :login @@ -146,25 +150,29 @@ def self.[](input) end end - it 'uses relation schema for the default input handler' do - conf.relation(:users_with_schema) do - schema(:users) do - attribute :id, ROM::SQL::Types::Serial - attribute :name, ROM::SQL::Types::String + context 'when using relation schema for the default input handler' do + setup_relations do + conf.relation(:users_with_schema) do + schema(:users) do + attribute :id, ROM::SQL::Types::Serial + attribute :name, ROM::SQL::Types::String + end end - end - conf.commands(:users_with_schema) do - define(:create) do - result :one + conf.commands(:users_with_schema) do + define(:create) do + result :one + end end end - create = container.commands[:users_with_schema][:create] + specify do + create = container.commands[:users_with_schema][:create] - expect(create.input[foo: 'bar', id: 1, name: 'Jane']).to eql( - id: 1, name: 'Jane' - ) + expect(create.input[foo: 'bar', id: 1, name: 'Jane']).to eql( + id: 1, name: 'Jane' + ) + end end it 'returns a single tuple when result is set to :one' do @@ -190,7 +198,7 @@ def self.[](input) context 'with json notes' do include_context 'json_notes' - before do + setup_relations do conf.commands(:json_notes) do define(:create) end @@ -208,7 +216,7 @@ def self.[](input) context 'with puppies' do include_context 'puppies' - before do + setup_relations do conf.relation(:puppies) do schema(infer: true) end @@ -277,13 +285,15 @@ def self.[](input) inferrable_relations.push(:user_group) end - before do + setup_tables do conn.create_table(:user_group) do primary_key [:user_id, :group_id] column :user_id, Integer, null: false column :group_id, Integer, null: false end + end + setup_relations do conf.relation(:user_group) do schema(infer: true) end @@ -322,7 +332,7 @@ def self.[](input) describe '#upsert' do let(:task) { { title: 'task 1' } } - before { create_task.call(task) } + seed { create_task.call(task) } it 'raises error without upsert marker' do expect { diff --git a/spec/integration/commands/delete_spec.rb b/spec/integration/commands/delete_spec.rb index 8b32a754..eb88b735 100644 --- a/spec/integration/commands/delete_spec.rb +++ b/spec/integration/commands/delete_spec.rb @@ -1,12 +1,25 @@ # frozen_string_literal: true RSpec.describe 'Commands / Delete' do - include_context 'users and tasks' + include_context 'database setup' let(:delete_user) { user_commands.delete } + let(:user_commands) { container.commands[:users] } + with_adapters do - before do + setup_tables do + conn.drop_table?(:users) + + conn.create_table(:users) do + primary_key :id + String :name, null: false + end + end + + let(:users) { container.relations[:users] } + + setup_relations do conf.relation(:users) do def by_name(name) where(name: name) @@ -18,7 +31,9 @@ def by_name(name) result :one end end + end + seed do users.insert(id: 3, name: 'Jade') users.insert(id: 4, name: 'John') end diff --git a/spec/integration/commands/update_spec.rb b/spec/integration/commands/update_spec.rb index 0905d963..7d1cca78 100644 --- a/spec/integration/commands/update_spec.rb +++ b/spec/integration/commands/update_spec.rb @@ -14,7 +14,7 @@ let(:peter) { { name: 'Peter' } } with_adapters do - before do + setup_relations do Test::User = Class.new(Dry::Struct) { attribute :id, Types::Strict::Integer attribute :name, Types::Strict::String @@ -53,7 +53,9 @@ def by_name(name) conf.mappers do register :users, entity: -> tuples { tuples.map { |tuple| Test::User.new(tuple) } } end + end + seed do users.insert(name: 'Piotr') users.insert(name: 'Jane') end @@ -114,7 +116,7 @@ def by_name(name) context 'with json notes' do include_context 'json_notes' - before do + setup_relations do conf.commands(:json_notes) do define(:update) end diff --git a/spec/integration/commands/upsert_spec.rb b/spec/integration/commands/upsert_spec.rb index a4a59ab2..71ab7c2c 100644 --- a/spec/integration/commands/upsert_spec.rb +++ b/spec/integration/commands/upsert_spec.rb @@ -5,9 +5,11 @@ include_context 'relations' - before do + setup_tables do conn.execute 'ALTER TABLE tasks add CONSTRAINT tasks_title_key UNIQUE (title)' + end + seed do conn[:users].insert id: 1, name: 'Jane' conn[:users].insert id: 2, name: 'Joe' conn[:users].insert id: 3, name: 'Jean' @@ -17,7 +19,7 @@ let(:task) { { title: 'task 1', user_id: 1 } } let(:excluded) { task.merge(user_id: 3) } - before do + setup_relations do command_config = self.command_config conf.commands(:tasks) do @@ -30,7 +32,7 @@ end end - before { command.relation.upsert(task) } + seed { command.relation.upsert(task) } context 'on conflict do nothing' do let(:command_config) { -> {} } @@ -54,9 +56,9 @@ end context 'with index predicate' do - before do + setup_tables do conn.execute <<~SQL - ALTER TABLE tasks DROP CONSTRAINT tasks_title_key; + ALTER TABLE tasks DROP CONSTRAINT IF EXISTS tasks_title_key; CREATE UNIQUE INDEX tasks_title_partial_index ON tasks (title) WHERE user_id = 1; @@ -74,7 +76,7 @@ context 'when predicate matches' do let(:excluded) { task } - it 'returns updated data', :aggregate_failures do + it 'returns updated data' do expect(command.call(excluded)).to eql(id: 1, user_id: 2, title: 'task 1') end end @@ -82,7 +84,7 @@ context 'when predicate does not match' do let(:excluded) { task.update(user_id: 2) } - it 'creates new task', :aggregate_failures do + it 'creates new task' do expect(command.call(excluded)).to eql(id: 2, user_id: 2, title: 'task 1') end end diff --git a/spec/integration/gateway_spec.rb b/spec/integration/gateway_spec.rb index ba505a3a..cc489f78 100644 --- a/spec/integration/gateway_spec.rb +++ b/spec/integration/gateway_spec.rb @@ -12,7 +12,6 @@ subject(:gateway) { container.gateways[:default] } let(:conf) { ROM::Configuration.new(:sql, conn) } - let(:container) { ROM.container(conf) } it 'allows creating and running migrations' do migration = gateway.migration do @@ -49,7 +48,6 @@ let(:migrator) { ROM::SQL::Migration::Migrator.new(conn, path: migration_dir) } let(:conf) { ROM::Configuration.new(:sql, [conn, migrator: migrator]) } - let(:container) { ROM.container(conf) } it 'returns true for pending migrations' do expect(container.gateways[:default].pending_migrations?).to be_truthy @@ -75,7 +73,6 @@ end let(:conf) { ROM::Configuration.new(:sql, [conn, migrator: { path: migration_dir }]) } - let(:container) { ROM.container(conf) } it 'runs migrations from a specified directory' do container.gateways[:default].run_migrations @@ -88,7 +85,7 @@ inferrable_relations.push(:names) end - before do + setup_tables do conn.create_table(:names) do String :name end diff --git a/spec/integration/migration_spec.rb b/spec/integration/migration_spec.rb index 0ce775aa..040be837 100644 --- a/spec/integration/migration_spec.rb +++ b/spec/integration/migration_spec.rb @@ -7,9 +7,13 @@ inferrable_relations.push(:dragons, :schema_migrations) end - with_adapters do - before { conf } + setup_tables do + %i[dragons turtles].each do |table| + conn.drop_table?(table) + end + end + with_adapters do it 'creates a migration for a specific gateway' do migration = ROM::SQL.migration(container) do change do @@ -37,6 +41,12 @@ let(:in_memory_connection) { container.gateways[:in_memory].connection } + before do + %i[dragons turtles].each do |table| + in_memory_connection.drop_table?(table) + end + end + it 'creates a migration for a specific gateway' do in_memory_migration = ROM::SQL.migration(container, :in_memory) do change do diff --git a/spec/integration/plugins/associates/many_to_many_spec.rb b/spec/integration/plugins/associates/many_to_many_spec.rb index 7928d393..05d9f983 100644 --- a/spec/integration/plugins/associates/many_to_many_spec.rb +++ b/spec/integration/plugins/associates/many_to_many_spec.rb @@ -14,32 +14,7 @@ users.by_pk(users.insert(name: 'John')).one end - before do - conf.relation(:tasks) do - schema(infer: true) do - associations do - has_many :tags, through: :task_tags - end - end - end - - conf.relation(:task_tags) do - schema(infer: true) do - associations do - belongs_to :tasks, as: :task - belongs_to :tags, as: :tag - end - end - end - - conf.relation(:tags) do - schema(infer: true) do - associations do - has_many :tasks, through: :task_tags - end - end - end - + setup_relations do conf.commands(:tags) do define(:create) do result :many diff --git a/spec/integration/plugins/associates_spec.rb b/spec/integration/plugins/associates_spec.rb index 46d48135..04baa8b5 100644 --- a/spec/integration/plugins/associates_spec.rb +++ b/spec/integration/plugins/associates_spec.rb @@ -9,7 +9,7 @@ let(:tasks) { container.commands[:tasks] } let(:tags) { container.commands[:tags] } - before do + setup_relations do conf.relation(:tasks) do schema(infer: true) do associations do @@ -35,7 +35,7 @@ { title: 'Task one' } end - before do + setup_relations do conf.commands(:users) do define(:create) { result :one } end @@ -61,12 +61,28 @@ ) end - it 'allows setting up multiple associations' do - command = tasks[:create] - .with_association(:user, key: %i[user_id id], parent: user) - .with_association(:other, key: %i[other_id id]) + context 'with multiple associations' do + setup_relations do + conf.relation(:users) do + schema(infer: true) + end + + conf.relation(:task_tags) do + schema(infer: true) + end + + conf.relation(:tags) do + schema(infer: true) + end + end + + it 'allows setting up multiple associations' do + command = tasks[:create] + .with_association(:user, key: %i[user_id id], parent: user) + .with_association(:other, key: %i[other_id id]) - expect(command.configured_associations).to eql(%i[user other]) + expect(command.configured_associations).to eql(%i[user other]) + end end end @@ -102,7 +118,7 @@ context 'with a schema' do include_context 'automatic FK setting' - before do + setup_relations do conf.relation(:tasks) do schema(infer: true) do associations do @@ -128,7 +144,9 @@ end context 'with many-to-many association' do - before do + setup_relations do + conf.relation(:users) { schema(infer: true) } + conf.relation(:tags) do schema(infer: true) do associations do @@ -221,15 +239,7 @@ container.commands[:tasks][:create].call(user_id: john[:id], title: 'John Task') end - before do - conf.relation(:tasks) do - schema(infer: true) do - associations do - belongs_to :user - end - end - end - + setup_relations do conf.commands(:users) do define(:create) do result :one diff --git a/spec/integration/plugins/auto_restrictions_spec.rb b/spec/integration/plugins/auto_restrictions_spec.rb index 2bfab97e..378030ba 100644 --- a/spec/integration/plugins/auto_restrictions_spec.rb +++ b/spec/integration/plugins/auto_restrictions_spec.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -RSpec.describe 'Plugins / :auto_restrictions', seeds: true do +RSpec.describe 'Plugins / :auto_restrictions' do include_context 'users and tasks' with_adapters do - before do + setup_tables(index: :tasks) do conn.add_index :tasks, :title, unique: true end @@ -19,7 +19,7 @@ end context 'with an inferred schema' do - before do + setup_tables(plguin: :index) do conf.plugin(:sql, relations: :auto_restrictions) end @@ -37,7 +37,7 @@ two: ROM.container(confs[:two]) } end - before do + setup_relations do class Test::Tasks < ROM::Relation[:sql] schema(:tasks, infer: true) end @@ -58,7 +58,7 @@ class Test::Tasks < ROM::Relation[:sql] end context 'with explicit schema' do - before do + setup_relations do conf.relation(:tasks) do schema do attribute :id, ROM::SQL::Types::Serial @@ -74,7 +74,7 @@ class Test::Tasks < ROM::Relation[:sql] end end - include_context 'auto-generated restriction view' + # include_context 'auto-generated restriction view' it 'generates restrictrions by a composite index' do expect(tasks.by_user_id_and_title(1, "Jane's task").first).to eql(id: 2, user_id: 1, title: "Jane's task") @@ -82,25 +82,29 @@ class Test::Tasks < ROM::Relation[:sql] end if metadata[:postgres] - # An auto-generated restriction should include the prediate from the index definition - # but it seems to be too much from my POV, better leave it to the user - # Note that this can be enabled later - it 'skips partial indexes' do - conf.relation(:tasks) do - schema do - attribute :id, ROM::SQL::Types::Serial - attribute :user_id, ROM::SQL::Types::Integer - attribute :title, ROM::SQL::Types::String - - indexes do - index :title, predicate: 'title is not null' + context 'with a partial index' do + setup_relations do + conf.relation(:tasks) do + schema do + attribute :id, ROM::SQL::Types::Serial + attribute :user_id, ROM::SQL::Types::Integer + attribute :title, ROM::SQL::Types::String + + indexes do + index :title, predicate: 'title is not null' + end end - end - use :auto_restrictions + use :auto_restrictions + end end - expect(tasks).not_to respond_to(:by_title) + # An auto-generated restriction should include the prediate from the index definition + # but it seems to be too much from my POV, better leave it to the user + # Note that this can be enabled later + it 'skips partial indexes' do + expect(tasks).not_to respond_to(:by_title) + end end end end diff --git a/spec/integration/plugins/explain_spec.rb b/spec/integration/plugins/explain_spec.rb index 155dc5b0..ac5a26df 100644 --- a/spec/integration/plugins/explain_spec.rb +++ b/spec/integration/plugins/explain_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'Plugins / :explain', :postgres do include_context 'users and tasks' - before do + setup_relations do conf.plugin(:sql, relations: :pg_explain) end diff --git a/spec/integration/plugins/full_text_search_spec.rb b/spec/integration/plugins/full_text_search_spec.rb index 0ee2d594..680fc4ba 100644 --- a/spec/integration/plugins/full_text_search_spec.rb +++ b/spec/integration/plugins/full_text_search_spec.rb @@ -5,7 +5,7 @@ RSpec.describe 'Plugins / :pg_full_text_search', :postgres do include_context 'users and tasks' - before do + setup_tables do conf.plugin(:sql, relations: :pg_full_text_search) end @@ -23,16 +23,12 @@ expect(result).to contain_exactly(task) end - it 'handles complex queries' do - conf.relation(:tasks) do - schema(infer: true) do - associations { belongs_to :user } - end - end - - searched_users = users.full_text_search([:name], 'Joe', language: 'simple') - result = tasks.exists(searched_users).pluck(:id) + context 'when handling complex queries' do + specify do + searched_users = users.full_text_search([:name], 'Joe', language: 'simple') + result = tasks.exists(searched_users).pluck(:id) - expect(result).to contain_exactly(1) + expect(result).to contain_exactly(1) + end end end diff --git a/spec/integration/plugins/pg_streaming_spec.rb b/spec/integration/plugins/pg_streaming_spec.rb index 1d999e9b..4e73a531 100644 --- a/spec/integration/plugins/pg_streaming_spec.rb +++ b/spec/integration/plugins/pg_streaming_spec.rb @@ -6,7 +6,7 @@ with_adapters(:postgres) do include_context 'users and tasks' - before do + setup_tables do skip 'it is not supported by jruby' if jruby? conf.plugin(:sql, relations: :pg_streaming) end @@ -25,10 +25,8 @@ tasks.with(auto_struct: true).stream_each { |task| result << task } - aggregate_failures do - tasks.dataset.to_a.each_with_index do |task_attrs, i| - expect(result[i]).to have_attributes(task_attrs) - end + tasks.dataset.to_a.each_with_index do |task_attrs, i| + expect(result[i]).to have_attributes(task_attrs) end end diff --git a/spec/integration/relation/default_views_spec.rb b/spec/integration/relation/default_views_spec.rb index 7aa533a9..8be38560 100644 --- a/spec/integration/relation/default_views_spec.rb +++ b/spec/integration/relation/default_views_spec.rb @@ -8,13 +8,15 @@ subject(:users) { relations[:users] } context 'when dataset is overridden' do - before do + setup_tables do conn.create_table(:users) do primary_key :id column :name, String column :email, String end + end + setup_relations do conf.relation(:users) do dataset { select(:name) } schema(infer: true) diff --git a/spec/integration/relation_schema_spec.rb b/spec/integration/relation_schema_spec.rb index 51a8c901..e88862e7 100644 --- a/spec/integration/relation_schema_spec.rb +++ b/spec/integration/relation_schema_spec.rb @@ -1,14 +1,12 @@ # frozen_string_literal: true -RSpec.describe 'Inferring schema from database' do +RSpec.describe 'Inferring schema from database', relations: false do include_context 'users' include_context 'posts' with_adapters do - context 'when database schema exists' do + context 'when database schema exists', relations: true do it 'infers the schema from the database relations' do - conf.relation(:users) - expect(container.relations.users.to_a) .to eql(container.gateways[:default][:users].to_a) end @@ -20,283 +18,363 @@ end end - context 'defining associations', seeds: false do - let(:config) { TestConfiguration.new(:sql, conn) } - let(:container) { ROM.container(config) } + context 'defining associations' do + let(:conf) { TestConfiguration.new(:sql, conn) } + + setup_relations do + conf.relation(:accounts) { schema(infer: true) } + conf.relation(:cards) { schema(infer: true) } + conf.relation(:posts_tags) { schema(infer: true) } + end let(:user_associations) do - config.relation(:accounts) { schema(infer: true) } - config.relation(:cards) { schema(infer: true) } - config.register_relation(Test::Users) container.relations[:users].associations end let(:post_associations) do - config.relation(:tags) { schema(infer: true) } - config.relation(:posts_tags) { schema(infer: true) } - config.register_relation(Test::Posts) container.relations[:posts].associations end let(:tag_associations) do - config.relation(:posts) { schema(infer: true) } - config.relation(:users) { schema(infer: true) } - config.relation(:posts_tags) { schema(infer: true) } - config.register_relation(Test::Tags) container.relations[:tags].associations end - it 'allows defining a one-to-many' do - class Test::Posts < ROM::Relation[:sql] - schema(:posts) do - associations do - one_to_many :tags + context 'with posts relation' do + context 'allows defining a one-to-many' do + setup_relations do + conf.relation(:tags) { schema(infer: true) } + + class Test::Posts < ROM::Relation[:sql] + schema(:posts) do + associations do + one_to_many :tags + end + end end + + conf.register_relation(Test::Posts) end - end - assoc = ROM::Associations::Definitions::OneToMany.new(:posts, :tags) + specify do + assoc = ROM::Associations::Definitions::OneToMany.new(:posts, :tags) - expect(post_associations[:tags].definition).to eql(assoc) - end + expect(post_associations[:tags].definition).to eql(assoc) + end + end - it 'allows defining a one-to-many using has_many shortcut' do - class Test::Posts < ROM::Relation[:sql] - schema(:posts) do - associations do - has_many :tags + context 'allows defining a one-to-many using has_many shortcut' do + setup_relations do + conf.relation(:tags) { schema(infer: true) } + + class Test::Posts < ROM::Relation[:sql] + schema(:posts) do + associations do + has_many :tags + end + end end + conf.register_relation(Test::Posts) end - end - assoc = ROM::Associations::Definitions::OneToMany.new(:posts, :tags) + specify do + assoc = ROM::Associations::Definitions::OneToMany.new(:posts, :tags) - expect(post_associations[:tags].definition).to eql(assoc) + expect(post_associations[:tags].definition).to eql(assoc) + end + end end - it 'allows defining a one-to-one' do - class Test::Users < ROM::Relation[:sql] - schema(:users) do - associations do - one_to_one :accounts + context 'allows defining a one-to-one' do + setup_relations do + class Test::Users < ROM::Relation[:sql] + schema(:users) do + associations do + one_to_one :accounts + end end end + conf.register_relation(Test::Users) end - assoc = ROM::Associations::Definitions::OneToOne.new(:users, :accounts) + specify do + assoc = ROM::Associations::Definitions::OneToOne.new(:users, :accounts) - expect(user_associations[:accounts].definition).to eql(assoc) + expect(user_associations[:accounts].definition).to eql(assoc) + end end - it 'allows defining a one-to-one using has_one shortcut' do - class Test::Users < ROM::Relation[:sql] - schema(:users) do - associations do - has_one :account + context 'allows defining a one-to-one using has_one shortcut' do + setup_relations do + class Test::Users < ROM::Relation[:sql] + schema(:users) do + associations do + has_one :account + end end end + conf.register_relation(Test::Users) end - assoc = ROM::Associations::Definitions::OneToOne.new(:users, :accounts, as: :account) + specify do + assoc = ROM::Associations::Definitions::OneToOne.new(:users, :accounts, as: :account) - expect(user_associations[:account].definition).to eql(assoc) - expect(user_associations[:account].definition.target).to be_aliased + expect(user_associations[:account].definition).to eql(assoc) + expect(user_associations[:account].definition.target).to be_aliased + end end - it 'allows defining a one-to-one using has_one shortcut with an alias' do - class Test::Users < ROM::Relation[:sql] - schema(:users) do - associations do - has_one :account, as: :user_account + context 'allows defining a one-to-one using has_one shortcut with an alias' do + setup_relations do + class Test::Users < ROM::Relation[:sql] + schema(:users) do + associations do + has_one :account, as: :user_account + end end end + conf.register_relation(Test::Users) end - assoc = ROM::Associations::Definitions::OneToOne.new(:users, :accounts, as: :user_account) + specify do + assoc = ROM::Associations::Definitions::OneToOne.new(:users, :accounts, as: :user_account) - expect(user_associations[:user_account].definition).to eql(assoc) - expect(user_associations[:user_account].definition.target).to be_aliased + expect(user_associations[:user_account].definition).to eql(assoc) + expect(user_associations[:user_account].definition.target).to be_aliased + end end - it 'allows defining a one-to-one-through' do - class Test::Users < ROM::Relation[:sql] - schema(:users) do - associations do - one_to_one :cards, through: :accounts + context 'allows defining a one-to-one-through' do + setup_relations do + class Test::Users < ROM::Relation[:sql] + schema(:users) do + associations do + one_to_one :cards, through: :accounts + end end end + conf.register_relation(Test::Users) end - assoc = ROM::Associations::Definitions::OneToOneThrough.new(:users, :cards, through: :accounts) + specify do + assoc = ROM::Associations::Definitions::OneToOneThrough.new(:users, :cards, through: :accounts) - expect(user_associations[:cards].definition).to eql(assoc) + expect(user_associations[:cards].definition).to eql(assoc) + end end - it 'allows defining a many-to-one' do - class Test::Tags < ROM::Relation[:sql] - schema(:tags) do - attribute :post_id, Types::Integer + context 'allows defining a many-to-one' do + setup_relations do + conf.relation(:posts) { schema(infer: true) } + + class Test::Tags < ROM::Relation[:sql] + schema(:tags) do + attribute :post_id, Types::Integer - associations do - many_to_one :posts + associations do + many_to_one :posts + end end end + conf.register_relation(Test::Tags) end - assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts) + specify do + assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts) - expect(tag_associations[:posts].definition).to eql(assoc) + expect(tag_associations[:posts].definition).to eql(assoc) + end end - it 'allows defining a many-to-one using belongs_to shortcut' do - class Test::Tags < ROM::Relation[:sql] - schema(:tags) do - attribute :post_id, Types::Integer + context 'allows defining a many-to-one using belongs_to shortcut' do + setup_relations do + conf.relation(:posts) { schema(infer: true) } + + class Test::Tags < ROM::Relation[:sql] + schema(:tags) do + attribute :post_id, Types::Integer - associations do - belongs_to :post + associations do + belongs_to :post + end end end + conf.register_relation(Test::Tags) end - assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts, as: :post) + specify do + assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts, as: :post) - expect(tag_associations[:post].definition).to eql(assoc) + expect(tag_associations[:post].definition).to eql(assoc) + end end - it 'allows defining a many-to-one using belongs_to shortcut' do - class Test::Tags < ROM::Relation[:sql] - schema(:tags) do - attribute :post_id, Types::Integer + context 'allows defining a many-to-one using belongs_to shortcut' do + setup_relations do + conf.relation(:posts) { schema(infer: true) } - associations do - belongs_to :post, as: :post_tag + class Test::Tags < ROM::Relation[:sql] + schema(:tags) do + attribute :post_id, Types::Integer + + associations do + belongs_to :post, as: :post_tag + end end end + conf.register_relation(Test::Tags) end - assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts, as: :post_tag) + specify do + assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts, as: :post_tag) - expect(tag_associations[:post_tag].definition).to eql(assoc) + expect(tag_associations[:post_tag].definition).to eql(assoc) + end end - it 'allows defining a many-to-many' do - class Test::Posts < ROM::Relation[:sql] - schema(:posts) do - associations do - one_to_many :tags, through: :posts_tags + context 'allows defining a many-to-many' do + setup_relations do + conf.relation(:tags) { schema(infer: true) } + + class Test::Posts < ROM::Relation[:sql] + schema(:posts) do + associations do + one_to_many :tags, through: :posts_tags + end end end + conf.register_relation(Test::Posts) end - assoc = ROM::Associations::Definitions::ManyToMany.new(:posts, :tags, through: :posts_tags) + specify do + assoc = ROM::Associations::Definitions::ManyToMany.new(:posts, :tags, through: :posts_tags) - expect(post_associations[:tags].definition).to eql(assoc) + expect(post_associations[:tags].definition).to eql(assoc) + end end - it 'allows defining a many-to-one with a custom name' do - class Test::Tags < ROM::Relation[:sql] - schema(:tags) do - attribute :post_id, Types::Integer + context 'allows defining a many-to-one with a custom name' do + setup_relations do + conf.relation(:posts) { schema(infer: true) } + + class Test::Tags < ROM::Relation[:sql] + schema(:tags) do + attribute :post_id, Types::Integer - associations do - many_to_one :posts, as: :published_posts + associations do + many_to_one :posts, as: :published_posts + end end end + conf.register_relation(Test::Tags) end - assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts, as: :published_posts) + specify do + assoc = ROM::Associations::Definitions::ManyToOne.new(:tags, :posts, as: :published_posts) - expect(tag_associations[:published_posts].definition).to eql(assoc) + expect(tag_associations[:published_posts].definition).to eql(assoc) + end end - it 'adds foreign keys to the schema' do - class Test::Tags < ROM::Relation[:sql] - schema(:tags) do - attribute :id, Types::Serial - attribute :post_id, Types::ForeignKey(:posts) + context 'adds foreign keys to the schema' do + setup_relations do + class Test::Tags < ROM::Relation[:sql] + schema(:tags) do + attribute :id, Types::Serial + attribute :post_id, Types::ForeignKey(:posts) + end end - end - config.relation(:posts) { schema(infer: true) } - config.relation(:users) { schema(infer: true) } - config.register_relation(Test::Tags) + conf.relation(:posts) { schema(infer: true) } + conf.relation(:users) { schema(infer: true) } + conf.register_relation(Test::Tags) + end - tags = container.relations[:tags].schema + specify do + tags = container.relations[:tags].schema - expect(tags.foreign_keys.size).to eql(1) + expect(tags.foreign_keys.size).to eql(1) - fk = tags.foreign_keys.first + fk = tags.foreign_keys.first - expect(fk.parent_table).to be(:posts) - expect(fk.attributes[0].name).to be(:post_id) + expect(fk.parent_table).to be(:posts) + expect(fk.attributes[0].name).to be(:post_id) + end end end context 'defining indexes', :helpers do |_ctx| - it 'allows defining indexes' do - class Test::Tags < ROM::Relation[:sql] - schema(:tags) do - attribute :id, Types::Serial - attribute :name, Types::String - attribute :created_at, Types::Time - attribute :updated_at, Types::Time - - indexes do - index :name - index :created_at, :name - index :updated_at, name: :recently_idx - index :created_at, name: :unique_date, unique: true - end - end - end - - conf.register_relation(Test::Tags) - schema = container.relations[:tags].schema - - expect(schema.indexes.to_a).to contain_exactly( - ROM::SQL::Index.new([define_attribute(:name, :String, source: schema.name)]), - ROM::SQL::Index.new( - [define_attribute(:created_at, :Time, source: schema.name), - define_attribute(:name, :String, source: schema.name)] - ), - ROM::SQL::Index.new( - [define_attribute(:updated_at, :Time, source: schema.name)], - name: :recently_idx - ), - ROM::SQL::Index.new( - [define_attribute(:created_at, :Time, source: schema.name)], - name: :unique_date, - unique: true - ) - ) - end - - if metadata[:postgres] - it 'can provide index type' do + context 'allows defining indexes' do + setup_relations do class Test::Tags < ROM::Relation[:sql] schema(:tags) do - attribute :id, Types::Serial - attribute :name, Types::String + attribute :id, Types::Serial + attribute :name, Types::String + attribute :created_at, Types::Time + attribute :updated_at, Types::Time indexes do - index :name, type: :gist + index :name + index :created_at, :name + index :updated_at, name: :recently_idx + index :created_at, name: :unique_date, unique: true end end end conf.register_relation(Test::Tags) + end + + specify do schema = container.relations[:tags].schema - index = schema.indexes.first - expect(index).to eql( + expect(schema.indexes.to_a).to contain_exactly( + ROM::SQL::Index.new([define_attribute(:name, :String, source: schema.name)]), + ROM::SQL::Index.new( + [define_attribute(:created_at, :Time, source: schema.name), + define_attribute(:name, :String, source: schema.name)] + ), ROM::SQL::Index.new( - [define_attribute(:name, :String, source: schema.name)], - type: :gist + [define_attribute(:updated_at, :Time, source: schema.name)], + name: :recently_idx + ), + ROM::SQL::Index.new( + [define_attribute(:created_at, :Time, source: schema.name)], + name: :unique_date, + unique: true ) ) + end + end + + if metadata[:postgres] + context 'can provide index type' do + setup_relations do + class Test::Tags < ROM::Relation[:sql] + schema(:tags) do + attribute :id, Types::Serial + attribute :name, Types::String + + indexes do + index :name, type: :gist + end + end + end + conf.register_relation(Test::Tags) + end + + specify do + schema = container.relations[:tags].schema + index = schema.indexes.first - expect(index.type).to eql(:gist) + expect(index).to eql( + ROM::SQL::Index.new( + [define_attribute(:name, :String, source: schema.name)], + type: :gist + ) + ) + + expect(index.type).to eql(:gist) + end end end end diff --git a/spec/integration/schema/call_spec.rb b/spec/integration/schema/call_spec.rb index 44f7b2a4..11aae489 100644 --- a/spec/integration/schema/call_spec.rb +++ b/spec/integration/schema/call_spec.rb @@ -6,12 +6,6 @@ include_context 'users' with_adapters :postgres do - before do - conf.relation(:users) do - schema(infer: true) - end - end - let(:schema) { relations[:users].schema } it 'auto-projects a relation' do diff --git a/spec/integration/schema/inferrer/mysql_spec.rb b/spec/integration/schema/inferrer/mysql_spec.rb index d669f873..a2b5adaa 100644 --- a/spec/integration/schema/inferrer/mysql_spec.rb +++ b/spec/integration/schema/inferrer/mysql_spec.rb @@ -7,7 +7,7 @@ inferrable_relations.push(:test_inferrence) end - before do + setup_tables do conn.create_table :test_inferrence do tinyint :tiny mediumint :medium @@ -26,7 +26,7 @@ end end - before do + setup_relations do conf.relation(:test_inferrence) do schema(infer: true) end diff --git a/spec/integration/schema/inferrer/postgres_spec.rb b/spec/integration/schema/inferrer/postgres_spec.rb index 8a6fbe14..c3c25113 100644 --- a/spec/integration/schema/inferrer/postgres_spec.rb +++ b/spec/integration/schema/inferrer/postgres_spec.rb @@ -9,7 +9,7 @@ colors = %w[red orange yellow green blue purple] - before do + setup_tables do conn.execute('create extension if not exists hstore') conn.execute('create extension if not exists ltree') @@ -68,7 +68,7 @@ end context 'when pg_enum in primary key' do - before do + setup_tables do conn.drop_table?(:test_inferrence) conn.create_table(:test_inferrence) do column :colours, :rainbow @@ -77,9 +77,11 @@ end it 'can infer primary key on enum column' do - expect(schema.to_h).to eql(attributes( - colours: ROM::SQL::Types::String.enum(*colors).meta(primary_key: true) - )) + expect(schema.to_h).to eql( + attributes( + colours: ROM::SQL::Types::String.enum(*colors).meta(primary_key: true) + ) + ) end end @@ -129,8 +131,11 @@ end context 'with a table without columns' do - before do + setup_tables do conn.create_table(:dummy) unless conn.table_exists?(:dummy) + end + + setup_relations do conf.relation(:dummy) { schema(infer: true) } end @@ -140,7 +145,7 @@ end context 'with a column with bi-directional mapping' do - before do + setup_tables do conn.execute('create extension if not exists hstore') conn.execute('create extension if not exists ltree') @@ -164,7 +169,9 @@ daterange :daterange ltree :ltree_path end + end + setup_relations do conf.relation(:test_bidirectional) { schema(infer: true) } conf.commands(:test_bidirectional) do diff --git a/spec/integration/schema/inferrer/sqlite_spec.rb b/spec/integration/schema/inferrer/sqlite_spec.rb index 5f0f9c42..76c22d56 100644 --- a/spec/integration/schema/inferrer/sqlite_spec.rb +++ b/spec/integration/schema/inferrer/sqlite_spec.rb @@ -7,7 +7,7 @@ inferrable_relations.push(:test_inferrence) end - before do + setup_tables do conn.create_table :test_inferrence do tinyint :tiny int8 :big @@ -17,7 +17,7 @@ end end - before do + setup_relations do conf.relation(:test_inferrence) do schema(infer: true) end diff --git a/spec/integration/schema/inferrer_spec.rb b/spec/integration/schema/inferrer_spec.rb index 06b2e612..02065f39 100644 --- a/spec/integration/schema/inferrer_spec.rb +++ b/spec/integration/schema/inferrer_spec.rb @@ -20,7 +20,7 @@ def index_by_name(indexes, name) with_adapters do |_adapter| describe 'inferring attributes' do - before do + setup_relations do dataset = self.dataset conf.relation(dataset) do schema(dataset, infer: true) @@ -63,7 +63,7 @@ def index_by_name(indexes, name) end context 'for complex table' do - before do |example| + setup_tables do |example| ctx = self conn.create_table :test_inferrence do @@ -118,7 +118,7 @@ def index_by_name(indexes, name) end context 'character datatypes' do - before do + setup_tables do conn.create_table :test_characters do String :text1, text: false, null: false String :text2, size: 100, null: false @@ -163,7 +163,7 @@ def index_by_name(indexes, name) end context 'numeric datatypes' do - before do + setup_tables do conn.create_table :test_numeric do primary_key :id decimal :dec, null: false @@ -221,7 +221,7 @@ def index_by_name(indexes, name) let(:relation) { container.relations[:people] } - before do + setup_relations do conf.relation(:people) do schema(infer: true) end @@ -237,7 +237,7 @@ def index_by_name(indexes, name) let(:create) { commands[:people].create } context "Sequel's types" do - before do + setup_tables do conn.create_table :people do primary_key :id String :name, null: false @@ -252,7 +252,7 @@ def index_by_name(indexes, name) end context 'nullable columns' do - before do + setup_tables do conn.create_table :people do primary_key :id String :name, null: false @@ -274,7 +274,7 @@ def index_by_name(indexes, name) end context 'columns with default value' do - before do + setup_tables do conn.create_table :people do primary_key :id String :name, null: false @@ -295,7 +295,7 @@ def index_by_name(indexes, name) context 'coercions' do context 'date' do - before do + setup_tables do conn.create_table :people do primary_key :id String :name, null: false @@ -315,7 +315,7 @@ def index_by_name(indexes, name) unless metadata[:sqlite] && defined? JRUBY_VERSION context 'timestamp' do - before do |ex| + setup_tables do |ex| ctx = self conn.create_table :people do @@ -375,48 +375,63 @@ def index_by_name(indexes, name) let(:dataset) { :test_inferrence } let(:source) { ROM::Relation::Name[dataset] } - it 'infers types with indices' do - conn.create_table :test_inferrence do - primary_key :id - Integer :foo - Integer :bar, null: false - Integer :baz, null: false + context 'inferring types with indices' do + setup_tables do + conn.create_table :test_inferrence do + primary_key :id + Integer :foo + Integer :bar, null: false + Integer :baz, null: false - index :foo, name: :foo_idx - index :bar, name: :bar_idx - index :baz, name: :baz1_idx - index :baz, name: :baz2_idx + index :foo, name: :foo_idx + index :bar, name: :bar_idx + index :baz, name: :baz1_idx + index :baz, name: :baz2_idx - index %i[bar baz], name: :composite_idx - index %i[foo bar], name: :unique_idx, unique: true + index %i[bar baz], name: :composite_idx + index %i[foo bar], name: :unique_idx, unique: true + end end - conf.relation(:test_inferrence) { schema(infer: true) } + setup_relations do + conf.relation(:test_inferrence) { schema(infer: true) } + end - expect(schema.indexes.map(&:name)).to match_array( - %i[foo_idx bar_idx baz1_idx baz2_idx composite_idx unique_idx] - ) + it 'infers types with indices' do + expect(schema.indexes.map(&:name)).to match_array( + %i[foo_idx bar_idx baz1_idx baz2_idx composite_idx unique_idx] + ) - unique_idx = index_by_name(schema.indexes, :unique_idx) + unique_idx = index_by_name(schema.indexes, :unique_idx) - expect(unique_idx).to be_unique + expect(unique_idx).to be_unique + end end if metadata[:postgres] - it 'infers cutsom index types' do - pending 'Sequel not returning index type' - conn.create_table :test_inferrence do - primary_key :id - Integer :foo - index :foo, name: :foo_idx, type: :gist + context 'infers cutsom index types' do + setup_tables do + conn.execute('create extension if not exists btree_gist') + + conn.create_table :test_inferrence do + primary_key :id + Integer :foo + index :foo, name: :foo_idx, type: :gist + end end - conf.relation(:test_inferrence) { schema(infer: true) } + setup_relations do + conf.relation(:test_inferrence) { schema(infer: true) } + end - index = schema.indexes.first + it 'infers cutsom index types' do + pending 'Sequel not returning index type' - expect(index.name).to eql(:foo_idx) - expect(index.type).to eql(:gist) + index = schema.indexes.first + + expect(index.name).to eql(:foo_idx) + expect(index.type).to eql(:gist) + end end end end diff --git a/spec/integration/schema/prefix_spec.rb b/spec/integration/schema/prefix_spec.rb index 401cef50..afeb962b 100644 --- a/spec/integration/schema/prefix_spec.rb +++ b/spec/integration/schema/prefix_spec.rb @@ -2,15 +2,9 @@ require 'spec_helper' -RSpec.describe ROM::SQL::Schema, '#prefix', :postgres, seeds: false do +RSpec.describe ROM::SQL::Schema, '#prefix', :postgres do include_context 'users' - before do - conf.relation(:users) do - schema(infer: true) - end - end - it 'auto-projects a relation with renamed columns using provided prefix' do expect(relations[:users].schema.prefix(:user).(relations[:users]).dataset.sql) .to eql('SELECT "users"."id" AS "user_id", "users"."name" AS "user_name" FROM "users" ORDER BY "users"."id"') diff --git a/spec/integration/schema/qualified_spec.rb b/spec/integration/schema/qualified_spec.rb index 505c967b..771b668b 100644 --- a/spec/integration/schema/qualified_spec.rb +++ b/spec/integration/schema/qualified_spec.rb @@ -2,15 +2,9 @@ require 'spec_helper' -RSpec.describe ROM::SQL::Schema, '#qualified', :postgres, seeds: false do +RSpec.describe ROM::SQL::Schema, '#qualified', :postgres do include_context 'users' - before do - conf.relation(:users) do - schema(infer: true) - end - end - it 'qualifies column names' do expect(relations[:users].schema.qualified.(relations[:users]).dataset.sql) .to eql('SELECT "users"."id", "users"."name" FROM "users" ORDER BY "users"."id"') diff --git a/spec/integration/schema/rename_spec.rb b/spec/integration/schema/rename_spec.rb index b455ee64..b52ca5fa 100644 --- a/spec/integration/schema/rename_spec.rb +++ b/spec/integration/schema/rename_spec.rb @@ -2,15 +2,9 @@ require 'spec_helper' -RSpec.describe ROM::SQL::Schema, '#rename', :postgres, seeds: false do +RSpec.describe ROM::SQL::Schema, '#rename', :postgres do include_context 'users' - before do - conf.relation(:users) do - schema(infer: true) - end - end - it 'auto-projects a relation with renamed' do expect(relations[:users].schema.qualified.rename(id: :user_id, name: :user_name).(relations[:users]).dataset.sql) .to eql('SELECT "users"."id" AS "user_id", "users"."name" AS "user_name" FROM "users" ORDER BY "users"."id"') diff --git a/spec/integration/schema/view_spec.rb b/spec/integration/schema/view_spec.rb index 4dedad16..de762305 100644 --- a/spec/integration/schema/view_spec.rb +++ b/spec/integration/schema/view_spec.rb @@ -7,7 +7,7 @@ with_adapters do describe 'defining a projected view' do - before do + setup_relations do conf.relation(:users) do schema(infer: true) @@ -16,7 +16,9 @@ relation { order(:name, :id) } end end + end + seed do container.relations[:users].insert(name: 'Joe') container.relations[:users].insert(name: 'Jane') container.relations[:users].insert(name: 'Jade') diff --git a/spec/integration/support/active_support_notifications_spec.rb b/spec/integration/support/active_support_notifications_spec.rb index cf07d5cf..f15dd0f8 100644 --- a/spec/integration/support/active_support_notifications_spec.rb +++ b/spec/integration/support/active_support_notifications_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe 'ActiveSupport::Notifications support', :postgres, seeds: false do +RSpec.describe 'ActiveSupport::Notifications support', :postgres do before do ROM::SQL.load_extensions(:active_support_notifications, :rails_log_subscriber) end diff --git a/spec/integration/support/rails_log_subscriber_spec.rb b/spec/integration/support/rails_log_subscriber_spec.rb index 91ae763b..33cf2f4a 100644 --- a/spec/integration/support/rails_log_subscriber_spec.rb +++ b/spec/integration/support/rails_log_subscriber_spec.rb @@ -4,7 +4,7 @@ require 'active_support/log_subscriber/test_helper' -RSpec.describe 'Rails log subscriber', :postgres, seeds: false do +RSpec.describe 'Rails log subscriber', :postgres do before do ROM::SQL.load_extensions(:active_support_notifications, :rails_log_subscriber) end diff --git a/spec/integration/wrap_spec.rb b/spec/integration/wrap_spec.rb index fa07e616..16eb512c 100644 --- a/spec/integration/wrap_spec.rb +++ b/spec/integration/wrap_spec.rb @@ -30,7 +30,7 @@ end context 'using association with inferred relation name' do - before do + setup_relations do conf.relation(:tasks) do auto_map false @@ -48,7 +48,7 @@ end context 'using association with an alias' do - before do + setup_relations do conf.relation(:tasks) do auto_map false @@ -66,7 +66,7 @@ end context 'using association with an aliased relation' do - before do + setup_relations do conf.relation(:tasks) do auto_map false @@ -91,7 +91,7 @@ end context 'using association with a view' do - before do + setup_relations do conf.relation(:users) do auto_map false diff --git a/spec/shared/accounts.rb b/spec/shared/accounts.rb index fa61dfae..46f150c2 100644 --- a/spec/shared/accounts.rb +++ b/spec/shared/accounts.rb @@ -8,7 +8,7 @@ inferrable_relations.push(:accounts, :cards, :subscriptions) end - before do |example| + setup_tables do |example| ctx = self conn.create_table :accounts do @@ -34,15 +34,15 @@ Integer :card_id String :service end + end + setup_relations do conf.relation(:accounts) { schema(infer: true) } conf.relation(:cards) { schema(infer: true) } conf.relation(:subscriptions) { schema(infer: true) } end - before do |example| - next if example.metadata[:seeds] == false - + seed do conn[:accounts].insert user_id: 1, number: '42', balance: 10_000.to_d conn[:cards].insert id: 1, account_id: 1, pan: '*6789' conn[:subscriptions].insert id: 1, card_id: 1, service: 'aws' diff --git a/spec/shared/articles.rb b/spec/shared/articles.rb index c3fd54fc..44508487 100644 --- a/spec/shared/articles.rb +++ b/spec/shared/articles.rb @@ -5,7 +5,7 @@ inferrable_relations.push(:articles) end - before do + setup_tables do conn.create_table :articles do primary_key :article_id String :author_name @@ -16,13 +16,13 @@ index :author_name index :status end + end + setup_relations do conf.relation(:articles) { schema(infer: true) } end - before do |example| - next if example.metadata[:seeds] == false - + seed do conn[:users].insert(name: 'John') conn[:articles].insert( diff --git a/spec/shared/database_setup.rb b/spec/shared/database_setup.rb index 80dfa7de..7dda034c 100644 --- a/spec/shared/database_setup.rb +++ b/spec/shared/database_setup.rb @@ -5,11 +5,11 @@ accounts cards subscriptions notes destinations flights categories user_group test_inferrence test_bidirectional people dragons - rabbits carrots names schema_migrations] + rabbits carrots names schema_migrations puzzles] cleared_dbs = [] - before do + setup_tables(:db) do unless cleared_dbs.include?(conn.database_type) all_tables.reverse.each { |table| conn.drop_table?(table) } cleared_dbs << conn.database_type @@ -32,7 +32,6 @@ let(:database_type) { conn.database_type } let(:inferrable_relations) { [] } let(:conf) { TestConfiguration.new(:sql, conn) } - let(:container) { ROM.container(conf) } let(:relations) { container.relations } let(:commands) { container.commands } diff --git a/spec/shared/json_notes.rb b/spec/shared/json_notes.rb index 22e729a2..efc1ed85 100644 --- a/spec/shared/json_notes.rb +++ b/spec/shared/json_notes.rb @@ -5,12 +5,15 @@ inferrable_relations.push(:json_notes) end - before do |_example| + setup_tables do + conn.drop_table?(:json_notes) conn.create_table :json_notes do primary_key :id String :note end + end + setup_relations do write_type = Dry.Types.Constructor(String) { |value| JSON.dump({ content: value }) } read_type = Dry.Types.Constructor(String) { |value| JSON.parse(value)['content'] } diff --git a/spec/shared/notes.rb b/spec/shared/notes.rb index a66445a7..e5295cf5 100644 --- a/spec/shared/notes.rb +++ b/spec/shared/notes.rb @@ -5,7 +5,7 @@ inferrable_relations.push(:notes) end - before do |example| + setup_tables(notes: :users) do |example| ctx = self conn.create_table :notes do @@ -18,7 +18,11 @@ DateTime :completed_at Date :written end + end - conf.relation(:notes) { schema(infer: true) } + setup_relations do |example| + if example.metadata[:relations] != false + conf.relation(:notes) { schema(infer: true) } + end end end diff --git a/spec/shared/posts.rb b/spec/shared/posts.rb index 10d84bfa..a7e0c961 100644 --- a/spec/shared/posts.rb +++ b/spec/shared/posts.rb @@ -5,32 +5,36 @@ inferrable_relations.push(:posts) end - before do |_example| + setup_tables(posts: :users) do conn.create_table :posts do primary_key :post_id foreign_key :author_id, :users String :title String :body end - - conf.relation(:posts) { schema(infer: true) } end - before do |example| - next if example.metadata[:seeds] == false + setup_relations do |example| + if example.metadata[:relations] != false + conf.relation(:posts) { schema(infer: true) } + end + end - conn[:posts].insert( - post_id: 1, - author_id: 2, - title: "Joe's post", - body: 'Joe wrote sutin' - ) + seed(posts: :users) do |example| + if example.metadata[:seeds] != false + conn[:posts].insert( + post_id: 1, + author_id: 2, + title: "Joe's post", + body: 'Joe wrote sutin' + ) - conn[:posts].insert( - post_id: 2, - author_id: 1, - title: "Jane's post", - body: 'Jane wrote sutin' - ) + conn[:posts].insert( + post_id: 2, + author_id: 1, + title: "Jane's post", + body: 'Jane wrote sutin' + ) + end end end diff --git a/spec/shared/puppies.rb b/spec/shared/puppies.rb index 677833dd..5620e4c7 100644 --- a/spec/shared/puppies.rb +++ b/spec/shared/puppies.rb @@ -5,13 +5,15 @@ inferrable_relations.push(:puppies) end - before do + setup_tables do conn.create_table :puppies do primary_key :id String :name, null: false boolean :cute, null: false, default: true end + end + setup_relations do conf.relation(:puppies) { schema(infer: true) } end end diff --git a/spec/shared/relations.rb b/spec/shared/relations.rb index 5159e851..a027a9d6 100644 --- a/spec/shared/relations.rb +++ b/spec/shared/relations.rb @@ -2,9 +2,4 @@ RSpec.shared_context 'relations' do include_context 'users and tasks' - - before do - conf.relation(:users) { schema(infer: true) } - conf.relation(:tasks) { schema(infer: true) } - end end diff --git a/spec/shared/users.rb b/spec/shared/users.rb index 8244ca09..8ac77d94 100644 --- a/spec/shared/users.rb +++ b/spec/shared/users.rb @@ -13,7 +13,7 @@ let(:jane_id) { 1 } let(:joe_id) { 2 } - before do |example| + setup_tables(users: :db) do |example| ctx = self conn.create_table :users do @@ -21,16 +21,18 @@ String :name, text: false, null: false check { char_length(name) > 2 } if ctx.postgres?(example) end + end + setup_relations(:users) do |example| if example.metadata[:relations] != false conf.relation(:users) { schema(infer: true) } end end - before do |example| - next if example.metadata[:seeds] == false - - conn[:users].insert name: 'Jane' - conn[:users].insert name: 'Joe' + seed(:users) do |example| + if example.metadata[:seeds] != false + conn[:users].insert name: 'Jane' + conn[:users].insert name: 'Joe' + end end end diff --git a/spec/shared/users_and_tasks.rb b/spec/shared/users_and_tasks.rb index 2fb8c669..8557e417 100644 --- a/spec/shared/users_and_tasks.rb +++ b/spec/shared/users_and_tasks.rb @@ -13,7 +13,7 @@ inferrable_relations.push(:tasks, :tags, :task_tags) end - before do |example| + setup_tables(tasks: :users) do |example| ctx = self conn.create_table :tasks do @@ -35,21 +35,53 @@ Integer :tag_id Integer :task_id end + end + setup_relations(tasks: :users) do |example| if example.metadata[:relations] != false - conf.relation(:tasks) { schema(infer: true) } - conf.relation(:task_tags) { schema(infer: true) } - conf.relation(:tags) { schema(infer: true) } + conf.relation(:users) do + schema(infer: true) do + associations do + has_many :tasks + end + end + end + + conf.relation(:tasks) do + schema(infer: true) do + associations do + belongs_to :user + has_many :tags, through: :task_tags + end + end + end + + conf.relation(:task_tags) do + schema(infer: true) do + associations do + belongs_to :task + belongs_to :tag + end + end + end + + conf.relation(:tags) do + schema(infer: true) do + associations do + has_many :tasks, through: :task_tags + end + end + end end end - before do |example| - next if example.metadata[:seeds] == false - - conn[:tasks].insert id: 1, user_id: 2, title: "Joe's task" - conn[:tasks].insert id: 2, user_id: 1, title: "Jane's task" + seed(tasks: :users) do |example| + if example.metadata[:seeds] != false + conn[:tasks].insert id: 1, user_id: 2, title: "Joe's task" + conn[:tasks].insert id: 2, user_id: 1, title: "Jane's task" - conn[:tags].insert id: 1, name: 'important' - conn[:task_tags].insert(tag_id: 1, task_id: 1) + conn[:tags].insert id: 1, name: 'important' + conn[:task_tags].insert(tag_id: 1, task_id: 1) + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 84d42a72..c8b6358f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -151,4 +151,6 @@ module Test config.include(Helpers, helpers: true) config.include ENVHelper + + ContainerHelper.install(config) end diff --git a/spec/support/container_helper.rb b/spec/support/container_helper.rb new file mode 100644 index 00000000..b2419e34 --- /dev/null +++ b/spec/support/container_helper.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +module ContainerHelper + Entry = ::Struct.new(:block, :provides, :depends_on) + + def self.install(config) + config.extend(DSL) + config.include(InstanceMethods) + end + + def self.parse_dependency(arg) + case arg + when nil + [nil, []] + when ::Symbol + [arg, []] + when ::Hash + raise ::ArgumentError, "expected a single-key hash, got #{arg.inspect}" if arg.size != 1 + + key, value = arg.first + [key, Array(value)] + else + raise ::ArgumentError, "expected a Symbol or a single-key Hash, got #{arg.inspect}" + end + end + + def self.sort_entries(entries) + sorted, unsorted = entries.partition(&:provides) + by_name = sorted.to_h { |entry| [entry.provides, entry] } + state = {} + result = [] + + visit = lambda do |entry| + case state[entry] + when :done then return + when :visiting then raise "circular dependency detected at #{entry.provides.inspect}" + end + + state[entry] = :visiting + entry.depends_on.each do |dep_name| + dep_entry = by_name[dep_name] + visit.call(dep_entry) if dep_entry + end + state[entry] = :done + result << entry + end + + sorted.each(&visit) + result.concat(unsorted) + end + + module DSL + def container_blocks + @container_blocks ||= ::Hash.new do |hash, key| + hash[key] = [] + end + end + + def all_blocks(group) + entries = ancestors.select { |ancestor| + ancestor.is_a?(::Class) && ancestor.respond_to?(:container_blocks) + }.reverse.flat_map { |klass| klass.container_blocks[group] } + + ContainerHelper.sort_entries(entries).map(&:block) + end + + def setup_relations(dependency = nil, &block) + provides, depends_on = ContainerHelper.parse_dependency(dependency) + container_blocks[:relations] << Entry.new(block, provides, depends_on) + end + + def seed(dependency = nil, &block) + provides, depends_on = ContainerHelper.parse_dependency(dependency) + container_blocks[:seeds] << Entry.new(block, provides, depends_on) + end + + def setup_tables(dependency = nil, &block) + provides, depends_on = ContainerHelper.parse_dependency(dependency) + container_blocks[:tables] << Entry.new(block, provides, depends_on) + end + end + + module InstanceMethods + def self.included(base) + base.let(:container) do |example| + self.class.all_blocks(:tables).each do |block| + instance_exec(example, &block) + end + + self.class.all_blocks(:relations).each do |block| + instance_exec(example, &block) + end + + ::ROM.container(conf) + end + + base.before do |example| + seeds = self.class.all_blocks(:seeds) + + if seeds.any? + container + + seeds.each do |block| + instance_exec(example, &block) + end + end + end + end + end +end diff --git a/spec/unit/commands/create_spec.rb b/spec/unit/commands/create_spec.rb index 6d34879b..f6b168ed 100644 --- a/spec/unit/commands/create_spec.rb +++ b/spec/unit/commands/create_spec.rb @@ -15,13 +15,15 @@ end describe '#call' do - before do + setup_tables do conn.create_table :books do primary_key :id column :author, String column :title, String end + end + setup_relations do conf.relation(:books) do schema do attribute :id, ROM::SQL::Types::Serial diff --git a/spec/unit/plugin/nullify_spec.rb b/spec/unit/plugin/nullify_spec.rb index 7acc82bd..f019ed83 100644 --- a/spec/unit/plugin/nullify_spec.rb +++ b/spec/unit/plugin/nullify_spec.rb @@ -5,7 +5,7 @@ RSpec.describe ROM::Relation, '#nullify' do include_context 'users' - before do + setup_relations do conf.relation(:users) do use :nullify end diff --git a/spec/unit/plugin/pagination_spec.rb b/spec/unit/plugin/pagination_spec.rb index e7f92156..37e56afe 100644 --- a/spec/unit/plugin/pagination_spec.rb +++ b/spec/unit/plugin/pagination_spec.rb @@ -6,9 +6,11 @@ include_context 'users' with_adapters do - before do + seed do 9.times { |i| conn[:users].insert(name: "User #{i}") } + end + setup_relations do conf.relation(:users) do use :pagination diff --git a/spec/unit/plugin/timestamp_spec.rb b/spec/unit/plugin/timestamp_spec.rb index c9ba9ed1..09cf62d3 100644 --- a/spec/unit/plugin/timestamp_spec.rb +++ b/spec/unit/plugin/timestamp_spec.rb @@ -5,7 +5,7 @@ include_context 'notes' with_adapters do - before do + setup_relations do conf.commands(:notes) do define :create do result :one diff --git a/spec/unit/relation/as_hash_spec.rb b/spec/unit/relation/as_hash_spec.rb index 6ed164b0..d01b086d 100644 --- a/spec/unit/relation/as_hash_spec.rb +++ b/spec/unit/relation/as_hash_spec.rb @@ -3,13 +3,7 @@ RSpec.describe ROM::Relation, '#as_hash' do subject(:relation) { container.relations.users } - include_context 'users and tasks' - - before do - conf.relation(:users) do - schema(infer: true) - end - end + include_context 'users' with_adapters do it 'returns a hash with all tuples been the key the primary key' do diff --git a/spec/unit/relation/assoc_spec.rb b/spec/unit/relation/assoc_spec.rb index 508ae430..94bfaa7b 100644 --- a/spec/unit/relation/assoc_spec.rb +++ b/spec/unit/relation/assoc_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ROM::SQL::Relation do +RSpec.describe ROM::SQL::Relation, 'associations' do include_context 'users and tasks' context 'with has_many' do @@ -10,16 +10,6 @@ let(:tasks) { relations[:tasks] } - before do - conf.relation(:users) do - schema(infer: true) do - associations do - has_many :tasks - end - end - end - end - with_adapters do it 'returns child tuples for a relation' do expect(users.assoc(:tasks).where(name: 'Jane').to_a).to eql([ @@ -32,24 +22,7 @@ context 'with has_many-through' do subject(:tasks) { relations[:tasks] } - before do - conf.relation(:task_tags) do - schema(infer: true) do - associations do - belongs_to :tasks - belongs_to :tags - end - end - end - - conf.relation(:tasks) do - schema(infer: true) do - associations do - has_many :tags, through: :task_tags - end - end - end - + seed do conn[:tags].insert id: 2, name: 'whatevah' conn[:task_tags].insert(tag_id: 2, task_id: 2) end @@ -73,16 +46,6 @@ context 'with belongs_to' do subject(:tasks) { relations[:tasks] } - before do - conf.relation(:tasks) do - schema(infer: true) do - associations do - belongs_to :users, as: :user - end - end - end - end - with_adapters do it 'returns parent tuples for a relation' do expect(tasks.assoc(:user).where(title: "Jane's task").to_a).to eql([ diff --git a/spec/unit/relation/associations_spec.rb b/spec/unit/relation/associations_spec.rb index 03f7c614..dad3d25e 100644 --- a/spec/unit/relation/associations_spec.rb +++ b/spec/unit/relation/associations_spec.rb @@ -7,7 +7,7 @@ with_adapters do context 'with schema' do - it 'returns configured primary key from the schema' do + setup_relations do conf.relation(:users) do schema(infer: true) do associations do @@ -15,12 +15,18 @@ end end end - + end + it 'returns configured primary key from the schema' do expect(relation.associations[:tasks]).to be(container.relations.users.schema.associations[:tasks]) end end context 'without schema' do + setup_relations do + conf.relation(:users) do + schema(infer: true) + end + end it 'returns an empty association set' do expect(relation.associations.elements).to be_empty end diff --git a/spec/unit/relation/batch_spec.rb b/spec/unit/relation/batch_spec.rb index b2a82d67..0ecd9989 100644 --- a/spec/unit/relation/batch_spec.rb +++ b/spec/unit/relation/batch_spec.rb @@ -6,7 +6,7 @@ context 'single-column PK' do subject(:relation) { relations[:users] } - before do + seed do 7.times do |i| conn[:users].insert name: "User #{i + 1}" end diff --git a/spec/unit/relation/by_pk_spec.rb b/spec/unit/relation/by_pk_spec.rb index 74e173c7..e373ac84 100644 --- a/spec/unit/relation/by_pk_spec.rb +++ b/spec/unit/relation/by_pk_spec.rb @@ -39,13 +39,15 @@ context 'without PK' do subject(:relation) { relations[:people] } - before do + setup_tables(people: :db) do conn.drop_table?(:people) conn.create_table(:people) do column :name, String end + end + setup_relations do conf.relation(:people) do schema do attribute :name, ROM::SQL::Types::String diff --git a/spec/unit/relation/dataset_spec.rb b/spec/unit/relation/dataset_spec.rb index ff380545..52b9099a 100644 --- a/spec/unit/relation/dataset_spec.rb +++ b/spec/unit/relation/dataset_spec.rb @@ -3,13 +3,13 @@ RSpec.describe ROM::Relation, '#dataset' do subject(:relation) { container.relations.users } - include_context 'users and tasks' + include_context 'users' let(:dataset) { container.gateways[:default].dataset(:users) } with_adapters do context 'with schema' do - before do + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial @@ -26,7 +26,7 @@ end context 'with cherry-picked attributes in schema' do - before do + setup_relations do conf.relation(:users) do schema do attribute :id, ROM::SQL::Types::Serial @@ -42,10 +42,6 @@ end context 'with inferred schema' do - before do - conf.relation(:users) { schema(infer: true) } - end - it 'selects all qualified columns and sorts by pk' do expect(relation.dataset.sql).to eql(dataset.select(*relation.schema.qualified).order(Sequel.qualify(:users, :id)).sql) end diff --git a/spec/unit/relation/distinct_spec.rb b/spec/unit/relation/distinct_spec.rb index 6699d129..e9b87830 100644 --- a/spec/unit/relation/distinct_spec.rb +++ b/spec/unit/relation/distinct_spec.rb @@ -5,7 +5,7 @@ include_context 'users and tasks' - before do + seed do relation.insert id: 3, name: 'Jane' end diff --git a/spec/unit/relation/exists_spec.rb b/spec/unit/relation/exists_spec.rb index bad782c4..25829219 100644 --- a/spec/unit/relation/exists_spec.rb +++ b/spec/unit/relation/exists_spec.rb @@ -1,26 +1,10 @@ # frozen_string_literal: true -RSpec.describe ROM::Relation, '#exists', relations: false do +RSpec.describe ROM::Relation, '#exists' do include_context 'users and tasks' - before do + seed do conn[:users].insert name: 'Jack' - - conf.relation(:users) do - schema(infer: true) do - associations do - has_many :tasks - end - end - end - - conf.relation(:tasks) do - schema(infer: true) do - associations do - belongs_to :user - end - end - end end with_adapters do diff --git a/spec/unit/relation/having_spec.rb b/spec/unit/relation/having_spec.rb index a5b74960..0ee42ae0 100644 --- a/spec/unit/relation/having_spec.rb +++ b/spec/unit/relation/having_spec.rb @@ -12,7 +12,7 @@ include_context 'users and tasks' with_adapters :postgres do - before do + seed do conn[:tasks].insert(id: 3, user_id: 2, title: "Joe's another task") end diff --git a/spec/unit/relation/import_spec.rb b/spec/unit/relation/import_spec.rb index 79808912..714c0c95 100644 --- a/spec/unit/relation/import_spec.rb +++ b/spec/unit/relation/import_spec.rb @@ -9,22 +9,26 @@ with_adapters(:postgres) do context 'within a single gateway' do - before do + setup_tables(users_for_loading: :db) do conn.drop_table?(:users_for_loading) conn.create_table(:users_for_loading) do primary_key :id column :full_name, String, null: false end + end - conn[:users_for_loading].insert(full_name: 'Jack') - conn[:users_for_loading].insert(full_name: 'John') - + setup_relations do conf.relation(:users_for_loading) do schema(:users_for_loading, infer: true) end end + seed do + conn[:users_for_loading].insert(full_name: 'Jack') + conn[:users_for_loading].insert(full_name: 'John') + end + it 'inserts data from another relation' do relation.import(source.project { [(id + 10).as(:id), full_name.as(:name)] }) @@ -44,7 +48,7 @@ ROM::Memory::Dataset.new(data) end - before do + setup_relations do conf.relation(:users_for_loading, adapter: :memory) do gateway :other diff --git a/spec/unit/relation/inner_join_spec.rb b/spec/unit/relation/inner_join_spec.rb index 5895ca16..c463306e 100644 --- a/spec/unit/relation/inner_join_spec.rb +++ b/spec/unit/relation/inner_join_spec.rb @@ -60,13 +60,15 @@ inferrable_relations.push(:puzzles) end - before do + setup_tables(puzzles: :users) do conn.create_table(:puzzles) do primary_key :id foreign_key :author_id, :users, null: false column :text, String, null: false end + end + setup_relations do conf.relation(:users) do schema(infer: true) do associations do @@ -95,6 +97,14 @@ end end + conf.relation(:tags) do + schema(infer: true) do + associations do + has_many :task_tags + end + end + end + conf.relation(:puzzles) do schema(infer: true) do associations do @@ -102,7 +112,9 @@ end end end + end + seed do relation.insert id: 3, name: 'Jade' puzzles.insert id: 1, author_id: 1, text: 'solved by Jane' end @@ -142,7 +154,7 @@ def name end describe 'joined relation with join keys inferred for m:m-through' do - before do + seed do tags.insert(id: 2, name: 'postponed') tasks.task_tags.insert(tag_id: 2, task_id: 2) end diff --git a/spec/unit/relation/instrument_spec.rb b/spec/unit/relation/instrument_spec.rb index 83c5ba95..d0208777 100644 --- a/spec/unit/relation/instrument_spec.rb +++ b/spec/unit/relation/instrument_spec.rb @@ -22,7 +22,7 @@ def instrument(*args, &) end.new end - before do + setup_tables do conn.create_table :users do primary_key :id column :name, String @@ -31,7 +31,9 @@ def instrument(*args, &) conf.plugin(:sql, relations: :instrumentation) do |p| p.notifications = notifications end + end + setup_relations do conf.relation(:users) do schema(infer: true) end @@ -76,13 +78,15 @@ def instrument(*args, &) let(:container_alt) { ROM.container(conf_alt) } - before do + setup_relations do conf_alt.plugin(:sql, relations: :instrumentation) conf_alt.relation(:users) do schema(infer: true) end + end + before do container_alt end diff --git a/spec/unit/relation/join_dsl_spec.rb b/spec/unit/relation/join_dsl_spec.rb index 79d3e6ea..69deaa26 100644 --- a/spec/unit/relation/join_dsl_spec.rb +++ b/spec/unit/relation/join_dsl_spec.rb @@ -1,26 +1,8 @@ # frozen_string_literal: true -RSpec.describe ROM::Relation, '#join_dsl', relations: false do +RSpec.describe ROM::Relation, '#join_dsl' do subject(:relation) { relations[:tasks] } - before do - conf.relation(:users) do - schema(infer: true) do - associations do - has_many :tasks - end - end - end - - conf.relation(:tasks) do - schema(infer: true) do - associations do - belongs_to :user - end - end - end - end - include_context 'users and tasks' shared_context 'valid joined relation' do diff --git a/spec/unit/relation/left_join_spec.rb b/spec/unit/relation/left_join_spec.rb index 943af27f..59b537df 100644 --- a/spec/unit/relation/left_join_spec.rb +++ b/spec/unit/relation/left_join_spec.rb @@ -23,19 +23,7 @@ end context 'with associations' do - before do - conf.relation(:users) do - schema(infer: true) do - associations { has_many :tasks } - end - end - - conf.relation(:tasks) do - schema(infer: true) do - associations { belongs_to :user } - end - end - + seed do relation.insert id: 3, name: 'Jade' end diff --git a/spec/unit/relation/lock_spec.rb b/spec/unit/relation/lock_spec.rb index 621578a4..821e9e77 100644 --- a/spec/unit/relation/lock_spec.rb +++ b/spec/unit/relation/lock_spec.rb @@ -3,7 +3,7 @@ require 'concurrent/atomic/count_down_latch' RSpec.describe ROM::Relation, '#lock' do - include_context 'users and tasks' + include_context 'users' subject(:relation) { users } diff --git a/spec/unit/relation/map_spec.rb b/spec/unit/relation/map_spec.rb index 1bbbf238..1432c84d 100644 --- a/spec/unit/relation/map_spec.rb +++ b/spec/unit/relation/map_spec.rb @@ -3,7 +3,7 @@ RSpec.describe ROM::Relation, '#map' do subject(:relation) { container.relations.users } - include_context 'users and tasks' + include_context 'users' with_adapters do it 'yields tuples' do diff --git a/spec/unit/relation/order_spec.rb b/spec/unit/relation/order_spec.rb index 1437b9a2..0d240db7 100644 --- a/spec/unit/relation/order_spec.rb +++ b/spec/unit/relation/order_spec.rb @@ -5,7 +5,7 @@ include_context 'users and tasks' - before do + seed do relation.insert(id: 3, name: 'Jade') end diff --git a/spec/unit/relation/prefix_spec.rb b/spec/unit/relation/prefix_spec.rb index cfaf7b66..3f52210a 100644 --- a/spec/unit/relation/prefix_spec.rb +++ b/spec/unit/relation/prefix_spec.rb @@ -5,7 +5,7 @@ include_context 'users and tasks' - before do + setup_relations do conf.relation(:users) do schema(infer: true) diff --git a/spec/unit/relation/primary_key_spec.rb b/spec/unit/relation/primary_key_spec.rb index a6db79a2..6a1a2f04 100644 --- a/spec/unit/relation/primary_key_spec.rb +++ b/spec/unit/relation/primary_key_spec.rb @@ -3,25 +3,25 @@ RSpec.describe ROM::Relation, '#primary_key' do subject(:relation) { container.relations.users } - include_context 'users and tasks' + include_context 'users' with_adapters do context 'with schema' do - it 'returns configured primary key from the schema' do + setup_relations do conf.relation(:users) do schema do attribute :name, ROM::SQL::Types::String.meta(primary_key: true) end end + end + it 'returns configured primary key from the schema' do expect(relation.primary_key).to be(:name) end end context 'without schema' do it 'returns :id by default' do - conf.relation(:users) { schema(infer: true) } - expect(relation.primary_key).to be(:id) end end diff --git a/spec/unit/relation/project_spec.rb b/spec/unit/relation/project_spec.rb index a6ff7d19..076a5760 100644 --- a/spec/unit/relation/project_spec.rb +++ b/spec/unit/relation/project_spec.rb @@ -5,7 +5,7 @@ include_context 'users and tasks' - before do + setup_relations do conf.relation(:users) do schema(infer: true) diff --git a/spec/unit/relation/qualified_columns_spec.rb b/spec/unit/relation/qualified_columns_spec.rb index 899b729d..5a63a05d 100644 --- a/spec/unit/relation/qualified_columns_spec.rb +++ b/spec/unit/relation/qualified_columns_spec.rb @@ -5,7 +5,7 @@ include_context 'users and tasks' - before do + setup_relations do conf.relation(:users) do schema(infer: true) @@ -19,8 +19,10 @@ def sorted it 'returns qualified column names' do columns = relation.sorted.prefix(:user).qualified_columns - expect(columns).to eql([Sequel.qualify(:users, :id).as(:user_id), - Sequel.qualify(:users, :name).as(:user_name)]) + expect(columns).to eql([ + Sequel.qualify(:users, :id).as(:user_id), + Sequel.qualify(:users, :name).as(:user_name) + ]) end it 'returns projected qualified column names' do diff --git a/spec/unit/relation/rename_spec.rb b/spec/unit/relation/rename_spec.rb index 3e4015c0..9bdfb5a3 100644 --- a/spec/unit/relation/rename_spec.rb +++ b/spec/unit/relation/rename_spec.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true -RSpec.describe ROM::Relation, '#rename' do +RSpec.describe ROM::Relation, '#rename', relations: false do subject(:relation) { container.relations.users } include_context 'users and tasks' - before do + setup_relations do conf.relation(:users) do schema(infer: true) diff --git a/spec/unit/relation/right_join_spec.rb b/spec/unit/relation/right_join_spec.rb index f408d442..dec19bd3 100644 --- a/spec/unit/relation/right_join_spec.rb +++ b/spec/unit/relation/right_join_spec.rb @@ -24,20 +24,6 @@ end context 'with associations' do - before do - conf.relation(:users) do - schema(infer: true) do - associations { has_many :tasks } - end - end - - conf.relation(:tasks) do - schema(infer: true) do - associations { belongs_to :user } - end - end - end - it 'joins relation with join keys inferred' do users.insert id: 3, name: 'Jade' relation.insert id: 3, title: 'Unassigned' diff --git a/spec/unit/relation/select_spec.rb b/spec/unit/relation/select_spec.rb index 94d2d4a7..d8326e8e 100644 --- a/spec/unit/relation/select_spec.rb +++ b/spec/unit/relation/select_spec.rb @@ -5,10 +5,6 @@ include_context 'users and tasks' - before do - conf.relation(:tasks) { schema(infer: true) } - end - with_adapters do it 'projects a relation using a list of symbols' do expect(relation.select(:id, :title).to_a).to eql( diff --git a/spec/unit/relation/union_spec.rb b/spec/unit/relation/union_spec.rb index 94ef3c5c..be3efbd8 100644 --- a/spec/unit/relation/union_spec.rb +++ b/spec/unit/relation/union_spec.rb @@ -5,7 +5,7 @@ include_context 'users and tasks' - before do + setup_relations do conf.relation(:tasks) do schema(infer: true) do associations do @@ -13,14 +13,6 @@ end end end - - conf.relation(:task_tags) do - schema(infer: true) do - associations do - belongs_to :task - end - end - end end with_adapters do diff --git a/spec/unit/relation/unique_predicate_spec.rb b/spec/unit/relation/unique_predicate_spec.rb index e0bcfdea..3a3697d9 100644 --- a/spec/unit/relation/unique_predicate_spec.rb +++ b/spec/unit/relation/unique_predicate_spec.rb @@ -6,7 +6,7 @@ include_context 'users and tasks' with_adapters do - before { relation.delete } + seed { relation.delete } it 'returns true when there is only one tuple matching criteria' do expect(relation.unique?(title: 'Task One')).to be(true) diff --git a/spec/unit/relation/where_spec.rb b/spec/unit/relation/where_spec.rb index a1c1b9ab..34206eca 100644 --- a/spec/unit/relation/where_spec.rb +++ b/spec/unit/relation/where_spec.rb @@ -121,7 +121,7 @@ end context 'with :read types' do - before do + setup_relations do conf.relation(:tasks) do schema(infer: true) do attribute :id, ROM::SQL::Types::Serial.constructor(&:to_i) From 1b66d42ebaeee836234733a0f1c236bfdcf48967 Mon Sep 17 00:00:00 2001 From: Nikita Shilnikov Date: Mon, 4 May 2026 12:00:07 +0200 Subject: [PATCH 2/2] Remove 3.5 from CI --- .github/workflows/ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 146b1f4d..376f668a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,6 @@ jobs: fail-fast: false matrix: ruby: - - '3.5' - '3.4' - '3.3' - '3.2'