| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- /**
- * Multi-word FIELD-NAME query retrieval (#1196).
- *
- * A query bag of object-literal keys / API field names (`profileInfo
- * isTrialEligible quotaInfo billingMethod`) has no nodes of its own — the
- * definers are methods whose names contain each token at a camel-hump
- * boundary (`profileInfo` → `getProfileInfoV2`). Three compounding defects
- * made those definers unreachable:
- * 1. the CamelCase-boundary LIKE step title-cased interior humps
- * (`profileInfo` → `Profileinfo`) and then compared case-SENSITIVELY,
- * dropping every row SQLite's case-insensitive LIKE had just found;
- * 2. that step's kind whitelist held only type-like kinds, so on
- * method-centric codebases it contributed nothing at all;
- * 3. explore's named-symbol seeding was exact-name only, so a field token
- * seeded no files and the output budget went to unrelated neighbors.
- */
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
- import * as fs from 'fs';
- import * as path from 'path';
- import * as os from 'os';
- import CodeGraph from '../src/index';
- import { ToolHandler } from '../src/mcp/tools';
- describe('field-name query retrieval (#1196)', () => {
- let testDir: string;
- let cg: CodeGraph;
- let handler: ToolHandler;
- beforeEach(async () => {
- testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-1196-'));
- fs.mkdirSync(path.join(testDir, 'controller'), { recursive: true });
- fs.mkdirSync(path.join(testDir, 'service'), { recursive: true });
- fs.writeFileSync(
- path.join(testDir, 'controller', 'profileController.js'),
- `const billing = require('../service/billing');
- class ProfileController {
- getProfileInfo(userId) {
- return { profileInfo: { id: userId }, isTrialEligible: this.checkTrialEligibility(userId) };
- }
- getProfileInfoV2(userId) {
- const quotaInfo = this.loadQuotaInfo(userId);
- return { profileInfo: { id: userId }, quotaInfo, billingMethod: billing.getBillingMethod(userId) };
- }
- checkTrialEligibility(userId) { return userId > 100; }
- loadQuotaInfo(userId) { return { used: 1, max: 10, userId }; }
- }
- module.exports = new ProfileController();
- `
- );
- fs.writeFileSync(
- path.join(testDir, 'service', 'billing.js'),
- `function _getCustomerBillingMethods(userId) {
- return [{ type: 'card', userId }];
- }
- function getBillingMethod(userId) {
- return _getCustomerBillingMethods(userId)[0];
- }
- module.exports = { getBillingMethod };
- `
- );
- // Noise files so the definers aren't the only content.
- for (let i = 1; i <= 5; i++) {
- fs.writeFileSync(
- path.join(testDir, 'service', `noise${i}.js`),
- `function unrelatedHelper${i}() { return ${i}; }\nmodule.exports = { unrelatedHelper${i} };\n`
- );
- }
- cg = CodeGraph.initSync(testDir);
- await cg.indexAll();
- handler = new ToolHandler(cg);
- });
- afterEach(() => {
- if (cg) cg.destroy();
- if (fs.existsSync(testDir)) fs.rmSync(testDir, { recursive: true, force: true });
- });
- it('a bag of field-name tokens surfaces the files that DEFINE those fields', async () => {
- const res = await handler.execute('codegraph_explore', {
- query: 'profileInfo isTrialEligible quotaInfo billingMethod',
- });
- const text = res.content[0]!.text as string;
- // The two definer files the reporter saw entirely absent.
- expect(text).toContain('profileController.js');
- expect(text).toContain('billing.js');
- // The camel-infix definers themselves are shown.
- expect(text).toMatch(/getProfileInfo(V2)?/);
- expect(text).toContain('BillingMethod');
- });
- it('exact-name seeding still wins when the token IS a real symbol', async () => {
- // `getBillingMethod` names a real function — the fallback must not
- // dilute or replace exact seeding.
- const res = await handler.execute('codegraph_explore', { query: 'getBillingMethod' });
- const text = res.content[0]!.text as string;
- expect(text).toContain('billing.js');
- expect(text).toContain('getBillingMethod');
- });
- });
|