entityManager.test.ts 25 KB

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