entityManager.test.ts 24 KB

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