Files
codegraph/__tests__/tauri-command-dead-code.test.ts
Colby MchenryandClaude Opus 5.5 87e79c6ee8 fix(dead-code): exclude annotated Tauri commands (#1543) (#2019)
Rust command attributes were not retained as decorators, so runtime entry points could appear as dead code.
Preserve tauri::command metadata in both native and fallback extraction and reuse the existing decorated exclusion.
Add SQLite regression coverage and native/fallback parity checks, including negative cases.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 13:10:17 +00:00

61 lines
2.5 KiB
TypeScript

import { afterEach, describe, expect, it } from 'vitest';
import fs from 'fs';
import os from 'os';
import path from 'path';
import CodeGraph from '../src/index';
import { buildDeadCodeReport } from '../src/graph/dead-code';
import { buildDeadCode } from '../src/ui-server/api/deadcode';
let root: string;
let cg: CodeGraph | undefined;
afterEach(() => {
cg?.close();
cg = undefined;
if (root) fs.rmSync(root, { recursive: true, force: true });
});
describe('Tauri command dead-code exclusion (#1543)', () => {
it('counts commands as decorated entry points while retaining unannotated helpers', async () => {
root = fs.mkdtempSync(path.join(os.tmpdir(), 'codegraph-tauri-dead-'));
fs.writeFileSync(path.join(root, 'commands.rs'), `
pub fn reached() {}
#[tauri::command]
fn get_mcp_port() -> u16 { 4000 }
#[tauri :: command(rename_all = "snake_case")]
// A comment between attributes and the function is allowed.
#[allow(non_snake_case)]
pub async fn readSettings() {}
fn unused_helper() {}
// #[tauri::command]
fn comment_only() {}
#[other::command]
fn unrelated_attribute() {}
const TEXT: &str = r#"#[tauri::command]"#;
fn string_only() {}
#[tauri::command]
fn outer_command() { fn nested_helper() {} }
fn next_function() {}
`);
fs.writeFileSync(path.join(root, 'main.rs'), 'mod commands;\nfn main() { commands::reached(); }\n');
cg = CodeGraph.initSync(root);
await cg.indexAll();
const nodes = cg.getNodesInFile('commands.rs');
for (const name of ['get_mcp_port', 'readSettings', 'outer_command']) {
expect(nodes.find(n => n.name === name)?.decorators, name).toEqual(['tauri::command']);
}
// Rust export coverage is incomplete; includeExported exercises the actual
// decorator rule rather than letting exportsUnknown hide the regression.
const report = buildDeadCodeReport(cg, { includeExported: true });
expect(report.excluded.decorated).toBe(3);
const wire = buildDeadCode(cg, root, new URLSearchParams('exported=1'));
expect(wire.excluded).toContainEqual({
reason: 'decorated', count: 3, label: 'carrying a decorator, so a framework registers them',
});
expect(buildDeadCodeReport(cg, { includeExported: true, readSource: null }).excluded.decorated).toBe(3);
expect(report.entries.map(e => e.node.name)).toEqual(expect.arrayContaining([
'unused_helper', 'comment_only', 'unrelated_attribute', 'string_only', 'nested_helper', 'next_function',
]));
expect(report.entries.some(e => ['get_mcp_port', 'readSettings', 'outer_command'].includes(e.node.name))).toBe(false);
});
});