fix: backend tests breaking with permision (#2067)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
release-please / release-please (push) Has been cancelled
test / test-backend (20.x) (push) Has been cancelled
test / test-backend (22.x) (push) Has been cancelled
test / backend (node env, api-test) (22.x) (push) Has been cancelled
test / puterjs (browser env, playwright) (22.x) (push) Has been cancelled
test / puterjs (node env, vitest) (22.x) (push) Has been cancelled

This commit is contained in:
Daniel Salazar
2025-11-29 13:50:27 -08:00
committed by GitHub
parent e8f00e1121
commit cea94d16cb
7 changed files with 56 additions and 16 deletions
+3 -3
View File
@@ -7,7 +7,7 @@ on:
branches: ["main"]
jobs:
test:
test-backend:
runs-on: ubuntu-latest
strategy:
@@ -22,13 +22,13 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
- name: Build
- name: Backend Tests
run: |
rm package-lock.json
npm install -g npm@latest
npm install
npm run build
npm run test
npm run test:backend
api-test:
name: backend (node env, api-test)
+1 -1
View File
@@ -42,7 +42,7 @@
"scripts": {
"test": "npx mocha src/phoenix/test && npx vitest run src/backend && node src/backend/tools/test.mjs",
"test:puterjs-api": "vitest run tests/puterJsApiTests",
"test:backend": "vitest run --config=src/backend/vitest.config.ts src/backend",
"test:backend": "npm run build:ts; vitest run --config=src/backend/vitest.config.ts src/backend",
"start=gui": "nodemon --exec \"node dev-server.js\" ",
"start": "node ./tools/run-selfhosted.js",
"prestart": "npm run build:ts",
@@ -156,14 +156,21 @@ class TogetherAIService extends BaseService {
};
}
// return completion.choices[0];
const ret = completion.choices[0];
ret.usage = {
input_tokens: completion.usage.prompt_tokens,
output_tokens: completion.usage.completion_tokens,
};
const trackedUsage = OpenAIUtil.extractMeteredUsage(completion.usage);
const costOverrides = {
prompt_tokens: trackedUsage.prompt_tokens * (modelDetails?.cost?.input ?? 0),
completion_tokens: trackedUsage.completion_tokens * (modelDetails?.cost?.output ?? 0),
};
// Metering: record usage for non-streamed completion
this.meteringService.utilRecordUsageObject(completion.usage, actor, modelId);
this.meteringService.utilRecordUsageObject(completion.usage, actor, modelId, costOverrides);
return ret;
},
},
@@ -1,8 +1,13 @@
const { AnomalyService } = require('../../services/AnomalyService');
const { GroupService } = require('../../services/auth/GroupService');
const { PermissionService } = require('../../services/auth/PermissionService');
const { CommandService } = require('../../services/CommandService');
const { SqliteDatabaseAccessService } = require('../../services/database/SqliteDatabaseAccessService');
const { DetailProviderService } = require('../../services/DetailProviderService');
const { EventService } = require('../../services/EventService');
const { GetUserService } = require('../../services/GetUserService');
const { MeteringServiceWrapper } = require('../../services/MeteringService/MeteringServiceWrapper.mjs');
const { DBKVServiceWrapper } = require('../../services/repositories/DBKVStore/index.mjs');
const { SUService } = require('../../services/SUService');
const { TraceService } = require('../../services/TraceService');
const { AlarmService } = require('../core/AlarmService');
@@ -18,6 +23,11 @@ class TestCoreModule {
services.registerService('alarm', AlarmService);
services.registerService('event', EventService);
services.registerService('commands', CommandService);
services.registerService('meteringService', MeteringServiceWrapper);
services.registerService('puter-kvstore', DBKVServiceWrapper);
services.registerService('permission', PermissionService);
services.registerService('group', GroupService);
services.registerService('anomaly', AnomalyService);
}
}
@@ -223,5 +223,5 @@ describe('MeteringService', async () => {
count: 1,
});
}
});
}, 10000);
});
@@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest';
import { createTestKernel } from '../../../../tools/test.mjs';
import * as config from '../../../config';
import { Actor } from '../../auth/Actor';
import { MeteringServiceWrapper } from '../../MeteringService/MeteringServiceWrapper.mjs';
import { DBKVServiceWrapper } from './index.mjs';
describe('DBKVStore', async () => {
@@ -18,10 +17,7 @@ describe('DBKVStore', async () => {
});
const testKernel = await createTestKernel({
serviceMap: {
meteringService: MeteringServiceWrapper,
'puter-kvstore': DBKVServiceWrapper,
},
serviceMap: {},
initLevelString: 'init',
testCore: true,
});
@@ -64,6 +60,29 @@ describe('DBKVStore', async () => {
expect(fromTwo).toBe('two');
});
it('retrieves single and multiple keys respecting app vs global scope', async () => {
const userId = 12;
const globalActor = makeActor(userId);
const appActor = makeActor(userId, 'scoped-app');
await su.sudo(globalActor, () => kvStore.set({ key: 'shared', value: 'global-shared' }));
await su.sudo(globalActor, () => kvStore.set({ key: 'global-only', value: 'global' }));
await su.sudo(appActor, () => kvStore.set({ key: 'shared', value: 'app-shared' }));
await su.sudo(appActor, () => kvStore.set({ key: 'app-only', value: 'app' }));
const globalSingle = await su.sudo(globalActor, () => kvStore.get({ key: 'shared' }));
const appSingle = await su.sudo(appActor, () => kvStore.get({ key: 'shared' }));
expect(globalSingle).toBe('global-shared');
expect(appSingle).toBe('app-shared');
const globalList = await su.sudo(globalActor, () => kvStore.get({ key: ['shared', 'app-only', 'global-only'] }));
const appList = await su.sudo(appActor, () => kvStore.get({ key: ['shared', 'app-only', 'global-only'] }));
expect(globalList).toEqual(['global-shared', null, 'global']);
expect(appList).toEqual(['app-shared', 'app', null]);
});
it('increments nested numeric paths and persists the aggregated totals', async () => {
const actor = makeActor(3);
const key = 'counter-key';
@@ -53,10 +53,14 @@ export class DBKVStore implements IDBKVStore {
if ( Array.isArray(key) ) {
const keys = key;
const key_hashes = keys.map((key: string) => murmurhash.v3(key));
const placeholders = key_hashes.map(() => '?').join(',');
const params = app
? [user.id, app.uid, ...key_hashes]
: [user.id, ...key_hashes];
const rows = app
? await this.#db.read('SELECT kkey, value, expireAt FROM kv WHERE user_id=? AND app=? AND kkey_hash IN (?)', [user.id, app.uid, key_hashes])
: await this.#db.read(`SELECT kkey, value, expireAt FROM kv WHERE user_id=? AND (app IS NULL OR app = '${GLOBAL_APP_KEY}') AND kkey_hash IN (${key_hashes.map(() => '?').join(',')})`,
[user.id, key_hashes]);
? await this.#db.read(`SELECT kkey, value, expireAt FROM kv WHERE user_id=? AND app=? AND kkey_hash IN (${placeholders})`, params)
: await this.#db.read(`SELECT kkey, value, expireAt FROM kv WHERE user_id=? AND (app IS NULL OR app = '${GLOBAL_APP_KEY}') AND kkey_hash IN (${placeholders})`,
params);
const kvPairs: Record<string, unknown> = {};
rows.forEach((row: { kkey: string, value: string }) => {
@@ -82,7 +86,7 @@ export class DBKVStore implements IDBKVStore {
deleteExpired(expiredKeys);
}
return keys.map((key: string) => kvPairs[key]) as unknown[];
return keys.map((key: string) => Object.prototype.hasOwnProperty.call(kvPairs, key) ? kvPairs[key] : null) as unknown[];
}
const key_hash = murmurhash.v3(key);