diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index e505bd54d..b16573ebd 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -24,7 +24,7 @@ def index @streaming_message = Message.where( content_text: [nil, ""], cancelled_at: nil - ).find_by(id: redis.get("conversation-#{@conversation.id}-latest-assistant_message-id")) + ).find_by(id: @conversation.last_assistant_message_id) end def show @@ -43,7 +43,7 @@ def create if @message.save after_create_assistant_reply = @message.conversation.latest_message_for_version(@message.version) - GetNextAIMessageJob.perform_later(after_create_assistant_reply.id, @assistant.id) + GetNextAIMessageJob.perform_later(current_user.id, after_create_assistant_reply.id, @assistant.id) redirect_to conversation_messages_path(@message.conversation, version: @message.version) else # what's the right flow for a failed message create? it's not this, but hacking it so tests pass until we have a plan @@ -110,8 +110,4 @@ def message_params end modified_params end - - def redis - RedisConnection.client - end end diff --git a/app/jobs/get_next_ai_message_job.rb b/app/jobs/get_next_ai_message_job.rb index 7a163a7b3..dfa817f44 100644 --- a/app/jobs/get_next_ai_message_job.rb +++ b/app/jobs/get_next_ai_message_job.rb @@ -12,12 +12,13 @@ def ai_backend end end - def perform(message_id, assistant_id, attempt = 1) - puts "\n### GetNextAIMessageJob.perform(#{message_id}, #{assistant_id}, #{attempt})" unless Rails.env.test? + def perform(user_id, message_id, assistant_id, attempt = 1) + puts "\n### GetNextAIMessageJob.perform(#{user_id}, #{message_id}, #{assistant_id}, #{attempt})" unless Rails.env.test? - @message = Message.find_by(id: message_id) + @user = User.find(user_id) + @message = Message.find(message_id) @conversation = @message.conversation - @assistant = Assistant.find_by(id: assistant_id) + @assistant = Assistant.find(assistant_id) @prev_message = @conversation.messages.assistant.for_conversation_version(@message.version).find_by(index: @message.index-1) return false if generation_was_cancelled? || message_is_populated? @@ -153,19 +154,15 @@ def generation_was_cancelled? def message_cancelled? @message.cancelled? || - (@cancel_counter > 1 && @message.id == redis.get("message-cancelled-id")&.to_i) + (@cancel_counter > 1 && @message.id == @user.reload.last_cancelled_message_id) end def newer_messages_in_conversation? @message != @conversation.latest_message_for_version(@message.version) || - (@cancel_counter > 1 && @message.id != redis.get("conversation-#{@conversation.id}-latest-assistant_message-id")&.to_i) + (@cancel_counter > 1 && @message.id != @conversation.reload.last_assistant_message_id) end def message_is_populated? @message.content_text.present? end - - def redis - RedisConnection.client - end end diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 36fd81239..33849c2c7 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -7,6 +7,7 @@ class Conversation < ApplicationRecord has_many :messages, dependent: :destroy has_many :runs, dependent: :destroy has_many :steps, dependent: :destroy + belongs_to :last_assistant_message, class_name: "Message", optional: true after_touch :set_title_async, if: -> { title.blank? && messages.count >= 2 } diff --git a/app/models/message.rb b/app/models/message.rb index a8463c39b..1a2b3e349 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -5,6 +5,7 @@ class Message < ApplicationRecord belongs_to :conversation belongs_to :content_document, class_name: "Document", optional: true belongs_to :run, optional: true + has_one :latest_assistant_message_for, class_name: "Conversation", foreign_key: :last_assistant_message_id, dependent: :nullify enum role: %w[user assistant].index_by(&:to_sym) @@ -20,7 +21,7 @@ class Message < ApplicationRecord validate :validate_assistant, if: -> { assistant.present? && Current.user } after_create :start_assistant_reply, if: :user? - after_create :set_latest_assistant_message, if: :assistant? + after_create :set_last_assistant_message, if: :assistant? after_save :update_assistant_on_conversation, if: -> { assistant.present? && conversation.present? } scope :ordered, -> { latest_version_for_conversation } @@ -49,8 +50,8 @@ def start_assistant_reply ) end - def set_latest_assistant_message - redis.set("conversation-#{conversation_id}-latest-assistant_message-id", id) + def set_last_assistant_message + conversation.update!(last_assistant_message: self) end def update_assistant_on_conversation diff --git a/app/models/message/cancellable.rb b/app/models/message/cancellable.rb index b0ae8bb30..c8d139004 100644 --- a/app/models/message/cancellable.rb +++ b/app/models/message/cancellable.rb @@ -2,16 +2,14 @@ module Message::Cancellable extend ActiveSupport::Concern included do - after_save :save_cancelled_id_to_redis, if: :saved_change_to_cancelled_at? + has_one :cancelled_by, class_name: "User", foreign_key: :last_cancelled_message_id, dependent: :nullify + + after_save :set_cancelled_by, if: :saved_change_to_cancelled_at? end private - def save_cancelled_id_to_redis - redis.set("message-cancelled-id", id) - end - - def redis - RedisConnection.client + def set_cancelled_by + Current.user&.update!(last_cancelled_message: self) end end diff --git a/app/models/user.rb b/app/models/user.rb index f45957a1d..22b7d21f7 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,6 +11,7 @@ class User < ApplicationRecord has_many :assistants, dependent: :destroy has_many :conversations, dependent: :destroy + belongs_to :last_cancelled_message, class_name: "Message", optional: true serialize :preferences, coder: JsonSerializer diff --git a/db/migrate/20240504212248_add_last_message_id_cancelled_to_users.rb b/db/migrate/20240504212248_add_last_message_id_cancelled_to_users.rb new file mode 100644 index 000000000..a57ea65e2 --- /dev/null +++ b/db/migrate/20240504212248_add_last_message_id_cancelled_to_users.rb @@ -0,0 +1,5 @@ +class AddLastMessageIdCancelledToUsers < ActiveRecord::Migration[7.1] + def change + add_reference :users, :last_cancelled_message, null: true, foreign_key: { to_table: :messages } + end +end diff --git a/db/migrate/20240504212345_add_last_assistant_message_id_to_conversations.rb b/db/migrate/20240504212345_add_last_assistant_message_id_to_conversations.rb new file mode 100644 index 000000000..981ce6f2f --- /dev/null +++ b/db/migrate/20240504212345_add_last_assistant_message_id_to_conversations.rb @@ -0,0 +1,5 @@ +class AddLastAssistantMessageIdToConversations < ActiveRecord::Migration[7.1] + def change + add_reference :conversations, :last_assistant_message, null: true, foreign_key: { to_table: :messages } + end +end diff --git a/db/schema.rb b/db/schema.rb index e4c4ddf1f..37bdacfeb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_05_01_215824) do +ActiveRecord::Schema[7.1].define(version: 2024_05_04_212345) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -75,7 +75,9 @@ t.string "title" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.bigint "last_assistant_message_id" t.index ["assistant_id"], name: "index_conversations_on_assistant_id" + t.index ["last_assistant_message_id"], name: "index_conversations_on_last_assistant_message_id" t.index ["updated_at"], name: "index_conversations_on_updated_at" t.index ["user_id"], name: "index_conversations_on_user_id" end @@ -292,6 +294,8 @@ t.string "openai_key" t.string "anthropic_key" t.jsonb "preferences" + t.bigint "last_cancelled_message_id" + t.index ["last_cancelled_message_id"], name: "index_users_on_last_cancelled_message_id" end add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" @@ -299,6 +303,7 @@ add_foreign_key "assistants", "users" add_foreign_key "chats", "users" add_foreign_key "conversations", "assistants" + add_foreign_key "conversations", "messages", column: "last_assistant_message_id" add_foreign_key "conversations", "users" add_foreign_key "documents", "assistants" add_foreign_key "documents", "messages" @@ -319,4 +324,5 @@ add_foreign_key "steps", "assistants" add_foreign_key "steps", "conversations" add_foreign_key "steps", "runs" + add_foreign_key "users", "messages", column: "last_cancelled_message_id" end diff --git a/test/fixtures/conversations.yml b/test/fixtures/conversations.yml index 8e886a12c..4a7e50dbc 100644 --- a/test/fixtures/conversations.yml +++ b/test/fixtures/conversations.yml @@ -2,36 +2,43 @@ greeting: user: keith assistant: samantha title: Meeting Samantha + last_assistant_message: im_a_bot attachment: user: keith assistant: samantha title: Testing one attachment + last_assistant_message: not_human attachments: user: keith assistant: samantha title: Testing attachments + last_assistant_message: yes_cat_and_dog javascript: user: keith assistant: keith_gpt4 title: Javascript popstate + last_assistant_message: popstate_event ruby_version: user: keith assistant: samantha title: Ruby version + last_assistant_message: latest_ruby_version hello_claude: user: keith assistant: keith_claude3 title: Meeting Claude + last_assistant_message: claude_age_replying debugging: user: rob assistant: rob_gpt4 title: Verifying Ruby syntax + last_assistant_message: filter_map_example # 0.1 # 1.1 @@ -47,6 +54,7 @@ versioned: user: keith assistant: samantha title: Versioned conversation + last_assistant_message: message5_v2 # 0.1 # 1.1 @@ -60,3 +68,4 @@ versioned2: user: keith assistant: samantha title: Versioned v2 conversation + last_assistant_message: msg2_v5 diff --git a/test/fixtures/messages.yml b/test/fixtures/messages.yml index 4560a115b..7ddf74564 100644 --- a/test/fixtures/messages.yml +++ b/test/fixtures/messages.yml @@ -68,6 +68,7 @@ dont_know_day: content_document: run: what_day_response created_at: 2023-12-30 1:05:00 + cancelled_at: 2023-12-30 1:05:03 index: 5 version: 1 diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index 85e489b22..e1220ec0a 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -5,6 +5,7 @@ keith: registered_at: 2023-12-19 08:40:05 openai_key: abc123 anthropic_key: def789 + last_cancelled_message: dont_know_day preferences: {} rob: diff --git a/test/jobs/get_next_ai_message_job_anthropic_test.rb b/test/jobs/get_next_ai_message_job_anthropic_test.rb index a4f0a17a0..d1dd5bd64 100644 --- a/test/jobs/get_next_ai_message_job_anthropic_test.rb +++ b/test/jobs/get_next_ai_message_job_anthropic_test.rb @@ -3,6 +3,7 @@ class GetNextAIMessageJobAnthropicTest < ActiveJob::TestCase setup do @conversation = conversations(:hello_claude) + @user = @conversation.user @conversation.messages.create! role: :user, content_text: "Still there?", assistant: @conversation.assistant @message = @conversation.latest_message_for_version(:latest) @test_client = TestClients::Anthropic.new(access_token: 'abc') @@ -10,7 +11,7 @@ class GetNextAIMessageJobAnthropicTest < ActiveJob::TestCase test "populates the latest message from the assistant" do assert_no_difference "@conversation.messages.reload.length" do - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + assert GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) end message_text = @test_client.messages @@ -18,34 +19,34 @@ class GetNextAIMessageJobAnthropicTest < ActiveJob::TestCase end test "returns early if the message id was invalid" do - refute GetNextAIMessageJob.perform_now(0, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, 0, @conversation.assistant.id) end test "returns early if the assistant id was invalid" do - refute GetNextAIMessageJob.perform_now(@message.id, 0) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, 0) end test "returns early if the message was already generated" do @message.update!(content_text: "Hello") - refute GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) end test "returns early if the user has replied after this" do @conversation.messages.create! role: :user, content_text: "Ignore that, new question:", assistant: @conversation.assistant - refute GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) end test "when anthropic key is blank, a nice error message is displayed" do user = conversations(:greeting).user user.update!(anthropic_key: "") - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + assert GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) assert_includes @conversation.latest_message_for_version(:latest).content_text, "need to enter a valid API key for Anthropic" end test "when API response key is, a nice error message is displayed" do TestClients::Anthropic.stub :text, "" do - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + assert GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) assert_includes @conversation.latest_message_for_version(:latest).content_text, "a blank response" end end diff --git a/test/jobs/get_next_ai_message_job_openai_test.rb b/test/jobs/get_next_ai_message_job_openai_test.rb index 2a8268014..7e1ac093c 100644 --- a/test/jobs/get_next_ai_message_job_openai_test.rb +++ b/test/jobs/get_next_ai_message_job_openai_test.rb @@ -3,6 +3,7 @@ class GetNextAIMessageJobOpenaiTest < ActiveJob::TestCase setup do @conversation = conversations(:greeting) + @user = @conversation.user @conversation.messages.create! role: :user, content_text: "Still there?", assistant: @conversation.assistant @message = @conversation.latest_message_for_version(:latest) @test_client = TestClients::OpenAI.new(access_token: 'abc') @@ -10,7 +11,7 @@ class GetNextAIMessageJobOpenaiTest < ActiveJob::TestCase test "populates the latest message from the assistant" do assert_no_difference "@conversation.messages.reload.length" do - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + assert GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) end message_text = @test_client.chat @@ -18,34 +19,34 @@ class GetNextAIMessageJobOpenaiTest < ActiveJob::TestCase end test "returns early if the message id was invalid" do - refute GetNextAIMessageJob.perform_now(0, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, 0, @conversation.assistant.id) end test "returns early if the assistant id was invalid" do - refute GetNextAIMessageJob.perform_now(@message.id, 0) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, 0) end test "returns early if the message was already generated" do @message.update!(content_text: "Hello") - refute GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) end test "returns early if the user has replied after this" do @conversation.messages.create! role: :user, content_text: "Ignore that, new question:", assistant: @conversation.assistant - refute GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) end test "when openai key is blank, a nice error message is displayed" do user = @conversation.user user.update!(openai_key: "") - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + assert GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) assert_includes @conversation.latest_message_for_version(:latest).content_text, "need to enter a valid API key for OpenAI" end test "when API response key is, a nice error message is displayed" do TestClients::OpenAI.stub :text, "" do - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + assert GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) assert_includes @conversation.latest_message_for_version(:latest).content_text, "a blank response" end end diff --git a/test/jobs/get_next_ai_message_job_test.rb b/test/jobs/get_next_ai_message_job_test.rb index c16c8269e..c62db49ba 100644 --- a/test/jobs/get_next_ai_message_job_test.rb +++ b/test/jobs/get_next_ai_message_job_test.rb @@ -3,6 +3,7 @@ class GetNextAIMessageJobOpenaiTest < ActiveJob::TestCase setup do @conversation = conversations(:greeting) + @user = @conversation.user @conversation.messages.create! role: :user, content_text: "Are you still there?", assistant: @conversation.assistant @message = @conversation.latest_message_for_version(:latest) @test_client = TestClients::OpenAI.new(access_token: 'abc') @@ -11,37 +12,32 @@ class GetNextAIMessageJobOpenaiTest < ActiveJob::TestCase test "if a new message is created BEFORE job starts, it does not process" do @conversation.messages.create! role: :user, content_text: "You there?", assistant: @conversation.assistant - refute GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) assert @message.content_text.blank? assert_nil @message.cancelled_at end - test "if a new message is created AFTER job starts, it stops streaming - this tests the redis state" do - m = @conversation.messages.create! role: :user, content_text: "And now?", assistant: @conversation.assistant - @conversation.messages.where("id >= ?", m.id).delete_all # we are reverting the database change but the redis change persists - - assert_changes "@message.content_text", from: nil, to: @test_client.chat do - assert_changes "@message.reload.cancelled_at", from: nil do - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) - end - end - end - test "if the cancel streaming button is clicked BEFORE job starts, it does not process" do @message.cancelled! - refute GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + refute GetNextAIMessageJob.perform_now(@user.id, @message.id, @conversation.assistant.id) assert @message.content_text.blank? assert_not_nil @message.cancelled_at end - test "if the cancel streaming button is clicked AFTER job starts, it does not process - this tests the redis state" do - @message.cancelled! # this changes database column AND alters a redis state - @message.update!(cancelled_at: nil) # this undoes the column change but the redis state persists - - assert_changes "@message.content_text", from: nil, to: @test_client.chat do - assert_changes "@message.reload.cancelled_at", from: nil do - assert GetNextAIMessageJob.perform_now(@message.id, @conversation.assistant.id) + test "if message_cancelled? starts returning true for any reason AFTER job starts, it cancels the message" do + # When the job first starts, it short circuits if the message is already cancelled so we are NOT going to + # set the message to be cancelled Instead, we are going to stub out the message_cancelled? checker so it + # returns true the first time it's used but false thereafter. This simulates message.cancelled! being + # toggled after the job has started. + false_on_first_run = 0 + job = GetNextAIMessageJob.new + job.stub(:message_cancelled?, -> { false_on_first_run += 1; false_on_first_run != 1 }) do + + assert_changes "@message.content_text", from: nil, to: @test_client.chat do + assert_changes "@message.reload.cancelled_at", from: nil do + assert job.perform(@user.id, @message.id, @conversation.assistant.id) + end end end end diff --git a/test/models/conversation_test.rb b/test/models/conversation_test.rb index e6b78ab51..58aecf32f 100644 --- a/test/models/conversation_test.rb +++ b/test/models/conversation_test.rb @@ -21,6 +21,12 @@ class ConversationTest < ActiveSupport::TestCase assert_instance_of Step, conversations(:greeting).steps.first end + test "has associated last_assistant_message but can also be nil" do + c = Conversation.create!(user: users(:keith), assistant: assistants(:samantha)) + assert_nil c.last_assistant_message + assert_instance_of Message, conversations(:greeting).last_assistant_message + end + test "simple create works" do assert_nothing_raised do Conversation.create!( @@ -65,7 +71,7 @@ class ConversationTest < ActiveSupport::TestCase latest_message = conversation.latest_message_for_version(:latest) assert latest_message.assistant? - GetNextAIMessageJob.perform_now(latest_message.id, assistants(:samantha).id) + GetNextAIMessageJob.perform_now(users(:keith).id, latest_message.id, assistants(:samantha).id) assert_equal "Hear me", conversation.reload.title diff --git a/test/models/message/cancellable_test.rb b/test/models/message/cancellable_test.rb index 46bd03c76..b9693a309 100644 --- a/test/models/message/cancellable_test.rb +++ b/test/models/message/cancellable_test.rb @@ -4,28 +4,24 @@ class Message::CancellableTest < ActiveSupport::TestCase setup do @conversation = conversations(:greeting) @previous_id = @conversation.latest_message_for_version(:latest).id - - redis.set("conversation-#{@conversation.id}-latest-assistant_message-id", @previous_id) end - teardown do - redis.set("conversation-#{@conversation.id}-latest-assistant_message-id", @previous_id) # cleanup + test "cancelled_by" do + assert_equal users(:keith), messages(:dont_know_day).cancelled_by end test "when a message is cancelled the redis key gets set" do - redis.set("message-cancelled-id", nil) + Current.user = users(:keith) assert_changes "messages(:im_a_bot).cancelled_at", from: nil do - assert_changes "redis.get('message-cancelled-id')&.to_i", to: messages(:im_a_bot).id do + assert_changes "users(:keith).last_cancelled_message", to: messages(:im_a_bot) do messages(:im_a_bot).cancelled! end end - - redis.set("message-cancelled-id", nil) end test "creating a new message on a conversation updates the redis key for that conversation" do - assert_changes "redis.get('conversation-#{@conversation.id}-latest-assistant_message-id')&.to_i", from: @previous_id do + assert_changes "@conversation.last_assistant_message_id", from: @previous_id do assert_difference "@conversation.messages.count", 2 do @conversation.messages.create!( assistant: @conversation.assistant, @@ -34,12 +30,6 @@ class Message::CancellableTest < ActiveSupport::TestCase end end id = @conversation.latest_message_for_version(:latest).reload.id - assert_equal id, redis.get("conversation-#{@conversation.id}-latest-assistant_message-id")&.to_i - end - - private - - def redis - RedisConnection.client + assert_equal id, @conversation.last_assistant_message_id end end diff --git a/test/models/message_test.rb b/test/models/message_test.rb index 01a18415b..ef516e42f 100644 --- a/test/models/message_test.rb +++ b/test/models/message_test.rb @@ -25,6 +25,11 @@ class MessageTest < ActiveSupport::TestCase assert_instance_of User, messages(:yes_i_do).user end + test "has associated latest_assistant_message_for but can be nil" do + assert_nil messages(:dont_know_day).latest_assistant_message_for + assert_instance_of Conversation, messages(:im_a_bot).latest_assistant_message_for + end + test "create without setting Current or conversation raises" do assert_raises ActiveRecord::RecordInvalid do Message.create!(content_text: "Hello") @@ -138,4 +143,27 @@ class MessageTest < ActiveSupport::TestCase conversations(:greeting).messages.create!(assistant: new_assistant, content_text: "Hello") assert_equal new_assistant, conversations(:greeting).reload.assistant end + + test "messages are destroyed when a conversation is destroyed" do + msg1 = messages(:popstate) + msg2 = messages(:popstate_event) + + assert_difference "Message.count", -2 do + msg1.conversation.destroy + end + + assert_equal 0, Message.where(id: [msg1.id, msg2.id]).count + end + + test "message which is referenced by conversation can be destroyed" do + assert_equal messages(:popstate_event), conversations(:javascript).last_assistant_message + messages(:popstate_event).destroy + assert_nil conversations(:javascript).reload.last_assistant_message + end + + test "message which is referenced by user can be destroyed" do + assert_equal messages(:dont_know_day), users(:keith).last_cancelled_message + messages(:dont_know_day).destroy + assert_nil users(:keith).reload.last_cancelled_message + end end diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 13d5fb71c..a09b74627 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -5,6 +5,11 @@ class UserTest < ActiveSupport::TestCase assert_instance_of Person, users(:keith).person end + test "has a last_cancelled_message but can be nil" do + assert_equal messages(:dont_know_day), users(:keith).last_cancelled_message + assert_nil users(:rob).last_cancelled_message + end + test "should not validate a new user without password" do user = User.new person = Person.new(email: "example@gmail.com", personable: user) diff --git a/test/system/conversations/messages/version_test.rb b/test/system/conversations/messages/version_test.rb index 7d8a7227f..d61671c14 100644 --- a/test/system/conversations/messages/version_test.rb +++ b/test/system/conversations/messages/version_test.rb @@ -7,7 +7,7 @@ class ConversationMessagesVersionTest < ApplicationSystemTestCase end test "previous icon shows tooltip and next is disabled" do - conversations(:versioned).messages.where(version: 2).where("index > 2").delete_all + conversations(:versioned).messages.where(version: 2).where("index > 2").destroy_all visit_and_scroll_wait conversation_messages_path(conversations(:versioned), version: 2) msg = hover_last_message