From 93eddd2238bab5fdf2d19b789895437bb4888449 Mon Sep 17 00:00:00 2001 From: Daniel Salazar Date: Wed, 26 Nov 2025 15:09:30 -0800 Subject: [PATCH] feat: MeteringService test fixes for now (#2050) --- src/backend/src/config.js | 1 + src/backend/src/services/EventService.d.ts | 10 +-- src/backend/src/services/GetUserService.js | 5 +- .../MeteringService/MeteringService.test.ts | 64 +++++++++++++++++-- src/backend/src/services/SUService.js | 3 +- src/backend/src/services/User.d.ts | 3 +- .../database/SqliteDatabaseAccessService.js | 10 +-- src/backend/tools/test.mjs | 10 ++- 8 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/backend/src/config.js b/src/backend/src/config.js index d908e2fa..9080a906 100644 --- a/src/backend/src/config.js +++ b/src/backend/src/config.js @@ -272,6 +272,7 @@ const config_pointer = {}; // Object.defineProperty(context_config, 'strict', { get: () => config_to_export.env === 'dev', + configurable: true, }); module.exports = config_to_export; diff --git a/src/backend/src/services/EventService.d.ts b/src/backend/src/services/EventService.d.ts index 8899eaae..f8db369e 100644 --- a/src/backend/src/services/EventService.d.ts +++ b/src/backend/src/services/EventService.d.ts @@ -1,7 +1,7 @@ // Minimal EventService type declaration for MeteringService type safety -export interface EventService { - emit(key: string, data?: any, meta?: any): Promise; - on(selector: string, callback: Function): { detach: () => void }; - on_all(callback: Function): void; - get_scoped(scope: string): any; +export class EventService { + emit (key: string, data?: any, meta?: any): Promise; + on (selector: string, callback: Function): { detach: () => void }; + on_all (callback: Function): void; + get_scoped (scope: string): any; } \ No newline at end of file diff --git a/src/backend/src/services/GetUserService.js b/src/backend/src/services/GetUserService.js index b4d90e0f..3d1f4c7a 100644 --- a/src/backend/src/services/GetUserService.js +++ b/src/backend/src/services/GetUserService.js @@ -17,7 +17,6 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ -const { Actor } = require('./auth/Actor'); const BaseService = require('./BaseService'); const { DB_READ } = require('./database/consts'); @@ -101,7 +100,7 @@ class GetUserService extends BaseService { if ( cached && !options.force ) { for ( const prop of this.id_properties ) { if ( options.hasOwnProperty(prop) ) { - const user = kv.get(`users:${prop}:${options[prop]}`); + const user = globalThis.kv?.get(`users:${prop}:${options[prop]}`); if ( user ) return user; } } @@ -132,7 +131,7 @@ class GetUserService extends BaseService { try { for ( const prop of this.id_properties ) { if ( user[prop] ) { - kv.set(`users:${prop}:${user[prop]}`, user); + globalThis.kv?.set(`users:${prop}:${user[prop]}`, user); } } } catch ( e ) { diff --git a/src/backend/src/services/MeteringService/MeteringService.test.ts b/src/backend/src/services/MeteringService/MeteringService.test.ts index 623f7bed..6bd44d5c 100644 --- a/src/backend/src/services/MeteringService/MeteringService.test.ts +++ b/src/backend/src/services/MeteringService/MeteringService.test.ts @@ -1,15 +1,65 @@ import { describe, expect, it } from 'vitest'; import { createTestKernel } from '../../../tools/test.mjs'; import { MeteringServiceWrapper } from './MeteringServiceWrapper.mjs'; - +import { DBKVServiceWrapper } from '../repositories/DBKVStore/index.mjs'; +import { SUService } from '../SUService'; +import { AlarmService } from '../../modules/core/AlarmService'; +import { EventService } from '../../services/EventService'; +import { SqliteDatabaseAccessService } from '../database/SqliteDatabaseAccessService'; +import { MeteringService } from './MeteringService'; +import * as config from '../../config'; +import { CommandService } from '../CommandService'; +import { TraceService } from '../TraceService'; +import { Actor } from '../auth/Actor'; +import { GetUserService } from '../GetUserService'; +import { DetailProviderService } from '../DetailProviderService'; describe('MeteringService', async () => { - const testKernel = await createTestKernel({ - serviceMap: { - metering: MeteringServiceWrapper, + + config.load_config({ + 'services': { + 'database': { + path: ':memory:', + }, }, }); - it('should have some services', () => { - expect(testKernel.services).not.toBeUndefined(); - expect(testKernel.services!.get('metering')).toBeInstanceOf(MeteringServiceWrapper); + const testKernel = await createTestKernel({ + serviceMap: { + 'whoami': DetailProviderService, + 'get-user': GetUserService, + database: SqliteDatabaseAccessService, + traceService: TraceService, + meteringService: MeteringServiceWrapper, + 'puter-kvstore': DBKVServiceWrapper, + su: SUService, + alarm: AlarmService, + event: EventService, + commands: CommandService, + }, + initLevelString: 'init', }); + await testKernel.services?.get('su').__on('boot.consolidation', []); + + const testSubject = testKernel.services!.get('meteringService') as MeteringServiceWrapper; + + it('should be instantiated', () => { + expect(testSubject).toBeInstanceOf(MeteringServiceWrapper); + }); + + it('should contain a copy of the public methods of meteringService too', () => { + // TODO DS: check all public MeteringService exist on the wrapper + }); + + it('should have meteringService instantiated', async () => { + expect(testSubject.meteringService).toBeInstanceOf(MeteringService); + }); + + it('should record usage for an actor', async () => { + const res = await testSubject.meteringService.incrementUsage({ type: { user: { uuid: 'test-user-id' } } } as unknown as Actor, + 'aws-polly:standard:character', + 1); + + console.log(res); + expect(res).toBeDefined(); + }); + }); diff --git a/src/backend/src/services/SUService.js b/src/backend/src/services/SUService.js index 1d3c22db..ebc1880c 100644 --- a/src/backend/src/services/SUService.js +++ b/src/backend/src/services/SUService.js @@ -18,7 +18,6 @@ */ // METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o-mini"}} -const { get_user } = require('../helpers'); const { Context } = require('../util/context'); const { TeePromise } = require('@heyputer/putility').libs.promise; const { Actor, UserActorType } = require('./auth/Actor'); @@ -56,7 +55,7 @@ class SUService extends BaseService { * system user and actor have been set. */ async ['__on_boot.consolidation'] () { - const sys_user = await get_user({ username: 'system' }); + const sys_user = await this.services.get('get-user').get_user({ username: 'system' }); this.sys_user_.resolve(sys_user); const sys_actor = new Actor({ type: new UserActorType({ diff --git a/src/backend/src/services/User.d.ts b/src/backend/src/services/User.d.ts index 6d0a9248..ae6860fb 100644 --- a/src/backend/src/services/User.d.ts +++ b/src/backend/src/services/User.d.ts @@ -1,6 +1,7 @@ import { SUB_POLICIES } from './MeteringService/subPolicies'; -export interface IUser { uuid: string, +export interface IUser { + uuid: string, username: string, email: string, subscription?: (typeof SUB_POLICIES)[number]['id'], diff --git a/src/backend/src/services/database/SqliteDatabaseAccessService.js b/src/backend/src/services/database/SqliteDatabaseAccessService.js index 4da6923b..2fc9f682 100644 --- a/src/backend/src/services/database/SqliteDatabaseAccessService.js +++ b/src/backend/src/services/database/SqliteDatabaseAccessService.js @@ -43,11 +43,9 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { const require = this.require; const Database = require('better-sqlite3'); - this._register_commands(this.services.get('commands')); - const fs = require('fs'); const path_ = require('path'); - const do_setup = !fs.existsSync(this.config.path); + const do_setup = this.config.path === ':memory:' || !fs.existsSync(this.config.path); this.db = new Database(this.config.path); @@ -185,7 +183,7 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { */ const TARGET_VERSION = (() => { const args = Context.get('args'); - if ( args['database-target-version'] ) { + if ( args?.['database-target-version'] ) { return parseInt(args['database-target-version']); } return HIGHEST_VERSION; @@ -291,6 +289,10 @@ class SqliteDatabaseAccessService extends BaseDatabaseAccessService { }); } + async '__on_boot.consolidation' () { + this._register_commands(this.services.get('commands')); + } + /** * Implementation for prepared statements for READ operations. */ diff --git a/src/backend/tools/test.mjs b/src/backend/tools/test.mjs index 5db6a0f4..92bcc344 100644 --- a/src/backend/tools/test.mjs +++ b/src/backend/tools/test.mjs @@ -105,6 +105,7 @@ export class TestKernel extends AdvancedBase { services, useapi: this.useapi, ['runtime-modules']: this.runtimeModuleRegistry, + args: {}, }, 'app'); this.root_context = root_context; globalThis.root_context = root_context; @@ -274,7 +275,8 @@ export const createTestKernel = async ({ serviceMap, initLevelString = 'construct', }) => { - const initLevelMap = { CONSTRUCT: 1 }; + + const initLevelMap = { CONSTRUCT: 1, INIT: 2 }; const initLevel = initLevelMap[(`${initLevelString}`).toUpperCase()]; const testKernel = new TestKernel(); testKernel.add_module(new Core2Module()); @@ -297,5 +299,11 @@ export const createTestKernel = async ({ await ins.construct(); } } + for ( const name of service_names ) { + const ins = testKernel.services.instances_[name]; + if ( initLevel >= initLevelMap.INIT ) { + await ins.init(); + } + } return testKernel; };