From 49b90bde1237722113df0496c9400480970d5cff Mon Sep 17 00:00:00 2001 From: KernelDeimos <7225168+KernelDeimos@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:11:16 -0500 Subject: [PATCH] test: add benchmark config and first unit bench Adds the necessary configuration and the `npm run bench` script, as well as the first benchmark to verify everything is working. The first benchmark is permissionUtils. This is important to benchmark because permission strings are used often and there is some non-trivial logic such as escaping of full-colon characters. Other overheads like database access will be much more significant, but it may be useful to know how much more significant in quantifiable terms. --- src/backend/package.json | 1 + .../services/auth/permissionUtils.bench.js | 180 ++++++++++++++++++ src/backend/vitest.bench.config.ts | 13 ++ 3 files changed, 194 insertions(+) create mode 100644 src/backend/src/services/auth/permissionUtils.bench.js create mode 100644 src/backend/vitest.bench.config.ts diff --git a/src/backend/package.json b/src/backend/package.json index 2aefad2f..7c4cf3c4 100644 --- a/src/backend/package.json +++ b/src/backend/package.json @@ -5,6 +5,7 @@ "main": "exports.js", "scripts": { "test": "npx mocha src/**/*.test.js && node ./tools/test.mjs", + "bench": "vitest bench --config=vitest.bench.config.ts --run", "build:worker": "cd src/services/worker && npm run build" }, "dependencies": { diff --git a/src/backend/src/services/auth/permissionUtils.bench.js b/src/backend/src/services/auth/permissionUtils.bench.js new file mode 100644 index 00000000..1a8dcbaf --- /dev/null +++ b/src/backend/src/services/auth/permissionUtils.bench.js @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2024-present Puter Technologies Inc. + * + * This file is part of Puter. + * + * Puter is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import { bench, describe } from 'vitest'; +import { PermissionUtil } from './permissionUtils.mjs'; + +// Sample permission strings for benchmarking +const simplePermissions = [ + 'fs:read', + 'fs:write', + 'app:execute', + 'user:profile:view', +]; + +const complexPermissions = [ + 'fs:aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:read', + 'app:my-app-name:config:update', + 'user:john_doe:profile:avatar:upload', + 'service:database:table:users:column:email:read', +]; + +const escapedPermissions = [ + 'fs:path\\Cwith\\Ccolons:read', + 'app:name\\Cwith\\Cmany\\Ccolons:execute', + 'user:email\\Cexample@test.com:verify', +]; + +// Generate large batch of permissions for bulk testing +const generatePermissions = (count) => { + const perms = []; + for ( let i = 0; i < count; i++ ) { + perms.push(`service:svc${i}:action${i % 10}:resource${i % 100}`); + } + return perms; +}; + +const bulkPermissions = generatePermissions(100); + +describe('PermissionUtil.split()', () => { + bench('split simple permissions', () => { + for ( const perm of simplePermissions ) { + PermissionUtil.split(perm); + } + }); + + bench('split complex permissions', () => { + for ( const perm of complexPermissions ) { + PermissionUtil.split(perm); + } + }); + + bench('split escaped permissions', () => { + for ( const perm of escapedPermissions ) { + PermissionUtil.split(perm); + } + }); + + bench('split bulk permissions (100)', () => { + for ( const perm of bulkPermissions ) { + PermissionUtil.split(perm); + } + }); +}); + +describe('PermissionUtil.join()', () => { + const simpleComponents = [['fs', 'read'], ['app', 'execute'], ['user', 'view']]; + const complexComponents = [ + ['fs', 'uuid-here', 'read'], + ['service', 'database', 'table', 'users', 'read'], + ['app', 'my-app', 'config', 'setting', 'update'], + ]; + const needsEscaping = [ + ['fs', 'path:with:colons', 'read'], + ['user', 'email:test@example.com', 'verify'], + ]; + + bench('join simple components', () => { + for ( const comps of simpleComponents ) { + PermissionUtil.join(...comps); + } + }); + + bench('join complex components', () => { + for ( const comps of complexComponents ) { + PermissionUtil.join(...comps); + } + }); + + bench('join components needing escaping', () => { + for ( const comps of needsEscaping ) { + PermissionUtil.join(...comps); + } + }); +}); + +describe('PermissionUtil.escape_permission_component()', () => { + const noEscape = ['simple', 'another_one', 'with-dashes', 'CamelCase']; + const needsEscape = ['has:colon', 'multiple:colons:here', ':starts:with', 'ends:']; + + bench('escape components without special chars', () => { + for ( const comp of noEscape ) { + PermissionUtil.escape_permission_component(comp); + } + }); + + bench('escape components with colons', () => { + for ( const comp of needsEscape ) { + PermissionUtil.escape_permission_component(comp); + } + }); +}); + +describe('PermissionUtil.unescape_permission_component()', () => { + const noUnescape = ['simple', 'another_one', 'with-dashes']; + const needsUnescape = ['has\\Ccolon', 'multiple\\Ccolons\\Chere', '\\Cstarts\\Cwith']; + + bench('unescape components without escape sequences', () => { + for ( const comp of noUnescape ) { + PermissionUtil.unescape_permission_component(comp); + } + }); + + bench('unescape components with escape sequences', () => { + for ( const comp of needsUnescape ) { + PermissionUtil.unescape_permission_component(comp); + } + }); +}); + +describe('PermissionUtil roundtrip (split then join)', () => { + bench('roundtrip simple permissions', () => { + for ( const perm of simplePermissions ) { + const parts = PermissionUtil.split(perm); + PermissionUtil.join(...parts); + } + }); + + bench('roundtrip complex permissions', () => { + for ( const perm of complexPermissions ) { + const parts = PermissionUtil.split(perm); + PermissionUtil.join(...parts); + } + }); +}); + +describe('PermissionUtil vs native string operations (baseline)', () => { + const perm = 'service:database:table:users:column:email:read'; + + bench('PermissionUtil.split()', () => { + PermissionUtil.split(perm); + }); + + bench('native String.split() (baseline, no unescaping)', () => { + perm.split(':'); + }); + + bench('PermissionUtil.join()', () => { + PermissionUtil.join('service', 'database', 'table', 'users'); + }); + + bench('native Array.join() (baseline, no escaping)', () => { + ['service', 'database', 'table', 'users'].join(':'); + }); +}); diff --git a/src/backend/vitest.bench.config.ts b/src/backend/vitest.bench.config.ts new file mode 100644 index 00000000..b1a44569 --- /dev/null +++ b/src/backend/vitest.bench.config.ts @@ -0,0 +1,13 @@ +// vitest.bench.config.ts - Vitest benchmark configuration for Puter backend +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + benchmark: { + include: ['src/**/*.bench.{js,ts}'], + reporters: ['default'], + }, + root: __dirname, + }, +}); +