# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/ require 'rails_helper' require 'models/application_model_examples' require 'models/concerns/can_be_imported_examples' require 'models/concerns/can_csv_import_examples' require 'models/concerns/has_history_examples' require 'models/concerns/has_object_manager_attributes_examples' require 'models/ticket/article/has_ticket_contact_attributes_impact_examples' RSpec.describe Ticket::Article, type: :model do subject(:article) { create(:ticket_article) } it_behaves_like 'ApplicationModel', can_param: { sample_data_attribute: :body } it_behaves_like 'CanBeImported' it_behaves_like 'CanCsvImport' it_behaves_like 'HasHistory' it_behaves_like 'HasObjectManagerAttributes' it_behaves_like 'Ticket::Article::HasTicketContactAttributesImpact' describe 'Callbacks, Observers, & Async Transactions -' do describe 'NULL byte handling (via ChecksAttributeValuesAndLength concern):' do it 'removes them from #subject on creation, if necessary (postgres doesn’t like them)' do expect(create(:ticket_article, subject: "com test 1\u0000")) .to be_persisted end it 'removes them from #body on creation, if necessary (postgres doesn’t like them)' do expect(create(:ticket_article, body: "some\u0000message 123")) .to be_persisted end end describe 'Setting of ticket_define_email_from' do subject(:article) do create(:ticket_article, created_by: created_by, sender_name: 'Agent', type_name: 'email') end context 'when AgentName' do before do Setting.set('ticket_define_email_from', 'AgentName') end context 'with real sender' do let(:created_by) { create(:user) } it 'sets the from to the realname of the user' do expect(article.reload.from).to be_in( [ "#{article.created_by.firstname} #{article.created_by.lastname} <#{article.ticket.group.email_address.email}>", "\"#{article.created_by.firstname} #{article.created_by.lastname}\" <#{article.ticket.group.email_address.email}>", ] ) end end context 'with no real sender (e.g. trigger or scheduler)' do let(:created_by) { User.find(1) } it 'sets the from to realname of the mail address)' do expect(article.reload.from).to eq("#{article.ticket.group.email_address.name} <#{article.ticket.group.email_address.email}>") end end end end describe 'Setting of ticket.create_article_{sender,type}' do let!(:ticket) { create(:ticket) } context 'on creation' do context 'of first article on a ticket' do subject(:article) do create(:ticket_article, ticket: ticket, sender_name: 'Agent', type_name: 'email') end it 'sets ticket sender/type attributes based on article sender/type' do expect { article } .to change { ticket.reload.create_article_sender&.name }.to('Agent') .and change { ticket.reload.create_article_type&.name }.to('email') end end context 'of subsequent articles on a ticket' do subject(:article) do create(:ticket_article, ticket: ticket, sender_name: 'Customer', type_name: 'twitter status') end let!(:first_article) do create(:ticket_article, ticket: ticket, sender_name: 'Agent', type_name: 'email') end it 'does not modify ticket’s sender/type attributes' do expect { article } .to not_change { ticket.reload.create_article_sender.name } .and not_change { ticket.reload.create_article_type.name } end end end end describe 'XSS protection:' do subject(:article) { create(:ticket_article, body: body, content_type: 'text/html') } before do # XSS processing may run into a timeout on slow CI systems, so turn the timeout off for the test. stub_const("#{HtmlSanitizer}::PROCESSING_TIMEOUT", nil) end context 'when body contains only injected JS' do let(:body) { <<~RAW.chomp } some other text RAW it 'removes RAW it 'removes