has_groups_examples.rb 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. RSpec.shared_examples 'HasGroups' do |group_access_factory:|
  3. describe 'group' do
  4. subject { create(group_access_factory) }
  5. let(:group_full) { create(:group) }
  6. let(:group_read) { create(:group) }
  7. let(:group_inactive) { create(:group, active: false) }
  8. describe '.group_through_identifier' do
  9. it 'responds to group_through_identifier' do
  10. expect(described_class).to respond_to(:group_through_identifier)
  11. end
  12. it 'returns a Symbol as identifier' do
  13. expect(described_class.group_through_identifier).to be_a(Symbol)
  14. end
  15. it 'instance responds to group_through_identifier method' do
  16. expect(subject).to respond_to(described_class.group_through_identifier)
  17. end
  18. end
  19. describe '.group_through' do
  20. it 'responds to group_through' do
  21. expect(described_class).to respond_to(:group_through)
  22. end
  23. it 'returns the Reflection instance of the has_many :through relation' do
  24. expect(described_class.group_through).to be_a(ActiveRecord::Reflection::HasManyReflection)
  25. end
  26. end
  27. describe '#groups' do
  28. it 'responds to groups' do
  29. expect(subject).to respond_to(:groups)
  30. end
  31. describe '#groups.access' do
  32. it 'responds to groups.access' do
  33. expect(subject.groups).to respond_to(:access)
  34. end
  35. describe 'result' do
  36. before do
  37. subject.group_names_access_map = {
  38. group_full.name => 'full',
  39. group_read.name => 'read',
  40. group_inactive.name => 'change',
  41. }
  42. end
  43. it 'returns all related Groups' do
  44. expect(subject.groups.access.size).to eq(3)
  45. end
  46. it 'adds join table attribute(s like) access' do
  47. expect(subject.groups.access.first).to respond_to(:access)
  48. end
  49. it 'filters for given access parameter' do
  50. expect(subject.groups.access('read')).to include(group_read)
  51. end
  52. it 'filters for given access list parameter' do
  53. expect(subject.groups.access('read', 'change')).to include(group_read, group_inactive)
  54. end
  55. it 'always includes full access groups' do
  56. expect(subject.groups.access('read')).to include(group_full)
  57. end
  58. end
  59. end
  60. end
  61. describe '#group_access?' do
  62. before do
  63. subject.group_names_access_map = {
  64. group_read.name => 'read',
  65. }
  66. end
  67. it 'responds to group_access?' do
  68. expect(subject).to respond_to(:group_access?)
  69. end
  70. context 'Group ID parameter' do
  71. include_examples '#group_access? call' do
  72. let(:group_parameter) { group_read.id }
  73. end
  74. end
  75. context 'Group parameter' do
  76. include_examples '#group_access? call' do
  77. let(:group_parameter) { group_read }
  78. end
  79. end
  80. it 'prevents inactive Group' do
  81. subject.group_names_access_map = {
  82. group_inactive.name => 'read',
  83. }
  84. expect(subject.group_access?(group_inactive.id, 'read')).to be false
  85. end
  86. it 'prevents inactive instances' do
  87. subject.update!(active: false)
  88. subject.group_names_access_map = {
  89. group_read.name => 'read',
  90. }
  91. expect(subject.group_access?(group_read.id, 'read')).to be false
  92. end
  93. end
  94. describe '#group_ids_access' do
  95. before do
  96. subject.group_names_access_map = {
  97. group_read.name => 'read',
  98. }
  99. end
  100. it 'responds to group_ids_access' do
  101. expect(subject).to respond_to(:group_ids_access)
  102. end
  103. it 'lists only active Group IDs' do
  104. subject.group_names_access_map = {
  105. group_read.name => 'read',
  106. group_inactive.name => 'read',
  107. }
  108. result = subject.group_ids_access('read')
  109. expect(result).not_to include(group_inactive.id)
  110. end
  111. it "doesn't list for inactive instances" do
  112. subject.update!(active: false)
  113. subject.group_names_access_map = {
  114. group_read.name => 'read',
  115. }
  116. expect(subject.group_ids_access('read')).to be_empty
  117. end
  118. describe 'single access' do
  119. it 'lists access Group IDs' do
  120. result = subject.group_ids_access('read')
  121. expect(result).to include(group_read.id)
  122. end
  123. it "doesn't list for no access" do
  124. result = subject.group_ids_access('change')
  125. expect(result).not_to include(group_read.id)
  126. end
  127. end
  128. describe 'access list' do
  129. it 'lists access Group IDs' do
  130. result = subject.group_ids_access(%w[read change])
  131. expect(result).to include(group_read.id)
  132. end
  133. it "doesn't list for no access" do
  134. result = subject.group_ids_access(%w[change create])
  135. expect(result).not_to include(group_read.id)
  136. end
  137. end
  138. end
  139. describe '#groups_access' do
  140. it 'responds to groups_access' do
  141. expect(subject).to respond_to(:groups_access)
  142. end
  143. it 'wraps #group_ids_access' do
  144. expect(subject).to receive(:group_ids_access)
  145. subject.groups_access('read')
  146. end
  147. it 'returns Groups' do
  148. subject.group_names_access_map = {
  149. group_read.name => 'read',
  150. }
  151. result = subject.groups_access('read')
  152. expect(result).to include(group_read)
  153. end
  154. end
  155. describe '#group_names_access_map=' do
  156. it 'responds to group_names_access_map=' do
  157. expect(subject).to respond_to(:group_names_access_map=)
  158. end
  159. context 'existing instance' do
  160. it 'stores Hash with String values' do
  161. expect do
  162. subject.group_names_access_map = {
  163. group_full.name => 'full',
  164. group_read.name => 'read',
  165. }
  166. end.to change {
  167. described_class.group_through.klass.count
  168. }.by(2)
  169. end
  170. it 'stores Hash with Array<String> values' do
  171. expect do
  172. subject.group_names_access_map = {
  173. group_full.name => 'full',
  174. group_read.name => %w[read change],
  175. }
  176. end.to change {
  177. described_class.group_through.klass.count
  178. }.by(3)
  179. end
  180. it 'allows empty Hash value' do
  181. subject.group_names_access_map = {
  182. group_full.name => 'full',
  183. group_read.name => %w[read change],
  184. }
  185. expect do
  186. subject.group_names_access_map = {}
  187. end.to change {
  188. described_class.group_through.klass.count
  189. }.by(-3)
  190. end
  191. it 'prevents having full and other privilege at the same time' do
  192. invalid_combination = %w[full read change]
  193. exception = ActiveRecord::RecordInvalid
  194. expect do
  195. subject.group_names_access_map = {
  196. group_full.name => invalid_combination,
  197. }
  198. end.to raise_error(exception)
  199. expect do
  200. subject.group_names_access_map = {
  201. group_full.name => invalid_combination.reverse,
  202. }
  203. end.to raise_error(exception)
  204. end
  205. end
  206. context 'new instance' do
  207. subject { build(group_access_factory) }
  208. it "doesn't store directly" do
  209. expect do
  210. subject.group_names_access_map = {
  211. group_full.name => 'full',
  212. group_read.name => 'read',
  213. }
  214. end.not_to change {
  215. described_class.group_through.klass.count
  216. }
  217. end
  218. it 'stores after save' do
  219. expect do
  220. subject.group_names_access_map = {
  221. group_full.name => 'full',
  222. group_read.name => 'read',
  223. }
  224. subject.save
  225. end.to change {
  226. described_class.group_through.klass.count
  227. }.by(2)
  228. end
  229. it 'allows empty Hash value' do
  230. expect do
  231. subject.group_names_access_map = {}
  232. subject.save
  233. end.not_to change {
  234. described_class.group_through.klass.count
  235. }
  236. end
  237. end
  238. end
  239. describe '#group_names_access_map' do
  240. it 'responds to group_names_access_map' do
  241. expect(subject).to respond_to(:group_names_access_map)
  242. end
  243. it 'returns instance Group name => access relations as Hash' do
  244. expected = {
  245. group_full.name => ['full'],
  246. group_read.name => ['read'],
  247. }
  248. subject.group_names_access_map = expected
  249. expect(subject.group_names_access_map).to eq(expected)
  250. end
  251. it "doesn't map for inactive instances" do
  252. subject.update!(active: false)
  253. subject.group_names_access_map = {
  254. group_full.name => ['full'],
  255. group_read.name => ['read'],
  256. }
  257. expect(subject.group_names_access_map).to be_empty
  258. end
  259. it 'returns empty map if none is stored' do
  260. subject.group_names_access_map = {
  261. group_full.name => 'full',
  262. group_read.name => 'read',
  263. }
  264. subject.group_names_access_map = {}
  265. expect(subject.group_names_access_map).to be_blank
  266. end
  267. end
  268. describe '#group_ids_access_map=' do
  269. it 'responds to group_ids_access_map=' do
  270. expect(subject).to respond_to(:group_ids_access_map=)
  271. end
  272. context 'existing instance' do
  273. it 'stores Hash with String values' do
  274. expect do
  275. subject.group_ids_access_map = {
  276. group_full.id => 'full',
  277. group_read.id => 'read',
  278. }
  279. end.to change {
  280. described_class.group_through.klass.count
  281. }.by(2)
  282. end
  283. it 'stores Hash with Array<String> values' do
  284. expect do
  285. subject.group_ids_access_map = {
  286. group_full.id => 'full',
  287. group_read.id => %w[read change],
  288. }
  289. end.to change {
  290. described_class.group_through.klass.count
  291. }.by(3)
  292. end
  293. it 'allows empty Hash value' do
  294. subject.group_ids_access_map = {
  295. group_full.id => 'full',
  296. group_read.id => %w[read change],
  297. }
  298. expect do
  299. subject.group_ids_access_map = {}
  300. end.to change {
  301. described_class.group_through.klass.count
  302. }.by(-3)
  303. end
  304. end
  305. context 'new instance' do
  306. subject { build(group_access_factory) }
  307. it "doesn't store directly" do
  308. expect do
  309. subject.group_ids_access_map = {
  310. group_full.id => 'full',
  311. group_read.id => 'read',
  312. }
  313. end.not_to change {
  314. described_class.group_through.klass.count
  315. }
  316. end
  317. it 'stores after save' do
  318. expect do
  319. subject.group_ids_access_map = {
  320. group_full.id => 'full',
  321. group_read.id => 'read',
  322. }
  323. subject.save
  324. end.to change {
  325. described_class.group_through.klass.count
  326. }.by(2)
  327. end
  328. it 'allows empty Hash value' do
  329. expect do
  330. subject.group_ids_access_map = {}
  331. subject.save
  332. end.not_to change {
  333. described_class.group_through.klass.count
  334. }
  335. end
  336. end
  337. end
  338. describe '#group_ids_access_map' do
  339. it 'responds to group_ids_access_map' do
  340. expect(subject).to respond_to(:group_ids_access_map)
  341. end
  342. it 'returns instance Group ID => access relations as Hash' do
  343. expected = {
  344. group_full.id => ['full'],
  345. group_read.id => ['read'],
  346. }
  347. subject.group_ids_access_map = expected
  348. expect(subject.group_ids_access_map).to eq(expected)
  349. end
  350. it "doesn't map for inactive instances" do
  351. subject.update!(active: false)
  352. subject.group_ids_access_map = {
  353. group_full.id => ['full'],
  354. group_read.id => ['read'],
  355. }
  356. expect(subject.group_ids_access_map).to be_empty
  357. end
  358. it 'returns empty map if none is stored' do
  359. subject.group_ids_access_map = {
  360. group_full.id => 'full',
  361. group_read.id => 'read',
  362. }
  363. subject.group_ids_access_map = {}
  364. expect(subject.group_ids_access_map).to be_blank
  365. end
  366. end
  367. describe '#associations_from_param' do
  368. it 'handles group_ids parameter as group_ids_access_map' do
  369. expected = {
  370. group_full.id => ['full'],
  371. group_read.id => ['read'],
  372. }
  373. subject.associations_from_param(group_ids: expected)
  374. expect(subject.group_ids_access_map).to eq(expected)
  375. end
  376. it 'handles groups parameter as group_names_access_map' do
  377. expected = {
  378. group_full.name => ['full'],
  379. group_read.name => ['read'],
  380. }
  381. subject.associations_from_param(groups: expected)
  382. expect(subject.group_names_access_map).to eq(expected)
  383. end
  384. end
  385. describe '#attributes_with_association_ids' do
  386. it 'includes group_ids as group_ids_access_map' do
  387. expected = {
  388. group_full.id => ['full'],
  389. group_read.id => ['read'],
  390. }
  391. subject.group_ids_access_map = expected
  392. result = subject.attributes_with_association_ids
  393. expect(result['group_ids']).to eq(expected)
  394. end
  395. end
  396. describe '#attributes_with_association_names' do
  397. it 'includes group_ids as group_ids_access_map' do
  398. expected = {
  399. group_full.id => ['full'],
  400. group_read.id => ['read'],
  401. }
  402. subject.group_ids_access_map = expected
  403. result = subject.attributes_with_association_names
  404. expect(result['group_ids']).to eq(expected)
  405. end
  406. it 'includes groups as group_names_access_map' do
  407. expected = {
  408. group_full.name => ['full'],
  409. group_read.name => ['read'],
  410. }
  411. subject.group_names_access_map = expected
  412. result = subject.attributes_with_association_names
  413. expect(result['groups']).to eq(expected)
  414. end
  415. end
  416. describe '.group_access' do
  417. before do
  418. subject.group_names_access_map = {
  419. group_read.name => 'read',
  420. }
  421. end
  422. it 'responds to group_access' do
  423. expect(described_class).to respond_to(:group_access)
  424. end
  425. it 'lists only active instances' do
  426. subject.update!(active: false)
  427. subject.group_names_access_map = {
  428. group_read.name => 'read',
  429. }
  430. result = described_class.group_access(group_read.id, 'read')
  431. expect(result).not_to include(subject)
  432. end
  433. context 'Group ID parameter' do
  434. include_examples '.group_access call' do
  435. let(:group_parameter) { group_read.id }
  436. end
  437. end
  438. context 'Group parameter' do
  439. include_examples '.group_access call' do
  440. let(:group_parameter) { group_read }
  441. end
  442. end
  443. end
  444. describe '.group_access_ids' do
  445. it 'responds to group_access_ids' do
  446. expect(described_class).to respond_to(:group_access_ids)
  447. end
  448. it 'wraps .group_access' do
  449. expect(described_class).to receive(:group_access).and_call_original
  450. described_class.group_access_ids(group_read, 'read')
  451. end
  452. it 'returns class instances' do
  453. subject.group_names_access_map = {
  454. group_read.name => 'read',
  455. }
  456. result = described_class.group_access_ids(group_read, 'read')
  457. expect(result).to include(subject.id)
  458. end
  459. end
  460. it 'destroys relations before instance gets destroyed' do
  461. subject.group_names_access_map = {
  462. group_full.name => 'full',
  463. group_read.name => 'read',
  464. group_inactive.name => 'change',
  465. }
  466. expect do
  467. subject.destroy
  468. end.to change {
  469. described_class.group_through.klass.count
  470. }.by(-3)
  471. end
  472. end
  473. end
  474. RSpec.shared_examples '#group_access? call' do
  475. context 'single access' do
  476. it 'checks positive' do
  477. expect(subject.group_access?(group_parameter, 'read')).to be true
  478. end
  479. it 'checks negative' do
  480. expect(subject.group_access?(group_parameter, 'change')).to be false
  481. end
  482. end
  483. context 'access list' do
  484. it 'checks positive' do
  485. expect(subject.group_access?(group_parameter, %w[read change])).to be true
  486. end
  487. it 'checks negative' do
  488. expect(subject.group_access?(group_parameter, %w[change create])).to be false
  489. end
  490. end
  491. end
  492. RSpec.shared_examples '.group_access call' do
  493. context 'single access' do
  494. it 'lists access IDs' do
  495. expect(described_class.group_access(group_parameter, 'read')).to include(subject)
  496. end
  497. it 'excludes non access IDs' do
  498. expect(described_class.group_access(group_parameter, 'change')).not_to include(subject)
  499. end
  500. end
  501. context 'access list' do
  502. it 'lists access IDs' do
  503. expect(described_class.group_access(group_parameter, %w[read change])).to include(subject)
  504. end
  505. it 'excludes non access IDs' do
  506. expect(described_class.group_access(group_parameter, %w[change create])).not_to include(subject)
  507. end
  508. end
  509. end