test(claude-import): use portable fixtures for marketplace discovery tests
This commit is contained in:
parent
232eaf41fe
commit
13730189f2
2 changed files with 52 additions and 59 deletions
|
|
@ -16,23 +16,20 @@ import {
|
|||
discoverMarketplace,
|
||||
resolvePluginRoot
|
||||
} from '../marketplace-discovery';
|
||||
import { getMarketplaceFixtures } from './marketplace-test-fixtures.js';
|
||||
|
||||
// Resolve paths relative to the gsd-2 project root so CI can skip cleanly when repos are absent
|
||||
const GSD2_ROOT = path.resolve(import.meta.dirname, '../../../../..');
|
||||
const CLAUDE_SKILLS_PATH = path.resolve(GSD2_ROOT, '../claude_skills');
|
||||
const CLAUDE_PLUGINS_OFFICIAL_PATH = path.resolve(GSD2_ROOT, '../claude-plugins-official');
|
||||
const fixtureSetup = getMarketplaceFixtures(import.meta.dirname);
|
||||
const fixtures = fixtureSetup.fixtures;
|
||||
const CLAUDE_SKILLS_PATH = fixtures?.claudeSkillsPath;
|
||||
const CLAUDE_PLUGINS_OFFICIAL_PATH = fixtures?.claudePluginsOfficialPath;
|
||||
|
||||
function marketplacesAvailable(): boolean {
|
||||
return fs.existsSync(CLAUDE_SKILLS_PATH) && fs.existsSync(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
}
|
||||
|
||||
const skipReason = !marketplacesAvailable()
|
||||
? `Marketplace repos not found: ${CLAUDE_SKILLS_PATH}, ${CLAUDE_PLUGINS_OFFICIAL_PATH}`
|
||||
const skipReason = !fixtureSetup.available
|
||||
? fixtureSetup.skipReason ?? 'Marketplace repos not found'
|
||||
: undefined;
|
||||
|
||||
describe('parseMarketplaceJson', { skip: skipReason }, () => {
|
||||
it('should parse jamie-style marketplace.json', () => {
|
||||
const result = parseMarketplaceJson(CLAUDE_SKILLS_PATH);
|
||||
const result = parseMarketplaceJson(CLAUDE_SKILLS_PATH!);
|
||||
assert.strictEqual(result.success, true);
|
||||
if (result.success) {
|
||||
assert.strictEqual(result.manifest.name, 'jamie-bitflight-skills');
|
||||
|
|
@ -41,7 +38,7 @@ describe('parseMarketplaceJson', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should parse official-style marketplace.json', () => {
|
||||
const result = parseMarketplaceJson(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = parseMarketplaceJson(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
assert.strictEqual(result.success, true);
|
||||
if (result.success) {
|
||||
assert.strictEqual(result.manifest.name, 'claude-plugins-official');
|
||||
|
|
@ -74,29 +71,29 @@ describe('parseMarketplaceJson', { skip: skipReason }, () => {
|
|||
|
||||
describe('resolvePluginRoot', () => {
|
||||
it('should resolve relative paths correctly', () => {
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH, './plugins/python3-development');
|
||||
assert.strictEqual(result, path.join(CLAUDE_SKILLS_PATH, 'plugins/python3-development'));
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, './plugins/python3-development');
|
||||
assert.strictEqual(result, path.join(CLAUDE_SKILLS_PATH!, 'plugins/python3-development'));
|
||||
});
|
||||
|
||||
it('should handle paths without ./ prefix', () => {
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH, 'plugins/python3-development');
|
||||
assert.strictEqual(result, path.join(CLAUDE_SKILLS_PATH, 'plugins/python3-development'));
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, 'plugins/python3-development');
|
||||
assert.strictEqual(result, path.join(CLAUDE_SKILLS_PATH!, 'plugins/python3-development'));
|
||||
});
|
||||
|
||||
it('should return null for external sources', () => {
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH, 'https://github.com/example/plugin');
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, 'https://github.com/example/plugin');
|
||||
assert.strictEqual(result, null);
|
||||
});
|
||||
|
||||
it('should return null for git sources', () => {
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH, { source: 'github', repo: 'example/plugin' });
|
||||
const result = resolvePluginRoot(CLAUDE_SKILLS_PATH!, { source: 'github', repo: 'example/plugin' });
|
||||
assert.strictEqual(result, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inspectPlugin', () => {
|
||||
it('should inspect a plugin with plugin.json', () => {
|
||||
const pluginDir = path.join(CLAUDE_SKILLS_PATH, 'plugins/python3-development');
|
||||
const pluginDir = path.join(CLAUDE_SKILLS_PATH!, 'plugins/python3-development');
|
||||
const result = inspectPlugin(pluginDir);
|
||||
|
||||
assert.strictEqual(result.status, 'ok');
|
||||
|
|
@ -119,7 +116,7 @@ describe('inspectPlugin', () => {
|
|||
|
||||
describe('discoverMarketplace', () => {
|
||||
it('should discover all plugins in jamie-style marketplace', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
|
||||
assert.strictEqual(result.status, 'ok');
|
||||
assert.strictEqual(result.pluginFormat, 'jamie-style');
|
||||
|
|
@ -132,7 +129,7 @@ describe('discoverMarketplace', () => {
|
|||
});
|
||||
|
||||
it('should discover all plugins in official-style marketplace', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
assert.strictEqual(result.status, 'ok');
|
||||
assert.strictEqual(result.pluginFormat, 'official-style');
|
||||
|
|
@ -150,7 +147,7 @@ describe('discoverMarketplace', () => {
|
|||
});
|
||||
|
||||
it('should inventory skills, agents, commands correctly', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const pythonPlugin = result.plugins.find(p => p.name === 'python3-development');
|
||||
|
||||
assert.ok(pythonPlugin !== undefined);
|
||||
|
|
@ -162,7 +159,7 @@ describe('discoverMarketplace', () => {
|
|||
});
|
||||
|
||||
it('should discover MCP servers from plugin.json', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const pythonPlugin = result.plugins.find(p => p.name === 'python3-development');
|
||||
|
||||
assert.ok(pythonPlugin !== undefined);
|
||||
|
|
@ -172,7 +169,7 @@ describe('discoverMarketplace', () => {
|
|||
});
|
||||
|
||||
it('should discover LSP servers from marketplace.json', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
const tsPlugin = result.plugins.find(p => p.name === 'typescript-lsp');
|
||||
|
||||
assert.ok(tsPlugin !== undefined);
|
||||
|
|
@ -182,7 +179,7 @@ describe('discoverMarketplace', () => {
|
|||
});
|
||||
|
||||
it('should detect external plugins correctly', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
const externalPlugin = result.plugins.find(p => p.name === 'atlassian');
|
||||
|
||||
assert.ok(externalPlugin !== undefined);
|
||||
|
|
@ -195,8 +192,8 @@ describe('discoverMarketplace', () => {
|
|||
|
||||
describe('smoke test', () => {
|
||||
it('should be able to run discovery from both marketplace repos', () => {
|
||||
const jamieResult = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const officialResult = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const jamieResult = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const officialResult = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
assert.strictEqual(jamieResult.status, 'ok');
|
||||
assert.strictEqual(officialResult.status, 'ok');
|
||||
|
|
|
|||
|
|
@ -20,25 +20,21 @@ import {
|
|||
discoverMarketplace,
|
||||
resolvePluginRoot
|
||||
} from '../resources/extensions/gsd/marketplace-discovery.js';
|
||||
import { getMarketplaceFixtures } from '../resources/extensions/gsd/tests/marketplace-test-fixtures.js';
|
||||
|
||||
// Resolve paths to the external marketplace repos
|
||||
// Tests run from src/tests/, so we need to go up to gsd-2, then into ../claude_skills
|
||||
const REPOS_BASE = path.resolve(import.meta.dirname, '../../..');
|
||||
const CLAUDE_SKILLS_PATH = path.join(REPOS_BASE, 'claude_skills');
|
||||
const CLAUDE_PLUGINS_OFFICIAL_PATH = path.join(REPOS_BASE, 'claude-plugins-official');
|
||||
const fixtureSetup = getMarketplaceFixtures(import.meta.dirname);
|
||||
const fixtures = fixtureSetup.fixtures;
|
||||
const CLAUDE_SKILLS_PATH = fixtures?.claudeSkillsPath;
|
||||
const CLAUDE_PLUGINS_OFFICIAL_PATH = fixtures?.claudePluginsOfficialPath;
|
||||
|
||||
function marketplacesAvailable(): boolean {
|
||||
return fs.existsSync(CLAUDE_SKILLS_PATH) && fs.existsSync(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
}
|
||||
|
||||
const skipReason = !marketplacesAvailable()
|
||||
? `Marketplace repos not found: ${CLAUDE_SKILLS_PATH}, ${CLAUDE_PLUGINS_OFFICIAL_PATH}`
|
||||
const skipReason = !fixtureSetup.available
|
||||
? fixtureSetup.skipReason ?? 'Marketplace repos not found'
|
||||
: undefined;
|
||||
|
||||
describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
||||
describe('claude_skills marketplace (jamie-style)', () => {
|
||||
it('should discover at least 15 plugins', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
|
||||
assert.strictEqual(result.status, 'ok', `Expected ok status, got error: ${result.error}`);
|
||||
assert.ok(result.plugins.length >= 15,
|
||||
|
|
@ -46,13 +42,13 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should detect jamie-style format', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
|
||||
assert.strictEqual(result.pluginFormat, 'jamie-style');
|
||||
});
|
||||
|
||||
it('should verify python3-development has skills and agents', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const pythonPlugin = result.plugins.find(p => p.name === 'python3-development');
|
||||
|
||||
assert.ok(pythonPlugin, 'python3-development plugin should exist');
|
||||
|
|
@ -73,7 +69,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should verify all resolved paths exist on disk', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
|
||||
// Filter plugins with resolved paths (local plugins, not external)
|
||||
const localPlugins = result.plugins.filter(p => p.resolvedPath !== null);
|
||||
|
|
@ -87,7 +83,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should preserve canonical names for known plugins', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const knownPluginNames = [
|
||||
'python3-development',
|
||||
'bash-development',
|
||||
|
|
@ -106,7 +102,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should have consistent summary counts', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
|
||||
assert.strictEqual(result.summary.total, result.plugins.length,
|
||||
'Total count should match plugins array length');
|
||||
|
|
@ -121,7 +117,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
|
||||
describe('claude-plugins-official marketplace (official-style)', () => {
|
||||
it('should discover at least 10 plugins', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
assert.strictEqual(result.status, 'ok', `Expected ok status, got error: ${result.error}`);
|
||||
assert.ok(result.plugins.length >= 10,
|
||||
|
|
@ -129,13 +125,13 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should detect official-style format', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
assert.strictEqual(result.pluginFormat, 'official-style');
|
||||
});
|
||||
|
||||
it('should extract LSP servers from inline marketplace metadata', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
// TypeScript LSP plugin should have lspServers from marketplace.json
|
||||
const tsPlugin = result.plugins.find(p => p.name === 'typescript-lsp');
|
||||
|
|
@ -152,7 +148,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should have description from inline metadata', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
const tsPlugin = result.plugins.find(p => p.name === 'typescript-lsp');
|
||||
assert.ok(tsPlugin, 'typescript-lsp plugin should exist');
|
||||
|
|
@ -162,7 +158,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should handle external plugins (URL sources) correctly', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
// Find plugins with URL sources (external)
|
||||
const externalPlugins = result.plugins.filter(p => p.resolvedPath === null);
|
||||
|
|
@ -178,7 +174,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should preserve canonical names for known official plugins', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
const knownPluginNames = [
|
||||
'typescript-lsp',
|
||||
'pyright-lsp',
|
||||
|
|
@ -197,7 +193,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should extract multiple LSP server types', () => {
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
// Check that multiple LSP plugins have their servers extracted
|
||||
const lspPlugins = [
|
||||
|
|
@ -313,7 +309,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
|
||||
describe('Component inventory accuracy', () => {
|
||||
it('should accurately count skills in python3-development', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const pythonPlugin = result.plugins.find(p => p.name === 'python3-development');
|
||||
|
||||
assert.ok(pythonPlugin, 'python3-development should exist');
|
||||
|
|
@ -334,7 +330,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should discover MCP servers from plugin.json', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const pythonPlugin = result.plugins.find(p => p.name === 'python3-development');
|
||||
|
||||
assert.ok(pythonPlugin, 'python3-development should exist');
|
||||
|
|
@ -343,7 +339,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should include commands in inventory when present', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const pythonPlugin = result.plugins.find(p => p.name === 'python3-development');
|
||||
|
||||
assert.ok(pythonPlugin, 'python3-development should exist');
|
||||
|
|
@ -352,7 +348,7 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should detect hooks when present', () => {
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const result = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
|
||||
// Find any plugin with hooks
|
||||
const pluginWithHooks = result.plugins.find(p =>
|
||||
|
|
@ -367,8 +363,8 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
|
||||
describe('Cross-marketplace consistency', () => {
|
||||
it('should return consistent type structure for both marketplaces', () => {
|
||||
const jamie = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const official = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const jamie = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const official = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
// Both should have the same top-level structure
|
||||
const requiredKeys = ['status', 'marketplacePath', 'marketplaceName',
|
||||
|
|
@ -388,8 +384,8 @@ describe('Marketplace Discovery Contract Tests', { skip: skipReason }, () => {
|
|||
});
|
||||
|
||||
it('should return consistent plugin structure', () => {
|
||||
const jamie = discoverMarketplace(CLAUDE_SKILLS_PATH);
|
||||
const official = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH);
|
||||
const jamie = discoverMarketplace(CLAUDE_SKILLS_PATH!);
|
||||
const official = discoverMarketplace(CLAUDE_PLUGINS_OFFICIAL_PATH!);
|
||||
|
||||
const jamiePlugin = jamie.plugins[0];
|
||||
const officialPlugin = official.plugins[0];
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue