diff --git a/Gemfile b/Gemfile index d8a3ce444..8564a8285 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,6 @@ gem "decidim-decidim_awesome", path: "." gem "bootsnap", "~> 1.4" gem "puma", ">= 5.5.1" -gem "uglifier", "~> 4.1" gem "faker", "~> 2.14" diff --git a/Gemfile.lock b/Gemfile.lock index 58fadac34..25df0b051 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -9,40 +9,40 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (6.1.7.4) - actionpack (= 6.1.7.4) - activesupport (= 6.1.7.4) + actioncable (6.1.7.6) + actionpack (= 6.1.7.6) + activesupport (= 6.1.7.6) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.1.7.4) - actionpack (= 6.1.7.4) - activejob (= 6.1.7.4) - activerecord (= 6.1.7.4) - activestorage (= 6.1.7.4) - activesupport (= 6.1.7.4) + actionmailbox (6.1.7.6) + actionpack (= 6.1.7.6) + activejob (= 6.1.7.6) + activerecord (= 6.1.7.6) + activestorage (= 6.1.7.6) + activesupport (= 6.1.7.6) mail (>= 2.7.1) - actionmailer (6.1.7.4) - actionpack (= 6.1.7.4) - actionview (= 6.1.7.4) - activejob (= 6.1.7.4) - activesupport (= 6.1.7.4) + actionmailer (6.1.7.6) + actionpack (= 6.1.7.6) + actionview (= 6.1.7.6) + activejob (= 6.1.7.6) + activesupport (= 6.1.7.6) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.1.7.4) - actionview (= 6.1.7.4) - activesupport (= 6.1.7.4) + actionpack (6.1.7.6) + actionview (= 6.1.7.6) + activesupport (= 6.1.7.6) rack (~> 2.0, >= 2.0.9) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.1.7.4) - actionpack (= 6.1.7.4) - activerecord (= 6.1.7.4) - activestorage (= 6.1.7.4) - activesupport (= 6.1.7.4) + actiontext (6.1.7.6) + actionpack (= 6.1.7.6) + activerecord (= 6.1.7.6) + activestorage (= 6.1.7.6) + activesupport (= 6.1.7.6) nokogiri (>= 1.8.5) - actionview (6.1.7.4) - activesupport (= 6.1.7.4) + actionview (6.1.7.6) + activesupport (= 6.1.7.6) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -50,22 +50,22 @@ GEM active_link_to (1.0.5) actionpack addressable - activejob (6.1.7.4) - activesupport (= 6.1.7.4) + activejob (6.1.7.6) + activesupport (= 6.1.7.6) globalid (>= 0.3.6) - activemodel (6.1.7.4) - activesupport (= 6.1.7.4) - activerecord (6.1.7.4) - activemodel (= 6.1.7.4) - activesupport (= 6.1.7.4) - activestorage (6.1.7.4) - actionpack (= 6.1.7.4) - activejob (= 6.1.7.4) - activerecord (= 6.1.7.4) - activesupport (= 6.1.7.4) + activemodel (6.1.7.6) + activesupport (= 6.1.7.6) + activerecord (6.1.7.6) + activemodel (= 6.1.7.6) + activesupport (= 6.1.7.6) + activestorage (6.1.7.6) + actionpack (= 6.1.7.6) + activejob (= 6.1.7.6) + activerecord (= 6.1.7.6) + activesupport (= 6.1.7.6) marcel (~> 1.0) mini_mime (>= 1.1.0) - activesupport (6.1.7.4) + activesupport (6.1.7.6) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) @@ -76,7 +76,7 @@ GEM addressable (2.8.5) public_suffix (>= 2.0.2, < 6.0) ast (2.4.2) - axe-core-api (4.7.0) + axe-core-api (4.8.0) dumb_delegator virtus axe-core-rspec (4.1.0) @@ -87,6 +87,7 @@ GEM descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) thread_safe (~> 0.3, >= 0.3.1) + base64 (0.1.1) batch-loader (1.5.0) bcrypt (3.1.19) better_html (1.0.16) @@ -132,7 +133,7 @@ GEM actionpack (>= 5.0) cells (>= 4.1.6, < 5.0.0) charlock_holmes (0.7.7) - chef-utils (18.2.7) + chef-utils (18.3.0) concurrent-ruby childprocess (4.1.0) codecov (0.6.0) @@ -151,7 +152,7 @@ GEM crack (0.4.5) rexml crass (1.0.6) - css_parser (1.14.0) + css_parser (1.16.0) addressable date (3.3.3) date_validator (0.12.0) @@ -338,7 +339,7 @@ GEM railties (>= 4.1.0) responders warden (~> 1.2.3) - devise-i18n (1.11.0) + devise-i18n (1.12.0) devise (>= 4.9.0) devise_invitable (2.0.8) actionmailer (>= 5.0) @@ -365,8 +366,8 @@ GEM temple erubi (1.12.0) escape_utils (1.3.0) - excon (0.100.0) - execjs (2.8.1) + excon (0.104.0) + execjs (2.9.1) extended-markdown-filter (0.7.0) html-pipeline (~> 2.9) factory_bot (4.11.1) @@ -376,11 +377,12 @@ GEM railties (>= 3.0.0) faker (2.23.0) i18n (>= 1.8.11, < 2) - faraday (2.7.10) + faraday (2.7.11) + base64 faraday-net_http (>= 2.0, < 3.1) ruby2_keywords (>= 0.0.4) faraday-net_http (3.0.2) - ffi (1.15.5) + ffi (1.16.3) file_validators (3.0.0) activemodel (>= 3.2) mime-types (>= 1.0) @@ -399,8 +401,8 @@ GEM railties (>= 4.1, < 7.1) gemoji (3.0.1) geocoder (1.8.2) - globalid (1.1.0) - activesupport (>= 5.0) + globalid (1.2.1) + activesupport (>= 6.1) graphql (1.12.24) graphql-docs (2.1.0) commonmarker (~> 0.16) @@ -479,7 +481,7 @@ GEM net-smtp marcel (1.0.2) matrix (0.4.2) - mdl (0.12.0) + mdl (0.13.0) kramdown (~> 2.3) kramdown-parser-gfm (~> 1.1) mixlib-cli (~> 2.1, >= 2.1.1) @@ -488,10 +490,10 @@ GEM method_source (1.0.0) mime-types (3.5.1) mime-types-data (~> 3.2015) - mime-types-data (3.2023.0808) + mime-types-data (3.2023.1003) mini_magick (4.12.0) mini_mime (1.1.5) - minitest (5.19.0) + minitest (5.20.0) mixlib-cli (2.1.8) mixlib-config (3.0.27) tomlrb @@ -500,18 +502,16 @@ GEM msgpack (1.7.2) multi_xml (0.6.0) mustache (1.1.1) - net-imap (0.3.7) + net-imap (0.4.0) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.1) timeout - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol nio4r (2.5.9) - nokogiri (1.14.5-arm64-darwin) - racc (~> 1.4) nokogiri (1.14.5-x86_64-linux) racc (~> 1.4) oauth (1.1.0) @@ -557,7 +557,7 @@ GEM parallel (1.23.0) parallel_tests (3.13.0) parallel - parser (3.2.2.3) + parser (3.2.2.4) ast (~> 2.4.1) racc pg (1.1.4) @@ -583,24 +583,24 @@ GEM rack (>= 2.0.0) rack-protection (3.1.0) rack (~> 2.2, >= 2.2.4) - rack-proxy (0.7.6) + rack-proxy (0.7.7) rack rack-test (2.1.0) rack (>= 1.3) - rails (6.1.7.4) - actioncable (= 6.1.7.4) - actionmailbox (= 6.1.7.4) - actionmailer (= 6.1.7.4) - actionpack (= 6.1.7.4) - actiontext (= 6.1.7.4) - actionview (= 6.1.7.4) - activejob (= 6.1.7.4) - activemodel (= 6.1.7.4) - activerecord (= 6.1.7.4) - activestorage (= 6.1.7.4) - activesupport (= 6.1.7.4) + rails (6.1.7.6) + actioncable (= 6.1.7.6) + actionmailbox (= 6.1.7.6) + actionmailer (= 6.1.7.6) + actionpack (= 6.1.7.6) + actiontext (= 6.1.7.6) + actionview (= 6.1.7.6) + activejob (= 6.1.7.6) + activemodel (= 6.1.7.6) + activerecord (= 6.1.7.6) + activestorage (= 6.1.7.6) + activesupport (= 6.1.7.6) bundler (>= 1.15.0) - railties (= 6.1.7.4) + railties (= 6.1.7.6) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) @@ -615,9 +615,9 @@ GEM rails-i18n (6.0.0) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 7) - railties (6.1.7.4) - actionpack (= 6.1.7.4) - activesupport (= 6.1.7.4) + railties (6.1.7.6) + actionpack (= 6.1.7.6) + activesupport (= 6.1.7.6) method_source rake (>= 12.2) thor (~> 1.0) @@ -730,28 +730,26 @@ GEM spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) - sprockets (4.2.0) + sprockets (4.2.1) concurrent-ruby (~> 1.0) rack (>= 2.2.4, < 4) sprockets-rails (3.4.2) actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) - ssrf_filter (1.1.1) - temple (0.10.2) + ssrf_filter (1.1.2) + temple (0.10.3) terminal-table (3.0.2) unicode-display_width (>= 1.1.1, < 3) thor (1.2.2) thread_safe (0.3.6) - tilt (2.2.0) + tilt (2.3.0) timeout (0.4.0) tomlrb (2.0.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) uber (0.1.0) - uglifier (4.2.0) - execjs (>= 0.3.0, < 3) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) valid_email2 (2.3.1) activemodel (>= 3.2) mail (~> 2.5) @@ -770,12 +768,12 @@ GEM rexml (~> 3.2) warden (1.2.9) rack (>= 2.0.9) - web-console (4.2.0) + web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webmock (3.18.1) + webmock (3.19.1) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) @@ -790,17 +788,16 @@ GEM websocket-driver (0.7.6) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - wicked_pdf (2.6.3) + wicked_pdf (2.7.0) activesupport wisper (2.0.1) wisper-rspec (1.1.0) wkhtmltopdf-binary (0.12.6.6) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.11) + zeitwerk (2.6.12) PLATFORMS - arm64-darwin-22 x86_64-linux DEPENDENCIES @@ -817,11 +814,10 @@ DEPENDENCIES rubocop-faker spring (~> 2.0) spring-watcher-listen (~> 2.0.0) - uglifier (~> 4.1) web-console RUBY VERSION - ruby 3.0.5p211 + ruby 3.0.6p216 BUNDLED WITH - 2.2.33 + 2.3.20 diff --git a/app/models/concerns/decidim/decidim_awesome/has_vote_weight.rb b/app/models/concerns/decidim/decidim_awesome/has_vote_weight.rb new file mode 100644 index 000000000..94c248c16 --- /dev/null +++ b/app/models/concerns/decidim/decidim_awesome/has_vote_weight.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + module HasVoteWeight + extend ActiveSupport::Concern + + included do + has_one :vote_weight, foreign_key: "proposal_vote_id", class_name: "Decidim::DecidimAwesome::VoteWeight", dependent: :destroy + end + end + end +end diff --git a/app/models/concerns/decidim/decidim_awesome/has_weight_cache.rb b/app/models/concerns/decidim/decidim_awesome/has_weight_cache.rb new file mode 100644 index 000000000..8a937bcfd --- /dev/null +++ b/app/models/concerns/decidim/decidim_awesome/has_weight_cache.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + module HasWeightCache + extend ActiveSupport::Concern + + included do + has_one :weight_cache, foreign_key: "decidim_proposal_id", class_name: "Decidim::DecidimAwesome::WeightCache", dependent: :destroy + end + end + end +end diff --git a/app/models/decidim/decidim_awesome/user_override.rb b/app/models/concerns/decidim/decidim_awesome/user_override.rb similarity index 100% rename from app/models/decidim/decidim_awesome/user_override.rb rename to app/models/concerns/decidim/decidim_awesome/user_override.rb diff --git a/app/models/decidim/decidim_awesome/vote_weight.rb b/app/models/decidim/decidim_awesome/vote_weight.rb new file mode 100644 index 000000000..ae5e19250 --- /dev/null +++ b/app/models/decidim/decidim_awesome/vote_weight.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + class VoteWeight < ApplicationRecord + self.table_name = "decidim_awesome_vote_weights" + belongs_to :vote, foreign_key: "proposal_vote_id", class_name: "Decidim::Proposals::ProposalVote" + + delegate :proposal, to: :vote + + after_destroy :update_vote_weight_totals + after_save :update_vote_weight_totals + + def update_vote_weight_totals + cache = Decidim::DecidimAwesome::WeightCache.find_or_initialize_by(proposal: proposal) + cache.totals = cache.totals || {} + + prev = weight_previous_change&.first + if prev.present? + cache.totals[prev.to_s] = Decidim::DecidimAwesome::VoteWeight.where(vote: proposal.votes, weight: prev).count + cache.totals.delete(prev.to_s) if cache.totals[prev.to_s].zero? + end + cache.totals[weight.to_s] = Decidim::DecidimAwesome::VoteWeight.where(vote: proposal.votes, weight: weight).count + cache.totals.delete(weight.to_s) if cache.totals[weight.to_s].zero? + cache.weight_total = cache.totals.inject(0) { |sum, (weight, count)| sum + (weight.to_i * count) } + cache.save! + end + end + end +end diff --git a/app/models/decidim/decidim_awesome/weight_cache.rb b/app/models/decidim/decidim_awesome/weight_cache.rb new file mode 100644 index 000000000..3c3d7cb64 --- /dev/null +++ b/app/models/decidim/decidim_awesome/weight_cache.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Decidim + module DecidimAwesome + class WeightCache < ApplicationRecord + self.table_name = "decidim_awesome_weight_caches" + + belongs_to :proposal, foreign_key: "decidim_proposal_id", class_name: "Decidim::Proposals::Proposal" + end + end +end diff --git a/db/migrate/20231006113837_create_decidim_awesome_vote_weights.rb b/db/migrate/20231006113837_create_decidim_awesome_vote_weights.rb new file mode 100644 index 000000000..138eb28f5 --- /dev/null +++ b/db/migrate/20231006113837_create_decidim_awesome_vote_weights.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class CreateDecidimAwesomeVoteWeights < ActiveRecord::Migration[6.0] + def change + create_table :decidim_awesome_vote_weights do |t| + # this might be polymorphic in the future (if other types of votes are supported) + t.references :proposal_vote, null: false, index: { name: "decidim_awesome_proposals_weights_vote" } + + t.integer :weight, null: false, default: 1 + t.timestamps + end + end +end diff --git a/db/migrate/20231006113841_create_decidim_awesome_weight_caches.rb b/db/migrate/20231006113841_create_decidim_awesome_weight_caches.rb new file mode 100644 index 000000000..778cf166d --- /dev/null +++ b/db/migrate/20231006113841_create_decidim_awesome_weight_caches.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class CreateDecidimAwesomeWeightCaches < ActiveRecord::Migration[6.0] + def change + create_table :decidim_awesome_weight_caches do |t| + # this might be polymorphic in the future (if other types of votes are supported) + t.references :decidim_proposal, null: false, index: { name: "decidim_awesome_proposals_weights_cache" } + + t.jsonb :totals + t.integer :weight_total, default: 0 + t.timestamps + end + end +end diff --git a/lib/decidim/decidim_awesome/engine.rb b/lib/decidim/decidim_awesome/engine.rb index d62874a18..00b37b382 100644 --- a/lib/decidim/decidim_awesome/engine.rb +++ b/lib/decidim/decidim_awesome/engine.rb @@ -50,6 +50,10 @@ class Engine < ::Rails::Engine # override user's admin property Decidim::User.include(Decidim::DecidimAwesome::UserOverride) if DecidimAwesome.enabled?(:scoped_admins) + # add vote weight to proposal vote + Decidim::Proposals::ProposalVote.include(Decidim::DecidimAwesome::HasVoteWeight) # if DecidimAwesome.enabled?(:proposal_vote_weight) + # add vote weight cache to proposal + Decidim::Proposals::Proposal.include(Decidim::DecidimAwesome::HasWeightCache) # if DecidimAwesome.enabled?(:proposal_vote_weight) Decidim::MenuPresenter.include(Decidim::DecidimAwesome::MenuPresenterOverride) Decidim::MenuItemPresenter.include(Decidim::DecidimAwesome::MenuItemPresenterOverride) diff --git a/lib/decidim/decidim_awesome/test/factories.rb b/lib/decidim/decidim_awesome/test/factories.rb index 542586375..09806377c 100644 --- a/lib/decidim/decidim_awesome/test/factories.rb +++ b/lib/decidim/decidim_awesome/test/factories.rb @@ -41,4 +41,22 @@ manifest_name { :awesome_iframe } participatory_space { create(:participatory_process, :with_steps, organization: organization) } end + + factory :awesome_vote_weight, class: "Decidim::DecidimAwesome::VoteWeight" do + vote { create :proposal_vote } + sequence(:weight) { |n| n } + end + + factory :awesome_weight_cache, class: "Decidim::DecidimAwesome::WeightCache" do + proposal { create :proposal } + + trait :with_votes do + after :create do |weight| + 5.times.collect do |n| + vote = create(:proposal_vote, proposal: weight.proposal, author: create(:user, organization: weight.proposal.organization)) + create(:awesome_vote_weight, vote: vote, weight: n + 1) + end + end + end + end end diff --git a/spec/models/vote_weight_spec.rb b/spec/models/vote_weight_spec.rb new file mode 100644 index 000000000..59482dff0 --- /dev/null +++ b/spec/models/vote_weight_spec.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::DecidimAwesome + describe VoteWeight do + subject { vote_weight } + + let(:vote_weight) { create(:awesome_vote_weight) } + + it { is_expected.to be_valid } + + it "has a vote associated" do + expect(vote_weight.vote).to be_a(Decidim::Proposals::ProposalVote) + end + + it "the associated proposal vote has a vote weight" do + expect(vote_weight.vote.vote_weight).to eq(vote_weight) + end + + context "when vote is destroyed" do + let(:vote) { create(:proposal_vote) } + let!(:vote_weight) { create(:awesome_vote_weight, vote: vote) } + + it "destroys the vote weight" do + expect { vote.destroy }.to change(Decidim::DecidimAwesome::VoteWeight, :count).by(-1) + end + end + + context "when vote weight is destroyed" do + let(:vote) { create(:proposal_vote) } + let!(:vote_weight) { create(:awesome_vote_weight, vote: vote) } + + it "does not destroy the vote" do + expect { vote_weight.destroy }.not_to change(Decidim::Proposals::ProposalVote, :count) + end + end + end +end diff --git a/spec/models/weight_cache_spec.rb b/spec/models/weight_cache_spec.rb new file mode 100644 index 000000000..7dcf8f6e9 --- /dev/null +++ b/spec/models/weight_cache_spec.rb @@ -0,0 +1,117 @@ +# frozen_string_literal: true + +require "spec_helper" + +module Decidim::DecidimAwesome + describe VoteWeight do + subject { weight_cache } + + let(:weight_cache) { create(:awesome_weight_cache) } + let(:proposal) { create(:proposal) } + + it { is_expected.to be_valid } + + it "has a proposal associated" do + expect(weight_cache.proposal).to be_a(Decidim::Proposals::Proposal) + end + + it "the associated proposal has a weight cache" do + expect(weight_cache.proposal.weight_cache).to eq(weight_cache) + end + + context "when proposal is destroyed" do + let!(:weight_cache) { create(:awesome_weight_cache, proposal: proposal) } + + it "destroys the proposal weight" do + expect { proposal.destroy }.to change(Decidim::DecidimAwesome::WeightCache, :count).by(-1) + end + end + + context "when proposal weight is destroyed" do + let!(:weight_cache) { create(:awesome_weight_cache, proposal: proposal) } + + it "does not destroy the proposal" do + expect { weight_cache.destroy }.not_to change(Decidim::Proposals::ProposalVote, :count) + end + end + + context "when vote weight is" do + describe "created" do + it "increments the weight cache" do + expect { create(:proposal_vote, proposal: proposal) }.to change { proposal.votes.count }.by(1) + expect { create(:awesome_vote_weight, vote: proposal.votes.first, weight: 3) }.to change(Decidim::DecidimAwesome::WeightCache, :count).by(1) + expect(proposal.weight_cache.totals).to eq({ "3" => 1 }) + expect(proposal.weight_cache.weight_total).to eq(3) + end + + context "when cache already exists" do + let(:another_proposal) { create :proposal, component: proposal.component } + let!(:weight_cache) { create(:awesome_weight_cache, :with_votes, proposal: proposal) } + let!(:another_weight_cache) { create(:awesome_weight_cache, :with_votes, proposal: another_proposal) } + + it "has weights and votes" do + expect(weight_cache.reload.totals).to eq({ "1" => 1, "2" => 1, "3" => 1, "4" => 1, "5" => 1 }) + expect(weight_cache.weight_total).to eq(15) + end + + it "increments the weight cache" do + create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 1) + create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 3) + create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 3) + expect(weight_cache.reload.totals).to eq({ "1" => 2, "2" => 1, "3" => 3, "4" => 1, "5" => 1 }) + expect(weight_cache.weight_total).to eq(22) + end + end + + context "when cache does not exist yet" do + let(:weight_cache) { proposal.reload.weight_cache } + + it "has no weights and votes" do + expect(weight_cache).to be_nil + end + + it "increments the weight cache" do + create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 1) + create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 3) + create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 3) + expect(weight_cache.totals).to eq({ "1" => 1, "3" => 2 }) + expect(weight_cache.weight_total).to eq(7) + end + end + end + + # this is un unlikely scenario as voting removes and creates new vote weights, just in case... + describe "updated" do + let!(:vote_weight1) { create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 1) } + let!(:vote_weight2) { create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 2) } + let(:weight_cache) { proposal.reload.weight_cache } + + it "increments the weight cache" do + vote_weight1.weight = 3 + vote_weight1.save + expect(weight_cache.totals).to eq({ "2" => 1, "3" => 1 }) + expect(weight_cache.weight_total).to eq(5) + end + + it "decreases the weight cache" do + vote_weight2.weight = 1 + vote_weight2.save + expect(weight_cache.totals).to eq({ "1" => 2 }) + expect(weight_cache.weight_total).to eq(2) + end + end + + describe "destroyed" do + let!(:vote_weight1) { create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 1) } + let!(:vote_weight2) { create(:awesome_vote_weight, vote: create(:proposal_vote, proposal: proposal), weight: 2) } + let(:weight_cache) { proposal.reload.weight_cache } + + it "decreases the weight cache" do + vote_weight1.destroy + expect(weight_cache.totals).to eq({ "2" => 1 }) + expect(weight_cache.weight_total).to eq(2) + end + end + end + end +end