Files
nairobi/spec/models/statisticable_spec.rb
Julian Herrero 16f844595d Don't use the cache in admin budget stats
In commit e51e03446, we started using the same code to show stats in the
public area and in the admin area. However, in doing so we introduced a
bug, since stats in the public area are only shown after a certain part
of the process has finished, meaning the stats appearing on the page
never change (in theory), so it's perfectly fine to cache them. However,
in the admin area stats can be accessed while the process is still
ongoing, so caching the stats will lead to the wrong results being
displayed.

We've thought about expiring the cache when new supports or ballot lines
are added; however, that means the methods calculating the stats for the
supporting phase would expire when supports are added/removed but the
methods calculating the stats for the voting phase would expire when
ballot lines are added/removed. It gets even more complex because the
`headings` method calculates stats for both the supporting and the
voting phases.

So, since loading stats in the admin section is fast even without the
cache because they only load very basic statistics, we're taking the
simple approach of disabling the cache in this case, so everything works
the same way it did before commit e51e03446.

Co-authored-by: Javi Martín <javim@elretirao.net>
2024-05-20 16:19:41 +02:00

271 lines
6.5 KiB
Ruby

require "rails_helper"
describe Statisticable do
before do
dummy_stats = Class.new do
include Statisticable
attr_accessor :total
stats_cache :total
def full_cache_key_for(key)
"dummy_stats/#{object_id}/#{key}"
end
def participants
User.all
end
alias_method :participants_from_original_table, :participants
def total_participants
User.count
end
def participation_date
Time.current
end
end
stub_const("DummyStats", dummy_stats)
end
let(:resource) { double(id: 1, class: double(table_name: "")) }
let(:stats) { DummyStats.new(resource) }
describe "#gender?" do
context "No participants" do
it "is false" do
expect(stats.gender?).to be false
end
end
context "All participants have no defined gender" do
before { create(:user, gender: nil) }
it "is false" do
expect(stats.gender?).to be false
end
end
context "There's a male participant" do
before { create(:user, gender: "male") }
it "is true" do
expect(stats.gender?).to be true
end
end
context "There's a female participant" do
before { create(:user, gender: "female") }
it "is true" do
expect(stats.gender?).to be true
end
end
end
describe "#age?" do
context "No participants" do
it "is false" do
expect(stats.age?).to be false
end
end
context "All participants have no defined age" do
before { create(:user, date_of_birth: nil) }
it "is false" do
expect(stats.age?).to be false
end
end
context "All participants have impossible ages" do
before do
create(:user, date_of_birth: 3.seconds.ago)
create(:user, date_of_birth: 3000.years.ago)
end
it "is false" do
expect(stats.age?).to be false
end
end
context "There's a participant with a defined age" do
before { create(:user, date_of_birth: 30.years.ago) }
it "is true" do
expect(stats.age?).to be true
end
end
context "Partipation took place a long time ago" do
before do
create(:user, date_of_birth: 2000.years.ago)
allow(stats).to receive(:participation_date).and_return(1900.years.ago)
end
it "is true" do
expect(stats.age?).to be true
end
end
end
describe "#geozone?" do
context "No participants" do
it "is false" do
expect(stats.geozone?).to be false
end
end
context "All participants have no defined geozone" do
before { create(:user, geozone: nil) }
it "is false" do
expect(stats.geozone?).to be false
end
end
context "There's a participant with a defined geozone" do
before { create(:user, geozone: create(:geozone)) }
it "is true" do
expect(stats.geozone?).to be true
end
end
end
describe "#total_no_demographic_data" do
it "returns users with no defined gender" do
create(:user, gender: nil)
expect(stats.total_no_demographic_data).to be 1
end
it "returns users with no defined age" do
create(:user, gender: "female", date_of_birth: nil)
expect(stats.total_no_demographic_data).to be 1
end
it "returns users with no defined geozone" do
create(:user, gender: "female", geozone: nil)
expect(stats.total_no_demographic_data).to be 1
end
it "returns users with no defined gender, age nor geozone" do
create(:user, gender: nil, date_of_birth: nil, geozone: nil)
expect(stats.total_no_demographic_data).to be 1
end
it "doesn't return users with defined gender, age and geozone" do
create(:user, gender: "male", date_of_birth: 20.years.ago, geozone: create(:geozone))
expect(stats.total_no_demographic_data).to be 0
end
end
describe "#stats_methods" do
it "includes total participants" do
expect(stats.stats_methods).to include(:total_participants)
end
context "no gender stats" do
before { allow(stats).to receive(:gender?).and_return(false) }
it "doesn't include gender methods" do
expect(stats.stats_methods).not_to include(:total_male_participants)
end
end
context "no age stats" do
before { allow(stats).to receive(:age?).and_return(false) }
it "doesn't include age methods" do
expect(stats.stats_methods).not_to include(:participants_by_age)
end
end
context "no geozone stats" do
before { allow(stats).to receive(:geozone?).and_return(false) }
it "doesn't include age methods" do
expect(stats.stats_methods).not_to include(:participants_by_geozone)
end
end
context "all gender, age and geozone stats" do
before do
allow(stats).to receive_messages(gender?: true, age?: true, geozone?: true)
end
it "includes all stats methods" do
expect(stats.stats_methods).to include(:total_male_participants)
expect(stats.stats_methods).to include(:participants_by_age)
expect(stats.stats_methods).to include(:participants_by_geozone)
end
end
end
describe "#generate" do
it "drops the temporary table after finishing" do
stats.generate
expect { stats.send(:participants_class).first }.to raise_exception(ActiveRecord::StatementInvalid)
end
it "can be executed twice without errors" do
stats.generate
expect { stats.generate }.not_to raise_exception
end
it "can be executed twice in parallel since it uses a transaction" do
other_stats = DummyStats.new(stats.resource)
[stats, other_stats].map do |stat|
Thread.new { stat.generate }
end.each(&:join)
end
end
describe "cache", :with_cache do
it "expires the cache at the end of the day by default" do
time = Time.current
travel_to(time) do
stats.total = 6
expect(stats.total).to eq 6
stats.total = 7
expect(stats.total).to eq 6
end
travel_to(time.end_of_day) do
expect(stats.total).to eq 6
end
travel_to(time.end_of_day + 1.second) do
expect(stats.total).to eq 7
end
end
it "does not use the cache with cache: false" do
stats = DummyStats.new(resource, cache: false)
time = Time.current
travel_to(time) do
stats.total = 6
expect(stats.total).to eq 6
stats.total = 7
expect(stats.total).to eq 7
end
end
end
end