diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bef2785..4d697c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,6 +20,5 @@ jobs: run: npm run lint - name: Build and Test run: | - npm run test:generate npm run test npm run build diff --git a/jest-perf.config.js b/jest-perf.config.js new file mode 100644 index 0000000..1339637 --- /dev/null +++ b/jest-perf.config.js @@ -0,0 +1,17 @@ +/** @type {import('jest').Config} */ +const config = { + preset: 'ts-jest', + testEnvironment: 'node', + setupFilesAfterEnv: ['./test/jest.setup.js'], + transform: { + '^.+\\.[tj]s$': 'ts-jest', + }, + transformIgnorePatterns: ['^.+/.js$'], + testRegex: '/test/unit/clickup/clickupFieldsDependencyTracker.perf.test.ts$', + testPathIgnorePatterns: ['/node_modules/', '/dist/', '/test/_utils/', '/test/scripts', '/test/jest.setup.js'], + collectCoverageFrom: ['**/*.[tj]s', '!src/grammar-parser/**'], +}; + +process.env.TZ = 'UTC'; + +module.exports = config; diff --git a/jest.config.js b/jest.config.js index 307b405..115b3a0 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,7 +8,14 @@ const config = { }, testRegex: '(/test/.*.(t|j)s)$', transformIgnorePatterns: ['^.+/.js$'], - testPathIgnorePatterns: ['/node_modules/', '/dist/', '/test/_utils/', '/test/scripts', '/test/jest.setup.js'], + testPathIgnorePatterns: [ + '/node_modules/', + '/dist/', + '/test/_utils/', + '/test/scripts', + '/test/jest.setup.js', + '/test/unit/clickup/clickupFieldsDependencyTracker.perf.test.ts', + ], collectCoverageFrom: ['**/*.[tj]s', '!src/grammar-parser/**'], }; diff --git a/package.json b/package.json index d8ab07d..b813df2 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "test:clear": "jest --clearCache", "test": "jest --detectOpenHandles", "test:coverage": "jest --coverage", + "test:perf": "jest --config=jest-perf.config.js", "test:generate": "ts-node test/scripts/generatePerfTestData.ts", "build": "tsc", "generate-parser": "cd src/grammar-parser && jison grammar-parser.jison", diff --git a/test/unit/clickup/clickupFieldsDependencyTracker.perf.test.ts b/test/unit/clickup/clickupFieldsDependencyTracker.perf.test.ts new file mode 100644 index 0000000..d1c0ea3 --- /dev/null +++ b/test/unit/clickup/clickupFieldsDependencyTracker.perf.test.ts @@ -0,0 +1,91 @@ +import fs from 'fs'; +import path from 'path'; +import { ClickUpFieldsDependencyTracker } from '../../../src'; +import { ClickUpParserVariable } from '../../../src/clickup/clickupParserVariable'; + +describe('ClickupFieldsDependencyTracker', () => { + describe('performance tests', () => { + interface AugmentedTracker { + getDependencyGraph(): unknown; + } + + const filePath = path.join(process.cwd(), 'test', 'data', 'test_custom_fields.json'); + const data = fs.readFileSync(filePath, 'utf-8'); + const variables = JSON.parse(data); + + function getVariables(): ClickUpParserVariable[] { + return [...variables]; + } + + function getRandomSample(arr: T[], sampleSize: number): T[] { + if (sampleSize > arr.length) { + throw new Error('Sample size cannot be larger than the array size.'); + } + + // Create a copy of the array to avoid modifying the original array + const arrayCopy = arr.slice(); + + // Fisher-Yates shuffle, optimized to shuffle only up to sampleSize + for (let i = 0; i < sampleSize; i++) { + const j = Math.floor(Math.random() * (arr.length - i)) + i; + [arrayCopy[i], arrayCopy[j]] = [arrayCopy[j], arrayCopy[i]]; + } + // Return the first N elements of the shuffled array + return arrayCopy.slice(0, sampleSize); + } + it('graph creation should be fast enough', () => { + const start = performance.now(); + const iterations = 10; + for (let i = 0; i < iterations; i++) { + const variables = getVariables(); + const tracker = new ClickUpFieldsDependencyTracker(variables) as unknown as AugmentedTracker; + const graph = tracker.getDependencyGraph(); + expect(graph).toBeDefined(); + } + const timeAverage = (performance.now() - start) / iterations; + + expect(timeAverage).toBeLessThan(250); + console.log(`Graph creation for ${variables.length} variables (average time): ${timeAverage} ms`); + }); + + it('dependencies validation should be fast enough', () => { + const variables = getVariables(); + const validator = new ClickUpFieldsDependencyTracker(variables); + { + // initialze the graph outside of the measurement + const augmented = validator as unknown as AugmentedTracker; + augmented.getDependencyGraph(); + } + + const start = performance.now(); + const iterations = 100; + for (let i = 0; i < iterations; i++) { + validator.validate(); + } + const time = (performance.now() - start) / iterations; + + expect(time).toBeLessThan(250); + console.log(`Dependencies validation for ${variables.length} variables (average time): ${time} ms`); + }); + + it('fetching dependants should be fast enough', () => { + const variables = getVariables(); + const tracker = new ClickUpFieldsDependencyTracker(variables); + { + // initialze the graph outside of the measurement + const augmented = tracker as unknown as AugmentedTracker; + augmented.getDependencyGraph(); + } + + const varsSample = getRandomSample(variables, 1000); + const start = performance.now(); + for (const variable of varsSample) { + tracker.getDependentFields(variable.name); + } + const time = (performance.now() - start) / variables.length; + + expect(time).toBeLessThan(250); + console.log(`Fetching dependants for ${variables.length} variables (average time): ${time} ms`); + }); + }); +}); diff --git a/test/unit/clickup/clickupFieldsDependencyTracker.test.ts b/test/unit/clickup/clickupFieldsDependencyTracker.test.ts index 62d7b95..85a4a1f 100644 --- a/test/unit/clickup/clickupFieldsDependencyTracker.test.ts +++ b/test/unit/clickup/clickupFieldsDependencyTracker.test.ts @@ -1,7 +1,5 @@ -import fs from 'fs'; -import path from 'path'; import { ClickUpFieldsDependencyTracker } from '../../../src/clickup/clickupFieldsDependencyTracker'; -import { ClickUpParserVariable, createClickUpParserVariable } from '../../../src/clickup/clickupParserVariable'; +import { createClickUpParserVariable } from '../../../src/clickup/clickupParserVariable'; describe('clickupFieldsValidator', () => { const createName = (id: string) => `CUSTOM_FIELD_${id}`; @@ -87,89 +85,4 @@ describe('clickupFieldsValidator', () => { }), }); }); - - describe('performance tests', () => { - interface AugmentedTracker { - getDependencyGraph(): unknown; - } - - const filePath = path.join(process.cwd(), 'test', 'data', 'test_custom_fields.json'); - const data = fs.readFileSync(filePath, 'utf-8'); - const variables = JSON.parse(data); - - function getVariables(): ClickUpParserVariable[] { - return [...variables]; - } - - function getRandomSample(arr: T[], sampleSize: number): T[] { - if (sampleSize > arr.length) { - throw new Error('Sample size cannot be larger than the array size.'); - } - - // Create a copy of the array to avoid modifying the original array - const arrayCopy = arr.slice(); - - // Fisher-Yates shuffle, optimized to shuffle only up to sampleSize - for (let i = 0; i < sampleSize; i++) { - const j = Math.floor(Math.random() * (arr.length - i)) + i; - [arrayCopy[i], arrayCopy[j]] = [arrayCopy[j], arrayCopy[i]]; - } - // Return the first N elements of the shuffled array - return arrayCopy.slice(0, sampleSize); - } - it('graph creation should be fast enough', () => { - const start = performance.now(); - const iterations = 10; - for (let i = 0; i < iterations; i++) { - const variables = getVariables(); - const tracker = new ClickUpFieldsDependencyTracker(variables) as unknown as AugmentedTracker; - const graph = tracker.getDependencyGraph(); - expect(graph).toBeDefined(); - } - const timeAverage = (performance.now() - start) / iterations; - - expect(timeAverage).toBeLessThan(250); - console.log(`Graph creation for ${variables.length} variables (average time): ${timeAverage} ms`); - }); - - it('dependencies validation should be fast enough', () => { - const variables = getVariables(); - const validator = new ClickUpFieldsDependencyTracker(variables); - { - // initialze the graph outside of the measurement - const augmented = validator as unknown as AugmentedTracker; - augmented.getDependencyGraph(); - } - - const start = performance.now(); - const iterations = 100; - for (let i = 0; i < iterations; i++) { - validator.validate(); - } - const time = (performance.now() - start) / iterations; - - expect(time).toBeLessThan(250); - console.log(`Dependencies validation for ${variables.length} variables (average time): ${time} ms`); - }); - - it('fetching dependants should be fast enough', () => { - const variables = getVariables(); - const tracker = new ClickUpFieldsDependencyTracker(variables); - { - // initialze the graph outside of the measurement - const augmented = tracker as unknown as AugmentedTracker; - augmented.getDependencyGraph(); - } - - const varsSample = getRandomSample(variables, 1000); - const start = performance.now(); - for (const variable of varsSample) { - tracker.getDependentFields(variable.name); - } - const time = (performance.now() - start) / variables.length; - - expect(time).toBeLessThan(250); - console.log(`Fetching dependants for ${variables.length} variables (average time): ${time} ms`); - }); - }); });