mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-18 05:41:18 +08:00
5da4a41d92
codegraph_explore's low-value check (the hard filter, the rank penalty and the comparator key) and the dominant-file, route-file and routing-manifest filter in db/queries.ts each kept a regex list of their own. They missed every test convention isTestFile learned since: C++ `foo_test.cc` and `FooTest.cpp`, Google's `foo_unittest.cc`, LLVM's `unittests/`, `testing/`, `e2e/`, `__mocks__/`, Gradle `foo-test/` modules, Kotlin source sets, and the examples, samples, fixtures and benchmarks that explore's gather already dampens through isTestFile. Both now build on isTestFile, keeping icons/i18n (explore) and generated files (queries), plus the singular `__test__/` the old lists matched. Dropping their lowercase suffix patterns also ends false positives such as `Latest.java` and `contest.cs`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
160 lines
4.7 KiB
TypeScript
160 lines
4.7 KiB
TypeScript
/**
|
|
* codegraph_explore drops test files from its answer, and the dominant-file
|
|
* and route-file detectors skip them — but each kept a list of test names of
|
|
* its own, so they missed every convention the engine's `isTestFile` learned
|
|
* later: LLVM's `unittests/` trees (a helper there has no test-like name),
|
|
* Google's `foo_unittest.cc`, a C++ `FooTest.cpp`, `e2e/` folders and the
|
|
* examples around a project. On an LLVM-style repo a unit test's harness, named
|
|
* like the code under test, took a file slot in the answer.
|
|
*/
|
|
import { describe, it, expect, afterAll, beforeAll } from 'vitest';
|
|
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
import { CodeGraph } from '../src';
|
|
import { ToolHandler } from '../src/mcp/tools';
|
|
|
|
const write = (root: string, files: Record<string, string>) => {
|
|
for (const [rel, content] of Object.entries(files)) {
|
|
fs.mkdirSync(path.dirname(path.join(root, rel)), { recursive: true });
|
|
fs.writeFileSync(path.join(root, rel), content);
|
|
}
|
|
};
|
|
|
|
const renderedFiles = (text: string): string[] => [...text.matchAll(/^\*\*`([^`]+)`\*\* —/gm)].map((m) => m[1]!);
|
|
|
|
describe('codegraph_explore leaves out unit tests the engine knows', () => {
|
|
let root = '';
|
|
let cg: CodeGraph;
|
|
|
|
beforeAll(async () => {
|
|
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-explore-low-value-'));
|
|
write(root, {
|
|
'src/lexer.h': `#pragma once
|
|
|
|
class Lexer {
|
|
public:
|
|
int nextToken();
|
|
};
|
|
`,
|
|
'src/lexer.cc': `#include "lexer.h"
|
|
|
|
int Lexer::nextToken() { return 1; }
|
|
`,
|
|
'src/parser.h': `#pragma once
|
|
|
|
class Parser {
|
|
public:
|
|
int parseExpression(const char* text);
|
|
int parseStatement(const char* text);
|
|
};
|
|
`,
|
|
'src/parser.cc': `#include "parser.h"
|
|
#include "lexer.h"
|
|
|
|
int Parser::parseExpression(const char* text) {
|
|
Lexer lexer;
|
|
return lexer.nextToken() + (text ? 1 : 0);
|
|
}
|
|
|
|
int Parser::parseStatement(const char* text) {
|
|
return parseExpression(text) + 1;
|
|
}
|
|
`,
|
|
// Google's naming: the unittest sits beside the code it tests.
|
|
'src/parser_unittest.cc': `#include "parser.h"
|
|
|
|
void ParsesExpressions() {
|
|
Parser parser;
|
|
parser.parseExpression("1 + 2");
|
|
parser.parseStatement("x = 1;");
|
|
}
|
|
`,
|
|
// LLVM's: a unittests/ tree, with a harness named like what it tests.
|
|
'unittests/Parse/ParserHarness.h': `#pragma once
|
|
|
|
#include "../../src/parser.h"
|
|
|
|
class ParserHarness {
|
|
public:
|
|
int parseExpression(const char* text) { return parser.parseExpression(text); }
|
|
int parseStatement(const char* text) { return parser.parseStatement(text); }
|
|
|
|
private:
|
|
Parser parser;
|
|
};
|
|
`,
|
|
'unittests/Parse/ParserTest.cpp': `#include "ParserHarness.h"
|
|
|
|
int ParsesStatements() {
|
|
ParserHarness harness;
|
|
return harness.parseStatement("y = 2;") + harness.parseExpression("3");
|
|
}
|
|
`,
|
|
});
|
|
cg = await CodeGraph.init(root, { index: true });
|
|
}, 60_000);
|
|
|
|
afterAll(() => {
|
|
cg?.close();
|
|
if (root) fs.rmSync(root, { recursive: true, force: true });
|
|
});
|
|
|
|
it('answers from the production files, not the unit tests that call them', async () => {
|
|
const handler = new ToolHandler(cg);
|
|
const result = await handler.execute('codegraph_explore', { query: 'Parser parseExpression parseStatement Lexer nextToken' });
|
|
const files = renderedFiles(result.content?.[0]?.text ?? '');
|
|
expect(files).toContain('src/parser.cc');
|
|
expect(files.filter((f) => /unittests\/|_unittest\.cc$|Test\.cpp$/.test(f))).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe('the route-file detector skips an e2e mock server', () => {
|
|
let root = '';
|
|
let cg: CodeGraph;
|
|
|
|
beforeAll(async () => {
|
|
root = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-route-low-value-'));
|
|
write(root, {
|
|
'package.json': JSON.stringify({ name: 'shop', dependencies: { express: '^4.19.0' } }),
|
|
'src/routes.js': `const express = require('express');
|
|
const router = express.Router();
|
|
|
|
function listOrders(req, res) { res.json([]); }
|
|
function showOrder(req, res) { res.json({}); }
|
|
function createOrder(req, res) { res.status(201).end(); }
|
|
|
|
router.get('/orders', listOrders);
|
|
router.get('/orders/:id', showOrder);
|
|
router.post('/orders', createOrder);
|
|
|
|
module.exports = router;
|
|
`,
|
|
// A stand-in payment provider the end-to-end suite starts.
|
|
'e2e/mock-server.js': `const express = require('express');
|
|
const app = express();
|
|
|
|
function ok(req, res) { res.json({ ok: true }); }
|
|
|
|
app.get('/v1/charges', ok);
|
|
app.post('/v1/charges', ok);
|
|
app.get('/v1/refunds', ok);
|
|
app.post('/v1/refunds', ok);
|
|
app.get('/v1/balance', ok);
|
|
|
|
module.exports = app;
|
|
`,
|
|
});
|
|
cg = await CodeGraph.init(root, { index: true });
|
|
}, 60_000);
|
|
|
|
afterAll(() => {
|
|
cg?.close();
|
|
if (root) fs.rmSync(root, { recursive: true, force: true });
|
|
});
|
|
|
|
it("names the app's own routes file", () => {
|
|
expect(cg.getTopRouteFile()?.filePath).toBe('src/routes.js');
|
|
});
|
|
});
|