Browse Source

complete unit tests

Olivier Massot 2 years ago
parent
commit
b760334fe4

+ 3 - 2
services/data/normalizer/hydraNormalizer.ts

@@ -9,6 +9,7 @@ import {isArray} from "lodash";
  * Normalisation et dé-normalisation du format de données Hydra
  */
 class HydraNormalizer {
+  static models = models
 
   /**
    * Normalize the given entity into a Hydra formatted content.
@@ -134,8 +135,8 @@ class HydraNormalizer {
       return item
     }
 
-    if (entity && models.hasOwnProperty(entity)) {
-      model = models[entity]
+    if (entity && HydraNormalizer.models.hasOwnProperty(entity)) {
+      model = HydraNormalizer.models[entity]
       return HydraNormalizer.denormalizeEntity(model, item)
     }
 

+ 9 - 0
services/utils/stringUtils.ts

@@ -18,4 +18,13 @@ export default class StringUtils
             .replace(/[-]/g, ' ')
             .trim()
     }
+
+    /**
+     * Convertit le paramètre d'entrée en entier
+     * A la différence de parseInt, cette méthode accepte aussi les nombres.
+     * @param s
+     */
+    public static parseInt(s: string | number) {
+        return typeof s === 'number' ? s : parseInt(s)
+    }
 }

+ 424 - 116
tests/units/services/data/normalizer/hydraNormalizer.test.ts

@@ -1,136 +1,133 @@
-import { describe, test, it, expect } from 'vitest'
+import { vi, describe, test, it, expect, afterEach } from 'vitest'
 import {AnyJson} from "~/types/data";
 import HydraNormalizer from "~/services/data/normalizer/hydraNormalizer";
 import {METADATA_TYPE} from "~/types/enum/data";
-import Access from "~/models/Access/Access";
+import ApiModel from "~/models/ApiModel";
+import {Str, Uid, Attr} from "pinia-orm/dist/decorators";
+import {IriEncoded} from "~/models/decorators";
+import UrlUtils from "~/services/utils/urlUtils";
+import ApiResource from "~/models/ApiResource";
 
-describe('denormalize', () => {
+class DummyApiChild extends ApiModel {
+    static entity = 'dummyChild'
 
-    test('should parse a API Item response and return a JSON Object', () => {
-        const data: AnyJson = {
-            '@context': '/api/contexts/Access',
-            '@id': '/api/accesses/7351',
-            '@type': 'Access',
-            organization: '/api/organizations/37306',
-            id: 7351,
-            person: {
-                '@type': 'Person',
-                id: 11344,
-                name: 'Trennet',
-                givenName: 'Charles',
-                username: "ctrennet"
-            }
-        }
+    @Uid()
+    declare id: number | string
+}
 
-        const result = HydraNormalizer.denormalize(data, Access)
+class DummyApiModel extends ApiModel {
+    static entity = 'dummyModel'
 
-        // @ts-ignore
-        expect(result.data).toEqual(HydraNormalizer.getData(data))
-        // @ts-ignore
-        expect(result.metadata).toEqual(HydraNormalizer.getMetadata(data))
+    @Uid()
+    declare id: number | string
 
-        const expected = {
-            "data": {
-                "id": 7351,
-                "organization": 37306,
-                "person": {
-                    "accessId": null,
-                    "givenName": "Charles",
-                    "id": 11344,
-                    "name": "Trennet",
-                    "username": "ctrennet"
-                },
-                "activityYear": 0,
-                "historical": {}
-            },
-            "metadata": {
-                "type": METADATA_TYPE.ITEM
-            }
-        }
+    @Attr(null)
+    @IriEncoded(DummyApiChild)
+    declare oneToOneRelation: number | null
 
-        expect(result).toEqual(expected)
+    @Attr([])
+    @IriEncoded(DummyApiChild)
+    declare oneToManyRelation: []
+
+    @Str(null)
+    declare name: null
+}
+
+afterEach(() => {
+    vi.clearAllMocks();
+    vi.resetAllMocks();
+});
+
+describe('normalize', () => {
+    const initialMakeUriMethod = UrlUtils.makeIRI
+    //@ts-ignore
+    const initialGetIriEncodedFieldsMethod = HydraNormalizer.getIriEncodedFields
+
+    afterEach(() => {
+        UrlUtils.makeIRI = initialMakeUriMethod
+        //@ts-ignore
+        HydraNormalizer.getIriEncodedFields = initialGetIriEncodedFieldsMethod
     })
 
-    it('should parse a API Collection response and return a JSON Object', () => {
-        const data: AnyJson = {
-            '@context': '/api/contexts/Access',
-            '@id': '/api/accesses',
-            '@type': 'hydra:Collection',
-            'hydra:member': [{
-                '@id': '/api/accesses/7351',
-                organization: '/api/organizations/37306',
-                id: 7351,
-                person: {
-                    id: 11344,
-                    name: 'Trennet',
-                    givenName: 'Charles',
-                    username: "ctrennet"
-                }
-            }, {
-                '@id': '/api/accesses/7352',
-                organization: '/api/organizations/37306',
-                id: 7352,
-                person: {
-                    id: 11345,
-                    name: 'BRASSENS',
-                    givenName: 'George'
+    test('should normalize an entity into a JSON Object', () => {
+
+        const entity: DummyApiModel = new DummyApiModel({
+            id: 7351,
+            oneToOneRelation: 99,
+            oneToManyRelation: [123, 124, 125]
+        })
+
+        //@ts-ignore
+        HydraNormalizer.getIriEncodedFields = vi.fn(
+            (entity) => {
+                return {
+                    oneToOneRelation: DummyApiChild,
+                    oneToManyRelation: DummyApiChild
                 }
             }
-            ],
-            "hydra:view": {
-                "@id": "/api/accesses?page=3",
-                "@type": "hydra:PartialCollectionView",
-                "hydra:first": "/api/accesses?page=1",
-                "hydra:last": "/api/accesses?page=5",
-                "hydra:next": "/api/accesses?page=4",
-                "hydra:previous": "/api/accesses?page=2"
+        )
+
+        //@ts-ignore
+        UrlUtils.makeIRI = vi.fn(
+            (targetEntity, id) => {
+                return {
+                    99: '/api/dummyChild/99',
+                    123: '/api/dummyChild/123',
+                    124: '/api/dummyChild/124',
+                    125: '/api/dummyChild/125',
+                }[id]
             }
+        )
+
+        const result = HydraNormalizer.normalizeEntity(entity)
+
+        const expected = {
+            id: 7351,
+            name: null,
+            oneToOneRelation: '/api/dummyChild/99',
+            oneToManyRelation: ['/api/dummyChild/123', '/api/dummyChild/124', '/api/dummyChild/125']
         }
 
-        const result = HydraNormalizer.denormalize(data, Access)
+        expect(result).toStrictEqual(expected)
+    })
+})
+
 
-        // @ts-ignore
-        expect(result.data).toEqual(HydraNormalizer.getData(data))
-        // @ts-ignore
-        expect(result.metadata).toEqual(HydraNormalizer.getMetadata(data))
-
-        const expected =
-        {
-            "data": [
-                {
-                    id: 7351,
-                    organization: 37306,
-                    person: {
-                        id: 11344,
-                        name: 'Trennet',
-                        givenName: 'Charles',
-                        accessId: null,
-                        username: "ctrennet"
-                    },
-                    activityYear: 0,
-                    historical: {}
-                },
-                {
-                    id: 7352,
-                    organization: 37306,
-                    person: {
-                        id: 11345,
-                        name: 'BRASSENS',
-                        givenName: 'George',
-                        accessId: null,
-                        username: null
-                    },
-                    activityYear: 0,
-                    historical: {}
-                }
-            ],
-            'metadata': {
-                firstPage: 1,
-                lastPage: 5,
-                nextPage: 4,
-                previousPage: 2,
-                totalItems: undefined,
-                type: 1
+describe('denormalize', () => {
+    //@ts-ignore
+    const initialGetDataMethod = HydraNormalizer.getData
+    //@ts-ignore
+    const initialGetMetadataMethod = HydraNormalizer.getMetadata
+
+    afterEach(() => {
+        //@ts-ignore
+        HydraNormalizer.getData = initialGetDataMethod
+        //@ts-ignore
+        HydraNormalizer.getMetadata = initialGetMetadataMethod
+    })
+
+    test('should parse a API Item response and return a JSON Object', () => {
+        const data: AnyJson = { id: 1, name: 'foo' };
+
+        //@ts-ignore
+        HydraNormalizer.getData = vi.fn((data, model) => {
+            return data
+        })
+
+        //@ts-ignore
+        HydraNormalizer.getMetadata = vi.fn((data, model) => {
+            return { "type": METADATA_TYPE.ITEM }
+        })
+
+        const result = HydraNormalizer.denormalize(data, DummyApiModel)
+
+        const expected = {
+            "data": {
+                id: 1,
+                name: 'foo',
+            },
+            "metadata": {
+                "type": METADATA_TYPE.ITEM
             }
         }
 
@@ -222,3 +219,314 @@ describe('getMetadata', () => {
         expect(metadata.type).toEqual(METADATA_TYPE.ITEM)
     })
 })
+
+describe('denormalizeItem', () => {
+    //@ts-ignore
+    const initialDenormalizeEntity = HydraNormalizer.denormalizeEntity
+    //@ts-ignore
+    const initialDenormalizeEnum = HydraNormalizer.denormalizeEnum
+    //@ts-ignore
+    const initialParseEntityIRI = HydraNormalizer.parseEntityIRI
+    const initialConsoleError = console.error
+
+    afterEach(() => {
+        //@ts-ignore
+        HydraNormalizer.denormalizeEntity = initialDenormalizeEntity
+        //@ts-ignore
+        HydraNormalizer.denormalizeEnum = initialDenormalizeEnum
+        //@ts-ignore
+        HydraNormalizer.parseEntityIRI = initialParseEntityIRI
+
+        console.error = initialConsoleError
+    })
+
+    test('With provided model', () => {
+
+        const item = {
+            '@id': '/api/dummyModel/1',
+            id: 1,
+            name: 'foo'
+        }
+
+        const model = DummyApiModel
+        const expected = new DummyApiModel(item)
+
+        //@ts-ignore
+        HydraNormalizer.denormalizeEntity = vi.fn((model, item) => {
+            return expected
+        })
+
+        //@ts-ignore
+        const result = HydraNormalizer.denormalizeItem(item, model)
+
+        expect(result).toEqual(expected)
+
+        //@ts-ignore
+        expect(HydraNormalizer.denormalizeEntity).toHaveBeenCalledWith(model, item)
+    })
+
+    test('with no @id prop', () => {
+        const item = {
+            id: 1,
+            name: 'foo'
+        }
+
+        console.error = vi.fn((msg) => {})
+
+        //@ts-ignore
+        const result = HydraNormalizer.denormalizeItem(item)
+
+        expect(result).toEqual(item)
+
+        expect(console.error).toHaveBeenCalledWith(
+            'De-normalization error : the item is not hydra formatted',
+            item
+        )
+    })
+
+    test('with enum', () => {
+        const item = {
+            '@id': '/api/enum/abc',
+            A: 1,
+            B: 2,
+            C: 3
+        }
+
+        //@ts-ignore
+        HydraNormalizer.denormalizeEnum = vi.fn((item) => {
+            return item
+        })
+
+        //@ts-ignore
+        const result = HydraNormalizer.denormalizeItem(item)
+
+        expect(result).toEqual(item)
+
+        //@ts-ignore
+        expect(HydraNormalizer.denormalizeEnum).toHaveBeenCalledWith(item)
+    })
+
+    test('with unparsable iri', () => {
+        const item = {
+            '@id': 'some_invalid_iri',
+            id: 1,
+            name: 'foo'
+        }
+
+        //@ts-ignore
+        HydraNormalizer.parseEntityIRI = vi.fn((iri) => {
+            throw('parsing error')
+        })
+
+        console.error = vi.fn((msg) => {})
+
+        //@ts-ignore
+        const result = HydraNormalizer.denormalizeItem(item)
+
+        expect(result).toEqual(item)
+
+        //@ts-ignore
+        expect(console.error).toHaveBeenCalledWith(
+            'De-normalization error : could not parse the IRI',
+            item
+        )
+    })
+
+    test('With valid iri and existing model', () => {
+
+        HydraNormalizer.models = {
+            'dummyModel': DummyApiModel
+        }
+
+        const item = {
+            '@id': '/api/dummyModel/1',
+            id: 1,
+            name: 'foo'
+        }
+
+        //@ts-ignore
+        HydraNormalizer.parseEntityIRI = vi.fn((iri) => {
+            return { entity: 'dummyModel' }
+        })
+
+        const expected = new DummyApiModel(item)
+
+        //@ts-ignore
+        HydraNormalizer.denormalizeEntity = vi.fn((model, item) => {
+            return expected
+        })
+
+        //@ts-ignore
+        const result = HydraNormalizer.denormalizeItem(item)
+
+        expect(result).toEqual(expected)
+
+        //@ts-ignore
+        expect(HydraNormalizer.denormalizeEntity).toHaveBeenCalledWith(DummyApiModel, item)
+    })
+
+    test('With valid iri and un-existing model', () => {
+
+        HydraNormalizer.models = {
+            'dummyModel': DummyApiModel
+        }
+
+        const item = {
+            '@id': '/api/unknownModel/1',
+            id: 1,
+            name: 'foo'
+        }
+
+        //@ts-ignore
+        HydraNormalizer.parseEntityIRI = vi.fn((iri) => {
+            return 'unknownModel'
+        })
+
+        expect(
+            //@ts-ignore
+            () => HydraNormalizer.denormalizeItem(item)
+        ).toThrowError()
+    })
+})
+
+describe('denormalizeEntity', () => {
+    //@ts-ignore
+    const initialGetIriEncodedFieldsMethod = HydraNormalizer.getIriEncodedFields
+    //@ts-ignore
+    const initialGetIdFromEntityIriMethod = HydraNormalizer.getIdFromEntityIri
+
+    afterEach(() => {
+        //@ts-ignore
+        HydraNormalizer.getIriEncodedFields = initialGetIriEncodedFieldsMethod
+        //@ts-ignore
+        HydraNormalizer.getIdFromEntityIri = initialGetIdFromEntityIriMethod
+    })
+
+    test('should denormalize a Json object into an entity', () => {
+        const data = {
+            id: 7351,
+            name: null,
+            oneToOneRelation: '/api/dummyChild/99',
+            oneToManyRelation: ['/api/dummyChild/123', '/api/dummyChild/124', '/api/dummyChild/125']
+        }
+
+        //@ts-ignore
+        HydraNormalizer.getIriEncodedFields = vi.fn(
+            (entity) => {
+                return {
+                    oneToOneRelation: DummyApiChild,
+                    oneToManyRelation: DummyApiChild
+                }
+            }
+        )
+
+        //@ts-ignore
+        HydraNormalizer.getIdFromEntityIri = vi.fn((iri) => {
+            return {
+                '/api/dummyChild/99': 99,
+                '/api/dummyChild/123': 123,
+                '/api/dummyChild/124': 124,
+                '/api/dummyChild/125': 125
+            }[iri]
+        })
+
+        //@ts-ignore
+        const result = HydraNormalizer.denormalizeEntity(DummyApiModel, data)
+
+        expect(result).toStrictEqual(new DummyApiModel({
+            id: 7351,
+            name: null,
+            oneToOneRelation: 99,
+            oneToManyRelation: [123, 124, 125]
+        }))
+
+    })
+})
+
+describe('denormalizeEnum', () => {
+    test('does nothing', () => {
+        const item = {
+            '@id': '/api/enum/abc',
+            A: 1,
+            B: 2,
+            C: 3
+        }
+
+        //@ts-ignore
+        expect(HydraNormalizer.denormalizeEnum(item)).toStrictEqual(item)
+    })
+})
+
+describe('parseEntityIRI', () => {
+    test('valid iri', () => {
+
+        const iri = '/api/someEntity/456'
+        const expected = {
+            entity: 'someEntity',
+            id: 456
+        }
+
+        //@ts-ignore
+        expect(HydraNormalizer.parseEntityIRI(iri)).toStrictEqual(expected)
+    })
+
+    test('invalid iri', () => {
+        const iri = 'some_invalid_iri'
+
+        //@ts-ignore
+        expect(() => HydraNormalizer.parseEntityIRI(iri)).toThrowError('could not parse the IRI : "some_invalid_iri"')
+    })
+})
+
+describe('getIriEncodedFields', () => {
+    const initialRelations = DummyApiModel.relations
+
+    afterEach(() => {
+        DummyApiModel.relations = initialRelations
+    })
+
+    test('get relations', () => {
+        //@ts-ignore
+        const relations:  Record<string, ApiResource> = {'someField': DummyApiChild}
+
+        DummyApiModel.relations = relations
+
+        const entity = new DummyApiModel()
+
+        //@ts-ignore
+        const results = HydraNormalizer.getIriEncodedFields(entity)
+
+        expect(results).toEqual(relations)
+    })
+})
+
+describe('getIdFromEntityIri', () => {
+    //@ts-ignore
+    const initialParseEntityIRI = HydraNormalizer.parseEntityIRI
+
+    afterEach(() => {
+        //@ts-ignore
+        HydraNormalizer.parseEntityIRI = initialParseEntityIRI
+    })
+
+    test('valid iri', () => {
+        const iri = '/api/someEntity/456'
+
+        //@ts-ignore
+        const result = HydraNormalizer.getIdFromEntityIri(iri, 'someEntity')
+
+        expect(result).toEqual(456)
+    })
+
+    test('entity not matching', () => {
+        const iri = '/api/someEntity/456'
+
+        //@ts-ignore
+        expect(
+            //@ts-ignore
+            () => HydraNormalizer.getIdFromEntityIri(iri, 'otherEntity')
+        ).toThrowError("IRI entity does not match the field's target entity (someEntity != otherEntity)")
+    })
+
+
+})

+ 1 - 1
tests/units/services/sse/sseSource.test.ts

@@ -1,4 +1,4 @@
-import { describe, test, it, expect } from 'vitest'
+import { describe, test, it, expect, beforeEach, afterEach, vi } from 'vitest'
 import SseSource from "~/services/sse/sseSource";
 import {EventSourcePolyfill} from "event-source-polyfill";
 

+ 12 - 0
tests/units/services/utils/dateUtils.test.ts

@@ -99,6 +99,18 @@ describe('getShortFormatPattern', () => {
     })
 })
 
+describe('getFormatPattern', () => {
+    test('standard call', () => {
+        expect(DateUtils.getFormatPattern(supportedLocales.FR)).toEqual('dd/MM/yyyy HH:mm')
+        expect(DateUtils.getFormatPattern(supportedLocales.EN)).toEqual('MM/dd/yyyy HH:mm')
+    })
+
+    test('unsupported locale', () => {
+        // @ts-ignore
+        expect(DateUtils.getFormatPattern('xx')).toEqual('dd/MM/yyyy HH:mm')
+    })
+})
+
 describe('formatIsoShortDate', () => {
     test('standard call', () => {
         expect(DateUtils.formatIsoShortDate(new Date(2023, 0, 10))).toEqual('2023-01-10')

+ 28 - 0
tests/units/services/utils/stringUtils.test.ts

@@ -0,0 +1,28 @@
+import { describe, test, expect } from 'vitest'
+import StringUtils from "~/services/utils/stringUtils";
+
+
+describe("normalize", () => {
+    test("simple cases", () => {
+
+        const assertEqual = (input: string, expected: string) => {
+            expect(StringUtils.normalize(input)).toBe(expected)
+        }
+
+        assertEqual("abc", "abc")
+        assertEqual("ABC", "abc")
+        assertEqual("éçï", "eci")
+        assertEqual("éèẽëê-ç-îïĩ-àã-öôõ-ûüũ", "eeeee c iii aa ooo uuu")
+        assertEqual("  abc   ", "abc")
+    })
+})
+
+describe("parseInt", () => {
+    test("simple cases", () => {
+        expect(StringUtils.parseInt(6)).toBe(6)
+        expect(StringUtils.parseInt("6")).toBe(6)
+    })
+})
+
+
+

+ 10 - 0
tests/units/services/utils/urlUtils.test.ts

@@ -118,3 +118,13 @@ describe('addQuery', () => {
         expect(UrlUtils.addQuery('https://foo.com/#/bar/', {'a': 1})).toEqual('https://foo.com/#/bar/?a=1')
     })
 })
+
+describe('makeIRI', () => {
+    test('valid parameters', () => {
+        expect(UrlUtils.makeIRI('someEntity', 123)).toEqual('/api/someEntity/123')
+    })
+    test('invalid id', () => {
+        //@ts-ignore
+        expect(() => UrlUtils.makeIRI('someEntity', 'abc')).toThrowError('Invalid id : abc')
+    })
+})

+ 2 - 2
vitest.config.ts

@@ -12,8 +12,8 @@ export default defineConfig({
         coverage: {
             enabled: true,
             include: [
-                'components/**',
-                'composables/**',
+                // 'components/**',
+                // 'composables/**',
                 'services/**',
             ],
             all: true,