activerecord-spanner-adapter 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (264) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/.github/sync-repo-settings.yaml +16 -0
  4. data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
  5. data/.github/workflows/acceptance-tests-on-production.yaml +36 -0
  6. data/.github/workflows/ci.yaml +33 -0
  7. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
  8. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
  9. data/.github/workflows/nightly-unit-tests.yaml +40 -0
  10. data/.github/workflows/release-please-label.yml +25 -0
  11. data/.github/workflows/release-please.yml +39 -0
  12. data/.github/workflows/rubocop.yaml +31 -0
  13. data/.gitignore +67 -5
  14. data/.kokoro/populate-secrets.sh +77 -0
  15. data/.kokoro/release.cfg +33 -0
  16. data/.kokoro/release.sh +15 -0
  17. data/.kokoro/trampoline_v2.sh +489 -0
  18. data/.rubocop.yml +46 -0
  19. data/.toys/release.rb +18 -0
  20. data/.trampolinerc +48 -0
  21. data/.yardopts +11 -0
  22. data/CHANGELOG.md +26 -0
  23. data/CODE_OF_CONDUCT.md +40 -0
  24. data/CONTRIBUTING.md +79 -0
  25. data/Gemfile +9 -4
  26. data/LICENSE +6 -6
  27. data/README.md +67 -30
  28. data/Rakefile +79 -3
  29. data/SECURITY.md +7 -0
  30. data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
  31. data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
  32. data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
  33. data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
  34. data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
  35. data/acceptance/cases/migration/change_schema_test.rb +433 -0
  36. data/acceptance/cases/migration/change_table_test.rb +115 -0
  37. data/acceptance/cases/migration/column_attributes_test.rb +122 -0
  38. data/acceptance/cases/migration/column_positioning_test.rb +48 -0
  39. data/acceptance/cases/migration/columns_test.rb +201 -0
  40. data/acceptance/cases/migration/command_recorder_test.rb +406 -0
  41. data/acceptance/cases/migration/create_join_table_test.rb +216 -0
  42. data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
  43. data/acceptance/cases/migration/foreign_key_test.rb +297 -0
  44. data/acceptance/cases/migration/index_test.rb +211 -0
  45. data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
  46. data/acceptance/cases/migration/references_index_test.rb +135 -0
  47. data/acceptance/cases/migration/references_statements_test.rb +166 -0
  48. data/acceptance/cases/migration/rename_column_test.rb +96 -0
  49. data/acceptance/cases/models/calculation_query_test.rb +128 -0
  50. data/acceptance/cases/models/generated_column_test.rb +126 -0
  51. data/acceptance/cases/models/mutation_test.rb +122 -0
  52. data/acceptance/cases/models/query_test.rb +147 -0
  53. data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
  54. data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
  55. data/acceptance/cases/transactions/read_only_transactions_test.rb +67 -0
  56. data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
  57. data/acceptance/cases/type/all_types_test.rb +152 -0
  58. data/acceptance/cases/type/binary_test.rb +59 -0
  59. data/acceptance/cases/type/boolean_test.rb +31 -0
  60. data/acceptance/cases/type/date_test.rb +32 -0
  61. data/acceptance/cases/type/date_time_test.rb +30 -0
  62. data/acceptance/cases/type/float_test.rb +27 -0
  63. data/acceptance/cases/type/integer_test.rb +44 -0
  64. data/acceptance/cases/type/numeric_test.rb +27 -0
  65. data/acceptance/cases/type/string_test.rb +79 -0
  66. data/acceptance/cases/type/text_test.rb +30 -0
  67. data/acceptance/cases/type/time_test.rb +87 -0
  68. data/acceptance/models/account.rb +13 -0
  69. data/acceptance/models/address.rb +9 -0
  70. data/acceptance/models/album.rb +12 -0
  71. data/acceptance/models/all_types.rb +8 -0
  72. data/acceptance/models/author.rb +11 -0
  73. data/acceptance/models/club.rb +12 -0
  74. data/acceptance/models/comment.rb +9 -0
  75. data/acceptance/models/customer.rb +9 -0
  76. data/acceptance/models/department.rb +9 -0
  77. data/acceptance/models/firm.rb +10 -0
  78. data/acceptance/models/member.rb +13 -0
  79. data/acceptance/models/member_type.rb +9 -0
  80. data/acceptance/models/membership.rb +10 -0
  81. data/acceptance/models/organization.rb +9 -0
  82. data/acceptance/models/post.rb +10 -0
  83. data/acceptance/models/singer.rb +10 -0
  84. data/acceptance/models/track.rb +20 -0
  85. data/acceptance/models/transaction.rb +9 -0
  86. data/acceptance/schema/schema.rb +143 -0
  87. data/acceptance/test_helper.rb +260 -0
  88. data/activerecord-spanner-adapter.gemspec +32 -17
  89. data/assets/solidus-db.png +0 -0
  90. data/benchmarks/README.md +17 -0
  91. data/benchmarks/Rakefile +14 -0
  92. data/benchmarks/application.rb +308 -0
  93. data/benchmarks/config/database.yml +8 -0
  94. data/benchmarks/config/environment.rb +12 -0
  95. data/benchmarks/db/migrate/01_create_tables.rb +25 -0
  96. data/benchmarks/db/schema.rb +29 -0
  97. data/benchmarks/models/album.rb +9 -0
  98. data/benchmarks/models/singer.rb +9 -0
  99. data/bin/console +6 -7
  100. data/examples/rails/README.md +262 -0
  101. data/examples/snippets/README.md +29 -0
  102. data/examples/snippets/Rakefile +57 -0
  103. data/examples/snippets/array-data-type/README.md +45 -0
  104. data/examples/snippets/array-data-type/Rakefile +13 -0
  105. data/examples/snippets/array-data-type/application.rb +45 -0
  106. data/examples/snippets/array-data-type/config/database.yml +8 -0
  107. data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
  108. data/examples/snippets/array-data-type/db/schema.rb +26 -0
  109. data/examples/snippets/array-data-type/db/seeds.rb +5 -0
  110. data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
  111. data/examples/snippets/bin/create_emulator_instance.rb +18 -0
  112. data/examples/snippets/bulk-insert/README.md +21 -0
  113. data/examples/snippets/bulk-insert/Rakefile +13 -0
  114. data/examples/snippets/bulk-insert/application.rb +64 -0
  115. data/examples/snippets/bulk-insert/config/database.yml +8 -0
  116. data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
  117. data/examples/snippets/bulk-insert/db/schema.rb +26 -0
  118. data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
  119. data/examples/snippets/bulk-insert/models/album.rb +9 -0
  120. data/examples/snippets/bulk-insert/models/singer.rb +9 -0
  121. data/examples/snippets/commit-timestamp/README.md +18 -0
  122. data/examples/snippets/commit-timestamp/Rakefile +13 -0
  123. data/examples/snippets/commit-timestamp/application.rb +53 -0
  124. data/examples/snippets/commit-timestamp/config/database.yml +8 -0
  125. data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
  126. data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
  127. data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
  128. data/examples/snippets/commit-timestamp/models/album.rb +9 -0
  129. data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
  130. data/examples/snippets/config/environment.rb +21 -0
  131. data/examples/snippets/create-records/README.md +12 -0
  132. data/examples/snippets/create-records/Rakefile +13 -0
  133. data/examples/snippets/create-records/application.rb +42 -0
  134. data/examples/snippets/create-records/config/database.yml +8 -0
  135. data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
  136. data/examples/snippets/create-records/db/schema.rb +26 -0
  137. data/examples/snippets/create-records/db/seeds.rb +5 -0
  138. data/examples/snippets/create-records/models/album.rb +9 -0
  139. data/examples/snippets/create-records/models/singer.rb +9 -0
  140. data/examples/snippets/date-data-type/README.md +19 -0
  141. data/examples/snippets/date-data-type/Rakefile +13 -0
  142. data/examples/snippets/date-data-type/application.rb +35 -0
  143. data/examples/snippets/date-data-type/config/database.yml +8 -0
  144. data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
  145. data/examples/snippets/date-data-type/db/schema.rb +21 -0
  146. data/examples/snippets/date-data-type/db/seeds.rb +16 -0
  147. data/examples/snippets/date-data-type/models/singer.rb +8 -0
  148. data/examples/snippets/generated-column/README.md +41 -0
  149. data/examples/snippets/generated-column/Rakefile +13 -0
  150. data/examples/snippets/generated-column/application.rb +37 -0
  151. data/examples/snippets/generated-column/config/database.yml +8 -0
  152. data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
  153. data/examples/snippets/generated-column/db/schema.rb +21 -0
  154. data/examples/snippets/generated-column/db/seeds.rb +18 -0
  155. data/examples/snippets/generated-column/models/singer.rb +8 -0
  156. data/examples/snippets/interleaved-tables/README.md +152 -0
  157. data/examples/snippets/interleaved-tables/Rakefile +13 -0
  158. data/examples/snippets/interleaved-tables/application.rb +109 -0
  159. data/examples/snippets/interleaved-tables/config/database.yml +8 -0
  160. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
  161. data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
  162. data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
  163. data/examples/snippets/interleaved-tables/models/album.rb +15 -0
  164. data/examples/snippets/interleaved-tables/models/singer.rb +20 -0
  165. data/examples/snippets/interleaved-tables/models/track.rb +25 -0
  166. data/examples/snippets/migrations/README.md +43 -0
  167. data/examples/snippets/migrations/Rakefile +13 -0
  168. data/examples/snippets/migrations/application.rb +26 -0
  169. data/examples/snippets/migrations/config/database.yml +8 -0
  170. data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
  171. data/examples/snippets/migrations/db/schema.rb +33 -0
  172. data/examples/snippets/migrations/db/seeds.rb +5 -0
  173. data/examples/snippets/migrations/models/album.rb +10 -0
  174. data/examples/snippets/migrations/models/singer.rb +10 -0
  175. data/examples/snippets/migrations/models/track.rb +9 -0
  176. data/examples/snippets/mutations/README.md +34 -0
  177. data/examples/snippets/mutations/Rakefile +13 -0
  178. data/examples/snippets/mutations/application.rb +47 -0
  179. data/examples/snippets/mutations/config/database.yml +8 -0
  180. data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
  181. data/examples/snippets/mutations/db/schema.rb +27 -0
  182. data/examples/snippets/mutations/db/seeds.rb +25 -0
  183. data/examples/snippets/mutations/models/album.rb +9 -0
  184. data/examples/snippets/mutations/models/singer.rb +9 -0
  185. data/examples/snippets/optimistic-locking/README.md +12 -0
  186. data/examples/snippets/optimistic-locking/Rakefile +13 -0
  187. data/examples/snippets/optimistic-locking/application.rb +48 -0
  188. data/examples/snippets/optimistic-locking/config/database.yml +8 -0
  189. data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
  190. data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
  191. data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
  192. data/examples/snippets/optimistic-locking/models/album.rb +9 -0
  193. data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
  194. data/examples/snippets/quickstart/README.md +26 -0
  195. data/examples/snippets/quickstart/Rakefile +13 -0
  196. data/examples/snippets/quickstart/application.rb +51 -0
  197. data/examples/snippets/quickstart/config/database.yml +8 -0
  198. data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
  199. data/examples/snippets/quickstart/db/schema.rb +26 -0
  200. data/examples/snippets/quickstart/db/seeds.rb +24 -0
  201. data/examples/snippets/quickstart/models/album.rb +9 -0
  202. data/examples/snippets/quickstart/models/singer.rb +9 -0
  203. data/examples/snippets/read-only-transactions/README.md +13 -0
  204. data/examples/snippets/read-only-transactions/Rakefile +13 -0
  205. data/examples/snippets/read-only-transactions/application.rb +49 -0
  206. data/examples/snippets/read-only-transactions/config/database.yml +8 -0
  207. data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
  208. data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
  209. data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
  210. data/examples/snippets/read-only-transactions/models/album.rb +9 -0
  211. data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
  212. data/examples/snippets/read-write-transactions/README.md +12 -0
  213. data/examples/snippets/read-write-transactions/Rakefile +13 -0
  214. data/examples/snippets/read-write-transactions/application.rb +39 -0
  215. data/examples/snippets/read-write-transactions/config/database.yml +8 -0
  216. data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
  217. data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
  218. data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
  219. data/examples/snippets/read-write-transactions/models/album.rb +9 -0
  220. data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
  221. data/examples/snippets/timestamp-data-type/README.md +17 -0
  222. data/examples/snippets/timestamp-data-type/Rakefile +13 -0
  223. data/examples/snippets/timestamp-data-type/application.rb +42 -0
  224. data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
  225. data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
  226. data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
  227. data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
  228. data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
  229. data/examples/solidus/README.md +172 -0
  230. data/lib/active_record/connection_adapters/spanner/database_statements.rb +224 -269
  231. data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
  232. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
  233. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
  234. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
  235. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
  236. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
  237. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
  238. data/lib/active_record/connection_adapters/spanner_adapter.rb +182 -78
  239. data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
  240. data/lib/active_record/type/spanner/array.rb +32 -0
  241. data/lib/active_record/type/spanner/bytes.rb +26 -0
  242. data/lib/active_record/type/spanner/spanner_active_record_converter.rb +32 -0
  243. data/lib/active_record/type/spanner/time.rb +37 -0
  244. data/lib/activerecord-spanner-adapter.rb +23 -0
  245. data/lib/activerecord_spanner_adapter/base.rb +217 -0
  246. data/lib/activerecord_spanner_adapter/connection.rb +324 -0
  247. data/lib/activerecord_spanner_adapter/errors.rb +13 -0
  248. data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
  249. data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
  250. data/lib/activerecord_spanner_adapter/index.rb +80 -0
  251. data/lib/activerecord_spanner_adapter/information_schema.rb +261 -0
  252. data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
  253. data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
  254. data/lib/activerecord_spanner_adapter/table.rb +61 -0
  255. data/lib/activerecord_spanner_adapter/transaction.rb +113 -0
  256. data/lib/activerecord_spanner_adapter/version.rb +9 -0
  257. data/lib/arel/visitors/spanner.rb +35 -0
  258. data/lib/spanner_client_ext.rb +82 -0
  259. data/renovate.json +5 -0
  260. metadata +387 -34
  261. data/.travis.yml +0 -5
  262. data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
  263. data/lib/active_record/connection_adapters/spanner.rb +0 -10
  264. data/lib/activerecord-spanner-adapter/version.rb +0 -3
data/Rakefile CHANGED
@@ -1,6 +1,82 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
1
7
  require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
8
+ require "rake/testtask"
9
+ require "securerandom"
10
+
11
+ desc "Run tests."
12
+ Rake::TestTask.new do |t|
13
+ t.libs << "test"
14
+ t.libs << "lib"
15
+ t.test_files = FileList["test/**/*_test.rb"]
16
+ t.warning = false
17
+ end
18
+
19
+ task :default => :test
20
+
21
+ require "yard"
22
+ require "yard/rake/yardoc_task"
23
+ YARD::Rake::YardocTask.new do |y|
24
+ # y.options << "--fail-on-warning"
25
+ end
26
+
27
+ desc "Run the spanner connector acceptance tests."
28
+ task :acceptance, [:project, :keyfile, :instance, :tests] do |t, args|
29
+ project = args[:project]
30
+ project ||= ENV["SPANNER_TEST_PROJECT"] || ENV["GCLOUD_TEST_PROJECT"]
31
+ emulator_host = args[:emulator_host]
32
+ emulator_host ||= ENV["SPANNER_EMULATOR_HOST"]
33
+ keyfile = args[:keyfile]
34
+ keyfile ||= ENV["SPANNER_TEST_KEYFILE"] || ENV["GCLOUD_TEST_KEYFILE"] || ENV["GOOGLE_APPLICATION_CREDENTIALS"]
35
+ if keyfile
36
+ keyfile = File.read keyfile
37
+ else
38
+ keyfile ||= ENV["SPANNER_TEST_KEYFILE_JSON"] || ENV["GCLOUD_TEST_KEYFILE_JSON"]
39
+ end
40
+ if project.nil? || (keyfile.nil? && emulator_host.nil?)
41
+ fail "You must provide a project and keyfile or emulator host name."
42
+ end
43
+ instance = args[:instance]
44
+ instance ||= ENV["SPANNER_TEST_INSTANCE"]
45
+ if instance.nil?
46
+ fail "You must provide an instance name"
47
+ end
48
+
49
+ # clear any env var already set
50
+ require "google/cloud/spanner/credentials"
51
+ Google::Cloud::Spanner::Credentials.env_vars.each do |path|
52
+ ENV[path] = nil
53
+ end
54
+
55
+ tests = args[:tests]
56
+ tests ||= "**"
57
+
58
+ # always overwrite when running tests
59
+ ENV["SPANNER_PROJECT"] = project
60
+ ENV["SPANNER_KEYFILE_JSON"] = keyfile
61
+ ENV["SPANNER_TEST_INSTANCE"] = instance
62
+ ENV["SPANNER_EMULATOR_HOST"] = emulator_host
63
+
64
+ Rake::TestTask.new :run do |t|
65
+ t.libs << "acceptance"
66
+ t.libs << "lib"
67
+ t.test_files = FileList["acceptance/#{tests}/*_test.rb"] unless tests.start_with? "exclude "
68
+ t.test_files = FileList.new("acceptance/**/*_test.rb") do |fl|
69
+ fl.exclude "acceptance/#{tests.split(" ")[1]}/*_test.rb"
70
+ puts "excluding acceptance/#{tests.split(" ")[1]}/*_test.rb"
71
+ end if tests.start_with? "exclude"
72
+ t.warning = false
73
+ end
3
74
 
4
- RSpec::Core::RakeTask.new("spec")
5
- task :default => :spec
75
+ Rake::Task["run"].invoke
76
+ end
6
77
 
78
+ desc +"Runs the `examples/snippets/quickstart` example on a Spanner emulator. See the directory `examples/snippets`"
79
+ "for more examples."
80
+ task :example do
81
+ Dir.chdir("examples/snippets/quickstart") { sh "bundle exec rake run" }
82
+ end
data/SECURITY.md ADDED
@@ -0,0 +1,7 @@
1
+ # Security Policy
2
+
3
+ To report a security issue, please use [g.co/vulnz](https://g.co/vulnz).
4
+
5
+ The Google Security Team will respond within 5 working days of your report on g.co/vulnz.
6
+
7
+ We use g.co/vulnz for our intake, and do coordination and disclosure here using GitHub Security Advisory to privately discuss and fix the issue.
@@ -0,0 +1,119 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require "test_helper"
10
+ require "models/firm"
11
+ require "models/account"
12
+ require "models/transaction"
13
+ require "models/department"
14
+ require "models/customer"
15
+
16
+ module ActiveRecord
17
+ module Associations
18
+ class HasManyTest < SpannerAdapter::TestCase
19
+ include SpannerAdapter::Associations::TestHelper
20
+
21
+ attr_accessor :customer
22
+
23
+ def setup
24
+ super
25
+
26
+ @customer = Customer.create name: "Customer - 1"
27
+
28
+ Account.create name: "Account - 1", customer: customer, credit_limit: 100
29
+ Account.create name: "Account - 2", customer: customer, credit_limit: 200
30
+ end
31
+
32
+ def teardown
33
+ Customer.destroy_all
34
+ Account.destroy_all
35
+ end
36
+
37
+ def test_has_many
38
+ assert_equal 2, customer.accounts.count
39
+ assert_equal customer.accounts.pluck(:credit_limit).sort, [100, 200]
40
+ end
41
+
42
+ def test_finding_using_associated_fields
43
+ assert_equal Account.where(customer_id: customer.id).to_a, customer.accounts.to_a
44
+ end
45
+
46
+ def test_successful_build_association
47
+ account = customer.accounts.build(name: "Account - 3", credit_limit: 1000)
48
+ assert account.save
49
+
50
+ customer.reload
51
+ assert_equal account, customer.accounts.find(account.id)
52
+ end
53
+
54
+ def test_create_and_destroy_associated_records
55
+ customer2 = Customer.new name: "Customer - 2"
56
+ customer2.accounts.build name: "Account - 11", credit_limit: 100
57
+ customer2.accounts.build name: "Account - 12", credit_limit: 200
58
+ customer2.save!
59
+
60
+ customer2.reload
61
+
62
+ assert_equal 2, customer2.accounts.count
63
+ assert_equal 4, Account.count
64
+
65
+ customer2.accounts.destroy_all
66
+ customer2.reload
67
+
68
+ assert_equal 0, customer2.accounts.count
69
+ assert_equal 2, Account.count
70
+ end
71
+
72
+ def test_create_and_delete_associated_records
73
+ customer2 = Customer.new name: "Customer - 2"
74
+ customer2.accounts.build name: "Account - 11", credit_limit: 100
75
+ customer2.accounts.build name: "Account - 12", credit_limit: 200
76
+ customer2.save!
77
+
78
+ customer2.reload
79
+
80
+ assert_equal 2, customer2.accounts.count
81
+ assert_equal 4, Account.count
82
+
83
+ assert_equal 2, customer2.accounts.delete_all
84
+ customer2.reload
85
+
86
+ assert_equal 0, customer2.accounts.count
87
+ assert_equal 2, Account.where(customer_id: nil).count
88
+ end
89
+
90
+ def test_update_associated_records
91
+ count = customer.accounts.update_all(name: "Account - Update", credit_limit: 1000)
92
+ assert_equal customer.accounts.count, count
93
+
94
+ customer.reload
95
+ customer.accounts.each do |account|
96
+ assert_equal "Account - Update", account.name
97
+ assert_equal 1000, account.credit_limit
98
+ end
99
+ end
100
+
101
+ def test_fetch_associated_record_with_order
102
+ accounts = customer.accounts.order(credit_limit: :desc)
103
+ assert_equal [200, 100], accounts.pluck(:credit_limit)
104
+
105
+ accounts = customer.accounts.order(credit_limit: :asc)
106
+ assert_equal [100, 200], accounts.pluck(:credit_limit)
107
+ end
108
+
109
+ def test_set_counter_cache
110
+ account = Account.first
111
+ account.transactions.create!(amount: 10)
112
+ account.transactions.create!(amount: 20)
113
+
114
+ account.reload
115
+ assert_equal 2, account.transactions_count
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,63 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require "test_helper"
10
+ require "models/member"
11
+ require "models/membership"
12
+ require "models/member_type"
13
+ require "models/club"
14
+
15
+ module ActiveRecord
16
+ module Associations
17
+ class HasManyThroughTest < SpannerAdapter::TestCase
18
+ include SpannerAdapter::Associations::TestHelper
19
+
20
+ attr_accessor :club, :member_one, :member_two
21
+
22
+ def setup
23
+ super
24
+
25
+ @club = Club.create name: "Club - 1"
26
+ @member_one = Member.create name: "Member - 1"
27
+ @member_two = Member.create name: "Member - 2"
28
+ end
29
+
30
+ def teardown
31
+ Member.destroy_all
32
+ Club.destroy_all
33
+ end
34
+
35
+ def test_has_many_through_create_record
36
+ assert club.members.create!(name: "Member - 3")
37
+ end
38
+
39
+ def test_through_association_with_joins
40
+ club.members = [member_one, member_two]
41
+ assert_equal [club, club], Club.where(id: club.id).joins(:members).to_a
42
+ end
43
+
44
+ def test_set_record_after_delete_association
45
+ club.members = [member_one, member_two]
46
+ club.reload
47
+ assert_equal 2, club.members.count
48
+
49
+ club.members = []
50
+ club.reload
51
+ assert_empty club.members
52
+ end
53
+
54
+ def test_has_many_through_eager_loading
55
+ club.members = [member_one, member_two]
56
+
57
+ clubs = Club.includes(:members).all.to_a
58
+ assert_equal 1, clubs.size
59
+ assert_not_nil assert_no_queries { clubs[0].members }
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,79 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require "test_helper"
10
+ require "models/firm"
11
+ require "models/account"
12
+ require "models/department"
13
+
14
+
15
+ module ActiveRecord
16
+ module Associations
17
+ class HasOneTest < SpannerAdapter::TestCase
18
+ include SpannerAdapter::Associations::TestHelper
19
+
20
+ attr_accessor :firm, :account
21
+
22
+ def setup
23
+ super
24
+
25
+ @account = Account.create name: "Account - #{rand 1000}", credit_limit: 100
26
+ @firm = Firm.create name: "Firm-#{rand 1000}", account: account
27
+
28
+ @account.reload
29
+ @firm.reload
30
+ end
31
+
32
+ def teardown
33
+ Firm.destroy_all
34
+ Account.destroy_all
35
+ Department.destroy_all
36
+ end
37
+
38
+ def test_has_one
39
+ assert_equal account, firm.account
40
+ assert_equal account.credit_limit, firm.account.credit_limit
41
+ end
42
+
43
+ def test_has_one_does_not_use_order_by
44
+ sql_log = capture_sql { firm.account }
45
+ assert sql_log.all? { |sql| !/order by/i.match?(sql) }, "ORDER BY was used in the query: #{sql_log}"
46
+ end
47
+
48
+ def test_finding_using_primary_key
49
+ assert_equal Account.find_by(firm_id: firm.id), firm.account
50
+ end
51
+
52
+ def test_successful_build_association
53
+ account = firm.build_account(credit_limit: 1000)
54
+ assert account.save
55
+
56
+ firm.reload
57
+ assert_equal account, firm.account
58
+ end
59
+
60
+ def test_delete_associated_records
61
+ assert_equal account, firm.account
62
+
63
+ firm.account.destroy
64
+ firm.reload
65
+ assert_nil firm.account
66
+ end
67
+
68
+ def test_polymorphic_association
69
+ assert_equal 0, firm.departments.count
70
+
71
+ firm.departments.create(name: "Department - 1")
72
+ firm.reload
73
+
74
+ assert_equal 1, firm.departments.count
75
+ assert_equal "Department - 1", firm.departments.first.name
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,98 @@
1
+ # Copyright 2020 Google LLC
2
+ #
3
+ # Use of this source code is governed by an MIT-style
4
+ # license that can be found in the LICENSE file or at
5
+ # https://opensource.org/licenses/MIT.
6
+
7
+ # frozen_string_literal: true
8
+
9
+ require "test_helper"
10
+ require "models/member"
11
+ require "models/membership"
12
+ require "models/member_type"
13
+ require "models/club"
14
+
15
+ module ActiveRecord
16
+ module Associations
17
+ class HasOneThroughTest < SpannerAdapter::TestCase
18
+ include SpannerAdapter::Associations::TestHelper
19
+
20
+ attr_accessor :club, :member, :member_type
21
+
22
+ def setup
23
+ super
24
+
25
+ @club = Club.create name: "Club - 1"
26
+ @member_type = MemberType.create name: "MemberType - 1"
27
+ @member = Member.create name: "Member - 1"
28
+ end
29
+
30
+ def teardown
31
+ Member.destroy_all
32
+ Club.destroy_all
33
+ Membership.destroy_all
34
+ MemberType.destroy_all
35
+ end
36
+
37
+ def test_has_one_through
38
+ member.club = club
39
+ member.reload
40
+ assert_equal club, member.club
41
+ end
42
+
43
+ def test_creating_association_creates_through_record
44
+ member.club = club
45
+
46
+ assert_equal member.id, member.membership.member_id
47
+ assert_equal club.id, member.membership.club.id
48
+ assert_equal club, member.club
49
+ end
50
+
51
+ def test_set_record_to_nil_should_delete_association
52
+ member.club = club
53
+ member.reload
54
+ assert_equal club, member.club
55
+
56
+ member.club = nil
57
+ member.reload
58
+ assert_nil member.membership
59
+ assert_nil member.club
60
+ end
61
+
62
+ def test_set_record_after_delete_association
63
+ member.club = club
64
+ member.reload
65
+ assert_equal club, member.club
66
+
67
+ member.club = nil
68
+ member.reload
69
+ assert_nil member.membership
70
+ assert_nil member.club
71
+
72
+ club = Club.create name: "Club - 2"
73
+ member.club = club
74
+ member.reload
75
+ assert_equal club, member.club
76
+ end
77
+
78
+ def test_has_one_through_eager_loading
79
+ member.club = club
80
+
81
+ members = Member.includes(:club).all.to_a
82
+ assert_equal 1, members.size
83
+ assert_not_nil assert_no_queries { members[0].club }
84
+ end
85
+
86
+ def test_has_one_through_with_where_condition
87
+ member.favourite_club = club
88
+ membership = member.membership
89
+ membership.favourite = true
90
+ membership.save
91
+
92
+ member.reload
93
+
94
+ assert_equal member.favourite_club, club
95
+ end
96
+ end
97
+ end
98
+ end