entityManager.test.ts 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  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. (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(entity, true)
  159. expect(result.id).toEqual(properties.id)
  160. })
  161. test('with no id provided', () => {
  162. const properties = { name: 'bob' }
  163. // @ts-ignore
  164. const repo = vi.fn() as Repository<ApiResource>
  165. // @ts-ignore
  166. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  167. return model === DummyApiResource ? repo : null
  168. })
  169. // @ts-ignore
  170. entityManager.saveInitialState = vi.fn(
  171. (model: typeof ApiResource, entity: ApiResource) => null,
  172. )
  173. // @ts-ignore
  174. const entity = new DummyApiResource(properties)
  175. // @ts-ignore
  176. repo.make = vi.fn((properties: object) => {
  177. // @ts-ignore
  178. entity.name = properties.name
  179. return entity
  180. })
  181. // @ts-ignore
  182. repo.save = vi.fn((record: Element) => entity)
  183. const result = entityManager.newInstance(DummyApiResource, properties)
  184. expect(result.id, "id is 'tmp' followed by a valid uuid-V4").toMatch(
  185. /tmp[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}/,
  186. )
  187. expect(result.name).toEqual(properties.name)
  188. })
  189. })
  190. describe('save', () => {
  191. test('simple call', () => {
  192. // @ts-ignore
  193. const repo = vi.fn() as Repository<ApiResource>
  194. // @ts-ignore
  195. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  196. return model === DummyApiResource ? repo : null
  197. })
  198. // @ts-ignore
  199. repo.save = vi.fn((record: Element) => entity)
  200. const entity = new DummyApiResource({ id: 1 })
  201. entityManager.save(entity)
  202. expect(repo.save).toHaveBeenCalledWith(entity)
  203. })
  204. })
  205. describe('find', () => {
  206. test('simple call', () => {
  207. // @ts-ignore
  208. const repo = vi.fn() as Repository<ApiResource>
  209. // @ts-ignore
  210. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  211. return model === DummyApiResource ? repo : null
  212. })
  213. const entity = new DummyApiResource({ id: 1 })
  214. // @ts-ignore
  215. repo.find = vi.fn((id: string | number) => entity)
  216. entityManager.find(DummyApiResource, 1)
  217. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiResource)
  218. expect(repo.find).toHaveBeenCalledWith(1)
  219. })
  220. })
  221. describe('fetch', () => {
  222. test('not in store, no force refresh', async () => {
  223. const properties = { id: 1 }
  224. const entity = new DummyApiResource({ id: 1 })
  225. // @ts-ignore
  226. entityManager.find = vi.fn(
  227. (model: typeof ApiResource, id: number) => undefined,
  228. )
  229. // @ts-ignore
  230. apiRequestService.get = vi.fn(async (url: string) => properties)
  231. // @ts-ignore
  232. entityManager.newInstance = vi.fn(
  233. (model: typeof ApiResource, props: object) => entity,
  234. )
  235. const result = await entityManager.fetch(DummyApiResource, 1)
  236. expect(entityManager.find).toHaveBeenCalledWith(DummyApiResource, 1)
  237. expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource/1')
  238. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  239. id: 1,
  240. name: null,
  241. _model: undefined,
  242. })
  243. expect(result).toEqual(entity)
  244. })
  245. test('in store, no force refresh', async () => {
  246. const properties = { id: 1 }
  247. const entity = new DummyApiResource({ id: 1 })
  248. // @ts-ignore
  249. entityManager.find = vi.fn(
  250. (model: typeof ApiResource, id: number) => entity,
  251. )
  252. const result = await entityManager.fetch(DummyApiResource, 1)
  253. expect(entityManager.find).toHaveBeenCalledWith(DummyApiResource, 1)
  254. expect(result).toEqual(entity)
  255. })
  256. test('in store, but with force refresh', async () => {
  257. const properties = { id: 1 }
  258. const entity = new DummyApiResource({ id: 1 })
  259. // @ts-ignore
  260. entityManager.find = vi.fn(
  261. (model: typeof ApiResource, id: number) => undefined,
  262. )
  263. // @ts-ignore
  264. apiRequestService.get = vi.fn(async (url: string) => properties)
  265. // @ts-ignore
  266. entityManager.newInstance = vi.fn(
  267. (model: typeof ApiResource, props: object) => entity,
  268. )
  269. const result = await entityManager.fetch(DummyApiResource, 1, true)
  270. expect(entityManager.find).toHaveBeenCalledTimes(0)
  271. expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource/1')
  272. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  273. id: 1,
  274. name: null,
  275. _model: undefined,
  276. })
  277. expect(result).toEqual(entity)
  278. })
  279. })
  280. describe('fetchCollection', () => {
  281. test('simple call', async () => {
  282. const collection = {
  283. '@type': 'hydra:Collection',
  284. 'hydra:totalItems': 3,
  285. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  286. }
  287. // @ts-ignore
  288. apiRequestService.get = vi.fn(async (url: string) => collection)
  289. // @ts-ignore
  290. entityManager.newInstance = vi.fn(
  291. (model: typeof ApiResource, props: object) => {
  292. return new DummyApiResource(props)
  293. },
  294. )
  295. const piniaOrmQuery = vi.fn()
  296. // @ts-ignore
  297. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  298. const result = await entityManager.fetchCollection(DummyApiResource, null)
  299. expect(apiRequestService.get).toHaveBeenCalledWith('api/dummyResource')
  300. expect(entityManager.newInstance).toHaveBeenCalledTimes(3)
  301. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  302. id: 1,
  303. name: null,
  304. _model: undefined,
  305. })
  306. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  307. id: 2,
  308. name: null,
  309. _model: undefined,
  310. })
  311. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiResource, {
  312. id: 3,
  313. name: null,
  314. _model: undefined,
  315. })
  316. // @ts-ignore
  317. piniaOrmQuery.get = vi.fn(() => [
  318. new DummyApiResource({ id: 1 }),
  319. new DummyApiResource({ id: 2 }),
  320. new DummyApiResource({ id: 3 }),
  321. ])
  322. expect(result.value.items).toEqual([
  323. new DummyApiResource({ id: 1 }),
  324. new DummyApiResource({ id: 2 }),
  325. new DummyApiResource({ id: 3 }),
  326. ])
  327. expect(result.value.pagination, 'default pagination').toEqual({
  328. first: 1,
  329. last: 1,
  330. next: undefined,
  331. previous: undefined,
  332. })
  333. })
  334. test('with a parent', async () => {
  335. const collection = {
  336. '@type': 'hydra:Collection',
  337. 'hydra:totalItems': 3,
  338. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  339. }
  340. // @ts-ignore
  341. apiRequestService.get = vi.fn(async (url: string) => collection)
  342. const piniaOrmQuery = vi.fn()
  343. // @ts-ignore
  344. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  345. // @ts-ignore
  346. entityManager.newInstance = vi.fn(
  347. (model: typeof ApiResource, props: object) => {
  348. return new DummyApiResource(props)
  349. },
  350. )
  351. const parent = new DummyApiModel()
  352. parent.id = 100
  353. parent.entity = 'dummyModel' // TODO: je ne comprend pas pqoi cette ligne est nécessaire...
  354. await entityManager.fetchCollection(DummyApiResource, parent)
  355. expect(apiRequestService.get).toHaveBeenCalledWith(
  356. 'api/dummyModel/100/dummyResource',
  357. )
  358. })
  359. test('with a query', async () => {
  360. const collection = {
  361. '@type': 'hydra:Collection',
  362. 'hydra:totalItems': 3,
  363. 'hydra:member': [{ id: 1 }, { id: 2 }, { id: 3 }],
  364. }
  365. const query = vi.fn()
  366. // @ts-ignore
  367. query.getUrlQuery = vi.fn(() => 'foo=bar')
  368. // @ts-ignore
  369. query.applyToPiniaOrmQuery = vi.fn((q) => q)
  370. const piniaOrmQuery = vi.fn()
  371. // @ts-ignore
  372. entityManager.getQuery = vi.fn((model: typeof ApiResource) => piniaOrmQuery)
  373. // @ts-ignore
  374. apiRequestService.get = vi.fn(async (url: string) => collection)
  375. // @ts-ignore
  376. entityManager.newInstance = vi.fn(
  377. (model: typeof ApiResource, props: object) => {
  378. return new DummyApiResource(props)
  379. },
  380. )
  381. // @ts-ignore
  382. await entityManager.fetchCollection(DummyApiResource, null, query)
  383. expect(apiRequestService.get).toHaveBeenCalledWith(
  384. 'api/dummyResource?foo=bar',
  385. )
  386. // @ts-ignore
  387. expect(query.getUrlQuery).toHaveBeenCalledWith()
  388. // @ts-ignore
  389. expect(query.applyToPiniaOrmQuery).toHaveBeenCalledWith(piniaOrmQuery)
  390. })
  391. })
  392. describe('persist', () => {
  393. test('new entity (POST)', async () => {
  394. const instance = new DummyApiModel({ id: 'tmp1', name: 'bob' })
  395. instance.isNew = vi.fn(() => true)
  396. // @ts-ignore
  397. instance.$toJson = vi.fn(() => {
  398. return { id: 'tmp1', name: 'bob' }
  399. })
  400. // @ts-ignore
  401. entityManager.cast = vi.fn(
  402. (model: typeof ApiResource, entity: ApiResource): ApiResource => entity,
  403. )
  404. const response = { id: 1, name: 'bob' }
  405. // @ts-ignore
  406. apiRequestService.post = vi.fn((url, data) => response)
  407. // @ts-ignore
  408. entityManager.newInstance = vi.fn((model, response) => {
  409. const newEntity = new DummyApiModel(response)
  410. // @ts-ignore
  411. newEntity.id = response.id
  412. // @ts-ignore
  413. newEntity.name = response.name
  414. return newEntity
  415. })
  416. // @ts-ignore
  417. entityManager.removeTempAfterPersist = vi.fn()
  418. entityManager.makeProfileHash = vi.fn(async () => await 'azerty')
  419. const result = await entityManager.persist(instance)
  420. // temp id should have been purged from the posted data
  421. expect(apiRequestService.post).toHaveBeenCalledWith(
  422. 'api/dummyModel',
  423. {
  424. name: 'bob',
  425. },
  426. null,
  427. { profileHash: 'azerty' },
  428. )
  429. expect(entityManager.newInstance).toHaveBeenCalledWith(
  430. DummyApiModel,
  431. response,
  432. )
  433. // @ts-ignore
  434. expect(entityManager.removeTempAfterPersist).toHaveBeenCalledWith(
  435. DummyApiModel,
  436. instance.id,
  437. )
  438. // @ts-ignore
  439. expect(entityManager.makeProfileHash).toHaveBeenCalledTimes(1)
  440. expect(result.id).toEqual(1)
  441. expect(result.name).toEqual('bob')
  442. })
  443. test('existing entity (PUT)', async () => {
  444. const props = { id: 1, name: 'bob' }
  445. const entity = new DummyApiModel(props)
  446. entity.id = 1
  447. entity.isNew = vi.fn(() => false)
  448. // @ts-ignore
  449. entity.$toJson = vi.fn(() => props)
  450. // TODO: attendre de voir si cet appel est nécessaire dans l'entity manager
  451. // entityManager.cast = vi.fn((model: typeof ApiResource, entity: ApiResource): ApiResource => entity)
  452. // @ts-ignore
  453. apiRequestService.put = vi.fn((url, data) => props)
  454. // @ts-ignore
  455. entityManager.newInstance = vi.fn((model, response) => {
  456. const newEntity = new DummyApiModel(response)
  457. // @ts-ignore
  458. newEntity.id = response.id
  459. // @ts-ignore
  460. newEntity.name = response.name
  461. return newEntity
  462. })
  463. // @ts-ignore
  464. entityManager.removeTempAfterPersist = vi.fn()
  465. entityManager.makeProfileHash = vi.fn(async () => await 'azerty')
  466. const result = await entityManager.persist(entity)
  467. expect(apiRequestService.put).toHaveBeenCalledWith(
  468. 'api/dummyModel/1',
  469. {
  470. id: 1,
  471. name: 'bob',
  472. },
  473. null,
  474. { profileHash: 'azerty' },
  475. )
  476. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiModel, props)
  477. // @ts-ignore
  478. expect(entityManager.removeTempAfterPersist).toHaveBeenCalledTimes(0)
  479. // @ts-ignore
  480. expect(entityManager.makeProfileHash).toHaveBeenCalledTimes(1)
  481. expect(result.id).toEqual(1)
  482. expect(result.name).toEqual('bob')
  483. })
  484. })
  485. describe('patch', () => {
  486. test('simple call', async () => {
  487. const props = { id: 1, name: 'bobby' }
  488. // @ts-ignore
  489. apiRequestService.put = vi.fn((url, data) => props)
  490. // @ts-ignore
  491. entityManager.newInstance = vi.fn((model, response) => {
  492. const newEntity = new DummyApiModel(response)
  493. // @ts-ignore
  494. newEntity.id = response.id
  495. // @ts-ignore
  496. newEntity.name = response.name
  497. return newEntity
  498. })
  499. const result = await entityManager.patch(DummyApiModel, 1, {
  500. name: 'bobby',
  501. })
  502. expect(apiRequestService.put).toHaveBeenCalledWith(
  503. 'api/dummyModel/1',
  504. '{"name":"bobby"}',
  505. )
  506. expect(entityManager.newInstance).toHaveBeenCalledWith(DummyApiModel, {
  507. id: 1,
  508. name: 'bobby',
  509. })
  510. expect(result.id).toEqual(1)
  511. expect(result.name).toEqual('bobby')
  512. })
  513. })
  514. describe('delete', () => {
  515. test('delete non persisted entity', () => {
  516. const entity = new DummyApiModel()
  517. entity.isNew = vi.fn(() => true)
  518. entity.id = 'tmp123'
  519. // @ts-ignore
  520. const repo = vi.fn() as Repository<ApiResource>
  521. // @ts-ignore
  522. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  523. return model === DummyApiModel ? repo : null
  524. })
  525. apiRequestService.delete = vi.fn()
  526. // @ts-ignore
  527. repo.destroy = vi.fn((id: number) => null)
  528. entityManager.delete(entity)
  529. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiModel)
  530. expect(apiRequestService.delete).toHaveBeenCalledTimes(0)
  531. expect(repo.destroy).toHaveBeenCalledWith('tmp123')
  532. })
  533. test('delete persisted entity', async () => {
  534. const entity = new DummyApiModel()
  535. entity.isNew = vi.fn(() => false)
  536. entity.id = 1
  537. // @ts-ignore
  538. const repo = vi.fn() as Repository<ApiResource>
  539. // @ts-ignore
  540. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  541. return model === DummyApiModel ? repo : null
  542. })
  543. // @ts-ignore
  544. apiRequestService.delete = vi.fn((id: number) => null)
  545. // @ts-ignore
  546. repo.destroy = vi.fn((id: number) => null)
  547. await entityManager.delete(entity)
  548. expect(entityManager.getRepository).toHaveBeenCalledWith(DummyApiModel)
  549. expect(apiRequestService.delete).toHaveBeenCalledWith('api/dummyModel/1')
  550. expect(repo.destroy).toHaveBeenCalledWith(1)
  551. })
  552. })
  553. describe('reset', () => {
  554. test('simple call', () => {
  555. const entity = new DummyApiModel()
  556. entity.id = 1
  557. entity.name = 'paul'
  558. const initialEntity = new DummyApiModel()
  559. initialEntity.id = 1
  560. initialEntity.name = 'serges'
  561. // @ts-ignore
  562. entityManager.getInitialStateOf = vi.fn(
  563. (model: typeof ApiResource, id: string | number) => initialEntity,
  564. )
  565. // @ts-ignore
  566. const repo = vi.fn() as Repository<ApiResource>
  567. // @ts-ignore
  568. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  569. return model === DummyApiModel ? repo : null
  570. })
  571. // @ts-ignore
  572. repo.save = vi.fn((data: any) => null)
  573. const result = entityManager.reset(DummyApiModel, entity)
  574. // @ts-ignore
  575. expect(entityManager.getInitialStateOf).toHaveBeenCalledWith(
  576. DummyApiModel,
  577. 1,
  578. )
  579. expect(repo.save).toHaveBeenCalledWith(initialEntity)
  580. expect(result).toEqual(initialEntity)
  581. })
  582. test('no initial state stored', () => {
  583. const entity = new DummyApiModel()
  584. entity.id = 1
  585. // @ts-ignore
  586. entityManager.getInitialStateOf = vi.fn(
  587. (model: typeof ApiResource, id: string | number) => null,
  588. )
  589. expect(() => entityManager.reset(DummyApiModel, entity)).toThrowError(
  590. 'no initial state recorded for this object - abort [dummyModel/1]',
  591. )
  592. })
  593. })
  594. describe('flush', () => {
  595. test('simple call', () => {
  596. // @ts-ignore
  597. const repo = vi.fn() as Repository<ApiResource>
  598. // @ts-ignore
  599. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  600. return model === DummyApiModel ? repo : null
  601. })
  602. repo.flush = vi.fn()
  603. entityManager.flush(DummyApiModel)
  604. expect(repo.flush).toHaveBeenCalled()
  605. })
  606. })
  607. describe('isNewEntity', () => {
  608. test('with new entity', () => {
  609. const entity = new DummyApiModel()
  610. entity.isNew = vi.fn(() => true)
  611. // @ts-ignore
  612. const repo = vi.fn() as Repository<ApiResource>
  613. // @ts-ignore
  614. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  615. return model === DummyApiModel ? repo : null
  616. })
  617. // @ts-ignore
  618. repo.find = vi.fn((id: number) => entity)
  619. const result = entityManager.isNewInstance(DummyApiModel, 1)
  620. expect(result).toBeTruthy()
  621. })
  622. test('with existing entity', () => {
  623. const entity = new DummyApiModel()
  624. entity.isNew = vi.fn(() => false)
  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).toBeFalsy()
  635. })
  636. test('non-existing entity', () => {
  637. // @ts-ignore
  638. const repo = vi.fn() as Repository<ApiResource>
  639. // @ts-ignore
  640. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  641. return model === DummyApiModel ? repo : null
  642. })
  643. // @ts-ignore
  644. repo.find = vi.fn((id: number) => null)
  645. console.error = vi.fn()
  646. const result = entityManager.isNewInstance(DummyApiModel, 1)
  647. expect(result).toBeFalsy()
  648. expect(console.error).toHaveBeenCalledWith('dummyModel/1 does not exist!')
  649. })
  650. })
  651. describe('saveInitialState', () => {
  652. test('simple call', () => {
  653. // @ts-ignore
  654. const entity = { id: 1, name: 'bob' } as DummyApiResource
  655. // @ts-ignore
  656. const repo = vi.fn() as Repository<ApiResource>
  657. // @ts-ignore
  658. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  659. return model === DummyApiResource ? repo : null
  660. })
  661. // @ts-ignore
  662. repo.save = vi.fn((record: Element) => null)
  663. // @ts-ignore
  664. entityManager.saveInitialState(DummyApiResource, entity)
  665. expect(repo.save).toHaveBeenCalledWith({ id: '_clone_1', name: 'bob' })
  666. expect(entity.id).toEqual(1)
  667. })
  668. })
  669. describe('getInitialStateOf', () => {
  670. test('with initial state', () => {
  671. // @ts-ignore
  672. const entity = { id: 1, name: 'bob' } as DummyApiResource
  673. // @ts-ignore
  674. const repo = vi.fn() as Repository<ApiResource>
  675. // @ts-ignore
  676. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  677. return model === DummyApiResource ? repo : null
  678. })
  679. // @ts-ignore
  680. repo.find = vi.fn((id: number | string) => {
  681. // @ts-ignore
  682. return { id: 1, name: 'robert' } as DummyApiResource
  683. })
  684. // @ts-ignore
  685. const result = entityManager.getInitialStateOf(
  686. DummyApiResource,
  687. 1,
  688. ) as DummyApiResource
  689. expect(repo.find).toHaveBeenCalledWith('_clone_1')
  690. expect(result.id).toEqual(1)
  691. expect(result.name).toEqual('robert')
  692. })
  693. test('without initial state', () => {
  694. // @ts-ignore
  695. const repo = vi.fn() as Repository<ApiResource>
  696. // @ts-ignore
  697. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  698. return model === DummyApiResource ? repo : null
  699. })
  700. // @ts-ignore
  701. repo.find = vi.fn((id: number | string) => null)
  702. // @ts-ignore
  703. const result = entityManager.getInitialStateOf(
  704. DummyApiResource,
  705. 1,
  706. ) as DummyApiResource
  707. expect(repo.find).toHaveBeenCalledWith('_clone_1')
  708. expect(result).toEqual(null)
  709. })
  710. })
  711. describe('removeTempAfterPersist', () => {
  712. test('simple call', () => {
  713. // @ts-ignore
  714. const entity = new DummyApiResource()
  715. entity.id = 'tmp123'
  716. entity.isNew = vi.fn(() => true)
  717. // @ts-ignore
  718. const repo = vi.fn() as Repository<ApiResource>
  719. // @ts-ignore
  720. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  721. return model === DummyApiResource ? repo : null
  722. })
  723. // @ts-ignore
  724. repo.find = vi.fn((id: number | string) => entity)
  725. // @ts-ignore
  726. repo.destroy = vi.fn()
  727. // @ts-ignore
  728. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123')
  729. expect(repo.destroy).toHaveBeenCalledWith('tmp123')
  730. expect(repo.destroy).toHaveBeenCalledWith('_clone_tmp123')
  731. })
  732. test('entity is not temporary', () => {
  733. // @ts-ignore
  734. const entity = new DummyApiResource()
  735. entity.id = 1
  736. entity.isNew = vi.fn(() => false)
  737. // @ts-ignore
  738. const repo = vi.fn() as Repository<ApiResource>
  739. // @ts-ignore
  740. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  741. return model === DummyApiResource ? repo : null
  742. })
  743. // @ts-ignore
  744. repo.find = vi.fn((id: number | string) => entity)
  745. // @ts-ignore
  746. repo.destroy = vi.fn()
  747. // @ts-ignore
  748. expect(() =>
  749. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123'),
  750. ).toThrowError('Error: Can not remove a non-temporary model instance')
  751. expect(repo.destroy).toHaveBeenCalledTimes(0)
  752. })
  753. test('entity does not exist', () => {
  754. // @ts-ignore
  755. const repo = vi.fn() as Repository<ApiResource>
  756. // @ts-ignore
  757. entityManager.getRepository = vi.fn((model: typeof ApiResource) => {
  758. return model === DummyApiResource ? repo : null
  759. })
  760. // @ts-ignore
  761. repo.find = vi.fn((id: number | string) => null)
  762. // @ts-ignore
  763. repo.destroy = vi.fn()
  764. console.error = vi.fn()
  765. // @ts-ignore
  766. entityManager.removeTempAfterPersist(DummyApiResource, 'tmp123')
  767. expect(repo.destroy).toHaveBeenCalledTimes(0)
  768. expect(console.error).toHaveBeenCalledWith(
  769. 'dummyResource/tmp123 does not exist!',
  770. )
  771. })
  772. })
  773. describe('makeProfileHash', () => {
  774. test('simple call', async () => {
  775. entityManager._getProfileMask = vi.fn(() => {
  776. return { a: 1 }
  777. })
  778. expect(await entityManager.makeProfileHash()).toEqual(
  779. '9f89c740ceb46d7418c924a78ac57941d5e96520',
  780. )
  781. })
  782. })