user_spec.rb 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377
  1. require 'rails_helper'
  2. require 'models/application_model_examples'
  3. require 'models/concerns/has_groups_examples'
  4. require 'models/concerns/has_history_examples'
  5. require 'models/concerns/has_roles_examples'
  6. require 'models/concerns/has_groups_permissions_examples'
  7. require 'models/concerns/has_xss_sanitized_note_examples'
  8. require 'models/concerns/can_be_imported_examples'
  9. require 'models/concerns/has_object_manager_attributes_validation_examples'
  10. require 'models/user/can_lookup_search_index_attributes_examples'
  11. require 'models/user/has_ticket_create_screen_impact_examples'
  12. require 'models/user/performs_geo_lookup_examples'
  13. require 'models/concerns/has_taskbars_examples'
  14. RSpec.describe User, type: :model do
  15. subject(:user) { create(:user) }
  16. let(:customer) { create(:customer) }
  17. let(:agent) { create(:agent) }
  18. let(:admin) { create(:admin) }
  19. it_behaves_like 'ApplicationModel', can_assets: { associations: :organization }
  20. it_behaves_like 'HasGroups', group_access_factory: :agent
  21. it_behaves_like 'HasHistory'
  22. it_behaves_like 'HasRoles', group_access_factory: :agent
  23. it_behaves_like 'HasXssSanitizedNote', model_factory: :user
  24. it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
  25. it_behaves_like 'CanBeImported'
  26. it_behaves_like 'HasObjectManagerAttributesValidation'
  27. it_behaves_like 'User::HasTicketCreateScreenImpact'
  28. it_behaves_like 'CanLookupSearchIndexAttributes'
  29. it_behaves_like 'HasTaskbars'
  30. it_behaves_like 'UserPerformsGeoLookup'
  31. describe 'Class methods:' do
  32. describe '.authenticate' do
  33. subject(:user) { create(:user, password: password) }
  34. let(:password) { Faker::Internet.password }
  35. context 'with valid credentials' do
  36. context 'using #login' do
  37. it 'returns the matching user' do
  38. expect(described_class.authenticate(user.login, password))
  39. .to eq(user)
  40. end
  41. it 'is not case-sensitive' do
  42. expect(described_class.authenticate(user.login.upcase, password))
  43. .to eq(user)
  44. end
  45. end
  46. context 'using #email' do
  47. it 'returns the matching user' do
  48. expect(described_class.authenticate(user.email, password))
  49. .to eq(user)
  50. end
  51. it 'is not case-sensitive' do
  52. expect(described_class.authenticate(user.email.upcase, password))
  53. .to eq(user)
  54. end
  55. end
  56. context 'but exceeding failed login limit' do
  57. before { user.update(login_failed: 999) }
  58. it 'returns nil' do
  59. expect(described_class.authenticate(user.login, password))
  60. .to be(nil)
  61. end
  62. end
  63. context 'when previous login was' do
  64. context 'never' do
  65. it 'updates #last_login and #updated_at' do
  66. expect { described_class.authenticate(user.login, password) }
  67. .to change { user.reload.last_login }
  68. .and change { user.reload.updated_at }
  69. end
  70. end
  71. context 'less than 10 minutes ago' do
  72. before do
  73. described_class.authenticate(user.login, password)
  74. travel 9.minutes
  75. end
  76. it 'does not update #last_login and #updated_at' do
  77. expect { described_class.authenticate(user.login, password) }
  78. .to not_change { user.reload.last_login }
  79. .and not_change { user.reload.updated_at }
  80. end
  81. end
  82. context 'more than 10 minutes ago' do
  83. before do
  84. described_class.authenticate(user.login, password)
  85. travel 11.minutes
  86. end
  87. it 'updates #last_login and #updated_at' do
  88. expect { described_class.authenticate(user.login, password) }
  89. .to change { user.reload.last_login }
  90. .and change { user.reload.updated_at }
  91. end
  92. end
  93. end
  94. end
  95. context 'with valid user and invalid password' do
  96. it 'increments failed login count' do
  97. expect(described_class).to receive(:sleep).with(1)
  98. expect { described_class.authenticate(user.login, password.next) }
  99. .to change { user.reload.login_failed }.by(1)
  100. end
  101. it 'returns nil' do
  102. expect(described_class).to receive(:sleep).with(1)
  103. expect(described_class.authenticate(user.login, password.next)).to be(nil)
  104. end
  105. end
  106. context 'with inactive user’s login' do
  107. before { user.update(active: false) }
  108. it 'returns nil' do
  109. expect(described_class.authenticate(user.login, password)).to be(nil)
  110. end
  111. end
  112. context 'with non-existent user login' do
  113. it 'returns nil' do
  114. expect(described_class.authenticate('john.doe', password)).to be(nil)
  115. end
  116. end
  117. context 'with empty login string' do
  118. it 'returns nil' do
  119. expect(described_class.authenticate('', password)).to be(nil)
  120. end
  121. end
  122. context 'with empty password string' do
  123. it 'returns nil' do
  124. expect(described_class.authenticate(user.login, '')).to be(nil)
  125. end
  126. end
  127. context 'with empty password string when the stored password is an empty string' do
  128. before { user.update_column(:password, '') }
  129. context 'when password is an empty string' do
  130. it 'returns nil' do
  131. expect(described_class.authenticate(user.login, '')).to be(nil)
  132. end
  133. end
  134. context 'when password is nil' do
  135. it 'returns nil' do
  136. expect(described_class.authenticate(user.login, nil)).to be(nil)
  137. end
  138. end
  139. end
  140. context 'with empty password string when the stored hash represents an empty string' do
  141. before { user.update(password: PasswordHash.crypt('')) }
  142. context 'when password is an empty string' do
  143. it 'returns nil' do
  144. expect(described_class.authenticate(user.login, '')).to be(nil)
  145. end
  146. end
  147. context 'when password is nil' do
  148. it 'returns nil' do
  149. expect(described_class.authenticate(user.login, nil)).to be(nil)
  150. end
  151. end
  152. end
  153. end
  154. describe '.identify' do
  155. it 'returns users by given login' do
  156. expect(described_class.identify(user.login)).to eq(user)
  157. end
  158. it 'returns users by given email' do
  159. expect(described_class.identify(user.email)).to eq(user)
  160. end
  161. end
  162. end
  163. describe 'Instance methods:' do
  164. describe '#max_login_failed?' do
  165. it { is_expected.to respond_to(:max_login_failed?) }
  166. context 'with "password_max_login_failed" setting' do
  167. before do
  168. Setting.set('password_max_login_failed', 5)
  169. user.update(login_failed: 5)
  170. end
  171. it 'returns true once user’s #login_failed count exceeds the setting' do
  172. expect { user.update(login_failed: 6) }
  173. .to change(user, :max_login_failed?).to(true)
  174. end
  175. end
  176. context 'without password_max_login_failed setting' do
  177. before do
  178. Setting.set('password_max_login_failed', nil)
  179. user.update(login_failed: 0)
  180. end
  181. it 'defaults to 0' do
  182. expect { user.update(login_failed: 1) }
  183. .to change(user, :max_login_failed?).to(true)
  184. end
  185. end
  186. end
  187. describe '#out_of_office?' do
  188. context 'without any out_of_office_* attributes set' do
  189. it 'returns false' do
  190. expect(agent.out_of_office?).to be(false)
  191. end
  192. end
  193. context 'with valid #out_of_office_* attributes' do
  194. before do
  195. agent.update(
  196. out_of_office_start_at: Time.current.yesterday,
  197. out_of_office_end_at: Time.current.tomorrow,
  198. out_of_office_replacement_id: 1
  199. )
  200. end
  201. context 'but #out_of_office: false' do
  202. before { agent.update(out_of_office: false) }
  203. it 'returns false' do
  204. expect(agent.out_of_office?).to be(false)
  205. end
  206. end
  207. context 'and #out_of_office: true' do
  208. before { agent.update(out_of_office: true) }
  209. it 'returns true' do
  210. expect(agent.out_of_office?).to be(true)
  211. end
  212. context 'after the #out_of_office_end_at time has passed' do
  213. before { travel 2.days }
  214. it 'returns false (even though #out_of_office has not changed)' do
  215. expect(agent.out_of_office).to be(true)
  216. expect(agent.out_of_office?).to be(false)
  217. end
  218. end
  219. end
  220. end
  221. end
  222. describe '#out_of_office_agent' do
  223. it { is_expected.to respond_to(:out_of_office_agent) }
  224. context 'when user has no designated substitute' do
  225. it 'returns nil' do
  226. expect(user.out_of_office_agent).to be(nil)
  227. end
  228. end
  229. context 'when user has designated substitute' do
  230. subject(:user) do
  231. create(:user,
  232. out_of_office: out_of_office,
  233. out_of_office_start_at: Time.zone.yesterday,
  234. out_of_office_end_at: Time.zone.tomorrow,
  235. out_of_office_replacement_id: substitute.id,)
  236. end
  237. let(:substitute) { create(:user) }
  238. context 'but is not out of office' do
  239. let(:out_of_office) { false }
  240. it 'returns nil' do
  241. expect(user.out_of_office_agent).to be(nil)
  242. end
  243. end
  244. context 'and is out of office' do
  245. let(:out_of_office) { true }
  246. it 'returns the designated substitute' do
  247. expect(user.out_of_office_agent).to eq(substitute)
  248. end
  249. end
  250. end
  251. end
  252. describe '#out_of_office_agent_of' do
  253. context 'when no other agents are out-of-office' do
  254. it 'returns an empty ActiveRecord::Relation' do
  255. expect(agent.out_of_office_agent_of)
  256. .to be_an(ActiveRecord::Relation)
  257. .and be_empty
  258. end
  259. end
  260. context 'when designated as the substitute' do
  261. let!(:agent_on_holiday) do
  262. create(
  263. :agent,
  264. out_of_office_start_at: Time.current.yesterday,
  265. out_of_office_end_at: Time.current.tomorrow,
  266. out_of_office_replacement_id: agent.id,
  267. out_of_office: out_of_office
  268. )
  269. end
  270. context 'of an in-office agent' do
  271. let(:out_of_office) { false }
  272. it 'returns an empty ActiveRecord::Relation' do
  273. expect(agent.out_of_office_agent_of)
  274. .to be_an(ActiveRecord::Relation)
  275. .and be_empty
  276. end
  277. end
  278. context 'of an out-of-office agent' do
  279. let(:out_of_office) { true }
  280. it 'returns an ActiveRecord::Relation including that agent' do
  281. expect(agent.out_of_office_agent_of)
  282. .to match_array([agent_on_holiday])
  283. end
  284. end
  285. end
  286. end
  287. describe '#by_reset_token' do
  288. subject(:user) { token.user }
  289. let(:token) { create(:token_password_reset) }
  290. context 'with a valid token' do
  291. it 'returns the matching user' do
  292. expect(described_class.by_reset_token(token.name)).to eq(user)
  293. end
  294. end
  295. context 'with an invalid token' do
  296. it 'returns nil' do
  297. expect(described_class.by_reset_token('not-existing')).to be(nil)
  298. end
  299. end
  300. end
  301. describe '#password_reset_via_token' do
  302. subject(:user) { token.user }
  303. let!(:token) { create(:token_password_reset) }
  304. it 'changes the password of the token user and destroys the token' do
  305. expect { described_class.password_reset_via_token(token.name, Faker::Internet.password) }
  306. .to change { user.reload.password }
  307. .and change(Token, :count).by(-1)
  308. end
  309. end
  310. describe '#permissions' do
  311. let(:user) { create(:agent).tap { |u| u.roles = [u.roles.first] } }
  312. let(:role) { user.roles.first }
  313. let(:permissions) { role.permissions }
  314. it 'is a simple association getter' do
  315. expect(user.permissions).to match_array(permissions)
  316. end
  317. context 'for inactive permissions' do
  318. before { permissions.first.update(active: false) }
  319. it 'omits them from the returned hash' do
  320. expect(user.permissions).not_to include(permissions.first)
  321. end
  322. end
  323. context 'for permissions on inactive roles' do
  324. before { role.update(active: false) }
  325. it 'omits them from the returned hash' do
  326. expect(user.permissions).not_to include(*role.permissions)
  327. end
  328. end
  329. end
  330. describe '#permissions?' do
  331. subject(:user) { create(:user, roles: [role]) }
  332. let(:role) { create(:role, permissions: [permission]) }
  333. let(:permission) { create(:permission, name: permission_name) }
  334. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  335. let(:permission_name) { 'foo' }
  336. context 'when given that exact permission' do
  337. it 'returns true' do
  338. expect(user.permissions?('foo')).to be(true)
  339. end
  340. end
  341. context 'when given an active sub-permission' do
  342. before { create(:permission, name: 'foo.bar') }
  343. it 'returns true' do
  344. expect(user.permissions?('foo.bar')).to be(true)
  345. end
  346. end
  347. describe 'chain-of-ancestry quirk' do
  348. context 'when given an inactive sub-permission' do
  349. before { create(:permission, name: 'foo.bar.baz', active: false) }
  350. it 'returns false, even with active ancestors' do
  351. expect(user.permissions?('foo.bar.baz')).to be(false)
  352. end
  353. end
  354. context 'when given a sub-permission that does not exist' do
  355. before { create(:permission, name: 'foo.bar', active: false) }
  356. it 'can return true, even with inactive ancestors' do
  357. expect(user.permissions?('foo.bar.baz')).to be(true)
  358. end
  359. end
  360. end
  361. context 'when given a glob' do
  362. context 'matching that permission' do
  363. it 'returns true' do
  364. expect(user.permissions?('foo.*')).to be(true)
  365. end
  366. end
  367. context 'NOT matching that permission' do
  368. it 'returns false' do
  369. expect(user.permissions?('bar.*')).to be(false)
  370. end
  371. end
  372. end
  373. end
  374. context 'with privileges for a sub-permission (e.g., "foo.bar", not "foo")' do
  375. let(:permission_name) { 'foo.bar' }
  376. context 'when given that exact sub-permission' do
  377. it 'returns true' do
  378. expect(user.permissions?('foo.bar')).to be(true)
  379. end
  380. context 'but the permission is inactive' do
  381. before { permission.update(active: false) }
  382. it 'returns false' do
  383. expect(user.permissions?('foo.bar')).to be(false)
  384. end
  385. end
  386. end
  387. context 'when given a sibling sub-permission' do
  388. let(:sibling_permission) { create(:permission, name: 'foo.baz') }
  389. context 'that exists' do
  390. before { sibling_permission }
  391. it 'returns false' do
  392. expect(user.permissions?('foo.baz')).to be(false)
  393. end
  394. end
  395. context 'that does not exist' do
  396. it 'returns false' do
  397. expect(user.permissions?('foo.baz')).to be(false)
  398. end
  399. end
  400. end
  401. context 'when given the parent permission' do
  402. it 'returns false' do
  403. expect(user.permissions?('foo')).to be(false)
  404. end
  405. end
  406. context 'when given a glob' do
  407. context 'matching that sub-permission' do
  408. it 'returns true' do
  409. expect(user.permissions?('foo.*')).to be(true)
  410. end
  411. context 'but the permission is inactive' do
  412. before { permission.update(active: false) }
  413. it 'returns false' do
  414. expect(user.permissions?('foo.*')).to be(false)
  415. end
  416. end
  417. end
  418. context 'NOT matching that sub-permission' do
  419. it 'returns false' do
  420. expect(user.permissions?('bar.*')).to be(false)
  421. end
  422. end
  423. end
  424. end
  425. end
  426. describe '#permissions_with_child_ids' do
  427. context 'with privileges for a root permission (e.g., "foo", not "foo.bar")' do
  428. subject(:user) { create(:user, roles: [role]) }
  429. let(:role) { create(:role, permissions: [permission]) }
  430. let!(:permission) { create(:permission, name: 'foo') }
  431. let!(:child_permission) { create(:permission, name: 'foo.bar') }
  432. let!(:inactive_child_permission) { create(:permission, name: 'foo.baz', active: false) }
  433. it 'includes the IDs of user’s explicit permissions' do
  434. expect(user.permissions_with_child_ids)
  435. .to include(permission.id)
  436. end
  437. it 'includes the IDs of user’s active sub-permissions' do
  438. expect(user.permissions_with_child_ids)
  439. .to include(child_permission.id)
  440. .and not_include(inactive_child_permission.id)
  441. end
  442. end
  443. end
  444. describe '#locale' do
  445. subject(:user) { create(:user, preferences: preferences) }
  446. context 'with no #preferences[:locale]' do
  447. let(:preferences) { {} }
  448. context 'with default locale' do
  449. before { Setting.set('locale_default', 'foo') }
  450. it 'returns the system-wide default locale' do
  451. expect(user.locale).to eq('foo')
  452. end
  453. end
  454. context 'without default locale' do
  455. before { Setting.set('locale_default', nil) }
  456. it 'returns en-us' do
  457. expect(user.locale).to eq('en-us')
  458. end
  459. end
  460. end
  461. context 'with a #preferences[:locale]' do
  462. let(:preferences) { { locale: 'bar' } }
  463. it 'returns the user’s configured locale' do
  464. expect(user.locale).to eq('bar')
  465. end
  466. end
  467. end
  468. end
  469. describe 'Attributes:' do
  470. describe '#out_of_office' do
  471. context 'with #out_of_office_start_at: nil' do
  472. before { agent.update(out_of_office_start_at: nil, out_of_office_end_at: Time.current) }
  473. it 'cannot be set to true' do
  474. expect { agent.update(out_of_office: true) }
  475. .to raise_error(Exceptions::UnprocessableEntity)
  476. end
  477. end
  478. context 'with #out_of_office_end_at: nil' do
  479. before { agent.update(out_of_office_start_at: Time.current, out_of_office_end_at: nil) }
  480. it 'cannot be set to true' do
  481. expect { agent.update(out_of_office: true) }
  482. .to raise_error(Exceptions::UnprocessableEntity)
  483. end
  484. end
  485. context 'when #out_of_office_start_at is AFTER #out_of_office_end_at' do
  486. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.next_month) }
  487. it 'cannot be set to true' do
  488. expect { agent.update(out_of_office: true) }
  489. .to raise_error(Exceptions::UnprocessableEntity)
  490. end
  491. end
  492. context 'when #out_of_office_start_at is AFTER Time.current' do
  493. before { agent.update(out_of_office_start_at: Time.current.tomorrow, out_of_office_end_at: Time.current.yesterday) }
  494. it 'cannot be set to true' do
  495. expect { agent.update(out_of_office: true) }
  496. .to raise_error(Exceptions::UnprocessableEntity)
  497. end
  498. end
  499. context 'when #out_of_office_end_at is BEFORE Time.current' do
  500. before { agent.update(out_of_office_start_at: Time.current.last_month, out_of_office_end_at: Time.current.yesterday) }
  501. it 'cannot be set to true' do
  502. expect { agent.update(out_of_office: true) }
  503. .to raise_error(Exceptions::UnprocessableEntity)
  504. end
  505. end
  506. end
  507. describe '#out_of_office_replacement_id' do
  508. it 'cannot be set to invalid user ID' do
  509. expect { agent.update(out_of_office_replacement_id: described_class.pluck(:id).max.next) }
  510. .to raise_error(ActiveRecord::InvalidForeignKey)
  511. end
  512. it 'can be set to a valid user ID' do
  513. expect { agent.update(out_of_office_replacement_id: 1) }
  514. .not_to raise_error
  515. end
  516. end
  517. describe '#login_failed' do
  518. before { user.update(login_failed: 1) }
  519. it 'is reset to 0 when password is updated' do
  520. expect { user.update(password: Faker::Internet.password) }
  521. .to change(user, :login_failed).to(0)
  522. end
  523. end
  524. describe '#password' do
  525. let(:password) { Faker::Internet.password }
  526. context 'when set to plaintext password' do
  527. it 'hashes password before saving to DB' do
  528. user.password = password
  529. expect { user.save }
  530. .to change { PasswordHash.crypted?(user.password) }
  531. end
  532. end
  533. context 'for existing user records' do
  534. context 'when changed to empty string' do
  535. before { user.update(password: password) }
  536. it 'keeps previous password' do
  537. expect { user.update!(password: '') }
  538. .not_to change(user, :password)
  539. end
  540. end
  541. context 'when changed to nil' do
  542. before { user.update(password: password) }
  543. it 'keeps previous password' do
  544. expect { user.update!(password: nil) }
  545. .not_to change(user, :password)
  546. end
  547. end
  548. end
  549. context 'for new user records' do
  550. context 'when passed as an empty string' do
  551. let(:another_user) { create(:user, password: '') }
  552. it 'sets password to nil' do
  553. expect(another_user.password).to eq(nil)
  554. end
  555. end
  556. context 'when passed as nil' do
  557. let(:another_user) { create(:user, password: nil) }
  558. it 'sets password to nil' do
  559. expect(another_user.password).to eq(nil)
  560. end
  561. end
  562. end
  563. context 'when set to SHA2 digest (to facilitate OTRS imports)' do
  564. it 'does not re-hash before saving' do
  565. user.password = "{sha2}#{Digest::SHA2.hexdigest(password)}"
  566. expect { user.save }.not_to change(user, :password)
  567. end
  568. end
  569. context 'when set to Argon2 digest' do
  570. it 'does not re-hash before saving' do
  571. user.password = PasswordHash.crypt(password)
  572. expect { user.save }.not_to change(user, :password)
  573. end
  574. end
  575. context 'when creating two users with the same password' do
  576. before { user.update(password: password) }
  577. let(:another_user) { create(:user, password: password) }
  578. it 'does not generate the same password hash' do
  579. expect(user.password).not_to eq(another_user.password)
  580. end
  581. end
  582. end
  583. describe '#phone' do
  584. subject(:user) { create(:user, phone: orig_number) }
  585. context 'when included on create' do
  586. let(:orig_number) { '1234567890' }
  587. it 'adds corresponding CallerId record' do
  588. expect { user }
  589. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(1)
  590. end
  591. end
  592. context 'when added on update' do
  593. let(:orig_number) { nil }
  594. let(:new_number) { '1234567890' }
  595. before { user } # create user
  596. it 'adds corresponding CallerId record' do
  597. expect { user.update(phone: new_number) }
  598. .to change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  599. end
  600. end
  601. context 'when falsely added on update (change: [nil, ""])' do
  602. let(:orig_number) { nil }
  603. let(:new_number) { '' }
  604. before { user } # create user
  605. it 'does not attempt to update CallerId record' do
  606. allow(Cti::CallerId).to receive(:build).with(any_args)
  607. expect(Cti::CallerId.where(object: 'User', o_id: user.id).count)
  608. .to eq(0)
  609. expect { user.update(phone: new_number) }
  610. .to change { Cti::CallerId.where(object: 'User', o_id: user.id).count }.by(0)
  611. expect(Cti::CallerId).not_to have_received(:build)
  612. end
  613. end
  614. context 'when removed on update' do
  615. let(:orig_number) { '1234567890' }
  616. let(:new_number) { nil }
  617. before { user } # create user
  618. it 'removes corresponding CallerId record' do
  619. expect { user.update(phone: nil) }
  620. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  621. end
  622. end
  623. context 'when changed on update' do
  624. let(:orig_number) { '1234567890' }
  625. let(:new_number) { orig_number.next }
  626. before { user } # create user
  627. it 'replaces CallerId record' do
  628. expect { user.update(phone: new_number) }
  629. .to change { Cti::CallerId.where(caller_id: orig_number).count }.by(-1)
  630. .and change { Cti::CallerId.where(caller_id: new_number).count }.by(1)
  631. end
  632. end
  633. end
  634. describe '#preferences' do
  635. describe '"mail_delivery_failed{,_data}" keys' do
  636. before do
  637. user.update(
  638. preferences: {
  639. mail_delivery_failed: true,
  640. mail_delivery_failed_data: Time.current
  641. }
  642. )
  643. end
  644. it 'deletes "mail_delivery_failed"' do
  645. expect { user.update(email: Faker::Internet.email) }
  646. .to change { user.preferences.key?(:mail_delivery_failed) }.to(false)
  647. end
  648. it 'leaves "mail_delivery_failed_data" untouched' do
  649. expect { user.update(email: Faker::Internet.email) }
  650. .to not_change { user.preferences[:mail_delivery_failed_data] }
  651. end
  652. end
  653. end
  654. describe '#image' do
  655. describe 'when value is invalid' do
  656. let(:value) { 'Th1515n0t4v4l1dh45h' }
  657. it 'prevents create' do
  658. expect { create(:user, image: value) }.to raise_error(Exceptions::UnprocessableEntity, /#{value}/)
  659. end
  660. it 'prevents update' do
  661. expect { create(:user).update!(image: value) }.to raise_error(Exceptions::UnprocessableEntity, /#{value}/)
  662. end
  663. end
  664. end
  665. describe '#image_source' do
  666. describe 'when value is invalid' do
  667. let(:value) { 'Th1515n0t4v4l1dh45h' }
  668. let(:escaped) { Regexp.escape(value) }
  669. it 'prevents create' do
  670. expect { create(:user, image_source: value) }.to raise_error(ActiveRecord::RecordInvalid, /Image source/)
  671. end
  672. it 'prevents update' do
  673. expect { create(:user).update!(image_source: value) }.to raise_error(ActiveRecord::RecordInvalid, /Image source/)
  674. end
  675. end
  676. end
  677. end
  678. describe 'Associations:' do
  679. subject(:user) { create(:agent, groups: [group_subject]) }
  680. let!(:group_subject) { create(:group) }
  681. it 'does remove references before destroy' do
  682. refs_known = { 'Group' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  683. 'Token' => { 'user_id' => 1 },
  684. 'Ticket::Article' =>
  685. { 'created_by_id' => 1, 'updated_by_id' => 1, 'origin_by_id' => 1 },
  686. 'Ticket::StateType' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  687. 'Ticket::Article::Sender' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  688. 'Ticket::Article::Type' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  689. 'Ticket::Article::Flag' => { 'created_by_id' => 0 },
  690. 'Ticket::Priority' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  691. 'Ticket::TimeAccounting' => { 'created_by_id' => 0 },
  692. 'Ticket::State' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  693. 'Ticket::Flag' => { 'created_by_id' => 0 },
  694. 'PostmasterFilter' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  695. 'OnlineNotification' => { 'user_id' => 1, 'created_by_id' => 0, 'updated_by_id' => 0 },
  696. 'Ticket' =>
  697. { 'created_by_id' => 0, 'updated_by_id' => 0, 'owner_id' => 1, 'customer_id' => 3 },
  698. 'Template' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  699. 'Avatar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  700. 'Scheduler' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  701. 'Chat' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  702. 'HttpLog' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  703. 'EmailAddress' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  704. 'Taskbar' => { 'user_id' => 1 },
  705. 'Sla' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  706. 'UserDevice' => { 'user_id' => 1 },
  707. 'Chat::Message' => { 'created_by_id' => 0 },
  708. 'Chat::Agent' => { 'created_by_id' => 1, 'updated_by_id' => 1 },
  709. 'Chat::Session' => { 'user_id' => 0, 'created_by_id' => 0, 'updated_by_id' => 0 },
  710. 'Tag' => { 'created_by_id' => 0 },
  711. 'Karma::User' => { 'user_id' => 0 },
  712. 'Karma::ActivityLog' => { 'user_id' => 1 },
  713. 'RecentView' => { 'created_by_id' => 1 },
  714. 'KnowledgeBase::Answer::Translation' =>
  715. { 'created_by_id' => 0, 'updated_by_id' => 0 },
  716. 'KnowledgeBase::Answer' =>
  717. { 'archived_by_id' => 1, 'published_by_id' => 1, 'internal_by_id' => 1 },
  718. 'Report::Profile' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  719. 'Package' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  720. 'Job' => { 'created_by_id' => 0, 'updated_by_id' => 1 },
  721. 'Store' => { 'created_by_id' => 0 },
  722. 'Cti::CallerId' => { 'user_id' => 1 },
  723. 'DataPrivacyTask' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  724. 'Trigger' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  725. 'Translation' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  726. 'ObjectManager::Attribute' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  727. 'User' => { 'created_by_id' => 1, 'out_of_office_replacement_id' => 1, 'updated_by_id' => 1 },
  728. 'Organization' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  729. 'Macro' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  730. 'Mention' => { 'created_by_id' => 1, 'updated_by_id' => 0, 'user_id' => 1 },
  731. 'Channel' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  732. 'Role' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  733. 'History' => { 'created_by_id' => 4 },
  734. 'Webhook' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  735. 'Overview' => { 'created_by_id' => 1, 'updated_by_id' => 0 },
  736. 'ActivityStream' => { 'created_by_id' => 0 },
  737. 'StatsStore' => { 'created_by_id' => 0 },
  738. 'TextModule' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  739. 'Calendar' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  740. 'UserGroup' => { 'user_id' => 1 },
  741. 'Signature' => { 'created_by_id' => 0, 'updated_by_id' => 0 },
  742. 'Authorization' => { 'user_id' => 1 } }
  743. # delete objects
  744. token = create(:token, user: user)
  745. online_notification = create(:online_notification, user: user)
  746. taskbar = create(:taskbar, user: user)
  747. user_device = create(:user_device, user: user)
  748. karma_activity_log = create(:karma_activity_log, user: user)
  749. cti_caller_id = create(:cti_caller_id, user: user)
  750. authorization = create(:twitter_authorization, user: user)
  751. recent_view = create(:recent_view, created_by: user)
  752. avatar = create(:avatar, o_id: user.id)
  753. overview = create(:overview, created_by_id: user.id, user_ids: [user.id])
  754. mention = create(:mention, mentionable: create(:ticket), user: user)
  755. mention_created_by = create(:mention, mentionable: create(:ticket), user: create(:agent), created_by: user)
  756. user_created_by = create(:customer, created_by_id: user.id, updated_by_id: user.id, out_of_office_replacement_id: user.id)
  757. expect(overview.reload.user_ids).to eq([user.id])
  758. # create a chat agent for admin user (id=1) before agent user
  759. # to be sure that the data gets removed and not mapped which
  760. # would result in a foreign key because of the unique key on the
  761. # created_by_id and updated_by_id.
  762. create(:'chat/agent')
  763. chat_agent_user = create(:'chat/agent', created_by_id: user.id, updated_by_id: user.id)
  764. # move ownership objects
  765. group = create(:group, created_by_id: user.id)
  766. job = create(:job, updated_by_id: user.id)
  767. ticket = create(:ticket, group: group_subject, owner: user)
  768. ticket_article = create(:ticket_article, ticket: ticket, created_by_id: user.id, updated_by_id: user.id, origin_by_id: user.id)
  769. customer_ticket1 = create(:ticket, group: group_subject, customer: user)
  770. customer_ticket2 = create(:ticket, group: group_subject, customer: user)
  771. customer_ticket3 = create(:ticket, group: group_subject, customer: user)
  772. knowledge_base_answer = create(:knowledge_base_answer, archived_by_id: user.id, published_by_id: user.id, internal_by_id: user.id)
  773. refs_user = Models.references('User', user.id, true)
  774. expect(refs_user).to eq(refs_known)
  775. user.destroy
  776. expect { token.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  777. expect { online_notification.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  778. expect { taskbar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  779. expect { user_device.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  780. expect { karma_activity_log.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  781. expect { cti_caller_id.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  782. expect { authorization.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  783. expect { recent_view.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  784. expect { avatar.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  785. expect { customer_ticket1.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  786. expect { customer_ticket2.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  787. expect { customer_ticket3.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  788. expect { chat_agent_user.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  789. expect { mention.reload }.to raise_exception(ActiveRecord::RecordNotFound)
  790. expect(mention_created_by.reload.created_by_id).not_to eq(user.id)
  791. expect(overview.reload.user_ids).to eq([])
  792. # move ownership objects
  793. expect { group.reload }.to change(group, :created_by_id).to(1)
  794. expect { job.reload }.to change(job, :updated_by_id).to(1)
  795. expect { ticket.reload }.to change(ticket, :owner_id).to(1)
  796. expect { ticket_article.reload }
  797. .to change(ticket_article, :origin_by_id).to(1)
  798. .and change(ticket_article, :updated_by_id).to(1)
  799. .and change(ticket_article, :created_by_id).to(1)
  800. expect { knowledge_base_answer.reload }
  801. .to change(knowledge_base_answer, :archived_by_id).to(1)
  802. .and change(knowledge_base_answer, :published_by_id).to(1)
  803. .and change(knowledge_base_answer, :internal_by_id).to(1)
  804. expect { user_created_by.reload }
  805. .to change(user_created_by, :created_by_id).to(1)
  806. .and change(user_created_by, :updated_by_id).to(1)
  807. .and change(user_created_by, :out_of_office_replacement_id).to(1)
  808. end
  809. it 'does delete cache after user deletion' do
  810. online_notification = create(:online_notification, created_by_id: user.id)
  811. online_notification.attributes_with_association_ids
  812. user.destroy
  813. expect(online_notification.reload.attributes_with_association_ids['created_by_id']).to eq(1)
  814. end
  815. it 'does return an exception on blocking dependencies' do
  816. expect { user.send(:destroy_move_dependency_ownership) }.to raise_error(RuntimeError, 'Failed deleting references! Check logic for UserGroup->user_id.')
  817. end
  818. describe '#organization' do
  819. describe 'email domain-based assignment' do
  820. subject(:user) { build(:user) }
  821. context 'when not set on creation' do
  822. before { user.assign_attributes(organization: nil) }
  823. context 'and #email domain matches an existing Organization#domain' do
  824. before { user.assign_attributes(email: 'user@example.com') }
  825. let(:organization) { create(:organization, domain: 'example.com') }
  826. context 'and Organization#domain_assignment is false (default)' do
  827. before { organization.update(domain_assignment: false) }
  828. it 'remains nil' do
  829. expect { user.save }.not_to change(user, :organization)
  830. end
  831. end
  832. context 'and Organization#domain_assignment is true' do
  833. before { organization.update(domain_assignment: true) }
  834. it 'is automatically set to matching Organization' do
  835. expect { user.save }
  836. .to change(user, :organization).to(organization)
  837. end
  838. end
  839. end
  840. context 'and #email domain doesn’t match any Organization#domain' do
  841. before { user.assign_attributes(email: 'user@example.net') }
  842. let(:organization) { create(:organization, domain: 'example.com') }
  843. context 'and Organization#domain_assignment is true' do
  844. before { organization.update(domain_assignment: true) }
  845. it 'remains nil' do
  846. expect { user.save }.not_to change(user, :organization)
  847. end
  848. end
  849. end
  850. end
  851. context 'when set on creation' do
  852. before { user.assign_attributes(organization: specified_organization) }
  853. let(:specified_organization) { create(:organization, domain: 'example.net') }
  854. context 'and #email domain matches a DIFFERENT Organization#domain' do
  855. before { user.assign_attributes(email: 'user@example.com') }
  856. let!(:matching_organization) { create(:organization, domain: 'example.com') }
  857. context 'and Organization#domain_assignment is true' do
  858. before { matching_organization.update(domain_assignment: true) }
  859. it 'is NOT automatically set to matching Organization' do
  860. expect { user.save }
  861. .not_to change(user, :organization).from(specified_organization)
  862. end
  863. end
  864. end
  865. end
  866. end
  867. end
  868. end
  869. describe 'Callbacks, Observers, & Async Transactions -' do
  870. describe 'System-wide agent limit checks:' do
  871. let(:agent_role) { Role.lookup(name: 'Agent') }
  872. let(:admin_role) { Role.lookup(name: 'Admin') }
  873. let(:current_agents) { described_class.with_permissions('ticket.agent') }
  874. describe '#validate_agent_limit_by_role' do
  875. context 'for Integer value of system_agent_limit' do
  876. context 'before exceeding the agent limit' do
  877. before { Setting.set('system_agent_limit', current_agents.count + 1) }
  878. it 'grants agent creation' do
  879. expect { create(:agent) }
  880. .to change(current_agents, :count).by(1)
  881. end
  882. it 'grants role change' do
  883. future_agent = create(:customer)
  884. expect { future_agent.roles = [agent_role] }
  885. .to change(current_agents, :count).by(1)
  886. end
  887. describe 'role updates' do
  888. let(:agent) { create(:agent) }
  889. it 'grants update by instances' do
  890. expect { agent.roles = [admin_role, agent_role] }
  891. .not_to raise_error
  892. end
  893. it 'grants update by id (Integer)' do
  894. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  895. .not_to raise_error
  896. end
  897. it 'grants update by id (String)' do
  898. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  899. .not_to raise_error
  900. end
  901. end
  902. end
  903. context 'when exceeding the agent limit' do
  904. it 'creation of new agents' do
  905. Setting.set('system_agent_limit', current_agents.count + 2)
  906. create_list(:agent, 2)
  907. expect { create(:agent) }
  908. .to raise_error(Exceptions::UnprocessableEntity)
  909. .and change(current_agents, :count).by(0)
  910. end
  911. it 'prevents role change' do
  912. Setting.set('system_agent_limit', current_agents.count)
  913. future_agent = create(:customer)
  914. expect { future_agent.roles = [agent_role] }
  915. .to raise_error(Exceptions::UnprocessableEntity)
  916. .and change(current_agents, :count).by(0)
  917. end
  918. end
  919. end
  920. context 'for String value of system_agent_limit' do
  921. context 'before exceeding the agent limit' do
  922. before { Setting.set('system_agent_limit', (current_agents.count + 1).to_s) }
  923. it 'grants agent creation' do
  924. expect { create(:agent) }
  925. .to change(current_agents, :count).by(1)
  926. end
  927. it 'grants role change' do
  928. future_agent = create(:customer)
  929. expect { future_agent.roles = [agent_role] }
  930. .to change(current_agents, :count).by(1)
  931. end
  932. describe 'role updates' do
  933. let(:agent) { create(:agent) }
  934. it 'grants update by instances' do
  935. expect { agent.roles = [admin_role, agent_role] }
  936. .not_to raise_error
  937. end
  938. it 'grants update by id (Integer)' do
  939. expect { agent.role_ids = [admin_role.id, agent_role.id] }
  940. .not_to raise_error
  941. end
  942. it 'grants update by id (String)' do
  943. expect { agent.role_ids = [admin_role.id.to_s, agent_role.id.to_s] }
  944. .not_to raise_error
  945. end
  946. end
  947. end
  948. context 'when exceeding the agent limit' do
  949. it 'creation of new agents' do
  950. Setting.set('system_agent_limit', (current_agents.count + 2).to_s)
  951. create_list(:agent, 2)
  952. expect { create(:agent) }
  953. .to raise_error(Exceptions::UnprocessableEntity)
  954. .and change(current_agents, :count).by(0)
  955. end
  956. it 'prevents role change' do
  957. Setting.set('system_agent_limit', current_agents.count.to_s)
  958. future_agent = create(:customer)
  959. expect { future_agent.roles = [agent_role] }
  960. .to raise_error(Exceptions::UnprocessableEntity)
  961. .and change(current_agents, :count).by(0)
  962. end
  963. end
  964. end
  965. end
  966. describe '#validate_agent_limit_by_attributes' do
  967. context 'for Integer value of system_agent_limit' do
  968. before { Setting.set('system_agent_limit', current_agents.count) }
  969. context 'when exceeding the agent limit' do
  970. it 'prevents re-activation of agents' do
  971. inactive_agent = create(:agent, active: false)
  972. expect { inactive_agent.update!(active: true) }
  973. .to raise_error(Exceptions::UnprocessableEntity)
  974. .and change(current_agents, :count).by(0)
  975. end
  976. end
  977. end
  978. context 'for String value of system_agent_limit' do
  979. before { Setting.set('system_agent_limit', current_agents.count.to_s) }
  980. context 'when exceeding the agent limit' do
  981. it 'prevents re-activation of agents' do
  982. inactive_agent = create(:agent, active: false)
  983. expect { inactive_agent.update!(active: true) }
  984. .to raise_error(Exceptions::UnprocessableEntity)
  985. .and change(current_agents, :count).by(0)
  986. end
  987. end
  988. end
  989. end
  990. end
  991. describe 'Touching associations on update:' do
  992. subject!(:user) { create(:customer) }
  993. let!(:organization) { create(:organization) }
  994. context 'when a customer gets a organization' do
  995. it 'touches its organization' do
  996. expect { user.update(organization: organization) }
  997. .to change { organization.reload.updated_at }
  998. end
  999. end
  1000. end
  1001. describe 'Cti::CallerId syncing:' do
  1002. context 'with a #phone attribute' do
  1003. subject(:user) { build(:user, phone: '1234567890') }
  1004. it 'adds CallerId record on creation (via Cti::CallerId.build)' do
  1005. expect(Cti::CallerId).to receive(:build).with(user)
  1006. user.save
  1007. end
  1008. it 'updates CallerId record on touch/update (via Cti::CallerId.build)' do
  1009. user.save
  1010. expect(Cti::CallerId).to receive(:build).with(user)
  1011. user.touch
  1012. end
  1013. it 'destroys CallerId record on deletion' do
  1014. user.save
  1015. expect { user.destroy }
  1016. .to change { Cti::CallerId.count }.by(-1)
  1017. end
  1018. end
  1019. end
  1020. describe 'Cti::Log syncing:' do
  1021. context 'with existing Log records' do
  1022. context 'for incoming calls from an unknown number' do
  1023. let!(:log) { create(:'cti/log', :with_preferences, from: '1234567890', direction: 'in') }
  1024. context 'when creating a new user with that number' do
  1025. subject(:user) { build(:user, phone: log.from) }
  1026. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  1027. expect do
  1028. user.save
  1029. Observer::Transaction.commit
  1030. Scheduler.worker(true)
  1031. end.to change { log.reload.preferences[:from]&.first }
  1032. .to(hash_including('caller_id' => user.phone))
  1033. end
  1034. end
  1035. context 'when updating a user with that number' do
  1036. subject(:user) { create(:user) }
  1037. it 'populates #preferences[:from] hash in all associated Log records (in a bg job)' do
  1038. expect do
  1039. user.update(phone: log.from)
  1040. Observer::Transaction.commit
  1041. Scheduler.worker(true)
  1042. end.to change { log.reload.preferences[:from]&.first }
  1043. .to(hash_including('object' => 'User', 'o_id' => user.id))
  1044. end
  1045. end
  1046. context 'when creating a new user with an empty number' do
  1047. subject(:user) { build(:user, phone: '') }
  1048. it 'does not modify any Log records' do
  1049. expect do
  1050. user.save
  1051. Observer::Transaction.commit
  1052. Scheduler.worker(true)
  1053. end.not_to change { log.reload.attributes }
  1054. end
  1055. end
  1056. context 'when creating a new user with no number' do
  1057. subject(:user) { build(:user, phone: nil) }
  1058. it 'does not modify any Log records' do
  1059. expect do
  1060. user.save
  1061. Observer::Transaction.commit
  1062. Scheduler.worker(true)
  1063. end.not_to change { log.reload.attributes }
  1064. end
  1065. end
  1066. end
  1067. context 'for incoming calls from the given user' do
  1068. subject(:user) { create(:user, phone: '1234567890') }
  1069. let!(:logs) { create_list(:'cti/log', 5, :with_preferences, from: user.phone, direction: 'in') }
  1070. context 'when updating #phone attribute' do
  1071. context 'to another number' do
  1072. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1073. expect do
  1074. user.update(phone: '0123456789')
  1075. Observer::Transaction.commit
  1076. Scheduler.worker(true)
  1077. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1078. .to(Array.new(5) { nil })
  1079. end
  1080. end
  1081. context 'to an empty string' do
  1082. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1083. expect do
  1084. user.update(phone: '')
  1085. Observer::Transaction.commit
  1086. Scheduler.worker(true)
  1087. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1088. .to(Array.new(5) { nil })
  1089. end
  1090. end
  1091. context 'to nil' do
  1092. it 'empties #preferences[:from] hash in all associated Log records (in a bg job)' do
  1093. expect do
  1094. user.update(phone: nil)
  1095. Observer::Transaction.commit
  1096. Scheduler.worker(true)
  1097. end.to change { logs.map(&:reload).map { |log| log.preferences[:from] } }
  1098. .to(Array.new(5) { nil })
  1099. end
  1100. end
  1101. end
  1102. context 'when updating attributes other than #phone' do
  1103. it 'does not modify any Log records' do
  1104. expect do
  1105. user.update(mobile: '2345678901')
  1106. Observer::Transaction.commit
  1107. Scheduler.worker(true)
  1108. end.not_to change { logs.map(&:reload).map(&:attributes) }
  1109. end
  1110. end
  1111. end
  1112. end
  1113. end
  1114. end
  1115. end