entityManager.test.ts 26 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
  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. }
  35. const _console: any = {
  36. log: console.log,
  37. warn: console.warn,
  38. error: console.error,
  39. }
  40. vi.mock('~/models/models', async () => {
  41. class MyModel {
  42. static entity = 'my-model'
  43. }
  44. const modelsIndex: Record<string, any> = { 'my-model': () => MyModel }
  45. return {
  46. default: modelsIndex,
  47. }
  48. })
  49. let apiRequestService: ApiRequestService
  50. let entityManager: TestableEntityManager
  51. let repo: Repository<ApiResource>
  52. let _getRepo: (model: typeof ApiResource) => Repository<ApiResource>
  53. let _getProfileMask: () => object
  54. beforeEach(() => {
  55. // @ts-ignore
  56. repo = vi.fn() as Repository<ApiResource>
  57. // @ts-ignore
  58. apiRequestService = vi.fn() as ApiRequestService
  59. _getRepo = vi.fn((model: typeof ApiResource) => repo)
  60. _getProfileMask = vi.fn(() => {
  61. return {}
  62. })
  63. entityManager = new TestableEntityManager(
  64. apiRequestService,
  65. _getRepo,
  66. _getProfileMask,
  67. )
  68. })
  69. afterEach(() => {
  70. // Reset console methods after mock
  71. console.log = _console.log
  72. console.warn = _console.warn
  73. console.error = _console.error
  74. })
  75. describe('getRepository', () => {
  76. test('simple call', () => {
  77. entityManager.getRepository(DummyApiResource)
  78. expect(_getRepo).toHaveBeenCalledWith(DummyApiResource)
  79. })
  80. })
  81. describe('getQuery', () => {
  82. test('simple call', () => {
  83. // @ts-ignore
  84. const repo = vi.fn()
  85. // @ts-ignore
  86. entityManager.getRepository = vi.fn(() => repo)
  87. const query = vi.fn()
  88. // @ts-ignore
  89. repo.where = vi.fn((method) => {
  90. // Query method is supposed to exclude NaN ids values
  91. expect(method({ id: 123 })).toBeTruthy()
  92. expect(method({ id: 'abc' })).toBeFalsy()
  93. return query
  94. })
  95. const result = entityManager.getQuery(DummyApiResource)
  96. expect(result).toEqual(query)
  97. })
  98. })
  99. describe('cast', () => {
  100. test('simple cast', () => {
  101. // @ts-ignore
  102. const result = entityManager.cast(DummyApiResource, { id: 1 })
  103. expect(result instanceof DummyApiResource).toEqual(true)
  104. })
  105. })
  106. describe('getModelFor', () => {
  107. test('simple call', async () => {
  108. const model = await entityManager.getModelFor('my-model')
  109. expect(model!.entity).toEqual('my-model')
  110. })
  111. test('non existing model', async () => {
  112. expect(
  113. async () => await entityManager.getModelFor('non-existing-model'),
  114. ).rejects.toThrowError(
  115. "No model found for entity name 'non-existing-model'",
  116. )
  117. })
  118. })
  119. describe('getModelFromIri', () => {
  120. test('simple call', async () => {
  121. // @ts-ignore
  122. entityManager.getModelFor = vi.fn(
  123. async (entityName: string) => DummyApiResource,
  124. )
  125. // @ts-ignore
  126. const result = await entityManager.getModelFromIri('/api/dummy/123')
  127. console.log(result)
  128. expect(result).toEqual(DummyApiResource)
  129. })
  130. test('invalide Iri', () => {
  131. expect(
  132. async () => await entityManager.getModelFromIri('/invalid'),
  133. ).rejects.toThrowError('cannot parse the IRI')
  134. })
  135. })
  136. describe('newInstance', () => {
  137. test('simple call', () => {
  138. const properties = { id: 1 }
  139. // @ts-ignore
  140. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  141. return model === DummyApiResource ? repo : null
  142. })
  143. // @ts-ignore
  144. const entity = new DummyApiResource(properties)
  145. // @ts-ignore
  146. repo.make = vi.fn((properties: object) => {
  147. // @ts-ignore
  148. entity.id = properties.id
  149. return entity
  150. })
  151. // @ts-ignore
  152. entityManager.save = vi.fn(
  153. (model: typeof ApiResource, entity: ApiResource, permanent: boolean) =>
  154. entity,
  155. )
  156. const result = entityManager.newInstance(DummyApiResource, properties)
  157. expect(repo.make).toHaveBeenCalledWith(properties)
  158. expect(entityManager.save).toHaveBeenCalledWith(
  159. DummyApiResource,
  160. entity,
  161. true,
  162. )
  163. expect(result.id).toEqual(properties.id)
  164. })
  165. test('with no id provided', () => {
  166. const properties = { name: 'bob' }
  167. // @ts-ignore
  168. const repo = vi.fn() as Repository<ApiResource>
  169. // @ts-ignore
  170. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  171. return model === DummyApiResource ? repo : null
  172. })
  173. // @ts-ignore
  174. entityManager.saveInitialState = vi.fn(
  175. (model: typeof ApiResource, entity: ApiResource) => null,
  176. )
  177. // @ts-ignore
  178. const entity = new DummyApiResource(properties)
  179. // @ts-ignore
  180. repo.make = vi.fn((properties: object) => {
  181. // @ts-ignore
  182. entity.name = properties.name
  183. return entity
  184. })
  185. // @ts-ignore
  186. repo.save = vi.fn((record: Element) => entity)
  187. const result = entityManager.newInstance(DummyApiResource, properties)
  188. expect(result.id, "id is 'tmp' followed by a valid uuid-V4").toMatch(
  189. /tmp[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}/,
  190. )
  191. expect(result.name).toEqual(properties.name)
  192. })
  193. })
  194. describe('save', () => {
  195. test('simple call', () => {
  196. // @ts-ignore
  197. const repo = vi.fn() as Repository<ApiResource>
  198. // @ts-ignore
  199. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  200. return model === DummyApiResource ? repo : null
  201. })
  202. // @ts-ignore
  203. repo.save = vi.fn((record: Element) => entity)
  204. const entity = new DummyApiResource()
  205. entityManager.save(DummyApiResource, 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. })
  338. test('with a parent', async () => {
  339. const collection = {
  340. '@type': 'hydra:Collection',
  341. 'hydra:totalItems': 3,
  342. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  343. }
  344. // @ts-ignore
  345. apiRequestService.get = vi.fn(async (url: string) => collection)
  346. const piniaOrmQuery = vi.fn()
  347. // @ts-ignore
  348. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  349. // @ts-ignore
  350. entityManager.newInstance = vi.fn(
  351. (model: typeof ApiResource, props: object) => {
  352. return new DummyApiResource(props)
  353. },
  354. )
  355. const parent = new DummyApiModel()
  356. parent.id = 100
  357. parent.entity = 'dummyModel' // TODO: je ne comprend pas pqoi cette ligne est nécessaire...
  358. await entityManager.fetchCollection(DummyApiResource, parent)
  359. expect(apiRequestService.get).toHaveBeenCalledWith(
  360. 'api/dummyModel/100/dummyResource',
  361. )
  362. })
  363. test('with a query', async () => {
  364. const collection = {
  365. '@type': 'hydra:Collection',
  366. 'hydra:totalItems': 3,
  367. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  368. }
  369. const query = vi.fn()
  370. // @ts-ignore
  371. query.getUrlQuery = vi.fn(() => 'foo=bar')
  372. // @ts-ignore
  373. query.applyToPiniaOrmQuery = vi.fn((q) => q)
  374. const piniaOrmQuery = vi.fn()
  375. // @ts-ignore
  376. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  377. // @ts-ignore
  378. apiRequestService.get = vi.fn(async (url: string) => collection)
  379. // @ts-ignore
  380. entityManager.newInstance = vi.fn(
  381. (model: typeof ApiResource, props: object) => {
  382. return new DummyApiResource(props)
  383. },
  384. )
  385. // @ts-ignore
  386. await entityManager.fetchCollection(DummyApiResource, null, query)
  387. expect(apiRequestService.get).toHaveBeenCalledWith(
  388. 'api/dummyResource?foo=bar',
  389. )
  390. // @ts-ignore
  391. expect(query.getUrlQuery).toHaveBeenCalledWith()
  392. // @ts-ignore
  393. expect(query.applyToPiniaOrmQuery).toHaveBeenCalledWith(piniaOrmQuery)
  394. })
  395. })
  396. describe('persist', () => {
  397. test('new entity (POST)', async () => {
  398. const instance = new DummyApiModel({ id: 'tmp1', name: 'bob' })
  399. instance.isNew = vi.fn(() => true)
  400. // @ts-ignore
  401. instance.$toJson = vi.fn(() => {
  402. return { id: 'tmp1', name: 'bob' }
  403. })
  404. // @ts-ignore
  405. entityManager.cast = vi.fn(
  406. (model: typeof ApiResource, entity: ApiResource): ApiResource => entity,
  407. )
  408. const response = { id: 1, name: 'bob' }
  409. // @ts-ignore
  410. apiRequestService.post = vi.fn((url, data) => response)
  411. // @ts-ignore
  412. entityManager.newInstance = vi.fn((model, response) => {
  413. const newEntity = new DummyApiModel(response)
  414. // @ts-ignore
  415. newEntity.id = response.id
  416. // @ts-ignore
  417. newEntity.name = response.name
  418. return newEntity
  419. })
  420. // @ts-ignore
  421. entityManager.removeTempAfterPersist = vi.fn()
  422. entityManager.makeProfileHash = vi.fn(async () => await 'azerty')
  423. const result = await entityManager.persist(DummyApiModel, instance)
  424. // temp id should have been purged from the posted data
  425. expect(apiRequestService.post).toHaveBeenCalledWith(
  426. 'api/dummyModel',
  427. {
  428. name: 'bob',
  429. },
  430. null,
  431. { profileHash: 'azerty' },
  432. )
  433. expect(entityManager.newInstance).toHaveBeenCalledWith(
  434. DummyApiModel,
  435. response,
  436. )
  437. // @ts-ignore
  438. expect(entityManager.removeTempAfterPersist).toHaveBeenCalledWith(
  439. DummyApiModel,
  440. instance.id,
  441. )
  442. // @ts-ignore
  443. expect(entityManager.makeProfileHash).toHaveBeenCalledTimes(1)
  444. expect(result.id).toEqual(1)
  445. expect(result.name).toEqual('bob')
  446. })
  447. test('existing entity (PUT)', async () => {
  448. const props = { id: 1, name: 'bob' }
  449. const entity = new DummyApiModel(props)
  450. entity.id = 1
  451. entity.isNew = vi.fn(() => false)
  452. // @ts-ignore
  453. entity.$toJson = vi.fn(() => props)
  454. // TODO: attendre de voir si cet appel est nécessaire dans l'entity manager
  455. // entityManager.cast = vi.fn((model: typeof ApiResource, entity: ApiResource): ApiResource => entity)
  456. // @ts-ignore
  457. apiRequestService.put = vi.fn((url, data) => props)
  458. // @ts-ignore
  459. entityManager.newInstance = vi.fn((model, response) => {
  460. const newEntity = new DummyApiModel(response)
  461. // @ts-ignore
  462. newEntity.id = response.id
  463. // @ts-ignore
  464. newEntity.name = response.name
  465. return newEntity
  466. })
  467. // @ts-ignore
  468. entityManager.removeTempAfterPersist = vi.fn()
  469. entityManager.makeProfileHash = vi.fn(async () => await 'azerty')
  470. const result = await entityManager.persist(DummyApiModel, entity)
  471. expect(apiRequestService.put).toHaveBeenCalledWith(
  472. 'api/dummyModel/1',
  473. {
  474. id: 1,
  475. name: 'bob',
  476. },
  477. null,
  478. { profileHash: 'azerty' },
  479. )
  480. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiModel, props)
  481. // @ts-ignore
  482. expect(entityManager.removeTempAfterPersist).toHaveBeenCalledTimes(0)
  483. // @ts-ignore
  484. expect(entityManager.makeProfileHash).toHaveBeenCalledTimes(1)
  485. expect(result.id).toEqual(1)
  486. expect(result.name).toEqual('bob')
  487. })
  488. })
  489. describe('patch', () => {
  490. test('simple call', async () => {
  491. const props = { id: 1, name: 'bobby' }
  492. // @ts-ignore
  493. apiRequestService.put = vi.fn((url, data) => props)
  494. // @ts-ignore
  495. entityManager.newInstance = vi.fn((model, response) => {
  496. const newEntity = new DummyApiModel(response)
  497. // @ts-ignore
  498. newEntity.id = response.id
  499. // @ts-ignore
  500. newEntity.name = response.name
  501. return newEntity
  502. })
  503. const result = await entityManager.patch(DummyApiModel, 1, {
  504. name: 'bobby',
  505. })
  506. expect(apiRequestService.put).toHaveBeenCalledWith(
  507. 'api/dummyModel/1',
  508. '{"name":"bobby"}',
  509. )
  510. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiModel, {
  511. id: 1,
  512. name: 'bobby',
  513. })
  514. expect(result.id).toEqual(1)
  515. expect(result.name).toEqual('bobby')
  516. })
  517. })
  518. describe('delete', () => {
  519. test('delete non persisted entity', () => {
  520. const entity = new DummyApiModel()
  521. entity.isNew = vi.fn(() => true)
  522. entity.id = 'tmp123'
  523. // @ts-ignore
  524. const repo = vi.fn() as Repository<ApiResource>
  525. // @ts-ignore
  526. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  527. return model === DummyApiModel ? repo : null
  528. })
  529. apiRequestService.delete = vi.fn()
  530. // @ts-ignore
  531. repo.destroy = vi.fn((id: number) => null)
  532. entityManager.delete(DummyApiModel, entity)
  533. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiModel)
  534. expect(apiRequestService.delete).toHaveBeenCalledTimes(0)
  535. expect(repo.destroy).toHaveBeenCalledWith('tmp123')
  536. })
  537. test('delete persisted entity', async () => {
  538. const entity = new DummyApiModel()
  539. entity.isNew = vi.fn(() => false)
  540. entity.id = 1
  541. // @ts-ignore
  542. const repo = vi.fn() as Repository<ApiResource>
  543. // @ts-ignore
  544. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  545. return model === DummyApiModel ? repo : null
  546. })
  547. // @ts-ignore
  548. apiRequestService.delete = vi.fn((id: number) => null)
  549. // @ts-ignore
  550. repo.destroy = vi.fn((id: number) => null)
  551. await entityManager.delete(DummyApiModel, entity)
  552. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiModel)
  553. expect(apiRequestService.delete).toHaveBeenCalledWith('api/dummyModel/1')
  554. expect(repo.destroy).toHaveBeenCalledWith(1)
  555. })
  556. })
  557. describe('reset', () => {
  558. test('simple call', () => {
  559. const entity = new DummyApiModel()
  560. entity.id = 1
  561. entity.name = 'paul'
  562. const initialEntity = new DummyApiModel()
  563. initialEntity.id = 1
  564. initialEntity.name = 'serges'
  565. // @ts-ignore
  566. entityManager.getInitialStateOf = vi.fn(
  567. (model: typeof ApiResource, id: string | number) => initialEntity,
  568. )
  569. // @ts-ignore
  570. const repo = vi.fn() as Repository<ApiResource>
  571. // @ts-ignore
  572. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  573. return model === DummyApiModel ? repo : null
  574. })
  575. // @ts-ignore
  576. repo.save = vi.fn((data: any) => null)
  577. const result = entityManager.reset(DummyApiModel, entity)
  578. // @ts-ignore
  579. expect(entityManager.getInitialStateOf).toHaveBeenCalledWith(
  580. DummyApiModel,
  581. 1,
  582. )
  583. expect(repo.save).toHaveBeenCalledWith(initialEntity)
  584. expect(result).toEqual(initialEntity)
  585. })
  586. test('no initial state stored', () => {
  587. const entity = new DummyApiModel()
  588. entity.id = 1
  589. // @ts-ignore
  590. entityManager.getInitialStateOf = vi.fn(
  591. (model: typeof ApiResource, id: string | number) => null,
  592. )
  593. expect(() => entityManager.reset(DummyApiModel, entity)).toThrowError(
  594. 'no initial state recorded for this object - abort [dummyModel/1]',
  595. )
  596. })
  597. })
  598. describe('flush', () => {
  599. test('simple call', () => {
  600. // @ts-ignore
  601. const repo = vi.fn() as Repository<ApiResource>
  602. // @ts-ignore
  603. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  604. return model === DummyApiModel ? repo : null
  605. })
  606. repo.flush = vi.fn()
  607. entityManager.flush(DummyApiModel)
  608. expect(repo.flush).toHaveBeenCalled()
  609. })
  610. })
  611. describe('isNewEntity', () => {
  612. test('with new entity', () => {
  613. const entity = new DummyApiModel()
  614. entity.isNew = vi.fn(() => true)
  615. // @ts-ignore
  616. const repo = vi.fn() as Repository<ApiResource>
  617. // @ts-ignore
  618. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  619. return model === DummyApiModel ? repo : null
  620. })
  621. // @ts-ignore
  622. repo.find = vi.fn((id: number) => entity)
  623. const result = entityManager.isNewInstance(DummyApiModel, 1)
  624. expect(result).toBeTruthy()
  625. })
  626. test('with existing entity', () => {
  627. const entity = new DummyApiModel()
  628. entity.isNew = vi.fn(() => false)
  629. // @ts-ignore
  630. const repo = vi.fn() as Repository<ApiResource>
  631. // @ts-ignore
  632. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  633. return model === DummyApiModel ? repo : null
  634. })
  635. // @ts-ignore
  636. repo.find = vi.fn((id: number) => entity)
  637. const result = entityManager.isNewInstance(DummyApiModel, 1)
  638. expect(result).toBeFalsy()
  639. })
  640. test('non-existing entity', () => {
  641. // @ts-ignore
  642. const repo = vi.fn() as Repository<ApiResource>
  643. // @ts-ignore
  644. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  645. return model === DummyApiModel ? repo : null
  646. })
  647. // @ts-ignore
  648. repo.find = vi.fn((id: number) => null)
  649. console.error = vi.fn()
  650. const result = entityManager.isNewInstance(DummyApiModel, 1)
  651. expect(result).toBeFalsy()
  652. expect(console.error).toHaveBeenCalledWith('dummyModel/1 does not exist!')
  653. })
  654. })
  655. describe('saveInitialState', () => {
  656. test('simple call', () => {
  657. // @ts-ignore
  658. const entity = { id: 1, name: 'bob' } as DummyApiResource
  659. // @ts-ignore
  660. const repo = vi.fn() as Repository<ApiResource>
  661. // @ts-ignore
  662. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  663. return model === DummyApiResource ? repo : null
  664. })
  665. // @ts-ignore
  666. repo.save = vi.fn((record: Element) => null)
  667. // @ts-ignore
  668. entityManager.saveInitialState(DummyApiResource, entity)
  669. expect(repo.save).toHaveBeenCalledWith({ id: '_clone_1', name: 'bob' })
  670. expect(entity.id).toEqual(1)
  671. })
  672. })
  673. describe('getInitialStateOf', () => {
  674. test('with initial state', () => {
  675. // @ts-ignore
  676. const entity = { id: 1, name: 'bob' } as DummyApiResource
  677. // @ts-ignore
  678. const repo = vi.fn() as Repository<ApiResource>
  679. // @ts-ignore
  680. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  681. return model === DummyApiResource ? repo : null
  682. })
  683. // @ts-ignore
  684. repo.find = vi.fn((id: number | string) => {
  685. // @ts-ignore
  686. return { id: 1, name: 'robert' } as DummyApiResource
  687. })
  688. // @ts-ignore
  689. const result = entityManager.getInitialStateOf(
  690. DummyApiResource,
  691. 1,
  692. ) as DummyApiResource
  693. expect(repo.find).toHaveBeenCalledWith('_clone_1')
  694. expect(result.id).toEqual(1)
  695. expect(result.name).toEqual('robert')
  696. })
  697. test('without initial state', () => {
  698. // @ts-ignore
  699. const repo = vi.fn() as Repository<ApiResource>
  700. // @ts-ignore
  701. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  702. return model === DummyApiResource ? repo : null
  703. })
  704. // @ts-ignore
  705. repo.find = vi.fn((id: number | string) => null)
  706. // @ts-ignore
  707. const result = entityManager.getInitialStateOf(
  708. DummyApiResource,
  709. 1,
  710. ) as DummyApiResource
  711. expect(repo.find).toHaveBeenCalledWith('_clone_1')
  712. expect(result).toEqual(null)
  713. })
  714. })
  715. describe('removeTempAfterPersist', () => {
  716. test('simple call', () => {
  717. // @ts-ignore
  718. const entity = new DummyApiResource()
  719. entity.id = 'tmp123'
  720. entity.isNew = vi.fn(() => true)
  721. // @ts-ignore
  722. const repo = vi.fn() as Repository<ApiResource>
  723. // @ts-ignore
  724. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  725. return model === DummyApiResource ? repo : null
  726. })
  727. // @ts-ignore
  728. repo.find = vi.fn((id: number | string) => entity)
  729. // @ts-ignore
  730. repo.destroy = vi.fn()
  731. // @ts-ignore
  732. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123')
  733. expect(repo.destroy).toHaveBeenCalledWith('tmp123')
  734. expect(repo.destroy).toHaveBeenCalledWith('_clone_tmp123')
  735. })
  736. test('entity is not temporary', () => {
  737. // @ts-ignore
  738. const entity = new DummyApiResource()
  739. entity.id = 1
  740. entity.isNew = vi.fn(() => false)
  741. // @ts-ignore
  742. const repo = vi.fn() as Repository<ApiResource>
  743. // @ts-ignore
  744. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  745. return model === DummyApiResource ? repo : null
  746. })
  747. // @ts-ignore
  748. repo.find = vi.fn((id: number | string) => entity)
  749. // @ts-ignore
  750. repo.destroy = vi.fn()
  751. // @ts-ignore
  752. expect(() =>
  753. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123'),
  754. ).toThrowError('Error: Can not remove a non-temporary model instance')
  755. expect(repo.destroy).toHaveBeenCalledTimes(0)
  756. })
  757. test('entity does not exist', () => {
  758. // @ts-ignore
  759. const repo = vi.fn() as Repository<ApiResource>
  760. // @ts-ignore
  761. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  762. return model === DummyApiResource ? repo : null
  763. })
  764. // @ts-ignore
  765. repo.find = vi.fn((id: number | string) => null)
  766. // @ts-ignore
  767. repo.destroy = vi.fn()
  768. console.error = vi.fn()
  769. // @ts-ignore
  770. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123')
  771. expect(repo.destroy).toHaveBeenCalledTimes(0)
  772. expect(console.error).toHaveBeenCalledWith(
  773. 'dummyResource/tmp123 does not exist!',
  774. )
  775. })
  776. })
  777. describe('makeProfileHash', () => {
  778. test('simple call', async () => {
  779. entityManager._getProfileMask = vi.fn(() => {
  780. return { a: 1 }
  781. })
  782. expect(await entityManager.makeProfileHash()).toEqual(
  783. '9f89c740ceb46d7418c924a78ac57941d5e96520',
  784. )
  785. })
  786. })