From 0820badcc3a209f3607b2fb461c67a980613b32b Mon Sep 17 00:00:00 2001 From: Kevin McConnell Date: Wed, 19 Mar 2025 11:27:47 +0000 Subject: [PATCH] Add involvement types to accesses This provides a way to set the level of involvement that a user has with a collection, and from which we determine the level of notifications to send. Users can be access-only, watching, or being notified about everything. If you're access-only, you won't get an notifications. If you're watching, you'll only get notifications for the items you're watching (which includes the items you've been assigned, have commented on, etc). If you're set to everything you'll get notifications about all activity in that collection. This change replaces our previous concept of subscriptions. Where previously you'd subscribe to a collection to get notifications in it, now you'll simply set the notification level on your access. --- app/controllers/bubbles/watches_controller.rb | 4 - .../buckets/involvements_controller.rb | 7 + .../buckets/subscriptions_controller.rb | 11 - app/helpers/accesses_helper.rb | 12 + app/helpers/buckets/involvements_helper.rb | 2 + app/models/access.rb | 2 + app/models/bubble/watchable.rb | 9 +- app/models/bucket.rb | 2 +- app/models/bucket/accessible.rb | 5 + app/models/concerns/subscribable.rb | 22 - app/models/notifier.rb | 2 +- app/models/notifier/assigned.rb | 2 +- app/models/notifier/published.rb | 2 +- app/models/subscription.rb | 5 - app/models/user.rb | 1 - .../buckets/involvements/update.html.erb | 3 + .../notifications/settings/_bucket.html.erb | 25 +- config/routes.rb | 1 + ...50319092704_add_involvement_to_accesses.rb | 7 + .../20250319161627_drop_subscriptions.rb | 14 + db/schema.rb | 23 +- db/schema_cache.yml | 827 +++++++++--------- .../buckets/involvements_controller_test.rb | 18 + .../buckets/subscriptions_controller_test.rb | 25 - test/fixtures/subscriptions.yml | 7 - test/models/bubble/commentable_test.rb | 2 +- test/models/bubble/watchable_test.rb | 13 +- test/models/notifier_test.rb | 21 +- 28 files changed, 531 insertions(+), 543 deletions(-) create mode 100644 app/controllers/buckets/involvements_controller.rb delete mode 100644 app/controllers/buckets/subscriptions_controller.rb create mode 100644 app/helpers/buckets/involvements_helper.rb delete mode 100644 app/models/concerns/subscribable.rb delete mode 100644 app/models/subscription.rb create mode 100644 app/views/buckets/involvements/update.html.erb create mode 100644 db/migrate/20250319092704_add_involvement_to_accesses.rb create mode 100644 db/migrate/20250319161627_drop_subscriptions.rb create mode 100644 test/controllers/buckets/involvements_controller_test.rb delete mode 100644 test/controllers/buckets/subscriptions_controller_test.rb delete mode 100644 test/fixtures/subscriptions.yml diff --git a/app/controllers/bubbles/watches_controller.rb b/app/controllers/bubbles/watches_controller.rb index e5a1873b5..cecd292b5 100644 --- a/app/controllers/bubbles/watches_controller.rb +++ b/app/controllers/bubbles/watches_controller.rb @@ -1,10 +1,6 @@ class Bubbles::WatchesController < ApplicationController include BubbleScoped, BucketScoped - def show - @watchers = @bubble.watchers_and_subscribers.sorted_with_user_first(Current.user) - end - def create set_watching_and_redirect(true) end diff --git a/app/controllers/buckets/involvements_controller.rb b/app/controllers/buckets/involvements_controller.rb new file mode 100644 index 000000000..dbfcbb6d7 --- /dev/null +++ b/app/controllers/buckets/involvements_controller.rb @@ -0,0 +1,7 @@ +class Buckets::InvolvementsController < ApplicationController + include BucketScoped + + def update + @bucket.access_for(Current.user).update!(involvement: params[:involvement]) + end +end diff --git a/app/controllers/buckets/subscriptions_controller.rb b/app/controllers/buckets/subscriptions_controller.rb deleted file mode 100644 index a5a3b92f7..000000000 --- a/app/controllers/buckets/subscriptions_controller.rb +++ /dev/null @@ -1,11 +0,0 @@ -class Buckets::SubscriptionsController < ApplicationController - include BucketScoped - - def update - if params[:subscribed] - @bucket.subscribe(Current.user) - else - @bucket.unsubscribe(Current.user) - end - end -end diff --git a/app/helpers/accesses_helper.rb b/app/helpers/accesses_helper.rb index e1fb3ad0f..4528191f0 100644 --- a/app/helpers/accesses_helper.rb +++ b/app/helpers/accesses_helper.rb @@ -12,4 +12,16 @@ module AccessesHelper locals: { selected: selected }, cached: ->(user) { [ user, selected ] } end + + def access_involvement_advance_button(bucket, user) + access = bucket.access_for(user) + + button_to access.involvement, bucket_involvement_path(bucket), method: :put, params: { involvement: next_involvement(access.involvement) } + end + + private + def next_involvement(involvement) + order = %w[ access_only watching everything ] + order[(order.index(involvement.to_s) + 1) % order.size] + end end diff --git a/app/helpers/buckets/involvements_helper.rb b/app/helpers/buckets/involvements_helper.rb new file mode 100644 index 000000000..c4adecb12 --- /dev/null +++ b/app/helpers/buckets/involvements_helper.rb @@ -0,0 +1,2 @@ +module Buckets::InvolvementsHelper +end diff --git a/app/models/access.rb b/app/models/access.rb index dbbb11590..1ca40d9e2 100644 --- a/app/models/access.rb +++ b/app/models/access.rb @@ -1,4 +1,6 @@ class Access < ApplicationRecord belongs_to :bucket belongs_to :user + + enum :involvement, %i[ access_only watching everything ].index_by(&:itself) end diff --git a/app/models/bubble/watchable.rb b/app/models/bubble/watchable.rb index efd2c5cb5..8ad47554d 100644 --- a/app/models/bubble/watchable.rb +++ b/app/models/bubble/watchable.rb @@ -9,15 +9,18 @@ module Bubble::Watchable end def watched_by?(user) - watchers_and_subscribers.include?(user) + watchers_and_subscribers(include_only_watching: true).include?(user) end def set_watching(user, watching) watches.where(user: user).first_or_create.update!(watching: watching) end - def watchers_and_subscribers - User.where(id: bucket.subscribers.pluck(:id) + + def watchers_and_subscribers(include_only_watching: false) + involvements = include_only_watching ? [ :watching, :everything ] : :everything + subscribers = bucket.users.where(accesses: { involvement: involvements }) + + User.where(id: subscribers.pluck(:id) + watches.watching.pluck(:user_id) - watches.not_watching.pluck(:user_id)) end diff --git a/app/models/bucket.rb b/app/models/bucket.rb index 2fcf38e13..20a7f0336 100644 --- a/app/models/bucket.rb +++ b/app/models/bucket.rb @@ -1,5 +1,5 @@ class Bucket < ApplicationRecord - include Accessible, Broadcastable, Filterable, Subscribable + include Accessible, Broadcastable, Filterable belongs_to :account belongs_to :creator, class_name: "User", default: -> { Current.user } diff --git a/app/models/bucket/accessible.rb b/app/models/bucket/accessible.rb index 4b21c117b..3c72e9983 100644 --- a/app/models/bucket/accessible.rb +++ b/app/models/bucket/accessible.rb @@ -20,6 +20,7 @@ module Bucket::Accessible end has_many :users, through: :accesses + has_many :access_only_users, -> { merge(Access.access_only) }, through: :accesses, source: :user scope :all_access, -> { where(all_access: true) } @@ -27,6 +28,10 @@ module Bucket::Accessible after_save_commit :grant_access_to_everyone end + def access_for(user) + accesses.find_by(user: user) + end + private def grant_access_to_everyone accesses.grant_to(account.users) if all_access_previously_changed?(to: true) diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb deleted file mode 100644 index 92cf92d1c..000000000 --- a/app/models/concerns/subscribable.rb +++ /dev/null @@ -1,22 +0,0 @@ -module Subscribable - extend ActiveSupport::Concern - - TYPES = %w[ Bucket ] - - included do - has_many :subscriptions, as: :subscribable, dependent: :destroy - has_many :subscribers, through: :subscriptions, source: :user - end - - def subscribe(user) - subscriptions.create_or_find_by!(user: user) - end - - def unsubscribe(user) - subscriptions.find_by(user: user)&.destroy! - end - - def subscribed_by?(user) - subscriptions.exists?(user: user) - end -end diff --git a/app/models/notifier.rb b/app/models/notifier.rb index 10b1ba3c2..9c89d6bdf 100644 --- a/app/models/notifier.rb +++ b/app/models/notifier.rb @@ -27,7 +27,7 @@ class Notifier end def recipients - bubble.watchers.without(creator) + bubble.watchers_and_subscribers.without(creator) end def resource diff --git a/app/models/notifier/assigned.rb b/app/models/notifier/assigned.rb index 283011b2a..3027ab56c 100644 --- a/app/models/notifier/assigned.rb +++ b/app/models/notifier/assigned.rb @@ -1,6 +1,6 @@ class Notifier::Assigned < Notifier private def recipients - event.assignees + event.assignees.excluding(bubble.bucket.access_only_users) end end diff --git a/app/models/notifier/published.rb b/app/models/notifier/published.rb index d43b8c8e3..3aadc3c93 100644 --- a/app/models/notifier/published.rb +++ b/app/models/notifier/published.rb @@ -1,6 +1,6 @@ class Notifier::Published < Notifier private def recipients - bubble.watchers_and_subscribers.without(creator) + bubble.watchers_and_subscribers(include_only_watching: true).without(creator) end end diff --git a/app/models/subscription.rb b/app/models/subscription.rb deleted file mode 100644 index 1a6ed8ad7..000000000 --- a/app/models/subscription.rb +++ /dev/null @@ -1,5 +0,0 @@ -class Subscription < ApplicationRecord - belongs_to :user - - delegated_type :subscribable, types: Subscribable::TYPES, inverse_of: :subscription -end diff --git a/app/models/user.rb b/app/models/user.rb index 57a8e636c..54dca6b3b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -9,7 +9,6 @@ class User < ApplicationRecord has_many :accesses, dependent: :destroy has_many :buckets, through: :accesses has_many :accessible_bubbles, through: :buckets, source: :bubbles - has_many :subscriptions, dependent: :destroy has_many :filters, foreign_key: :creator_id, inverse_of: :creator, dependent: :destroy diff --git a/app/views/buckets/involvements/update.html.erb b/app/views/buckets/involvements/update.html.erb new file mode 100644 index 000000000..0acb52d76 --- /dev/null +++ b/app/views/buckets/involvements/update.html.erb @@ -0,0 +1,3 @@ +<%= turbo_frame_tag dom_id(@bucket, :involvement) do %> + <%= render "notifications/settings/bucket", bucket: @bucket, user: Current.user %> +<% end %> diff --git a/app/views/notifications/settings/_bucket.html.erb b/app/views/notifications/settings/_bucket.html.erb index a03384467..16839b0c3 100644 --- a/app/views/notifications/settings/_bucket.html.erb +++ b/app/views/notifications/settings/_bucket.html.erb @@ -1,18 +1,13 @@ -<%= turbo_frame_tag dom_id(bucket, :subscription) do %> - <%= form_with url: bucket_subscriptions_path(bucket), method: :put, data: { controller: "form"} do |form| %> -
-

- <%= bucket.name %> -

+<%= turbo_frame_tag dom_id(bucket, :involvement) do %> +
+

+ <%= bucket.name %> +

- + - -
- <% end %> + +
<% end %> diff --git a/config/routes.rb b/config/routes.rb index 4468079a5..0aacdba10 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -36,6 +36,7 @@ Rails.application.routes.draw do scope module: :buckets do resource :subscriptions resource :workflow, only: :update + resource :involvement end resources :bubbles do diff --git a/db/migrate/20250319092704_add_involvement_to_accesses.rb b/db/migrate/20250319092704_add_involvement_to_accesses.rb new file mode 100644 index 000000000..73c70a3a9 --- /dev/null +++ b/db/migrate/20250319092704_add_involvement_to_accesses.rb @@ -0,0 +1,7 @@ +class AddInvolvementToAccesses < ActiveRecord::Migration[8.1] + def change + change_table :accesses do |t| + t.string :involvement, null: false, default: "watching" + end + end +end diff --git a/db/migrate/20250319161627_drop_subscriptions.rb b/db/migrate/20250319161627_drop_subscriptions.rb new file mode 100644 index 000000000..9734075c8 --- /dev/null +++ b/db/migrate/20250319161627_drop_subscriptions.rb @@ -0,0 +1,14 @@ +class DropSubscriptions < ActiveRecord::Migration[8.1] + def change + execute " + update accesses set involvement = 'access_only' + " + execute " + update accesses set involvement = 'watching' + from (select user_id, subscribable_id as bucket_id from subscriptions) as subscriptions + where subscriptions.user_id = accesses.user_id and subscriptions.bucket_id = accesses.bucket_id + " + + drop_table :subscriptions + end +end diff --git a/db/schema.rb b/db/schema.rb index ef0105d28..68ace61c5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,10 +10,11 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.1].define(version: 2025_03_27_191456) do +ActiveRecord::Schema[8.1].define(version: 2025_04_03_094604) do create_table "accesses", force: :cascade do |t| t.integer "bucket_id", null: false t.datetime "created_at", null: false + t.string "involvement", default: "watching", null: false t.datetime "updated_at", null: false t.integer "user_id", null: false t.index ["bucket_id", "user_id"], name: "index_accesses_on_bucket_id_and_user_id", unique: true @@ -94,6 +95,13 @@ ActiveRecord::Schema[8.1].define(version: 2025_03_27_191456) do t.index ["bubble_id"], name: "index_assignments_on_bubble_id" end + create_table "bubble_engagements", force: :cascade do |t| + t.integer "bubble_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["bubble_id"], name: "index_bubble_engagements_on_bubble_id" + end + create_table "bubbles", force: :cascade do |t| t.float "activity_score", default: 0.0, null: false t.datetime "activity_score_at" @@ -222,6 +230,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_03_27_191456) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.integer "user_id", null: false + t.index ["bubble_id", "user_id"], name: "index_pins_on_bubble_id_and_user_id", unique: true t.index ["bubble_id"], name: "index_pins_on_bubble_id" t.index ["user_id"], name: "index_pins_on_user_id" end @@ -254,17 +263,6 @@ ActiveRecord::Schema[8.1].define(version: 2025_03_27_191456) do t.index ["user_id"], name: "index_sessions_on_user_id" end - create_table "subscriptions", force: :cascade do |t| - t.datetime "created_at", null: false - t.integer "subscribable_id", null: false - t.string "subscribable_type", null: false - t.datetime "updated_at", null: false - t.integer "user_id", null: false - t.index ["subscribable_type", "subscribable_id", "user_id"], name: "idx_on_subscribable_type_subscribable_id_user_id_81936d569b", unique: true - t.index ["subscribable_type", "subscribable_id"], name: "index_subscriptions_on_subscribable" - t.index ["user_id"], name: "index_subscriptions_on_user_id" - end - create_table "taggings", force: :cascade do |t| t.integer "bubble_id", null: false t.datetime "created_at", null: false @@ -337,7 +335,6 @@ ActiveRecord::Schema[8.1].define(version: 2025_03_27_191456) do add_foreign_key "pops", "bubbles" add_foreign_key "pops", "users" add_foreign_key "sessions", "users" - add_foreign_key "subscriptions", "users" add_foreign_key "taggings", "bubbles" add_foreign_key "taggings", "tags" add_foreign_key "users", "accounts" diff --git a/db/schema_cache.yml b/db/schema_cache.yml index 40d083029..7e5d9cea3 100644 --- a/db/schema_cache.yml +++ b/db/schema_cache.yml @@ -21,7 +21,7 @@ columns: default_function: collation: comment: - - &21 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &23 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: bucket_id cast_type: *1 @@ -31,16 +31,6 @@ columns: default_function: collation: comment: - - &30 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: user_id - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - &6 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: created_at @@ -60,7 +50,7 @@ columns: default_function: collation: comment: - - &7 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &9 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: updated_at cast_type: *3 @@ -70,55 +60,9 @@ columns: default_function: collation: comment: - accounts: - - *5 - - &10 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &30 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: name - cast_type: &8 !ruby/object:ActiveModel::Type::String - true: t - false: f - precision: - scale: - limit: - sql_type_metadata: &9 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: varchar - type: :string - limit: - precision: - scale: - 'null': false - default: - default_function: - collation: - comment: - - *6 - - *7 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: join_code - cast_type: *8 - sql_type_metadata: *9 - 'null': true - default: - default_function: - collation: - comment: - action_text_markdowns: - - *5 - - &11 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: record_type - cast_type: *8 - sql_type_metadata: *9 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: record_id + name: user_id cast_type: *1 sql_type_metadata: *2 'null': false @@ -126,7 +70,52 @@ columns: default_function: collation: comment: - - *10 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: involvement + cast_type: &7 !ruby/object:ActiveModel::Type::String + true: t + false: f + precision: + scale: + limit: + sql_type_metadata: &8 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata + sql_type: varchar + type: :string + limit: + precision: + scale: + 'null': false + default: watching + default_function: + collation: + comment: + accounts: + - *5 + - *6 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: join_code + cast_type: *7 + sql_type_metadata: *8 + 'null': true + default: + default_function: + collation: + comment: + - &10 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: name + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + - *9 + action_text_markdowns: + - *5 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: content @@ -148,21 +137,40 @@ columns: collation: comment: - *6 - - *7 - active_storage_attachments: - - *5 - *10 - - *11 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: record_id - cast_type: &12 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer + cast_type: *1 + sql_type_metadata: *2 + 'null': false + default: + default_function: + collation: + comment: + - &13 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: record_type + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + - *9 + active_storage_attachments: + - *5 + - &16 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: blob_id + cast_type: &11 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3Adapter::SQLite3Integer precision: scale: limit: max: 9223372036854775808 min: -9223372036854775808 - sql_type_metadata: &13 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata + sql_type_metadata: &12 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata sql_type: bigint type: :integer limit: @@ -173,22 +181,24 @@ columns: default_function: collation: comment: - - &16 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - *6 + - *10 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: blob_id - cast_type: *12 - sql_type_metadata: *13 + name: record_id + cast_type: *11 + sql_type_metadata: *12 'null': false default: default_function: collation: comment: - - *6 + - *13 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: slug - cast_type: *8 - sql_type_metadata: *9 + cast_type: *7 + sql_type_metadata: *8 'null': true default: default_function: @@ -196,11 +206,11 @@ columns: comment: active_storage_blobs: - *5 - - &17 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: key - cast_type: *8 - sql_type_metadata: *9 + name: byte_size + cast_type: *11 + sql_type_metadata: *12 'null': false default: default_function: @@ -208,10 +218,10 @@ columns: comment: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: filename - cast_type: *8 - sql_type_metadata: *9 - 'null': false + name: checksum + cast_type: *7 + sql_type_metadata: *8 + 'null': true default: default_function: collation: @@ -219,13 +229,34 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: content_type - cast_type: *8 - sql_type_metadata: *9 + cast_type: *7 + sql_type_metadata: *8 'null': true default: default_function: collation: comment: + - *6 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: filename + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + - &17 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: key + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: metadata @@ -239,42 +270,21 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: service_name - cast_type: *8 - sql_type_metadata: *9 + cast_type: *7 + sql_type_metadata: *8 'null': false default: default_function: collation: comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: byte_size - cast_type: *12 - sql_type_metadata: *13 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: checksum - cast_type: *8 - sql_type_metadata: *9 - 'null': true - default: - default_function: - collation: - comment: - - *6 active_storage_variant_records: - *5 - *16 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: variation_digest - cast_type: *8 - sql_type_metadata: *9 + cast_type: *7 + sql_type_metadata: *8 'null': false default: default_function: @@ -285,26 +295,16 @@ columns: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: value - cast_type: *8 - sql_type_metadata: *9 + cast_type: *7 + sql_type_metadata: *8 'null': true default: default_function: collation: comment: - *6 - - *7 + - *9 assignees_filters: - - &18 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: filter_id - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - &19 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: assignee_id @@ -315,8 +315,17 @@ columns: default_function: collation: comment: + - &18 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: filter_id + cast_type: *1 + sql_type_metadata: *2 + 'null': false + default: + default_function: + collation: + comment: assigners_filters: - - *18 - &20 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: assigner_id @@ -327,9 +336,11 @@ columns: default_function: collation: comment: + - *18 assignments: - *5 - *19 + - *20 - &25 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: bubble_id @@ -341,32 +352,103 @@ columns: collation: comment: - *6 - - *7 - - *20 + - *9 + bubble_engagements: + - *5 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: bubble_id + cast_type: *1 + sql_type_metadata: *2 + 'null': true + default: + default_function: + collation: + comment: + - *6 + - *9 bubbles: - *5 - - &32 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: title - cast_type: *8 - sql_type_metadata: *9 + name: activity_score + cast_type: &21 !ruby/object:ActiveModel::Type::Float + precision: + scale: + limit: + sql_type_metadata: &22 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata + sql_type: float + type: :float + limit: + precision: + scale: + 'null': false + default: 0.0 + default_function: + collation: + comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: activity_score_at + cast_type: *3 + sql_type_metadata: *4 'null': true default: default_function: collation: comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: activity_score_order + cast_type: *21 + sql_type_metadata: *22 + 'null': false + default: 0.0 + default_function: + collation: + comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: auto_pop_at + cast_type: *3 + sql_type_metadata: *4 + 'null': false + default: + default_function: + collation: + comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: boosts_count + cast_type: *1 + sql_type_metadata: *2 + 'null': false + default: 0 + default_function: + collation: + comment: + - *23 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: color - cast_type: *8 - sql_type_metadata: *9 + cast_type: *7 + sql_type_metadata: *8 'null': true default: default_function: collation: comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: comments_count + cast_type: *1 + sql_type_metadata: *2 + 'null': false + default: 0 + default_function: + collation: + comment: - *6 - - *7 - &24 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: creator_id @@ -396,17 +478,6 @@ columns: default_function: collation: comment: - - *21 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: boosts_count - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: 0 - default_function: - collation: - comment: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: stage_id @@ -417,34 +488,6 @@ columns: default_function: collation: comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: comments_count - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: 0 - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: activity_score - cast_type: &22 !ruby/object:ActiveModel::Type::Float - precision: - scale: - limit: - sql_type_metadata: &23 !ruby/object:ActiveRecord::ConnectionAdapters::SqlTypeMetadata - sql_type: float - type: :float - limit: - precision: - scale: - 'null': false - default: 0.0 - default_function: - collation: - comment: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: status @@ -455,39 +498,20 @@ columns: default_function: collation: comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &33 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: auto_pop_at - cast_type: *3 - sql_type_metadata: *4 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: activity_score_at - cast_type: *3 - sql_type_metadata: *4 + name: title + cast_type: *7 + sql_type_metadata: *8 'null': true default: default_function: collation: comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: activity_score_order - cast_type: *22 - sql_type_metadata: *23 - 'null': false - default: 0.0 - default_function: - collation: - comment: + - *9 buckets: - *5 - - &33 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + - &32 !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: account_id cast_type: *1 @@ -497,10 +521,6 @@ columns: default_function: collation: comment: - - *24 - - *10 - - *6 - - *7 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: all_access @@ -519,6 +539,10 @@ columns: default_function: collation: comment: + - *6 + - *24 + - *10 + - *9 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: workflow_id @@ -530,23 +554,45 @@ columns: collation: comment: buckets_filters: + - *23 - *18 - - *21 comments: - *5 - - *24 - *6 - - *7 - creators_filters: - - *18 - *24 + - *9 + creators_filters: + - *24 + - *18 event_summaries: - *5 - *6 - - *7 + - *9 events: - *5 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: action + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + - *25 + - *6 - *24 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: due_date + cast_type: *26 + sql_type_metadata: *27 + 'null': true + default: + default_function: + collation: + comment: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: particulars @@ -565,18 +611,6 @@ columns: default_function: collation: comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: action - cast_type: *8 - sql_type_metadata: *9 - 'null': false - default: - default_function: - collation: - comment: - - *6 - - *7 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: summary_id @@ -587,32 +621,11 @@ columns: default_function: collation: comment: - - *25 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: due_date - cast_type: *26 - sql_type_metadata: *27 - 'null': true - default: - default_function: - collation: - comment: + - *9 filters: - *5 - - *24 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: params_digest - cast_type: *8 - sql_type_metadata: *9 - 'null': false - default: - default_function: - collation: - comment: - *6 - - *7 + - *24 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: fields @@ -623,6 +636,17 @@ columns: default_function: collation: comment: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: params_digest + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + - *9 filters_stages: - *18 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column @@ -650,16 +674,7 @@ columns: messages: - *5 - *25 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: messageable_type - cast_type: *8 - sql_type_metadata: *9 - 'null': false - default: - default_function: - collation: - comment: + - *6 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: messageable_id @@ -670,11 +685,21 @@ columns: default_function: collation: comment: - - *6 - - *7 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: messageable_type + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + - *9 notifications: - *5 - - *30 + - *25 + - *6 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: event_id @@ -685,13 +710,12 @@ columns: default_function: collation: comment: - - *25 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: resource_type - cast_type: *8 - sql_type_metadata: *9 - 'null': false + name: read_at + cast_type: *3 + sql_type_metadata: *4 + 'null': true default: default_function: collation: @@ -706,27 +730,29 @@ columns: default_function: collation: comment: - - *6 - - *7 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: read_at - cast_type: *3 - sql_type_metadata: *4 - 'null': true + name: resource_type + cast_type: *7 + sql_type_metadata: *8 + 'null': false default: default_function: collation: comment: + - *9 + - *30 pins: - *5 - *25 - - *30 - *6 - - *7 + - *9 + - *30 pops: - *5 - *25 + - *6 + - *9 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: user_id @@ -737,8 +763,6 @@ columns: default_function: collation: comment: - - *6 - - *7 reactions: - *5 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column @@ -751,16 +775,6 @@ columns: default_function: collation: comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: reacter_id - cast_type: *1 - sql_type_metadata: *2 - 'null': false - default: - default_function: - collation: - comment: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: content @@ -782,58 +796,9 @@ columns: collation: comment: - *6 - - *7 - schema_migrations: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: - name: version - cast_type: *8 - sql_type_metadata: *9 - 'null': false - default: - default_function: - collation: - comment: - sessions: - - *5 - - *30 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: ip_address - cast_type: *8 - sql_type_metadata: *9 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: user_agent - cast_type: *8 - sql_type_metadata: *9 - 'null': true - default: - default_function: - collation: - comment: - - *6 - - *7 - subscriptions: - - *5 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: subscribable_type - cast_type: *8 - sql_type_metadata: *9 - 'null': false - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: subscribable_id + name: reacter_id cast_type: *1 sql_type_metadata: *2 'null': false @@ -841,45 +806,58 @@ columns: default_function: collation: comment: - - *30 + - *9 + schema_migrations: + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: version + cast_type: *7 + sql_type_metadata: *8 + 'null': false + default: + default_function: + collation: + comment: + sessions: + - *5 - *6 - - *7 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: ip_address + cast_type: *7 + sql_type_metadata: *8 + 'null': true + default: + default_function: + collation: + comment: + - *9 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: user_agent + cast_type: *7 + sql_type_metadata: *8 + 'null': true + default: + default_function: + collation: + comment: + - *30 taggings: - *5 - *25 - - *31 - *6 - - *7 + - *31 + - *9 tags: - *5 - *32 - *6 - - *7 - *33 + - *9 users: - *5 - - *33 - - *10 - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: email_address - cast_type: *8 - sql_type_metadata: *9 - 'null': true - default: - default_function: - collation: - comment: - - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column - auto_increment: - name: password_digest - cast_type: *8 - sql_type_metadata: *9 - 'null': true - default: - default_function: - collation: - comment: + - *32 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: active @@ -891,21 +869,44 @@ columns: collation: comment: - *6 - - *7 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: email_address + cast_type: *7 + sql_type_metadata: *8 + 'null': true + default: + default_function: + collation: + comment: + - *10 + - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column + auto_increment: + name: password_digest + cast_type: *7 + sql_type_metadata: *8 + 'null': true + default: + default_function: + collation: + comment: - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: role - cast_type: *8 - sql_type_metadata: *9 + cast_type: *7 + sql_type_metadata: *8 'null': false default: member default_function: collation: comment: + - *9 watches: - *5 - - *30 - *25 + - *6 + - *9 + - *30 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: watching @@ -916,10 +917,11 @@ columns: default_function: collation: comment: - - *6 - - *7 workflow_stages: - *5 + - *6 + - *10 + - *9 - !ruby/object:ActiveRecord::ConnectionAdapters::SQLite3::Column auto_increment: name: workflow_id @@ -930,15 +932,12 @@ columns: default_function: collation: comment: - - *10 - - *6 - - *7 workflows: - *5 - - *33 - - *10 + - *32 - *6 - - *7 + - *10 + - *9 primary_keys: accesses: id accounts: id @@ -950,6 +949,7 @@ primary_keys: assignees_filters: assigners_filters: assignments: id + bubble_engagements: id bubbles: id buckets: id buckets_filters: @@ -967,7 +967,6 @@ primary_keys: reactions: id schema_migrations: version sessions: id - subscriptions: id taggings: id tags: id users: id @@ -985,6 +984,7 @@ data_sources: assignees_filters: true assigners_filters: true assignments: true + bubble_engagements: true bubbles: true buckets: true buckets_filters: true @@ -1002,7 +1002,6 @@ data_sources: reactions: true schema_migrations: true sessions: true - subscriptions: true taggings: true tags: true users: true @@ -1299,6 +1298,23 @@ indexes: nulls_not_distinct: comment: valid: true + bubble_engagements: + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: bubble_engagements + name: index_bubble_engagements_on_bubble_id + unique: false + columns: + - bubble_id + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true bubbles: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: bubbles @@ -1768,6 +1784,23 @@ indexes: nulls_not_distinct: comment: valid: true + - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition + table: pins + name: index_pins_on_bubble_id_and_user_id + unique: true + columns: + - bubble_id + - user_id + lengths: {} + orders: {} + opclasses: {} + where: + type: + using: + include: + nulls_not_distinct: + comment: + valid: true pops: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: pops @@ -1852,58 +1885,6 @@ indexes: nulls_not_distinct: comment: valid: true - subscriptions: - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: subscriptions - name: index_subscriptions_on_user_id - unique: false - columns: - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: subscriptions - name: index_subscriptions_on_subscribable - unique: false - columns: - - subscribable_type - - subscribable_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true - - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition - table: subscriptions - name: idx_on_subscribable_type_subscribable_id_user_id_81936d569b - unique: true - columns: - - subscribable_type - - subscribable_id - - user_id - lengths: {} - orders: {} - opclasses: {} - where: - type: - using: - include: - nulls_not_distinct: - comment: - valid: true taggings: - !ruby/object:ActiveRecord::ConnectionAdapters::IndexDefinition table: taggings @@ -2071,4 +2052,4 @@ indexes: nulls_not_distinct: comment: valid: true -version: 20250327191456 +version: 20250403094604 diff --git a/test/controllers/buckets/involvements_controller_test.rb b/test/controllers/buckets/involvements_controller_test.rb new file mode 100644 index 000000000..014b98f1e --- /dev/null +++ b/test/controllers/buckets/involvements_controller_test.rb @@ -0,0 +1,18 @@ +require "test_helper" + +class Buckets::InvolvementsControllerTest < ActionDispatch::IntegrationTest + setup do + sign_in_as :kevin + end + + test "update" do + bucket = buckets(:writebook) + bucket.access_for(users(:kevin)).access_only! + + assert_changes -> { bucket.access_for(users(:kevin)).involvement }, from: "access_only", to: "watching" do + put bucket_involvement_url(bucket, involvement: "watching") + end + + assert_response :success + end +end diff --git a/test/controllers/buckets/subscriptions_controller_test.rb b/test/controllers/buckets/subscriptions_controller_test.rb deleted file mode 100644 index ccd019145..000000000 --- a/test/controllers/buckets/subscriptions_controller_test.rb +++ /dev/null @@ -1,25 +0,0 @@ -require "test_helper" - -class Buckets::SubscriptionsControllerTest < ActionDispatch::IntegrationTest - setup do - sign_in_as :david - end - - test "subscribing to a bucket" do - assert_changes -> { buckets(:writebook).subscribed_by?(users(:david)) }, from: false, to: true do - put bucket_subscriptions_url(buckets(:writebook)), params: { subscribed: "1" } - end - - assert_response :success - end - - test "unsubscribing from a bucket" do - buckets(:writebook).subscribe(users(:david)) - - assert_changes -> { buckets(:writebook).subscribed_by?(users(:david)) }, from: true, to: false do - put bucket_subscriptions_url(buckets(:writebook)) - end - - assert_response :success - end -end diff --git a/test/fixtures/subscriptions.yml b/test/fixtures/subscriptions.yml deleted file mode 100644 index 6ca23ecf3..000000000 --- a/test/fixtures/subscriptions.yml +++ /dev/null @@ -1,7 +0,0 @@ -writebook_kevin: - subscribable: writebook (Bucket) - user: kevin - -writebook_jz: - subscribable: writebook (Bucket) - user: jz diff --git a/test/models/bubble/commentable_test.rb b/test/models/bubble/commentable_test.rb index 43100006e..dc5f6903b 100644 --- a/test/models/bubble/commentable_test.rb +++ b/test/models/bubble/commentable_test.rb @@ -2,7 +2,7 @@ require "test_helper" class Bubble::CommentableTest < ActiveSupport::TestCase test "creating a comment on a bubble makes the creator watch the bubble" do - buckets(:writebook).subscriptions.destroy_all + buckets(:writebook).access_for(users(:kevin)).access_only! assert_not bubbles(:text).watched_by?(users(:kevin)) with_current_user(:kevin) do diff --git a/test/models/bubble/watchable_test.rb b/test/models/bubble/watchable_test.rb index 2f9c4a0f0..267bbf2d8 100644 --- a/test/models/bubble/watchable_test.rb +++ b/test/models/bubble/watchable_test.rb @@ -3,7 +3,7 @@ require "test_helper" class Bubble::WatchableTest < ActiveSupport::TestCase setup do Watch.destroy_all - Subscription.destroy_all + Access.all.update!(involvement: :access_only) end test "watched_by?" do @@ -16,9 +16,8 @@ class Bubble::WatchableTest < ActiveSupport::TestCase assert_not bubbles(:logo).watched_by?(users(:kevin)) end - test "watched_by? when subscribed to the bucket" do - buckets(:writebook).subscribe(users(:kevin)) - + test "watched_by? when notifications are set on the bucket" do + buckets(:writebook).access_for(users(:kevin)).watching! assert bubbles(:text).watched_by?(users(:kevin)) bubbles(:logo).set_watching(users(:kevin), false) @@ -26,16 +25,14 @@ class Bubble::WatchableTest < ActiveSupport::TestCase end test "bubbles are initially watched by their creator" do - buckets(:writebook).unsubscribe(users(:kevin)) - bubble = buckets(:writebook).bubbles.create!(creator: users(:kevin)) assert bubble.watched_by?(users(:kevin)) end test "watchers_and_subscribers" do - buckets(:writebook).subscribe(users(:kevin)) - buckets(:writebook).subscribe(users(:jz)) + buckets(:writebook).access_for(users(:kevin)).watching! + buckets(:writebook).access_for(users(:jz)).everything! bubbles(:logo).set_watching(users(:kevin), true) bubbles(:logo).set_watching(users(:jz), false) diff --git a/test/models/notifier_test.rb b/test/models/notifier_test.rb index df6faa127..49050b20a 100644 --- a/test/models/notifier_test.rb +++ b/test/models/notifier_test.rb @@ -28,6 +28,14 @@ class NotifierTest < ActiveSupport::TestCase assert_equal [ users(:kevin) ], notifications.map(&:user) end + test "does not create a notification for access-only users" do + buckets(:writebook).access_for(users(:kevin)).access_only! + + notifications = Notifier.for(events(:layout_commented)).generate + + assert_equal [ users(:kevin) ], notifications.map(&:user) + end + test "the published event creates notifications for subscribers as well as watchers" do notifications = Notifier.for(events(:logo_published)).generate @@ -40,9 +48,20 @@ class NotifierTest < ActiveSupport::TestCase assert_equal bubbles(:logo), Notification.last.resource end - test "for assignment events only create a notification for the assignee" do + test "assignment events only create a notification for the assignee" do + buckets(:writebook).access_for(users(:jz)).watching! + buckets(:writebook).access_for(users(:kevin)).everything! + notifications = Notifier.for(events(:logo_assignment_jz)).generate assert_equal [ users(:jz) ], notifications.map(&:user) end + + test "assignment events do not notify users who are access-only for the collection" do + buckets(:writebook).access_for(users(:jz)).access_only! + + notifications = Notifier.for(events(:logo_assignment_jz)).generate + + assert_empty notifications + end end