diff --git a/app/services/messages/markdown_renderer_service.rb b/app/services/messages/markdown_renderer_service.rb index a90cfca06..00a731a6d 100644 --- a/app/services/messages/markdown_renderer_service.rb +++ b/app/services/messages/markdown_renderer_service.rb @@ -96,10 +96,10 @@ class Messages::MarkdownRendererService restore_multiple_newlines(result) end - # Preserve multiple consecutive newlines (3+) by replacing them with placeholders - # Standard markdown treats 2 newlines as paragraph break, we preserve 3+ + # Preserve multiple consecutive newlines (2+) by replacing them with placeholders + # Standard markdown treats 2 newlines as paragraph break which collapses to 1 newline, we preserve 2+ def preserve_multiple_newlines(content) - content.gsub(/\n{3,}/) do |match| + content.gsub(/\n{2,}/) do |match| "{{PRESERVE_#{match.length}_NEWLINES}}" end end diff --git a/spec/controllers/linear/callbacks_controller_spec.rb b/spec/controllers/linear/callbacks_controller_spec.rb index b3245fbc5..66018776c 100644 --- a/spec/controllers/linear/callbacks_controller_spec.rb +++ b/spec/controllers/linear/callbacks_controller_spec.rb @@ -3,7 +3,8 @@ require 'rails_helper' RSpec.describe Linear::CallbacksController, type: :request do let(:account) { create(:account) } let(:code) { SecureRandom.hex(10) } - let(:state) { SecureRandom.hex(10) } + let(:client_secret) { 'test_linear_secret' } + let(:state) { JWT.encode({ sub: account.id, iat: Time.current.to_i }, client_secret, 'HS256') } let(:linear_redirect_uri) { "#{ENV.fetch('FRONTEND_URL', '')}/app/accounts/#{account.id}/settings/integrations/linear" } describe 'GET /linear/callback' do @@ -19,10 +20,9 @@ RSpec.describe Linear::CallbacksController, type: :request do before do stub_const('ENV', ENV.to_hash.merge('FRONTEND_URL' => 'http://www.example.com')) - - controller = described_class.new - allow(controller).to receive(:verify_linear_token).with(state).and_return(account.id) - allow(described_class).to receive(:new).and_return(controller) + allow(GlobalConfigService).to receive(:load).and_call_original + allow(GlobalConfigService).to receive(:load).with('LINEAR_CLIENT_SECRET', nil).and_return(client_secret) + allow(GlobalConfigService).to receive(:load).with('LINEAR_CLIENT_ID', nil).and_return('test_client_id') end context 'when successful' do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 213988a0d..d263708af 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -14,7 +14,7 @@ RSpec.describe User do context 'with associations' do it { is_expected.to have_many(:accounts).through(:account_users) } it { is_expected.to have_many(:account_users) } - it { is_expected.to have_many(:assigned_conversations).class_name('Conversation').dependent(:nullify) } + it { is_expected.to have_many(:assigned_conversations).dependent(:nullify) } it { is_expected.to have_many(:inbox_members).dependent(:destroy_async) } it { is_expected.to have_many(:notification_settings).dependent(:destroy_async) } it { is_expected.to have_many(:messages) } diff --git a/spec/services/messages/markdown_renderer_service_spec.rb b/spec/services/messages/markdown_renderer_service_spec.rb index 89210dc0e..5b8e02e2f 100644 --- a/spec/services/messages/markdown_renderer_service_spec.rb +++ b/spec/services/messages/markdown_renderer_service_spec.rb @@ -241,10 +241,37 @@ RSpec.describe Messages::MarkdownRendererService, type: :service do expect(result).to include('link text') end - it 'preserves newlines' do + it 'preserves single newlines' do content = "line 1\nline 2" result = described_class.new(content, channel_type).render expect(result).to include("\n") + expect(result).to include("line 1\nline 2") + end + + it 'preserves double newlines (paragraph breaks)' do + content = "para 1\n\npara 2" + result = described_class.new(content, channel_type).render + expect(result.scan("\n").count).to eq(2) + expect(result).to include("para 1\n\npara 2") + end + + it 'preserves multiple consecutive newlines' do + content = "para 1\n\n\n\npara 2" + result = described_class.new(content, channel_type).render + expect(result.scan("\n").count).to eq(4) + expect(result).to include("para 1\n\n\n\npara 2") + end + + it 'preserves newlines with varying amounts of whitespace between them' do + # Test with 1 space, 3 spaces, 5 spaces, and tabs to ensure it handles any amount of whitespace + content = "hello\n \n \n \n\t\nworld" + result = described_class.new(content, channel_type).render + # Whitespace-only lines are normalized, so we should have at least 5 newlines preserved + expect(result.scan("\n").count).to be >= 5 + expect(result).to include('hello') + expect(result).to include('world') + # Should not collapse to just 1-2 newlines + expect(result.scan("\n").count).to be > 3 end it 'converts strikethrough to HTML' do