entityManager.test.ts 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046
  1. import { describe, test, vi, expect, beforeEach, afterEach } from 'vitest'
  2. import { Repository } from 'pinia-orm'
  3. import type { Element } from 'pinia-orm'
  4. import { Str, Uid } from 'pinia-orm/dist/decorators'
  5. import EntityManager from '~/services/data/entityManager'
  6. import ApiResource from '~/models/ApiResource'
  7. import ApiModel from '~/models/ApiModel'
  8. import ApiRequestService from '~/services/data/apiRequestService'
  9. class DummyApiResource extends ApiResource {
  10. static entity = 'dummyResource'
  11. @Uid()
  12. declare id: number | string
  13. @Str(null)
  14. declare name: string
  15. }
  16. class DummyApiModel extends ApiModel {
  17. static entity = 'dummyModel'
  18. @Uid()
  19. declare id: number | string
  20. @Str(null)
  21. declare name: string
  22. }
  23. class TestableEntityManager extends EntityManager {
  24. public _getProfileMask: () => object
  25. public removeTempAfterPersist(
  26. model: typeof ApiResource,
  27. tempInstanceId: number | string,
  28. ) {
  29. return super.removeTempAfterPersist(model, tempInstanceId)
  30. }
  31. public makeProfileHash() {
  32. return super.makeProfileHash()
  33. }
  34. public validateEntity(instance: unknown): void {
  35. return super.validateEntity(instance)
  36. }
  37. }
  38. const _console: any = {
  39. log: console.log,
  40. warn: console.warn,
  41. error: console.error,
  42. }
  43. vi.mock('~/models/models', async () => {
  44. class MyModel {
  45. static entity = 'my-model'
  46. }
  47. const modelsIndex: Record<string, any> = { 'my-model': () => MyModel }
  48. return {
  49. default: modelsIndex,
  50. }
  51. })
  52. let apiRequestService: ApiRequestService
  53. let entityManager: TestableEntityManager
  54. let repo: Repository<ApiResource>
  55. let _getRepo: (model: typeof ApiResource) => Repository<ApiResource>
  56. let _getProfileMask: () => object
  57. beforeEach(() => {
  58. // @ts-ignore
  59. repo = vi.fn() as Repository<ApiResource>
  60. // @ts-ignore
  61. apiRequestService = vi.fn() as ApiRequestService
  62. _getRepo = vi.fn((model: typeof ApiResource) => repo)
  63. _getProfileMask = vi.fn(() => {
  64. return {}
  65. })
  66. entityManager = new TestableEntityManager(
  67. apiRequestService,
  68. _getRepo,
  69. _getProfileMask,
  70. )
  71. })
  72. afterEach(() => {
  73. // Reset console methods after mock
  74. console.log = _console.log
  75. console.warn = _console.warn
  76. console.error = _console.error
  77. })
  78. describe('getRepository', () => {
  79. test('simple call', () => {
  80. entityManager.getRepository(DummyApiResource)
  81. expect(_getRepo).toHaveBeenCalledWith(DummyApiResource)
  82. })
  83. })
  84. describe('getQuery', () => {
  85. test('simple call', () => {
  86. // @ts-ignore
  87. const repo = vi.fn()
  88. // @ts-ignore
  89. entityManager.getRepository = vi.fn(() => repo)
  90. const query = vi.fn()
  91. // @ts-ignore
  92. repo.where = vi.fn((method) => {
  93. // Query method is supposed to exclude NaN ids values
  94. expect(method({ id: 123 })).toBeTruthy()
  95. expect(method({ id: 'abc' })).toBeFalsy()
  96. return query
  97. })
  98. const result = entityManager.getQuery(DummyApiResource)
  99. expect(result).toEqual(query)
  100. })
  101. })
  102. describe('cast', () => {
  103. test('simple cast', () => {
  104. // @ts-ignore
  105. const result = entityManager.cast(DummyApiResource, { id: 1 })
  106. expect(result instanceof DummyApiResource).toEqual(true)
  107. })
  108. })
  109. describe('getModelFor', () => {
  110. test('simple call', async () => {
  111. const model = await entityManager.getModelFor('my-model')
  112. expect(model!.entity).toEqual('my-model')
  113. })
  114. test('non existing model', async () => {
  115. expect(
  116. async () => await entityManager.getModelFor('non-existing-model'),
  117. ).rejects.toThrowError(
  118. "No model found for entity name 'non-existing-model'",
  119. )
  120. })
  121. })
  122. describe('getModelFromIri', () => {
  123. test('simple call', async () => {
  124. // @ts-ignore
  125. entityManager.getModelFor = vi.fn(
  126. async (entityName: string) => DummyApiResource,
  127. )
  128. // @ts-ignore
  129. const result = await entityManager.getModelFromIri('/api/dummy/123')
  130. console.log(result)
  131. expect(result).toEqual(DummyApiResource)
  132. })
  133. test('invalide Iri', () => {
  134. expect(
  135. async () => await entityManager.getModelFromIri('/invalid'),
  136. ).rejects.toThrowError('cannot parse the IRI')
  137. })
  138. })
  139. describe('newInstance', () => {
  140. test('simple call', () => {
  141. const properties = { id: 1 }
  142. // @ts-ignore
  143. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  144. return model === DummyApiResource ? repo : null
  145. })
  146. // @ts-ignore
  147. const entity = new DummyApiResource(properties)
  148. // @ts-ignore
  149. repo.make = vi.fn((properties: object) => {
  150. // @ts-ignore
  151. entity.id = properties.id
  152. return entity
  153. })
  154. // @ts-ignore
  155. entityManager.save = vi.fn(
  156. (entity: ApiResource, permanent: boolean) => entity,
  157. )
  158. const result = entityManager.newInstance(DummyApiResource, properties)
  159. expect(repo.make).toHaveBeenCalledWith(properties)
  160. expect(entityManager.save).toHaveBeenCalledWith(entity, true)
  161. expect(result.id).toEqual(properties.id)
  162. })
  163. test('with no id provided', () => {
  164. const properties = { name: 'bob' }
  165. // @ts-ignore
  166. const repo = vi.fn() as Repository<ApiResource>
  167. // @ts-ignore
  168. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  169. return model === DummyApiResource ? repo : null
  170. })
  171. // @ts-ignore
  172. entityManager.saveInitialState = vi.fn(
  173. (model: typeof ApiResource, entity: ApiResource) => null,
  174. )
  175. // @ts-ignore
  176. const entity = new DummyApiResource(properties)
  177. // @ts-ignore
  178. repo.make = vi.fn((properties: object) => {
  179. // @ts-ignore
  180. entity.name = properties.name
  181. return entity
  182. })
  183. // @ts-ignore
  184. repo.save = vi.fn((record: Element) => entity)
  185. const result = entityManager.newInstance(DummyApiResource, properties)
  186. expect(result.id, "id is 'tmp' followed by a valid uuid-V4").toMatch(
  187. /tmp[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}/,
  188. )
  189. expect(result.name).toEqual(properties.name)
  190. })
  191. })
  192. describe('save', () => {
  193. test('simple call', () => {
  194. // @ts-ignore
  195. const repo = vi.fn() as Repository<ApiResource>
  196. // @ts-ignore
  197. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  198. return model === DummyApiResource ? repo : null
  199. })
  200. entityManager.validateEntity = vi.fn((instance: unknown) => {})
  201. // @ts-ignore
  202. repo.save = vi.fn((record: Element) => entity)
  203. const entity = new DummyApiResource({ id: 1 })
  204. entityManager.save(entity)
  205. expect(entityManager.validateEntity).toHaveBeenCalledWith(entity)
  206. expect(repo.save).toHaveBeenCalledWith(entity)
  207. })
  208. })
  209. describe('find', () => {
  210. test('simple call', () => {
  211. // @ts-ignore
  212. const repo = vi.fn() as Repository<ApiResource>
  213. // @ts-ignore
  214. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  215. return model === DummyApiResource ? repo : null
  216. })
  217. const entity = new DummyApiResource({ id: 1 })
  218. // @ts-ignore
  219. repo.find = vi.fn((id: string | number) => entity)
  220. entityManager.find(DummyApiResource, 1)
  221. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiResource)
  222. expect(repo.find).toHaveBeenCalledWith(1)
  223. })
  224. })
  225. describe('fetch', () => {
  226. test('not in store, no force refresh', async () => {
  227. const properties = { id: 1 }
  228. const entity = new DummyApiResource({ id: 1 })
  229. // @ts-ignore
  230. entityManager.find = vi.fn(
  231. (model: typeof ApiResource, id: number) => undefined,
  232. )
  233. // @ts-ignore
  234. apiRequestService.get = vi.fn(async (url: string) => properties)
  235. // @ts-ignore
  236. entityManager.newInstance = vi.fn(
  237. (model: typeof ApiResource, props: object) => entity,
  238. )
  239. const result = await entityManager.fetch(DummyApiResource, 1)
  240. expect(entityManager.find).toHaveBeenCalledWith(DummyApiResource, 1)
  241. expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource/1')
  242. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  243. id: 1,
  244. name: null,
  245. _model: undefined,
  246. })
  247. expect(result).toEqual(entity)
  248. })
  249. test('in store, no force refresh', async () => {
  250. const properties = { id: 1 }
  251. const entity = new DummyApiResource({ id: 1 })
  252. // @ts-ignore
  253. entityManager.find = vi.fn(
  254. (model: typeof ApiResource, id: number) => entity,
  255. )
  256. const result = await entityManager.fetch(DummyApiResource, 1)
  257. expect(entityManager.find).toHaveBeenCalledWith(DummyApiResource, 1)
  258. expect(result).toEqual(entity)
  259. })
  260. test('in store, but with force refresh', async () => {
  261. const properties = { id: 1 }
  262. const entity = new DummyApiResource({ id: 1 })
  263. // @ts-ignore
  264. entityManager.find = vi.fn(
  265. (model: typeof ApiResource, id: number) => undefined,
  266. )
  267. // @ts-ignore
  268. apiRequestService.get = vi.fn(async (url: string) => properties)
  269. // @ts-ignore
  270. entityManager.newInstance = vi.fn(
  271. (model: typeof ApiResource, props: object) => entity,
  272. )
  273. const result = await entityManager.fetch(DummyApiResource, 1, true)
  274. expect(entityManager.find).toHaveBeenCalledTimes(0)
  275. expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource/1')
  276. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  277. id: 1,
  278. name: null,
  279. _model: undefined,
  280. })
  281. expect(result).toEqual(entity)
  282. })
  283. })
  284. describe('fetchCollection', () => {
  285. test('simple call', async () => {
  286. const collection = {
  287. '@type': 'hydra:Collection',
  288. 'hydra:totalItems': 3,
  289. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  290. }
  291. // @ts-ignore
  292. apiRequestService.get = vi.fn(async (url: string) => collection)
  293. // @ts-ignore
  294. entityManager.newInstance = vi.fn(
  295. (model: typeof ApiResource, props: object) => {
  296. return new DummyApiResource(props)
  297. },
  298. )
  299. const piniaOrmQuery = vi.fn()
  300. // @ts-ignore
  301. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  302. const result = await entityManager.fetchCollection(DummyApiResource, null)
  303. expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource')
  304. expect(entityManager.newInstance).toHaveBeenCalledTimes(3)
  305. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  306. id: 1,
  307. name: null,
  308. _model: undefined,
  309. })
  310. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  311. id: 2,
  312. name: null,
  313. _model: undefined,
  314. })
  315. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  316. id: 3,
  317. name: null,
  318. _model: undefined,
  319. })
  320. // @ts-ignore
  321. piniaOrmQuery.get = vi.fn(() => [
  322. new DummyApiResource({ id: 1 }),
  323. new DummyApiResource({ id: 2 }),
  324. new DummyApiResource({ id: 3 }),
  325. ])
  326. expect(result.value.items).toEqual([
  327. new DummyApiResource({ id: 1 }),
  328. new DummyApiResource({ id: 2 }),
  329. new DummyApiResource({ id: 3 }),
  330. ])
  331. expect(result.value.pagination, 'default pagination').toEqual({
  332. first: 1,
  333. last: 1,
  334. next: undefined,
  335. previous: undefined,
  336. })
  337. // @ts-expect-error Needed to avoid 'Cannot stringify non POJO' occasional bugs
  338. expect(result.toJSON()).toEqual(
  339. 'Computed result from fetchCollection at : api/dummyResource',
  340. )
  341. })
  342. test('with a parent', async () => {
  343. const collection = {
  344. '@type': 'hydra:Collection',
  345. 'hydra:totalItems': 3,
  346. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  347. }
  348. // @ts-ignore
  349. apiRequestService.get = vi.fn(async (url: string) => collection)
  350. const piniaOrmQuery = vi.fn()
  351. // @ts-ignore
  352. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  353. // @ts-ignore
  354. entityManager.newInstance = vi.fn(
  355. (model: typeof ApiResource, props: object) => {
  356. return new DummyApiResource(props)
  357. },
  358. )
  359. const parent = new DummyApiModel()
  360. parent.id = 100
  361. parent.entity = 'dummyModel' // TODO: je ne comprend pas pqoi cette ligne est nécessaire...
  362. await entityManager.fetchCollection(DummyApiResource, parent)
  363. expect(apiRequestService.get).toHaveBeenCalledWith(
  364. 'api/dummyModel/100/dummyResource',
  365. )
  366. })
  367. test('with a query', async () => {
  368. const collection = {
  369. '@type': 'hydra:Collection',
  370. 'hydra:totalItems': 3,
  371. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  372. }
  373. const query = vi.fn()
  374. // @ts-ignore
  375. query.getUrlQuery = vi.fn(() => 'foo=bar')
  376. // @ts-ignore
  377. query.applyToPiniaOrmQuery = vi.fn((q) => q)
  378. const piniaOrmQuery = vi.fn()
  379. // @ts-ignore
  380. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  381. // @ts-ignore
  382. apiRequestService.get = vi.fn(async (url: string) => collection)
  383. // @ts-ignore
  384. entityManager.newInstance = vi.fn(
  385. (model: typeof ApiResource, props: object) => {
  386. return new DummyApiResource(props)
  387. },
  388. )
  389. // @ts-ignore
  390. await entityManager.fetchCollection(DummyApiResource, null, query)
  391. expect(apiRequestService.get).toHaveBeenCalledWith(
  392. 'api/dummyResource?foo=bar',
  393. )
  394. // @ts-ignore
  395. expect(query.getUrlQuery).toHaveBeenCalledWith()
  396. // @ts-ignore
  397. expect(query.applyToPiniaOrmQuery).toHaveBeenCalledWith(piniaOrmQuery)
  398. })
  399. })
  400. describe('persist', () => {
  401. test('new entity (POST)', async () => {
  402. const instance = new DummyApiModel({ id: 'tmp1', name: 'bob' })
  403. instance.isNew = vi.fn(() => true)
  404. // @ts-ignore
  405. instance.$toJson = vi.fn(() => {
  406. return { id: 'tmp1', name: 'bob' }
  407. })
  408. // @ts-ignore
  409. entityManager.cast = vi.fn(
  410. (model: typeof ApiResource, entity: ApiResource): ApiResource => entity,
  411. )
  412. entityManager.validateEntity = vi.fn((instance: unknown) => {})
  413. const response = { id: 1, name: 'bob' }
  414. // @ts-ignore
  415. apiRequestService.post = vi.fn((url, data) => response)
  416. // @ts-ignore
  417. entityManager.newInstance = vi.fn((model, response) => {
  418. const newEntity = new DummyApiModel(response)
  419. // @ts-ignore
  420. newEntity.id = response.id
  421. // @ts-ignore
  422. newEntity.name = response.name
  423. return newEntity
  424. })
  425. // @ts-ignore
  426. entityManager.removeTempAfterPersist = vi.fn()
  427. entityManager.makeProfileHash = vi.fn(async () => await 'azerty')
  428. const result = await entityManager.persist(instance)
  429. // temp id should have been purged from the posted data
  430. expect(apiRequestService.post).toHaveBeenCalledWith(
  431. 'api/dummyModel',
  432. {
  433. name: 'bob',
  434. },
  435. null,
  436. { profileHash: 'azerty' },
  437. )
  438. expect(entityManager.newInstance).toHaveBeenCalledWith(
  439. DummyApiModel,
  440. response,
  441. )
  442. // @ts-ignore
  443. expect(entityManager.removeTempAfterPersist).toHaveBeenCalledWith(
  444. DummyApiModel,
  445. instance.id,
  446. )
  447. // @ts-ignore
  448. expect(entityManager.makeProfileHash).toHaveBeenCalledTimes(1)
  449. expect(result.id).toEqual(1)
  450. expect(result.name).toEqual('bob')
  451. expect(entityManager.validateEntity).toHaveBeenCalledWith(instance)
  452. })
  453. test('existing entity (PUT)', async () => {
  454. const props = { id: 1, name: 'bob' }
  455. const entity = new DummyApiModel(props)
  456. entity.id = 1
  457. entity.isNew = vi.fn(() => false)
  458. // @ts-ignore
  459. entity.$toJson = vi.fn(() => props)
  460. // TODO: attendre de voir si cet appel est nécessaire dans l'entity manager
  461. // entityManager.cast = vi.fn((model: typeof ApiResource, entity: ApiResource): ApiResource => entity)
  462. // @ts-ignore
  463. apiRequestService.put = vi.fn((url, data) => props)
  464. // @ts-ignore
  465. entityManager.newInstance = vi.fn((model, response) => {
  466. const newEntity = new DummyApiModel(response)
  467. // @ts-ignore
  468. newEntity.id = response.id
  469. // @ts-ignore
  470. newEntity.name = response.name
  471. return newEntity
  472. })
  473. // @ts-ignore
  474. entityManager.removeTempAfterPersist = vi.fn()
  475. entityManager.makeProfileHash = vi.fn(async () => await 'azerty')
  476. entityManager.validateEntity = vi.fn((instance: unknown) => {})
  477. const result = await entityManager.persist(entity)
  478. expect(apiRequestService.put).toHaveBeenCalledWith(
  479. 'api/dummyModel/1',
  480. {
  481. id: 1,
  482. name: 'bob',
  483. },
  484. null,
  485. { profileHash: 'azerty' },
  486. )
  487. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiModel, props)
  488. // @ts-ignore
  489. expect(entityManager.removeTempAfterPersist).toHaveBeenCalledTimes(0)
  490. // @ts-ignore
  491. expect(entityManager.makeProfileHash).toHaveBeenCalledTimes(1)
  492. expect(result.id).toEqual(1)
  493. expect(result.name).toEqual('bob')
  494. expect(entityManager.validateEntity).toHaveBeenCalledWith(entity)
  495. })
  496. })
  497. describe('patch', () => {
  498. test('simple call', async () => {
  499. const props = { id: 1, name: 'bobby' }
  500. // @ts-ignore
  501. apiRequestService.put = vi.fn((url, data) => props)
  502. // @ts-ignore
  503. entityManager.newInstance = vi.fn((model, response) => {
  504. const newEntity = new DummyApiModel(response)
  505. // @ts-ignore
  506. newEntity.id = response.id
  507. // @ts-ignore
  508. newEntity.name = response.name
  509. return newEntity
  510. })
  511. const result = await entityManager.patch(DummyApiModel, 1, {
  512. name: 'bobby',
  513. })
  514. expect(apiRequestService.put).toHaveBeenCalledWith(
  515. 'api/dummyModel/1',
  516. '{"name":"bobby"}',
  517. )
  518. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiModel, {
  519. id: 1,
  520. name: 'bobby',
  521. })
  522. expect(result.id).toEqual(1)
  523. expect(result.name).toEqual('bobby')
  524. })
  525. })
  526. describe('delete', () => {
  527. test('delete non persisted entity', () => {
  528. const entity = new DummyApiModel()
  529. entity.isNew = vi.fn(() => true)
  530. entity.id = 'tmp123'
  531. // @ts-ignore
  532. const repo = vi.fn() as Repository<ApiResource>
  533. // @ts-ignore
  534. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  535. return model === DummyApiModel ? repo : null
  536. })
  537. entityManager.validateEntity = vi.fn((instance: unknown) => {})
  538. apiRequestService.delete = vi.fn()
  539. // @ts-ignore
  540. repo.destroy = vi.fn((id: number) => null)
  541. entityManager.delete(entity)
  542. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiModel)
  543. expect(apiRequestService.delete).toHaveBeenCalledTimes(0)
  544. expect(repo.destroy).toHaveBeenCalledWith('tmp123')
  545. expect(entityManager.validateEntity).toHaveBeenCalledWith(entity)
  546. })
  547. test('delete persisted entity', async () => {
  548. const entity = new DummyApiModel()
  549. entity.isNew = vi.fn(() => false)
  550. entity.id = 1
  551. // @ts-ignore
  552. const repo = vi.fn() as Repository<ApiResource>
  553. // @ts-ignore
  554. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  555. return model === DummyApiModel ? repo : null
  556. })
  557. // @ts-ignore
  558. apiRequestService.delete = vi.fn((id: number) => null)
  559. // @ts-ignore
  560. repo.destroy = vi.fn((id: number) => null)
  561. await entityManager.delete(entity)
  562. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiModel)
  563. expect(apiRequestService.delete).toHaveBeenCalledWith('api/dummyModel/1')
  564. expect(repo.destroy).toHaveBeenCalledWith(1)
  565. })
  566. })
  567. describe('reset', () => {
  568. test('simple call', () => {
  569. const entity = new DummyApiModel()
  570. entity.id = 1
  571. entity.name = 'paul'
  572. const initialEntity = new DummyApiModel()
  573. initialEntity.id = 1
  574. initialEntity.name = 'serges'
  575. // @ts-ignore
  576. entityManager.getInitialStateOf = vi.fn(
  577. (model: typeof ApiResource, id: string | number) => initialEntity,
  578. )
  579. // @ts-ignore
  580. const repo = vi.fn() as Repository<ApiResource>
  581. // @ts-ignore
  582. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  583. return model === DummyApiModel ? repo : null
  584. })
  585. // @ts-ignore
  586. repo.save = vi.fn((data: any) => null)
  587. const result = entityManager.reset(DummyApiModel, entity)
  588. // @ts-ignore
  589. expect(entityManager.getInitialStateOf).toHaveBeenCalledWith(
  590. DummyApiModel,
  591. 1,
  592. )
  593. expect(repo.save).toHaveBeenCalledWith(initialEntity)
  594. expect(result).toEqual(initialEntity)
  595. })
  596. test('no initial state stored', () => {
  597. const entity = new DummyApiModel()
  598. entity.id = 1
  599. // @ts-ignore
  600. entityManager.getInitialStateOf = vi.fn(
  601. (model: typeof ApiResource, id: string | number) => null,
  602. )
  603. expect(() => entityManager.reset(DummyApiModel, entity)).toThrowError(
  604. 'no initial state recorded for this object - abort [dummyModel/1]',
  605. )
  606. })
  607. })
  608. describe('flush', () => {
  609. test('simple call', () => {
  610. // @ts-ignore
  611. const repo = vi.fn() as Repository<ApiResource>
  612. // @ts-ignore
  613. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  614. return model === DummyApiModel ? repo : null
  615. })
  616. repo.flush = vi.fn()
  617. entityManager.flush(DummyApiModel)
  618. expect(repo.flush).toHaveBeenCalled()
  619. })
  620. })
  621. describe('isNewEntity', () => {
  622. test('with new entity', () => {
  623. const entity = new DummyApiModel()
  624. entity.isNew = vi.fn(() => true)
  625. // @ts-ignore
  626. const repo = vi.fn() as Repository<ApiResource>
  627. // @ts-ignore
  628. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  629. return model === DummyApiModel ? repo : null
  630. })
  631. // @ts-ignore
  632. repo.find = vi.fn((id: number) => entity)
  633. const result = entityManager.isNewInstance(DummyApiModel, 1)
  634. expect(result).toBeTruthy()
  635. })
  636. test('with existing entity', () => {
  637. const entity = new DummyApiModel()
  638. entity.isNew = vi.fn(() => false)
  639. // @ts-ignore
  640. const repo = vi.fn() as Repository<ApiResource>
  641. // @ts-ignore
  642. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  643. return model === DummyApiModel ? repo : null
  644. })
  645. // @ts-ignore
  646. repo.find = vi.fn((id: number) => entity)
  647. const result = entityManager.isNewInstance(DummyApiModel, 1)
  648. expect(result).toBeFalsy()
  649. })
  650. test('non-existing entity', () => {
  651. // @ts-ignore
  652. const repo = vi.fn() as Repository<ApiResource>
  653. // @ts-ignore
  654. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  655. return model === DummyApiModel ? repo : null
  656. })
  657. // @ts-ignore
  658. repo.find = vi.fn((id: number) => null)
  659. console.error = vi.fn()
  660. const result = entityManager.isNewInstance(DummyApiModel, 1)
  661. expect(result).toBeFalsy()
  662. expect(console.error).toHaveBeenCalledWith('dummyModel/1 does not exist!')
  663. })
  664. })
  665. describe('saveInitialState', () => {
  666. test('simple call', () => {
  667. // @ts-ignore
  668. const entity = { id: 1, name: 'bob' } as DummyApiResource
  669. // @ts-ignore
  670. const repo = vi.fn() as Repository<ApiResource>
  671. // @ts-ignore
  672. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  673. return model === DummyApiResource ? repo : null
  674. })
  675. // @ts-ignore
  676. repo.save = vi.fn((record: Element) => null)
  677. // @ts-ignore
  678. entityManager.saveInitialState(DummyApiResource, entity)
  679. expect(repo.save).toHaveBeenCalledWith({ id: '_clone_1', name: 'bob' })
  680. expect(entity.id).toEqual(1)
  681. })
  682. })
  683. describe('getInitialStateOf', () => {
  684. test('with initial state', () => {
  685. // @ts-ignore
  686. const entity = { id: 1, name: 'bob' } as DummyApiResource
  687. // @ts-ignore
  688. const repo = vi.fn() as Repository<ApiResource>
  689. // @ts-ignore
  690. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  691. return model === DummyApiResource ? repo : null
  692. })
  693. // @ts-ignore
  694. repo.find = vi.fn((id: number | string) => {
  695. // @ts-ignore
  696. return { id: 1, name: 'robert' } as DummyApiResource
  697. })
  698. // @ts-ignore
  699. const result = entityManager.getInitialStateOf(
  700. DummyApiResource,
  701. 1,
  702. ) as DummyApiResource
  703. expect(repo.find).toHaveBeenCalledWith('_clone_1')
  704. expect(result.id).toEqual(1)
  705. expect(result.name).toEqual('robert')
  706. })
  707. test('without initial state', () => {
  708. // @ts-ignore
  709. const repo = vi.fn() as Repository<ApiResource>
  710. // @ts-ignore
  711. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  712. return model === DummyApiResource ? repo : null
  713. })
  714. // @ts-ignore
  715. repo.find = vi.fn((id: number | string) => null)
  716. // @ts-ignore
  717. const result = entityManager.getInitialStateOf(
  718. DummyApiResource,
  719. 1,
  720. ) as DummyApiResource
  721. expect(repo.find).toHaveBeenCalledWith('_clone_1')
  722. expect(result).toEqual(null)
  723. })
  724. })
  725. describe('removeTempAfterPersist', () => {
  726. test('simple call', () => {
  727. // @ts-ignore
  728. const entity = new DummyApiResource()
  729. entity.id = 'tmp123'
  730. entity.isNew = vi.fn(() => true)
  731. // @ts-ignore
  732. const repo = vi.fn() as Repository<ApiResource>
  733. // @ts-ignore
  734. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  735. return model === DummyApiResource ? repo : null
  736. })
  737. // @ts-ignore
  738. repo.find = vi.fn((id: number | string) => entity)
  739. // @ts-ignore
  740. repo.destroy = vi.fn()
  741. // @ts-ignore
  742. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123')
  743. expect(repo.destroy).toHaveBeenCalledWith('tmp123')
  744. expect(repo.destroy).toHaveBeenCalledWith('_clone_tmp123')
  745. })
  746. test('entity is not temporary', () => {
  747. // @ts-ignore
  748. const entity = new DummyApiResource()
  749. entity.id = 1
  750. entity.isNew = vi.fn(() => false)
  751. // @ts-ignore
  752. const repo = vi.fn() as Repository<ApiResource>
  753. // @ts-ignore
  754. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  755. return model === DummyApiResource ? repo : null
  756. })
  757. // @ts-ignore
  758. repo.find = vi.fn((id: number | string) => entity)
  759. // @ts-ignore
  760. repo.destroy = vi.fn()
  761. // @ts-ignore
  762. expect(() =>
  763. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123'),
  764. ).toThrowError('Error: Can not remove a non-temporary model instance')
  765. expect(repo.destroy).toHaveBeenCalledTimes(0)
  766. })
  767. test('entity does not exist', () => {
  768. // @ts-ignore
  769. const repo = vi.fn() as Repository<ApiResource>
  770. // @ts-ignore
  771. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  772. return model === DummyApiResource ? repo : null
  773. })
  774. // @ts-ignore
  775. repo.find = vi.fn((id: number | string) => null)
  776. // @ts-ignore
  777. repo.destroy = vi.fn()
  778. console.error = vi.fn()
  779. // @ts-ignore
  780. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123')
  781. expect(repo.destroy).toHaveBeenCalledTimes(0)
  782. expect(console.error).toHaveBeenCalledWith(
  783. 'dummyResource/tmp123 does not exist!',
  784. )
  785. })
  786. })
  787. describe('makeProfileHash', () => {
  788. test('simple call', async () => {
  789. entityManager._getProfileMask = vi.fn(() => {
  790. return { a: 1 }
  791. })
  792. expect(await entityManager.makeProfileHash()).toEqual(
  793. '9f89c740ceb46d7418c924a78ac57941d5e96520',
  794. )
  795. })
  796. })
  797. describe('validateEntity', () => {
  798. test('instance with numeric id', async () => {
  799. entityManager.validateEntity({ id: 123 })
  800. })
  801. test('instance with temp id', async () => {
  802. entityManager.validateEntity({ id: 'tmpazerty' })
  803. })
  804. test('invalid entity', async () => {
  805. expect(() => entityManager.validateEntity({ id: 'azerty' })).toThrowError(
  806. 'Definition error for the entity, did you use the entityManager.newInstance(...) method?\n{"id":"azerty"}',
  807. )
  808. })
  809. })