activerecord-spanner-adapter 1.5.1 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/acceptance-tests-on-emulator.yaml +1 -1
  3. data/.github/workflows/acceptance-tests-on-production.yaml +5 -3
  4. data/.github/workflows/ci.yaml +1 -1
  5. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +1 -1
  6. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +5 -3
  7. data/.github/workflows/nightly-unit-tests.yaml +1 -1
  8. data/.github/workflows/release-please-label.yml +1 -1
  9. data/.github/workflows/rubocop.yaml +1 -1
  10. data/.release-please-manifest.json +1 -1
  11. data/.rubocop.yml +1 -1
  12. data/CHANGELOG.md +14 -0
  13. data/Gemfile +7 -2
  14. data/README.md +10 -10
  15. data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +6 -0
  16. data/acceptance/cases/migration/change_schema_test.rb +19 -3
  17. data/acceptance/cases/migration/schema_dumper_test.rb +10 -1
  18. data/acceptance/cases/models/interleave_test.rb +6 -0
  19. data/acceptance/cases/tasks/database_tasks_test.rb +340 -2
  20. data/acceptance/cases/transactions/optimistic_locking_test.rb +6 -0
  21. data/acceptance/cases/transactions/read_write_transactions_test.rb +24 -0
  22. data/acceptance/models/table_with_sequence.rb +10 -0
  23. data/acceptance/schema/schema.rb +65 -19
  24. data/acceptance/test_helper.rb +1 -1
  25. data/activerecord-spanner-adapter.gemspec +1 -1
  26. data/benchmarks/application.rb +1 -1
  27. data/examples/snippets/bit-reversed-sequence/README.md +103 -0
  28. data/examples/snippets/bit-reversed-sequence/Rakefile +13 -0
  29. data/examples/snippets/bit-reversed-sequence/application.rb +68 -0
  30. data/examples/snippets/bit-reversed-sequence/config/database.yml +8 -0
  31. data/examples/snippets/bit-reversed-sequence/db/migrate/01_create_tables.rb +33 -0
  32. data/examples/snippets/bit-reversed-sequence/db/schema.rb +31 -0
  33. data/examples/snippets/bit-reversed-sequence/db/seeds.rb +31 -0
  34. data/examples/snippets/bit-reversed-sequence/models/album.rb +11 -0
  35. data/examples/snippets/bit-reversed-sequence/models/singer.rb +15 -0
  36. data/examples/snippets/interleaved-tables/README.md +44 -53
  37. data/examples/snippets/interleaved-tables/Rakefile +2 -2
  38. data/examples/snippets/interleaved-tables/application.rb +2 -2
  39. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +12 -18
  40. data/examples/snippets/interleaved-tables/db/schema.rb +9 -7
  41. data/examples/snippets/interleaved-tables/db/seeds.rb +1 -1
  42. data/examples/snippets/interleaved-tables/models/album.rb +3 -7
  43. data/examples/snippets/interleaved-tables/models/singer.rb +1 -1
  44. data/examples/snippets/interleaved-tables/models/track.rb +6 -7
  45. data/examples/snippets/interleaved-tables-before-7.1/README.md +167 -0
  46. data/examples/snippets/interleaved-tables-before-7.1/Rakefile +13 -0
  47. data/examples/snippets/interleaved-tables-before-7.1/application.rb +126 -0
  48. data/examples/snippets/interleaved-tables-before-7.1/config/database.yml +8 -0
  49. data/examples/snippets/interleaved-tables-before-7.1/db/migrate/01_create_tables.rb +44 -0
  50. data/examples/snippets/interleaved-tables-before-7.1/db/schema.rb +37 -0
  51. data/examples/snippets/interleaved-tables-before-7.1/db/seeds.rb +40 -0
  52. data/examples/snippets/interleaved-tables-before-7.1/models/album.rb +20 -0
  53. data/examples/snippets/interleaved-tables-before-7.1/models/singer.rb +18 -0
  54. data/examples/snippets/interleaved-tables-before-7.1/models/track.rb +28 -0
  55. data/examples/snippets/query-logs/README.md +43 -0
  56. data/examples/snippets/query-logs/Rakefile +13 -0
  57. data/examples/snippets/query-logs/application.rb +63 -0
  58. data/examples/snippets/query-logs/config/database.yml +8 -0
  59. data/examples/snippets/query-logs/db/migrate/01_create_tables.rb +21 -0
  60. data/examples/snippets/query-logs/db/schema.rb +31 -0
  61. data/examples/snippets/query-logs/db/seeds.rb +24 -0
  62. data/examples/snippets/query-logs/models/album.rb +9 -0
  63. data/examples/snippets/query-logs/models/singer.rb +9 -0
  64. data/examples/snippets/read-only-transactions/application.rb +1 -1
  65. data/lib/active_record/connection_adapters/spanner/column.rb +13 -0
  66. data/lib/active_record/connection_adapters/spanner/database_statements.rb +144 -35
  67. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +3 -21
  68. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +11 -2
  69. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +4 -0
  70. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +12 -2
  71. data/lib/active_record/connection_adapters/spanner_adapter.rb +28 -9
  72. data/lib/activerecord_spanner_adapter/base.rb +64 -20
  73. data/lib/activerecord_spanner_adapter/foreign_key.rb +6 -2
  74. data/lib/activerecord_spanner_adapter/index/column.rb +3 -1
  75. data/lib/activerecord_spanner_adapter/index.rb +4 -2
  76. data/lib/activerecord_spanner_adapter/information_schema.rb +124 -72
  77. data/lib/activerecord_spanner_adapter/primary_key.rb +1 -1
  78. data/lib/activerecord_spanner_adapter/table/column.rb +7 -10
  79. data/lib/activerecord_spanner_adapter/version.rb +1 -1
  80. data/lib/arel/visitors/spanner.rb +3 -1
  81. data/lib/spanner_client_ext.rb +6 -1
  82. metadata +34 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73df0f7ee319edb7461b0fa60f8f8b611b38d798de2c0caf20a9c7d6796a7a85
4
- data.tar.gz: 9dedd540069c5bc85d458008c20e48b9b550a75388a1f4d48b1b29b34bb689d4
3
+ metadata.gz: 931e6b17bf3e6d6b068c57ca879bc0dbb06933790101aa05415514c6448fd004
4
+ data.tar.gz: c4981e814899be433830e0380f69060dfa589ec39dfa7e831aad616e224030ec
5
5
  SHA512:
6
- metadata.gz: faac93588f6e703f183a773caf553d61b64370439093ac27a7d1a4893f6d887c887fbf1be9082baf78bb81e2c23b24f45a1e4053672e386595ff95c0980a0c4b
7
- data.tar.gz: 4d6061c3daf7760d3015a33b52a41354f6bafa21b594a0c2b44852ae928935e1b2e23421ce417de2e65eecdc56de0890d97d0321e07fd3a3c3c104cf56856c88
6
+ metadata.gz: a989bdd8195d98206802a05294b09a53fe76c0684fba781d22dc15aaa5a38106ee3f193555cbe83e5f65254e3b39d95ed1cfb6445ded245ad80683d9752fdfd2
7
+ data.tar.gz: 0dbe1209ccad8da313fafb110bbb4fb2c1ef9ff9209230e88673f031ad02c0c372a13422fd2a5756fb0bc97a2318c003433350e071f64bcef37ba5a7754e868e
@@ -19,7 +19,7 @@ jobs:
19
19
  max-parallel: 4
20
20
  matrix:
21
21
  ruby: ["2.7", "3.0", "3.1", "3.2"]
22
- ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4"]
22
+ ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4", "~> 7.1.0"]
23
23
  # Exclude combinations that are not supported.
24
24
  exclude:
25
25
  - ruby: "3.0"
@@ -34,12 +34,14 @@ jobs:
34
34
  with:
35
35
  bundler-cache: true
36
36
  ruby-version: ${{ matrix.ruby }}
37
+ - name: Authenticate Google Cloud
38
+ uses: google-github-actions/auth@v2
39
+ with:
40
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
37
41
  - name: Setup GCloud
38
- uses: google-github-actions/setup-gcloud@v0
42
+ uses: google-github-actions/setup-gcloud@v2
39
43
  with:
40
44
  project_id: ${{ secrets.GCP_PROJECT_ID }}
41
- service_account_key: ${{ secrets.GCP_SA_KEY }}
42
- export_default_credentials: true
43
45
  - name: Install dependencies
44
46
  run: bundle install
45
47
  - name: Run acceptance tests on production
@@ -11,7 +11,7 @@ jobs:
11
11
  max-parallel: 4
12
12
  matrix:
13
13
  ruby: ["2.7", "3.0", "3.1", "3.2"]
14
- ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4"]
14
+ ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4", "~> 7.1.0"]
15
15
  # Exclude combinations that are not supported.
16
16
  exclude:
17
17
  - ruby: "3.0"
@@ -20,7 +20,7 @@ jobs:
20
20
  matrix:
21
21
  # Run acceptance tests all supported combinations of Ruby and ActiveRecord.
22
22
  ruby: [2.7, 3.0, 3.1, 3.2]
23
- ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5, 7.0.6, 7.0.7]
23
+ ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5, 7.0.6, 7.0.7, 7.1.0, 7.1.1, 7.1.2]
24
24
  # Exclude combinations that are not supported.
25
25
  exclude:
26
26
  - ruby: 3.0
@@ -20,12 +20,14 @@ jobs:
20
20
  with:
21
21
  bundler-cache: true
22
22
  ruby-version: ${{ matrix.ruby }}
23
+ - name: Authenticate Google Cloud
24
+ uses: google-github-actions/auth@v2
25
+ with:
26
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
23
27
  - name: Setup GCloud
24
- uses: google-github-actions/setup-gcloud@v0
28
+ uses: google-github-actions/setup-gcloud@v2
25
29
  with:
26
30
  project_id: ${{ secrets.GCP_PROJECT_ID }}
27
- service_account_key: ${{ secrets.GCP_SA_KEY }}
28
- export_default_credentials: true
29
31
  - name: Install dependencies
30
32
  run: bundle install
31
33
  - name: Run acceptance tests on production
@@ -12,7 +12,7 @@ jobs:
12
12
  matrix:
13
13
  # Run unit tests all supported combinations of Ruby and ActiveRecord.
14
14
  ruby: [2.7, 3.0, 3.1, 3.2]
15
- ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5]
15
+ ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5, 7.1.0, 7.1.1, 7.1.2]
16
16
  # Exclude combinations that are not supported.
17
17
  exclude:
18
18
  - ruby: 3.0
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - name: ReleaseLabel
14
- uses: actions/github-script@v6
14
+ uses: actions/github-script@v7
15
15
  with:
16
16
  github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
17
17
  script: |
@@ -18,7 +18,7 @@ jobs:
18
18
  with:
19
19
  ruby-version: '2.7'
20
20
  - name: cache gems
21
- uses: actions/cache@v3
21
+ uses: actions/cache@v4
22
22
  with:
23
23
  path: vendor/bundle
24
24
  key: ${{ runner.os }}-rubocop-${{ hashFiles('**/Gemfile.lock') }}
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.5.1"
2
+ ".": "1.6.1"
3
3
  }
data/.rubocop.yml CHANGED
@@ -41,6 +41,6 @@ Style/WordArray:
41
41
  Style/RegexpLiteral:
42
42
  Enabled: false
43
43
  Metrics/MethodLength:
44
- Max: 40
44
+ Max: 60
45
45
  Metrics/BlockLength:
46
46
  Max: 30
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ### 1.6.1 (2024-02-05)
4
+
5
+ #### Bug Fixes
6
+
7
+ * _insert_record failed for other adapters ([#298](https://github.com/googleapis/ruby-spanner-activerecord/issues/298))
8
+
9
+ ### 1.6.0 (2023-12-20)
10
+
11
+ #### Features
12
+
13
+ * interleaved tables with built-in composite pk ([#282](https://github.com/googleapis/ruby-spanner-activerecord/issues/282))
14
+ * support Query Logs ([#291](https://github.com/googleapis/ruby-spanner-activerecord/issues/291))
15
+ * support Rails 7.1 ([#278](https://github.com/googleapis/ruby-spanner-activerecord/issues/278))
16
+
3
17
  ### 1.5.1 (2023-12-12)
4
18
 
5
19
  #### Bug Fixes
data/Gemfile CHANGED
@@ -4,12 +4,17 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  gem "activerecord", ENV.fetch("AR_VERSION", "~> 6.1.6.1")
7
- gem "minitest", "~> 5.19.0"
7
+ gem "minitest", "~> 5.20.0"
8
+ gem "minitest-rg", "~> 5.3.0"
8
9
  gem "pry", "~> 0.13.0"
9
10
  gem "pry-byebug", "~> 3.9.0"
11
+ # Add sqlite3 for testing for compatibility with other adapters.
12
+ gem "sqlite3"
10
13
 
11
14
  # Required for samples and testing.
12
- gem "composite_primary_keys"
15
+ install_if -> { ENV.fetch("AR_VERSION", "~> 6.1.6.1").dup.to_s.sub("~>", "").strip < "7.1.0" && !ENV["SKIP_COMPOSITE_PK"] } do
16
+ gem "composite_primary_keys"
17
+ end
13
18
 
14
19
  # Required for samples
15
20
  gem "docker-api"
data/README.md CHANGED
@@ -84,19 +84,19 @@ Some noteworthy examples in the snippets directory:
84
84
  for inserting, updating and deleting data in a Cloud Spanner database. Mutations can have a significant performance
85
85
  advantage compared to DML statements, but do not allow read-your-writes semantics during a transaction.
86
86
  - [array-data-type](examples/snippets/array-data-type): Shows how to work with `ARRAY` data types.
87
- - [interleaved-tables](examples/snippets/interleaved-tables): Shows how to work with [Interleaved Tables](https://cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables).
87
+ - [interleaved-tables](examples/snippets/interleaved-tables-before-7.1): Shows how to work with [Interleaved Tables](https://cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables).
88
88
 
89
89
  ## Limitations
90
90
 
91
- Limitation|Comment|Resolution
92
- ---|---|---
93
- Interleaved tables require composite primary keys| Cloud Spanner requires composite primary keys for interleaved tables. See {file:examples/snippets/interleaved-tables/README.md this example} for an example on how to use interleaved tables with ActiveRecord |Use composite primary keys.
94
- Lack of sequential and auto-assigned IDs|Cloud Spanner doesn't autogenerate IDs and this integration instead creates UUID4 to avoid [hotspotting](https://cloud.google.com/spanner/docs/schema-design#uuid_primary_key) so you SHOULD NOT rely on IDs being sorted| UUID4s are automatically generated for primary keys.
95
- Table without Primary Key| Cloud Spanner support does not support tables without a primary key.| Always define a primary key for your table.
96
- Table names CANNOT have spaces within them whether back-ticked or not|Cloud Spanner DOES NOT support tables with spaces in them for example `Entity ID`|Ensure that your table names don't contain spaces.
97
- Table names CANNOT have punctuation marks and MUST contain valid UTF-8|Cloud Spanner DOES NOT support punctuation marks e.g. periods ".", question marks "?" in table names|Ensure that your table names don't contain punctuation marks.
98
- Index with fields length [add_index](https://apidock.com/rails/v5.2.3/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index)|Cloud Spanner does not support index with fields length | Ensure that your database definition does not include index definitions with field lengths.
99
- Only GoogleSQL-dialect databases| Cloud Spanner supports both GoogleSQL- and PostgreSQL-dialect databases. This adapter only supports GoogleSQL-dialect databases. | Ensure that your database uses the GoogleSQL dialect.
91
+ | Limitation | Comment | Resolution |
92
+ |-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
93
+ | Interleaved tables require composite primary keys | Cloud Spanner requires composite primary keys for interleaved tables. See {file:examples/snippets/interleaved-tables/README.md this example} for an example on how to use interleaved tables with ActiveRecord | Use composite primary keys. |
94
+ | Lack of sequential IDs | Cloud Spanner uses either using bit-reversed sequences or UUID4 to generated primary keys to avoid [hotspotting](https://cloud.google.com/spanner/docs/schema-design#uuid_primary_key) so you SHOULD NOT rely on IDs being sorted | Use either UUID4s or bit-reversed sequences to automatically generate primary keys. |
95
+ | Table without Primary Key | Cloud Spanner support does not support tables without a primary key. | Always define a primary key for your table. |
96
+ | Table names CANNOT have spaces within them whether back-ticked or not | Cloud Spanner DOES NOT support tables with spaces in them for example `Entity ID` | Ensure that your table names don't contain spaces. |
97
+ | Table names CANNOT have punctuation marks and MUST contain valid UTF-8 | Cloud Spanner DOES NOT support punctuation marks e.g. periods ".", question marks "?" in table names | Ensure that your table names don't contain punctuation marks. |
98
+ | Index with fields length [add_index](https://apidock.com/rails/v5.2.3/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index) | Cloud Spanner does not support index with fields length | Ensure that your database definition does not include index definitions with field lengths. |
99
+ | Only GoogleSQL-dialect databases | Cloud Spanner supports both GoogleSQL- and PostgreSQL-dialect databases. This adapter only supports GoogleSQL-dialect databases. | Ensure that your database uses the GoogleSQL dialect. |
100
100
 
101
101
  ## Contributing
102
102
 
@@ -6,6 +6,12 @@
6
6
 
7
7
  # frozen_string_literal: true
8
8
 
9
+ # ActiveRecord 7.1 introduced native support for composite primary keys.
10
+ # This deprecates the https://github.com/composite-primary-keys/composite_primary_keys gem that was previously used in
11
+ # this library to support composite primary keys, which again are needed for interleaved tables. These tests use the
12
+ # third-party composite primary key gem and are therefore not executed for Rails 7.1 and higher.
13
+ return if ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
14
+
9
15
  require "test_helper"
10
16
  require "models/singer"
11
17
  require "models/album"
@@ -196,8 +196,14 @@ module ActiveRecord
196
196
  end
197
197
  end
198
198
  end
199
+ expected = if ActiveRecord::gem_version < Gem::Version.create('7.1.0')
200
+ "you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table."
201
+ else
202
+ "you can't redefine the primary key column 'id' on 'testings'. To define a custom primary key, pass { id: false } to create_table."
203
+ end
204
+
199
205
 
200
- assert_equal "you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table.", error.message
206
+ assert_equal expected, error.message
201
207
  end
202
208
 
203
209
  def test_create_table_raises_when_redefining_custom_primary_key_column
@@ -209,7 +215,12 @@ module ActiveRecord
209
215
  end
210
216
  end
211
217
 
212
- assert_equal "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table.", error.message
218
+ expected = if ActiveRecord::gem_version < Gem::Version.create('7.1.0')
219
+ "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table."
220
+ else
221
+ "you can't redefine the primary key column 'testing_id' on 'testings'. To define a custom primary key, pass { id: false } to create_table."
222
+ end
223
+ assert_equal expected, error.message
213
224
  end
214
225
 
215
226
  def test_create_table_raises_when_defining_existing_column
@@ -222,7 +233,12 @@ module ActiveRecord
222
233
  end
223
234
  end
224
235
 
225
- assert_equal "you can't define an already defined column 'testing_column'.", error.message
236
+ expected = if ActiveRecord::gem_version < Gem::Version.create('7.1.0')
237
+ "you can't define an already defined column 'testing_column'."
238
+ else
239
+ "you can't define an already defined column 'testing_column' on 'testings'."
240
+ end
241
+ assert_equal expected, error.message
226
242
  end
227
243
 
228
244
  def test_create_table_with_timestamps_should_create_datetime_columns
@@ -13,6 +13,10 @@ module ActiveRecord
13
13
  class IndexTest < SpannerAdapter::TestCase
14
14
  include SpannerAdapter::Migration::TestHelper
15
15
 
16
+ def is_7_1_or_higher?
17
+ ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
18
+ end
19
+
16
20
  def test_dump_schema_contains_start_batch_ddl
17
21
  connection = ActiveRecord::Base.connection
18
22
  schema = StringIO.new
@@ -34,7 +38,12 @@ module ActiveRecord
34
38
  connection = ActiveRecord::Base.connection
35
39
  schema = StringIO.new
36
40
  ActiveRecord::SchemaDumper.dump connection, schema
37
- assert schema.string.include?("create_table \"albums\", primary_key: \"albumid\"")
41
+ sql = schema.string
42
+ if is_7_1_or_higher?
43
+ assert schema.string.include?("create_table \"albums\", primary_key: [\"singerid\", \"albumid\"]"), sql
44
+ else
45
+ assert schema.string.include?("create_table \"albums\", primary_key: \"albumid\""), sql
46
+ end
38
47
  end
39
48
 
40
49
  def test_dump_schema_contains_interleaved_index
@@ -6,6 +6,12 @@
6
6
 
7
7
  # frozen_string_literal: true
8
8
 
9
+ # ActiveRecord 7.1 introduced native support for composite primary keys.
10
+ # This deprecates the https://github.com/composite-primary-keys/composite_primary_keys gem that was previously used in
11
+ # this library to support composite primary keys, which again are needed for interleaved tables. These tests use the
12
+ # third-party composite primary key gem and are therefore not executed for Rails 7.1 and higher.
13
+ return if ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
14
+
9
15
  require "test_helper"
10
16
  require "test_helpers/with_separate_database"
11
17