Skip to content

Commit 7aed799

Browse files
authored
chore: move test to minitest (#434)
feat: bump with_advisory_lock to 5.0.0
1 parent 7ba50f7 commit 7aed799

File tree

5 files changed

+146
-39
lines changed

5 files changed

+146
-39
lines changed

closure_tree.gemspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
2121
gem.required_ruby_version = '>= 3.0.0'
2222

2323
gem.add_runtime_dependency 'activerecord', '>= 6.1.0'
24-
gem.add_runtime_dependency 'with_advisory_lock', '>= 4.0.0'
24+
gem.add_runtime_dependency 'with_advisory_lock', '>= 5.0.0'
2525

2626
gem.add_development_dependency 'appraisal'
2727
gem.add_development_dependency 'database_cleaner'

spec/closure_tree/cache_invalidation_spec.rb

-38
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
require 'test_helper'
2+
3+
class CacheInvalidationTest < ActiveSupport::TestCase
4+
def setup
5+
Timecop.travel(10.seconds.ago) do
6+
#create a long tree with 2 branch
7+
@root = MenuItem.create(
8+
name: SecureRandom.hex(10)
9+
)
10+
2.times do
11+
parent = @root
12+
10.times do
13+
parent = parent.children.create(
14+
name: SecureRandom.hex(10)
15+
)
16+
end
17+
end
18+
@first_leaf = MenuItem.leaves.first
19+
@second_leaf = MenuItem.leaves.last
20+
end
21+
end
22+
23+
test "touch option should invalidate cache for all it ancestors" do
24+
old_time_stamp = @first_leaf.ancestors.pluck(:updated_at)
25+
@first_leaf.touch
26+
new_time_stamp = @first_leaf.ancestors.pluck(:updated_at)
27+
assert_not_equal old_time_stamp, new_time_stamp, 'Cache not invalidated for all ancestors'
28+
end
29+
30+
test "touch option should not invalidate cache for another branch" do
31+
old_time_stamp = @second_leaf.updated_at
32+
@first_leaf.touch
33+
new_time_stamp = @second_leaf.updated_at
34+
assert_equal old_time_stamp, new_time_stamp, 'Cache incorrectly invalidated for another branch'
35+
end
36+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
require "test_helper"
2+
3+
class HasClosureTreeRootTest < ActiveSupport::TestCase
4+
setup do
5+
ENV['FLOCK_DIR'] = Dir.mktmpdir
6+
end
7+
8+
teardown do
9+
FileUtils.remove_entry_secure ENV['FLOCK_DIR']
10+
end
11+
def create_tree(group)
12+
@ct1 = ContractType.create!(name: "Type1")
13+
@ct2 = ContractType.create!(name: "Type2")
14+
@user1 = User.create!(email: "1@example.com", group_id: group.id)
15+
@user2 = User.create!(email: "2@example.com", group_id: group.id)
16+
@user3 = User.create!(email: "3@example.com", group_id: group.id)
17+
@user4 = User.create!(email: "4@example.com", group_id: group.id)
18+
@user5 = User.create!(email: "5@example.com", group_id: group.id)
19+
@user6 = User.create!(email: "6@example.com", group_id: group.id)
20+
21+
# The tree (contract types in parens)
22+
#
23+
# U1(1)
24+
# / \
25+
# U2(1) U3(1&2)
26+
# / / \
27+
# U4(2) U5(1) U6(2)
28+
29+
@user1.children << @user2
30+
@user1.children << @user3
31+
@user2.children << @user4
32+
@user3.children << @user5
33+
@user3.children << @user6
34+
35+
@user1.contracts.create!(title: "Contract 1", contract_type: @ct1)
36+
@user2.contracts.create!(title: "Contract 2", contract_type: @ct1)
37+
@user3.contracts.create!(title: "Contract 3", contract_type: @ct1)
38+
@user3.contracts.create!(title: "Contract 4", contract_type: @ct2)
39+
@user4.contracts.create!(title: "Contract 5", contract_type: @ct2)
40+
@user5.contracts.create!(title: "Contract 6", contract_type: @ct1)
41+
@user6.contracts.create!(title: "Contract 7", contract_type: @ct2)
42+
end
43+
44+
test "loads all nodes in a constant number of queries" do
45+
group = Group.create!(name: "TheGrouping")
46+
create_tree(group)
47+
reloaded_group = group.reload
48+
exceed_query_limit(2) do
49+
root = reloaded_group.root_user_including_tree
50+
assert_equal "2@example.com", root.children[0].email
51+
assert_equal "3@example.com", root.children[0].parent.children[1].email
52+
end
53+
end
54+
55+
test "loads all nodes plus single association in a constant number of queries" do
56+
group = Group.create!(name: "TheGrouping")
57+
create_tree(group)
58+
reloaded_group = group.reload
59+
exceed_query_limit(3) do
60+
root = reloaded_group.root_user_including_tree(:contracts)
61+
assert_equal "2@example.com", root.children[0].email
62+
assert_equal "3@example.com", root.children[0].parent.children[1].email
63+
assert_equal "Contract 7", root.children[0].children[0].contracts[0].user.parent.parent.children[1].children[1].contracts[0].title
64+
end
65+
end
66+
67+
test "loads all nodes and associations in a constant number of queries" do
68+
group = Group.create!(name: "TheGrouping")
69+
create_tree(group)
70+
reloaded_group = group.reload
71+
exceed_query_limit(4) do
72+
root = reloaded_group.root_user_including_tree(contracts: :contract_type)
73+
assert_equal "2@example.com", root.children[0].email
74+
assert_equal "3@example.com", root.children[0].parent.children[1].email
75+
assert_equal %w[Type1 Type2], root.children[1].contracts.map(&:contract_type).map(&:name)
76+
assert_equal "Type1", root.children[1].children[0].contracts[0].contract_type.name
77+
assert_equal "Type2", root.children[0].children[0].contracts[0].user.parent.parent.children[1].children[1].contracts[0].contract_type.name
78+
end
79+
end
80+
end

test/test_helper.rb

+29
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
require 'database_cleaner'
1111
require 'support/query_counter'
1212
require 'parallel'
13+
require 'timecop'
1314

1415
ActiveRecord::Base.configurations = {
1516
default_env: {
@@ -61,9 +62,37 @@ class Spec
6162
end
6263
end
6364

65+
class ActiveSupport::TestCase
66+
def exceed_query_limit(num, &block)
67+
counter = QueryCounter.new
68+
ActiveSupport::Notifications.subscribed(counter.to_proc, 'sql.active_record', &block)
69+
assert counter.query_count <= num, "Expected to run maximum #{num} queries, but ran #{counter.query_count}"
70+
end
71+
72+
class QueryCounter
73+
attr_reader :query_count
74+
75+
def initialize
76+
@query_count = 0
77+
end
78+
79+
def to_proc
80+
lambda(&method(:callback))
81+
end
82+
83+
def callback(name, start, finish, message_id, values)
84+
@query_count += 1 unless %w(CACHE SCHEMA).include?(values[:name])
85+
end
86+
end
87+
end
88+
6489
# Configure parallel tests
6590
Thread.abort_on_exception = true
6691

92+
# Configure advisory_lock
93+
# See: https://github.com/ClosureTree/with_advisory_lock
94+
ENV['WITH_ADVISORY_LOCK_PREFIX'] ||= SecureRandom.hex
95+
6796
require 'closure_tree'
6897
require_relative '../spec/support/schema'
6998
require_relative '../spec/support/models'

0 commit comments

Comments
 (0)