Skip to content

Commit 2f68d4e

Browse files
authored
Better test organization (#36)
1 parent f11d18d commit 2f68d4e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+2000
-561
lines changed

lib/universalid/extensions/active_record/base_unpacker.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,18 @@ def assign_descendants(record, attributes)
4545

4646
new_models = models.select(&:new_record?)
4747
models -= new_models
48+
association_collection = record.public_send(name)
4849

4950
# restore persisted models
5051
# NOTE: ActiveRecord is smart enough to not re-create or re-add
5152
# existing records for has_many associations
5253
record.public_send :"#{name}=", models if models.any?
5354

5455
# restore new unsaved models
55-
record.public_send(name).target.concat new_models if new_models.any?
56+
association_collection.target.concat new_models if new_models.any?
57+
58+
# mark association relation as loaded
59+
association_collection.proxy_association.instance_variable_set :@loaded, true
5660
end
5761
end
5862
end

lib/universalid/message_pack_types.rb

+11-11
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
# NOTE: MessagePack scans registered type in linear order and first match wins
44

55
# scalars
6-
require_relative "message_pack_types/ruby/scalars/bigdecimal"
7-
require_relative "message_pack_types/ruby/scalars/complex"
8-
require_relative "message_pack_types/ruby/scalars/rational"
9-
require_relative "message_pack_types/ruby/scalars/date_time"
10-
require_relative "message_pack_types/ruby/scalars/date"
11-
require_relative "message_pack_types/ruby/scalars/range"
12-
require_relative "message_pack_types/ruby/scalars/regexp"
6+
require_relative "message_pack_types/scalars/bigdecimal"
7+
require_relative "message_pack_types/scalars/complex"
8+
require_relative "message_pack_types/scalars/rational"
9+
require_relative "message_pack_types/scalars/date_time"
10+
require_relative "message_pack_types/scalars/date"
11+
require_relative "message_pack_types/scalars/range"
12+
require_relative "message_pack_types/scalars/regexp"
1313

1414
# composites
15-
require_relative "message_pack_types/ruby/composites/module"
16-
require_relative "message_pack_types/ruby/composites/open_struct"
17-
require_relative "message_pack_types/ruby/composites/struct"
18-
require_relative "message_pack_types/ruby/composites/set"
15+
require_relative "message_pack_types/composites/module"
16+
require_relative "message_pack_types/composites/open_struct"
17+
require_relative "message_pack_types/composites/struct"
18+
require_relative "message_pack_types/composites/set"
1919

2020
# extensions
2121
Dir["#{__dir__}/extensions/**/*.rb"].sort.each { |f| require f }

lib/universalid/message_pack_types/ruby/composites/struct.rb lib/universalid/message_pack_types/composites/struct.rb

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
unpacker: ->(unpacker) do
1212
class_name = unpacker.read
1313
hash = unpacker.read
14-
klass = Object.const_get(class_name) if Object.const_defined?(class_name)
14+
klass = Object.const_get(class_name) if class_name && Object.const_defined?(class_name)
15+
klass ||= Struct.new(*hash.keys)
1516

1617
if klass
1718
# shenanigans to support ::Ruby 3.0.X and 3.1.X

test/rails_kit/models/active_record_forge.rb

+25-24
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ def self.included(klass)
99
klass.define_singleton_method(:foundry) { @foundry ||= ActiveRecordForge::Foundry.new(self) }
1010
klass.define_singleton_method(:forge) { |*args, **kwargs| foundry.forge(*args, **kwargs) }
1111
klass.define_singleton_method(:forge!) { |*args, **kwargs| foundry.forge!(*args, **kwargs) }
12+
klass.define_singleton_method(:generate_attributes) { foundry.generate_attributes }
1213
end
1314

1415
class Foundry
@@ -43,30 +44,6 @@ def forge!(...)
4344
forge(...).tap { |forged| forged.is_a?(Array) ? forged.each(&:save!) : forged.save! }
4445
end
4546

46-
private
47-
48-
def_delegators :klass, :columns, :column_names, :primary_key, :reflections, :reflect_on_all_associations
49-
50-
def association(name)
51-
associations.find { |a| a.name.to_s == name.to_s }
52-
end
53-
54-
def associations(macro: nil)
55-
list = reflect_on_all_associations
56-
list = list.select { |a| a.macro == macro.to_sym } if macro
57-
list
58-
end
59-
60-
def association_names(macro: nil)
61-
associations(macro: macro).map { |a| a.name.to_s }
62-
end
63-
64-
def foreign_key_column_names
65-
reflections.each_with_object([]) do |(name, reflection), memo|
66-
memo << reflection.foreign_key if reflection.macro == :belongs_to
67-
end
68-
end
69-
7047
def generate_attributes
7148
attributes = {}
7249

@@ -103,5 +80,29 @@ def generate_attributes
10380

10481
attributes.with_indifferent_access
10582
end
83+
84+
private
85+
86+
def_delegators :klass, :columns, :column_names, :primary_key, :reflections, :reflect_on_all_associations
87+
88+
def association(name)
89+
associations.find { |a| a.name.to_s == name.to_s }
90+
end
91+
92+
def associations(macro: nil)
93+
list = reflect_on_all_associations
94+
list = list.select { |a| a.macro == macro.to_sym } if macro
95+
list
96+
end
97+
98+
def association_names(macro: nil)
99+
associations(macro: macro).map { |a| a.name.to_s }
100+
end
101+
102+
def foreign_key_column_names
103+
reflections.each_with_object([]) do |(name, reflection), memo|
104+
memo << reflection.foreign_key if reflection.macro == :belongs_to
105+
end
106+
end
106107
end
107108
end

test/rails_kit/models/campaign.rb

+4
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,8 @@
33
class Campaign < ApplicationRecord
44
has_many :emails, dependent: :destroy
55
accepts_nested_attributes_for :emails
6+
7+
scope :email_subjects_like, ->(value) do
8+
where id: Email.subject_like(value).select(:campaign_id)
9+
end
610
end

test/rails_kit/models/email.rb

+4
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@ class Email < ApplicationRecord
44
belongs_to :campaign
55
has_many :attachments, dependent: :destroy
66
accepts_nested_attributes_for :attachments
7+
8+
scope :subject_like, ->(subject) do
9+
where(arel_table[:subject].matches("%#{subject}%"))
10+
end
711
end

test/rails_kit/setup.rb

+1
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,5 @@
4848
require_relative "models/attachment"
4949

5050
# Seed some data
51+
# ActiveRecord::Base.logger = Logger.new(STDOUT)
5152
Campaign.forge! 10, emails: 5, attachments: 3

test/test_extension.rb

+109-9
Original file line numberDiff line numberDiff line change
@@ -19,26 +19,30 @@
1919
Minitest.parallel_executor = Minitest::Parallel::Executor.new([Etc.nprocessors, 1].max) # thread count
2020
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
2121

22+
# TODO: Get coverage working
2223
# Coverage setup
23-
SimpleCov.start do
24-
project_name "UniversalID"
25-
add_filter [
26-
"lib/universalid/message_pack/types", # coverage doesn't work on these
27-
"lib/universalid/message_pack_types", # coverage not needed
28-
"lib/universalid/refinements", # coverage doesn't work on these
29-
"test" # coverage not wanted
30-
]
31-
end
24+
# SimpleCov.start do
25+
# project_name "UniversalID"
26+
# add_filter [
27+
# "lib/universalid/message_pack_types", # ....................coverage doesn't work on these
28+
# "lib/universalid/extensions/**/*message_pack_type.rb", # ...coverage doesn't work on these
29+
# "lib/universalid/refinements", # ...........................coverage doesn't work on these
30+
# "test" # ...................................................coverage not wanted
31+
# ]
32+
# end
3233

3334
require "universalid"
3435

3536
# Minimal subset of Rails tooling for testing purposes
3637
require_relative "rails_kit/setup"
3738

39+
module UniversalID::MessagePack; end
40+
3841
class Minitest::Test
3942
alias_method :original_run, :run
4043

4144
def run
45+
Campaign.destroy_all
4246
result = nil
4347
time = Benchmark.measure { result = original_run }
4448
time = time.real.round(5)
@@ -58,4 +62,100 @@ def run
5862

5963
result
6064
end
65+
66+
def load_has_many(record, depth: 0)
67+
count = 0
68+
with_has_many record do |relation|
69+
next unless count < depth
70+
count += 1
71+
relation.load
72+
relation.each { |rec| load_has_many(rec, depth: depth - 1) }
73+
end
74+
end
75+
76+
def with_has_many(record)
77+
record.class.reflect_on_all_associations.each do |association|
78+
next unless association.is_a?(ActiveRecord::Reflection::HasManyReflection)
79+
relation = record.public_send(association.name)
80+
yield relation
81+
end
82+
end
83+
84+
def assert_has_many_loaded(record, depth: 1)
85+
count = 0
86+
with_has_many record do |relation|
87+
next unless count <= depth
88+
count += 1
89+
assert relation.loaded?
90+
relation.each { |rec| assert_has_many_loaded(rec, depth: depth - 1) }
91+
end
92+
end
93+
94+
def refute_has_many_loaded(record)
95+
with_has_many record do |relation|
96+
refute relation.loaded?
97+
end
98+
end
99+
100+
def assert_has_many(expected, actual)
101+
assert_equal expected.size, actual.size
102+
expected.each_with_index { |record, i| assert_record record, actual[i] }
103+
end
104+
105+
def assert_record(expected, actual)
106+
assert_equal expected.attributes, actual.attributes
107+
assert_equal expected.persisted?, actual.persisted?
108+
assert_equal expected.changed?, actual.changed?
109+
110+
with_has_many expected do |relation|
111+
expected_relation = relation
112+
actual_relation = actual.public_send(expected_relation.proxy_association.reflection.name)
113+
assert_equal expected_relation.size, actual_relation.size
114+
next unless expected_relation.loaded?
115+
next unless actual_relation.loaded?
116+
117+
expected_relation.each_with_index do |record, i|
118+
assert_record record, actual_relation[i]
119+
end
120+
end
121+
end
122+
123+
def self.scalars
124+
{
125+
bigdecimal: BigDecimal("123.45"),
126+
complex: Complex(1, 2),
127+
date: Date.today,
128+
datetime: DateTime.now,
129+
false_class: false,
130+
float: 123.45,
131+
integer: 123,
132+
nil_class: nil,
133+
range: 1..100,
134+
rational: Rational(3, 4),
135+
regexp: /abc/,
136+
string: "hello",
137+
symbol: :symbol,
138+
time: Time.now,
139+
true_class: true
140+
}
141+
end
142+
143+
def scalars
144+
self.class.scalars
145+
end
146+
147+
def time_with_zones
148+
month = ActiveSupport::TimeZone["UTC"].now.beginning_of_year
149+
150+
Timecop.freeze time do
151+
11.times do |i|
152+
ActiveSupport::TimeZone.all.each do |time_zone|
153+
time = month.in_time_zone(time_zone).advance(days: rand(1..28), minutes: rand(1..59))
154+
yield time
155+
end
156+
157+
month.advance months: 1
158+
end
159+
end
160+
end
61161
end

0 commit comments

Comments
 (0)