entityManager.test.ts 25 KB

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