diff --git a/src/modules/qqbot/core/application/config/qqbot-config.service.ts b/src/modules/qqbot/core/application/config/qqbot-config.service.ts index 2cc44aa..30a5e38 100644 --- a/src/modules/qqbot/core/application/config/qqbot-config.service.ts +++ b/src/modules/qqbot/core/application/config/qqbot-config.service.ts @@ -48,7 +48,7 @@ export class QqbotConfigService { const record = await this.configRepository.findOne({ where: { configKey }, }); - return record?.configValue || undefined; + return record?.configValue ?? undefined; } /** diff --git a/test/modules/qqbot/plugin-platform/plugin-host-bridge.spec.ts b/test/modules/qqbot/plugin-platform/plugin-host-bridge.spec.ts index 4de1c6c..1e8f350 100644 --- a/test/modules/qqbot/plugin-platform/plugin-host-bridge.spec.ts +++ b/test/modules/qqbot/plugin-platform/plugin-host-bridge.spec.ts @@ -27,16 +27,9 @@ describe('QQBot plugin host bridge', () => { SAMPLE_TOKEN: 'token-1', SAMPLE_EMPTY: undefined, }; - const configService = { - /** - * Reads the fixture config value requested by a plugin host call. - * @param configKey - Package-owned config key from the host call arguments. - * @returns Stored fixture value or `undefined` when the key is absent. - */ - getConfigValue: jest.fn( - async (configKey: string) => configValues[configKey], - ), - } as unknown as QqbotConfigService; + const configService = new QqbotConfigService( + createConfigRepository(configValues), + ); const dictService = { getDictByKey: jest.fn(), getDictItemsByKey: jest.fn(), @@ -84,6 +77,23 @@ describe('QQBot plugin host bridge', () => { }); }); + it('preserves configured empty string values in config snapshots', async () => { + configValues.SAMPLE_EMPTY = ''; + + await expect( + bridge.handleHostCall(createDescriptor(), { + args: { keys: ['SAMPLE_EMPTY'] }, + method: 'getConfigMany', + pluginKey: 'sample', + }), + ).resolves.toEqual({ + ok: true, + value: { + SAMPLE_EMPTY: '', + }, + }); + }); + it('rejects JSON reads that try to escape the package root', async () => { await expect( bridge.handleHostCall(createDescriptor(), { @@ -195,3 +205,32 @@ describe('QQBot plugin host bridge', () => { }; } }); + +/** + * Creates a minimal config repository that lets QqbotConfigService read fixture values through its real raw-value method. + * @param values - Config key/value fixture; absent keys behave like missing records. + * @returns Repository-shaped fixture used by QqbotConfigService in host bridge tests. + */ +function createConfigRepository( + values: Record, +): ConstructorParameters[0] { + /** + * Finds one QQBot config fixture using the TypeORM `where.configKey` query shape. + * @param query - TypeORM-style lookup object produced by QqbotConfigService. + * @returns Fixture record with the stored config value, or `null` when the key is not present. + */ + const findOne = async (query: { where?: { configKey?: string } }) => { + const configKey = query.where?.configKey; + if ( + !configKey || + !Object.prototype.hasOwnProperty.call(values, configKey) + ) { + return null; + } + return { configValue: values[configKey] }; + }; + + return { + findOne: jest.fn(findOne), + } as unknown as ConstructorParameters[0]; +}