with_model 2.3.0 → 2.4.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.
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe WithModel::Table do
6
+ let(:connection_owner) do
7
+ klass = Class.new(ActiveRecord::Base)
8
+ stub_const("TableSpecConnection", klass)
9
+ klass.abstract_class = true
10
+ klass.establish_connection(adapter: "sqlite3", database: ":memory:")
11
+ klass
12
+ end
13
+ let(:connection) { connection_owner.connection }
14
+ let(:table) { described_class.new(:temporary_records, connection:) }
15
+
16
+ after { connection_owner.connection_pool.disconnect! }
17
+
18
+ describe "#destroy" do
19
+ it "does not raise after its connection reconnects before teardown" do
20
+ table.create
21
+
22
+ connection.disconnect!
23
+ connection.reconnect!
24
+
25
+ expect(connection.data_source_exists?(:temporary_records)).to be false
26
+ expect { table.destroy }.not_to raise_error
27
+ end
28
+
29
+ it "drops an existing table" do
30
+ table.create
31
+
32
+ table.destroy
33
+
34
+ expect(connection.data_source_exists?(:temporary_records)).to be false
35
+ end
36
+ end
37
+ end
@@ -124,7 +124,7 @@ describe "a temporary ActiveRecord model created with with_model" do
124
124
 
125
125
  it "does not singularize the constant name" do
126
126
  expect(BlogPosts).to be
127
- expect(-> { BlogPost }).to raise_error(NameError)
127
+ expect { BlogPost }.to raise_error(NameError)
128
128
  end
129
129
  end
130
130
 
@@ -198,7 +198,7 @@ describe "a temporary ActiveRecord model created with with_model" do
198
198
  end
199
199
 
200
200
  it "has the mixin" do
201
- expect(-> { WithAMixin.new.foo }).not_to raise_error
201
+ expect { WithAMixin.new.foo }.not_to raise_error
202
202
  expect(WithAMixin.include?(AMixin)).to be true
203
203
  end
204
204
  end
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_unit_helper"
4
+
5
+ class TestUnitLifecycleBaseTest < Test::Unit::TestCase
6
+ FIXTURE_EVENTS = []
7
+
8
+ with_model :TestUnitLifecycleParent do
9
+ table { |t| t.string "name" }
10
+ end
11
+
12
+ def setup
13
+ FIXTURE_EVENTS.clear
14
+ assert_kind_of Class, TestUnitLifecycleParent
15
+ FIXTURE_EVENTS << :inherited_setup
16
+ end
17
+
18
+ def teardown
19
+ assert_kind_of Class, TestUnitLifecycleParent
20
+ FIXTURE_EVENTS << :inherited_teardown
21
+ end
22
+ end
23
+
24
+ class TestUnitLifecycleTest < TestUnitLifecycleBaseTest
25
+ with_model :TestUnitLifecycleChild do
26
+ table do |t|
27
+ t.references "parent", foreign_key: {to_table: TestUnitLifecycleParent.table_name}
28
+ end
29
+
30
+ model { belongs_to :parent, class_name: TestUnitLifecycleParent.name }
31
+ end
32
+
33
+ def setup
34
+ super
35
+ assert_kind_of Class, TestUnitLifecycleChild
36
+ FIXTURE_EVENTS << :local_setup
37
+ end
38
+
39
+ def teardown
40
+ assert_kind_of Class, TestUnitLifecycleChild
41
+ FIXTURE_EVENTS << :local_teardown
42
+ super
43
+ return unless name == "test_models_are_available_through_user_fixtures"
44
+
45
+ assert_equal [
46
+ :inherited_setup,
47
+ :local_setup,
48
+ :test_body,
49
+ :local_teardown,
50
+ :inherited_teardown
51
+ ], FIXTURE_EVENTS
52
+ end
53
+
54
+ def test_models_are_available_through_user_fixtures
55
+ parent = TestUnitLifecycleParent.create!(name: "parent")
56
+ child = TestUnitLifecycleChild.create!(parent: parent)
57
+
58
+ assert_equal parent, child.reload.parent
59
+ FIXTURE_EVENTS << :test_body
60
+ end
61
+
62
+ def test_model_destruction_unwinds_inheritance_order
63
+ parent_test_case = Class.new(Test::Unit::TestCase) do
64
+ with_model :TestUnitLifecycleOrderParent do
65
+ table
66
+ end
67
+ end
68
+ test_case = Class.new(parent_test_case) do
69
+ teardown(after: :prepend) do
70
+ assert_false Object.const_defined?(:TestUnitLifecycleOrderChild)
71
+ assert_false ActiveRecord::Base.connection.data_source_exists?(child_table_name)
72
+ assert_kind_of Class, TestUnitLifecycleOrderParent
73
+ assert ActiveRecord::Base.connection.data_source_exists?(parent_table_name)
74
+ end
75
+
76
+ with_model :TestUnitLifecycleOrderChild do
77
+ table
78
+ end
79
+
80
+ define_method(:parent_table_name) { TestUnitLifecycleOrderParent.table_name }
81
+ define_method(:child_table_name) { @child_table_name }
82
+ define_method(:test_lifecycle) { @child_table_name = TestUnitLifecycleOrderChild.table_name }
83
+ end
84
+
85
+ result = Test::Unit::TestResult.new
86
+ test_case.new("test_lifecycle").run(result) {}
87
+
88
+ assert_equal 1, result.run_count
89
+ assert_equal 0, result.error_count
90
+ end
91
+ end
92
+
93
+ class TestUnitUserHookFailureTest < Test::Unit::TestCase
94
+ def test_setup_error_does_not_prevent_model_cleanup
95
+ test_case = Class.new(Test::Unit::TestCase) do
96
+ class << self
97
+ attr_accessor :created_table_name
98
+ end
99
+
100
+ with_model :TestUnitSetupFailureModel do
101
+ table
102
+ end
103
+
104
+ def setup
105
+ self.class.created_table_name = TestUnitSetupFailureModel.table_name
106
+ raise "setup failed"
107
+ end
108
+
109
+ def test_setup_failure
110
+ end
111
+ end
112
+
113
+ result = Test::Unit::TestResult.new
114
+ test_case.new("test_setup_failure").run(result) {}
115
+
116
+ assert_hook_failure_cleans_up_model(
117
+ result,
118
+ RuntimeError,
119
+ "setup failed",
120
+ :TestUnitSetupFailureModel,
121
+ test_case.created_table_name
122
+ )
123
+ end
124
+
125
+ def test_teardown_error_does_not_prevent_model_cleanup
126
+ test_case = Class.new(Test::Unit::TestCase) do
127
+ class << self
128
+ attr_accessor :created_table_name
129
+ end
130
+
131
+ with_model :TestUnitTeardownFailureModel do
132
+ table
133
+ end
134
+
135
+ def teardown
136
+ self.class.created_table_name = TestUnitTeardownFailureModel.table_name
137
+ raise "teardown failed"
138
+ end
139
+
140
+ def test_teardown_failure
141
+ end
142
+ end
143
+
144
+ result = Test::Unit::TestResult.new
145
+ test_case.new("test_teardown_failure").run(result) {}
146
+
147
+ assert_hook_failure_cleans_up_model(
148
+ result,
149
+ RuntimeError,
150
+ "teardown failed",
151
+ :TestUnitTeardownFailureModel,
152
+ test_case.created_table_name
153
+ )
154
+ end
155
+
156
+ private
157
+
158
+ def assert_hook_failure_cleans_up_model(result, error_class, message, constant, table_name)
159
+ assert_equal 1, result.run_count
160
+ assert_equal 1, result.error_count
161
+ assert_equal error_class, result.errors.first.exception.class
162
+ assert_equal message, result.errors.first.exception.message
163
+ assert_false Object.const_defined?(constant)
164
+ assert_false ActiveRecord::Base.connection.data_source_exists?(table_name)
165
+ end
166
+ end
167
+
168
+ class TestUnitPartialModelCreationFailureTest < Test::Unit::TestCase
169
+ def test_preserves_the_setup_error_when_model_creation_does_not_assign_a_model
170
+ test_case = Class.new(Test::Unit::TestCase) do
171
+ with_model :TestUnitPartiallyCreatedModel, superclass: Object do
172
+ table(false)
173
+ end
174
+
175
+ def test_setup_failure
176
+ end
177
+ end
178
+
179
+ result = Test::Unit::TestResult.new
180
+ test_case.new("test_setup_failure").run(result) {}
181
+
182
+ assert_equal 1, result.run_count
183
+ assert_equal 1, result.error_count
184
+ assert_kind_of WithModel::InvalidSuperclass, result.errors.first.exception
185
+ end
186
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_unit_helper"
4
+
5
+ # Stands in for an application's own model, the usual reason to want single table
6
+ # inheritance: its table is created once and outlives every test here, so a
7
+ # with_model child's rows cannot be disposed of by dropping the table.
8
+ module TestUnitAppModels
9
+ class Cupboard < ActiveRecord::Base
10
+ end
11
+ end
12
+
13
+ ActiveRecord::Base.connection.create_table TestUnitAppModels::Cupboard.table_name,
14
+ force: true do |t|
15
+ t.string "type"
16
+ t.string "name"
17
+ end
18
+
19
+ # A with_model parent and an STI child in the same test case. The child's
20
+ # superclass is resolved per-example, so this only works if the parent is
21
+ # created before the child and destroyed after it.
22
+ class TestUnitStiTest < Test::Unit::TestCase
23
+ with_model :TestUnitVehicle do
24
+ table do |t|
25
+ t.string "type"
26
+ t.string "name"
27
+ end
28
+ end
29
+
30
+ with_model :TestUnitTruck, superclass: -> { TestUnitVehicle } do
31
+ table(false)
32
+ end
33
+
34
+ with_model :TestUnitChest, superclass: "TestUnitAppModels::Cupboard" do
35
+ table(false)
36
+ end
37
+
38
+ def test_the_child_shares_the_parents_table
39
+ assert_equal TestUnitVehicle.table_name, TestUnitTruck.table_name
40
+ end
41
+
42
+ def test_the_child_stores_its_own_type
43
+ truck = TestUnitTruck.create!(name: "Ford F-150")
44
+
45
+ assert_equal "TestUnitTruck", truck.reload.type
46
+ assert_instance_of TestUnitTruck, TestUnitVehicle.first
47
+ end
48
+
49
+ # Rows in a table the child does not own have to be deleted when it goes away,
50
+ # since nothing else will: Cupboard's table is still standing afterwards, and a
51
+ # row naming a class that no longer exists makes it unloadable. Both tests write
52
+ # one and first insist the table is empty, so whichever test happens to run
53
+ # second fails if the rows survived teardown.
54
+ def test_the_childs_rows_do_not_outlive_it
55
+ assert_empty TestUnitAppModels::Cupboard.all,
56
+ "a previous test's rows outlived it"
57
+
58
+ TestUnitChest.create!(name: "sideboard")
59
+
60
+ assert_equal 1, TestUnitAppModels::Cupboard.count
61
+ end
62
+
63
+ def test_the_childs_rows_leave_the_superclass_loadable
64
+ assert_empty TestUnitAppModels::Cupboard.all,
65
+ "a previous test's rows outlived it"
66
+
67
+ TestUnitChest.create!(name: "wardrobe")
68
+
69
+ assert_instance_of TestUnitChest, TestUnitAppModels::Cupboard.first
70
+ end
71
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+
5
+ require "active_record"
6
+ require "test/unit"
7
+ require "with_model"
8
+
9
+ WithModel.runner = :test_unit
10
+
11
+ Test::Unit::TestCase.extend WithModel
12
+
13
+ # WithModel requires ActiveRecord::Base.connection to be established.
14
+ # If ActiveRecord already has a connection, as in a Rails app, this is unnecessary.
15
+ ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_unit_helper"
4
+
5
+ class TestUnitWithModelTest < Test::Unit::TestCase
6
+ with_model :TestUnitBlogPost do
7
+ table { |t| t.string "title" }
8
+ model { define_method(:fancy_title) { "Title: #{title}" } }
9
+ end
10
+
11
+ with_table :test_unit_widgets do |t|
12
+ t.string "name"
13
+ end
14
+
15
+ def test_creates_a_temporary_active_record_model
16
+ record = TestUnitBlogPost.create!(title: "New blog post")
17
+
18
+ assert_equal "New blog post", record.reload.title
19
+ end
20
+
21
+ def test_defines_model_methods
22
+ assert_equal "Title: New blog post", TestUnitBlogPost.new(title: "New blog post").fancy_title
23
+ end
24
+
25
+ def test_defines_the_model_constant
26
+ assert_kind_of Class, TestUnitBlogPost
27
+ end
28
+
29
+ def test_creates_a_temporary_table
30
+ assert_true ActiveRecord::Base.connection.data_source_exists?("test_unit_widgets")
31
+ end
32
+ end
33
+
34
+ class TestUnitPerDeclarationRunnerTest < Test::Unit::TestCase
35
+ with_model :TestUnitPerDeclarationModel, runner: :test_unit do
36
+ table
37
+ end
38
+
39
+ def test_supports_a_test_unit_runner_override
40
+ assert_kind_of Class, TestUnitPerDeclarationModel
41
+ end
42
+ end
43
+
44
+ class TestUnitRunnerValidationTest < Test::Unit::TestCase
45
+ def test_names_all_supported_runners_for_an_unsupported_runner
46
+ error = assert_raise(ArgumentError) do
47
+ Class.new(Test::Unit::TestCase) do
48
+ extend WithModel
49
+
50
+ with_model :UnsupportedTestUnitModel, runner: :unsupported do
51
+ table
52
+ end
53
+ end
54
+ end
55
+
56
+ assert_equal "Unsupported test runner set, expected :rspec, :minitest, or :test_unit", error.message
57
+ end
58
+ end
data/with_model.gemspec CHANGED
@@ -9,7 +9,8 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Case Commons, LLC", "Grant Hutchins", "Andrew Marshall"]
10
10
  spec.email = %w[casecommons-dev@googlegroups.com gems@nertzy.com andrew@johnandrewmarshall.com]
11
11
  spec.homepage = "https://github.com/Casecommons/with_model"
12
- spec.summary = "Dynamically build a model within an RSpec context"
12
+ spec.summary = "Dynamically build an Active Record model for " \
13
+ "RSpec, Minitest, and Test::Unit tests"
13
14
  spec.description = spec.summary
14
15
  spec.license = "MIT"
15
16
  spec.metadata["rubygems_mfa_required"] = "true"
@@ -20,5 +21,5 @@ Gem::Specification.new do |spec|
20
21
 
21
22
  spec.required_ruby_version = ">= 3.3"
22
23
 
23
- spec.add_dependency "activerecord", ">= 7.2"
24
+ spec.add_dependency "activerecord", ">= 8.0"
24
25
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: with_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Case Commons, LLC
@@ -17,15 +17,16 @@ dependencies:
17
17
  requirements:
18
18
  - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '7.2'
20
+ version: '8.0'
21
21
  type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
25
  - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '7.2'
28
- description: Dynamically build a model within an RSpec context
27
+ version: '8.0'
28
+ description: Dynamically build an Active Record model for RSpec, Minitest, and Test::Unit
29
+ tests
29
30
  email:
30
31
  - casecommons-dev@googlegroups.com
31
32
  - gems@nertzy.com
@@ -47,29 +48,41 @@ files:
47
48
  - LICENSE
48
49
  - README.md
49
50
  - Rakefile
51
+ - docs/minitest.md
52
+ - docs/rspec.md
53
+ - docs/test-unit.md
50
54
  - lib/with_model.rb
51
55
  - lib/with_model/constant_stubber.rb
52
56
  - lib/with_model/descendants_tracker.rb
53
57
  - lib/with_model/invalid_superclass.rb
54
58
  - lib/with_model/methods.rb
59
+ - lib/with_model/minitest.rb
55
60
  - lib/with_model/missing_superclass.rb
56
61
  - lib/with_model/model.rb
57
62
  - lib/with_model/model/dsl.rb
58
63
  - lib/with_model/null_table.rb
64
+ - lib/with_model/rspec.rb
59
65
  - lib/with_model/table.rb
66
+ - lib/with_model/test_unit.rb
60
67
  - lib/with_model/version.rb
61
68
  - spec/active_record_behaviors_spec.rb
62
69
  - spec/constant_stubber_spec.rb
63
70
  - spec/descendants_tracking_spec.rb
64
71
  - spec/readme_spec.rb
72
+ - spec/runner_entrypoints_spec.rb
65
73
  - spec/spec_helper.rb
66
74
  - spec/table_false_spec.rb
75
+ - spec/table_spec.rb
67
76
  - spec/with_model_spec.rb
68
77
  - test/declaration_order_test.rb
69
78
  - test/spec_dsl_test.rb
70
79
  - test/sti_test.rb
71
80
  - test/test_helper.rb
72
81
  - test/with_model_test.rb
82
+ - test_unit/lifecycle_test.rb
83
+ - test_unit/sti_test.rb
84
+ - test_unit/test_unit_helper.rb
85
+ - test_unit/with_model_test.rb
73
86
  - with_model.gemspec
74
87
  homepage: https://github.com/Casecommons/with_model
75
88
  licenses:
@@ -90,7 +103,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
103
  - !ruby/object:Gem::Version
91
104
  version: '0'
92
105
  requirements: []
93
- rubygems_version: 4.0.16
106
+ rubygems_version: 4.0.20
94
107
  specification_version: 4
95
- summary: Dynamically build a model within an RSpec context
108
+ summary: Dynamically build an Active Record model for RSpec, Minitest, and Test::Unit
109
+ tests
96
110
  test_files: []