test: 固化Bilibili卡片插件架构门禁
This commit is contained in:
parent
98e26616bb
commit
414136a7bd
@ -249,12 +249,81 @@ INSERT INTO qqbot_plugin (
|
|||||||
'Repeater',
|
'Repeater',
|
||||||
'Built-in repeater event plugin metadata.',
|
'Built-in repeater event plugin metadata.',
|
||||||
'installed'
|
'installed'
|
||||||
|
),
|
||||||
|
(
|
||||||
|
1000000000000000105,
|
||||||
|
'bilibili-card',
|
||||||
|
'Bilibili Card',
|
||||||
|
'Built-in Bilibili card event plugin metadata.',
|
||||||
|
'installed'
|
||||||
)
|
)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
plugin_name = VALUES(plugin_name),
|
plugin_name = VALUES(plugin_name),
|
||||||
description = VALUES(description),
|
description = VALUES(description),
|
||||||
status = VALUES(status);
|
status = VALUES(status);
|
||||||
|
|
||||||
|
INSERT INTO qqbot_plugin_version (
|
||||||
|
id,
|
||||||
|
plugin_id,
|
||||||
|
version,
|
||||||
|
package_hash,
|
||||||
|
manifest_json
|
||||||
|
) VALUES (
|
||||||
|
1000000000000001105,
|
||||||
|
1000000000000000105,
|
||||||
|
'1.0.0',
|
||||||
|
'builtin-bilibili-card-1.0.0',
|
||||||
|
JSON_OBJECT(
|
||||||
|
'key', 'bilibili-card',
|
||||||
|
'name', 'Bilibili Card',
|
||||||
|
'version', '1.0.0',
|
||||||
|
'entry', 'src/index.ts',
|
||||||
|
'events', JSON_ARRAY(JSON_OBJECT(
|
||||||
|
'key', 'bilibili-card.message',
|
||||||
|
'eventName', 'message',
|
||||||
|
'handlerName', 'handleMessage',
|
||||||
|
'name', 'Bilibili 卡片解析'
|
||||||
|
))
|
||||||
|
)
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
package_hash = VALUES(package_hash),
|
||||||
|
manifest_json = VALUES(manifest_json);
|
||||||
|
|
||||||
|
INSERT INTO qqbot_plugin_installation (
|
||||||
|
id,
|
||||||
|
plugin_id,
|
||||||
|
version_id,
|
||||||
|
status,
|
||||||
|
runtime_status,
|
||||||
|
installed_path
|
||||||
|
) VALUES (
|
||||||
|
1000000000000001205,
|
||||||
|
1000000000000000105,
|
||||||
|
1000000000000001105,
|
||||||
|
'installed',
|
||||||
|
'idle',
|
||||||
|
'src/modules/qqbot/plugins/bilibili-card'
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
status = VALUES(status),
|
||||||
|
runtime_status = VALUES(runtime_status),
|
||||||
|
installed_path = VALUES(installed_path);
|
||||||
|
|
||||||
|
INSERT INTO qqbot_plugin_event_handler (
|
||||||
|
id,
|
||||||
|
plugin_id,
|
||||||
|
event_key,
|
||||||
|
handler_name,
|
||||||
|
enabled
|
||||||
|
) VALUES (
|
||||||
|
1000000000000001305,
|
||||||
|
1000000000000000105,
|
||||||
|
'bilibili-card.message',
|
||||||
|
'handleMessage',
|
||||||
|
1
|
||||||
|
) ON DUPLICATE KEY UPDATE
|
||||||
|
handler_name = VALUES(handler_name),
|
||||||
|
enabled = VALUES(enabled);
|
||||||
|
|
||||||
INSERT INTO qqbot_command (
|
INSERT INTO qqbot_command (
|
||||||
id,
|
id,
|
||||||
operation_key,
|
operation_key,
|
||||||
|
|||||||
@ -36,6 +36,17 @@ FROM qqbot_plugin
|
|||||||
WHERE plugin_key = 'bangdream'
|
WHERE plugin_key = 'bangdream'
|
||||||
AND status = 'installed';
|
AND status = 'installed';
|
||||||
|
|
||||||
|
SELECT 'seed_qqbot_plugin_bilibili_card' AS check_name, COUNT(*) AS matched_rows
|
||||||
|
FROM qqbot_plugin
|
||||||
|
WHERE plugin_key = 'bilibili-card'
|
||||||
|
AND status = 'installed';
|
||||||
|
|
||||||
|
SELECT 'seed_qqbot_plugin_event_bilibili_card' AS check_name, COUNT(*) AS matched_rows
|
||||||
|
FROM qqbot_plugin_event_handler
|
||||||
|
WHERE event_key = 'bilibili-card.message'
|
||||||
|
AND handler_name = 'handleMessage'
|
||||||
|
AND enabled = 1;
|
||||||
|
|
||||||
SELECT 'seed_qqbot_command_bangdream_song' AS check_name, COUNT(*) AS matched_rows
|
SELECT 'seed_qqbot_command_bangdream_song' AS check_name, COUNT(*) AS matched_rows
|
||||||
FROM qqbot_command
|
FROM qqbot_command
|
||||||
WHERE command_key = 'bangdream_song'
|
WHERE command_key = 'bangdream_song'
|
||||||
|
|||||||
@ -142,6 +142,7 @@ describe('QQBot current operation matrix', () => {
|
|||||||
it('freezes built-in plugin manifests and exposed capabilities', () => {
|
it('freezes built-in plugin manifests and exposed capabilities', () => {
|
||||||
const manifests = {
|
const manifests = {
|
||||||
bangdream: readManifest('bangdream'),
|
bangdream: readManifest('bangdream'),
|
||||||
|
bilibiliCard: readManifest('bilibili-card'),
|
||||||
ff14: readManifest('ff14-market'),
|
ff14: readManifest('ff14-market'),
|
||||||
fflogs: readManifest('fflogs'),
|
fflogs: readManifest('fflogs'),
|
||||||
repeater: readManifest('repeater'),
|
repeater: readManifest('repeater'),
|
||||||
@ -157,6 +158,23 @@ describe('QQBot current operation matrix', () => {
|
|||||||
})),
|
})),
|
||||||
).toEqual(bangdreamOperations);
|
).toEqual(bangdreamOperations);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
manifests.bilibiliCard.events.map((event) => ({
|
||||||
|
eventName: event.eventName,
|
||||||
|
handlerName: event.handlerName,
|
||||||
|
key: event.key,
|
||||||
|
name: event.name,
|
||||||
|
})),
|
||||||
|
).toEqual([
|
||||||
|
{
|
||||||
|
eventName: 'message',
|
||||||
|
handlerName: 'handleMessage',
|
||||||
|
key: 'bilibili-card.message',
|
||||||
|
name: 'Bilibili 卡片解析',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
expect(manifests.bilibiliCard.operations).toEqual([]);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
manifests.ff14.operations.map((operation) => ({
|
manifests.ff14.operations.map((operation) => ({
|
||||||
aliases: operation.aliases,
|
aliases: operation.aliases,
|
||||||
@ -215,6 +233,10 @@ describe('QQBot current operation matrix', () => {
|
|||||||
|
|
||||||
it('freezes current online command seed linkage for command plugins', () => {
|
it('freezes current online command seed linkage for command plugins', () => {
|
||||||
const seedSql = readFileSync(join(repoRoot, 'sql/qqbot-init.sql'), 'utf8');
|
const seedSql = readFileSync(join(repoRoot, 'sql/qqbot-init.sql'), 'utf8');
|
||||||
|
const refactorSeedSql = readFileSync(
|
||||||
|
join(repoRoot, 'sql/refactor-v3/01-seed-core.sql'),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
for (const operation of bangdreamOperations) {
|
for (const operation of bangdreamOperations) {
|
||||||
expect(seedSql).toContain(`'${operation.key}'`);
|
expect(seedSql).toContain(`'${operation.key}'`);
|
||||||
@ -224,5 +246,7 @@ describe('QQBot current operation matrix', () => {
|
|||||||
expect(seedSql).toContain(`'ff14-market', 'ff14.market.price'`);
|
expect(seedSql).toContain(`'ff14-market', 'ff14.market.price'`);
|
||||||
expect(seedSql).toContain(`'fflogs', 'fflogs.character.summary'`);
|
expect(seedSql).toContain(`'fflogs', 'fflogs.character.summary'`);
|
||||||
expect(seedSql).toContain(`'bangdream', 'bangdream.event.stage', 'plain'`);
|
expect(seedSql).toContain(`'bangdream', 'bangdream.event.stage', 'plain'`);
|
||||||
|
expect(refactorSeedSql).toContain(`'bilibili-card'`);
|
||||||
|
expect(refactorSeedSql).toContain(`'bilibili-card.message'`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -97,6 +97,7 @@ describe('QQBot plugin package boundary', () => {
|
|||||||
|
|
||||||
expect(pluginDirs).toEqual([
|
expect(pluginDirs).toEqual([
|
||||||
'bangdream',
|
'bangdream',
|
||||||
|
'bilibili-card',
|
||||||
'ff14-market',
|
'ff14-market',
|
||||||
'fflogs',
|
'fflogs',
|
||||||
'repeater',
|
'repeater',
|
||||||
@ -104,8 +105,13 @@ describe('QQBot plugin package boundary', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('uses the same package shape for every built-in plugin', () => {
|
it('uses the same package shape for every built-in plugin', () => {
|
||||||
const missing = ['bangdream', 'ff14-market', 'fflogs', 'repeater'].flatMap(
|
const missing = [
|
||||||
(pluginKey) => {
|
'bangdream',
|
||||||
|
'bilibili-card',
|
||||||
|
'ff14-market',
|
||||||
|
'fflogs',
|
||||||
|
'repeater',
|
||||||
|
].flatMap((pluginKey) => {
|
||||||
const manifest = JSON.parse(
|
const manifest = JSON.parse(
|
||||||
readFileSync(join(pluginRoot, pluginKey, 'plugin.json'), 'utf8'),
|
readFileSync(join(pluginRoot, pluginKey, 'plugin.json'), 'utf8'),
|
||||||
) as { events?: unknown[]; operations?: unknown[] };
|
) as { events?: unknown[]; operations?: unknown[] };
|
||||||
@ -119,8 +125,7 @@ describe('QQBot plugin package boundary', () => {
|
|||||||
return requiredPaths
|
return requiredPaths
|
||||||
.map((pathName) => `${pluginKey}/${pathName}`)
|
.map((pathName) => `${pluginKey}/${pathName}`)
|
||||||
.filter((pathName) => !existsSync(join(pluginRoot, pathName)));
|
.filter((pathName) => !existsSync(join(pluginRoot, pathName)));
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
expect(missing).toEqual([]);
|
expect(missing).toEqual([]);
|
||||||
});
|
});
|
||||||
@ -128,6 +133,7 @@ describe('QQBot plugin package boundary', () => {
|
|||||||
it('does not keep third-phase package directories as empty shells', () => {
|
it('does not keep third-phase package directories as empty shells', () => {
|
||||||
const emptyRequiredDirs = [
|
const emptyRequiredDirs = [
|
||||||
'bangdream',
|
'bangdream',
|
||||||
|
'bilibili-card',
|
||||||
'ff14-market',
|
'ff14-market',
|
||||||
'fflogs',
|
'fflogs',
|
||||||
'repeater',
|
'repeater',
|
||||||
@ -242,6 +248,7 @@ describe('QQBot plugin package boundary', () => {
|
|||||||
it('keeps plugin package entrypoints limited to createPlugin', () => {
|
it('keeps plugin package entrypoints limited to createPlugin', () => {
|
||||||
const extraExports = [
|
const extraExports = [
|
||||||
'bangdream',
|
'bangdream',
|
||||||
|
'bilibili-card',
|
||||||
'ff14-market',
|
'ff14-market',
|
||||||
'fflogs',
|
'fflogs',
|
||||||
'repeater',
|
'repeater',
|
||||||
@ -319,7 +326,7 @@ describe('plugin platform package decoupling', () => {
|
|||||||
.join('|'),
|
.join('|'),
|
||||||
);
|
);
|
||||||
const forbiddenBranchPattern =
|
const forbiddenBranchPattern =
|
||||||
/pluginKey\s*(?:={2,3})\s*['"`](bangdream|ff14-market|fflogs|repeater)['"`]|case\s+['"`](bangdream|ff14-market|fflogs|repeater)['"`]/;
|
/pluginKey\s*(?:={2,3})\s*['"`](bangdream|bilibili-card|ff14-market|fflogs|repeater)['"`]|case\s+['"`](bangdream|bilibili-card|ff14-market|fflogs|repeater)['"`]/;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Escapes a literal token before it is placed into the architecture gate regexp.
|
* Escapes a literal token before it is placed into the architecture gate regexp.
|
||||||
|
|||||||
@ -41,7 +41,13 @@ describe('QQBot existing plugin platform migration', () => {
|
|||||||
readdirSync(pluginRoot)
|
readdirSync(pluginRoot)
|
||||||
.filter((name) => statSync(join(pluginRoot, name)).isDirectory())
|
.filter((name) => statSync(join(pluginRoot, name)).isDirectory())
|
||||||
.sort(),
|
.sort(),
|
||||||
).toEqual(['bangdream', 'ff14-market', 'fflogs', 'repeater']);
|
).toEqual([
|
||||||
|
'bangdream',
|
||||||
|
'bilibili-card',
|
||||||
|
'ff14-market',
|
||||||
|
'fflogs',
|
||||||
|
'repeater',
|
||||||
|
]);
|
||||||
|
|
||||||
const legacySources = collectFiles(legacyPluginRoot).filter((filePath) =>
|
const legacySources = collectFiles(legacyPluginRoot).filter((filePath) =>
|
||||||
filePath.endsWith('.ts'),
|
filePath.endsWith('.ts'),
|
||||||
@ -50,8 +56,13 @@ describe('QQBot existing plugin platform migration', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('declares parseable platform manifests for every existing plugin', () => {
|
it('declares parseable platform manifests for every existing plugin', () => {
|
||||||
const manifests = ['bangdream', 'ff14-market', 'fflogs', 'repeater'].map(
|
const manifests = [
|
||||||
(pluginName) => {
|
'bangdream',
|
||||||
|
'bilibili-card',
|
||||||
|
'ff14-market',
|
||||||
|
'fflogs',
|
||||||
|
'repeater',
|
||||||
|
].map((pluginName) => {
|
||||||
const root = join(pluginRoot, pluginName);
|
const root = join(pluginRoot, pluginName);
|
||||||
const manifest = parseQqbotPluginManifest(
|
const manifest = parseQqbotPluginManifest(
|
||||||
readJson(join(root, 'plugin.json')),
|
readJson(join(root, 'plugin.json')),
|
||||||
@ -60,11 +71,11 @@ describe('QQBot existing plugin platform migration', () => {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
return manifest;
|
return manifest;
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
expect(manifests.map((manifest) => manifest.pluginKey).sort()).toEqual([
|
expect(manifests.map((manifest) => manifest.pluginKey).sort()).toEqual([
|
||||||
'bangdream',
|
'bangdream',
|
||||||
|
'bilibili-card',
|
||||||
'ff14-market',
|
'ff14-market',
|
||||||
'fflogs',
|
'fflogs',
|
||||||
'repeater',
|
'repeater',
|
||||||
@ -79,6 +90,16 @@ describe('QQBot existing plugin platform migration', () => {
|
|||||||
).toBe(true);
|
).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('seeds Bilibili card plugin event metadata in refactor v3 SQL', () => {
|
||||||
|
const refactorSeedSql = readFileSync(
|
||||||
|
join(repoRoot, 'sql/refactor-v3/01-seed-core.sql'),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(refactorSeedSql).toContain(`'bilibili-card'`);
|
||||||
|
expect(refactorSeedSql).toContain(`'bilibili-card.message'`);
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps BangDream manifest operations as the single metadata source', () => {
|
it('keeps BangDream manifest operations as the single metadata source', () => {
|
||||||
const manifest = parseQqbotPluginManifest(
|
const manifest = parseQqbotPluginManifest(
|
||||||
readJson(join(pluginRoot, 'bangdream/plugin.json')),
|
readJson(join(pluginRoot, 'bangdream/plugin.json')),
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user