has_groups_examples.rb 17 KB

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