entityManager.test.ts 27 KB

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