-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathprototype-method-resolution.test.ts
More file actions
108 lines (96 loc) · 3.27 KB
/
Copy pathprototype-method-resolution.test.ts
File metadata and controls
108 lines (96 loc) · 3.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* Integration test for #1317: prototype-based method call resolution.
*
* Verifies that the call-resolver correctly builds edges for:
* 1. `Foo.prototype.bar = function(){}` — direct method definition
* 2. `(new Foo).bar()` — inline new-expression receiver
*/
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import Database from 'better-sqlite3';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
import { buildGraph } from '../../src/domain/graph/builder.js';
const FIXTURE = {
'proto.js': `
function Animal(name) {
this.name = name;
}
Animal.prototype.speak = function() {
return this.name + ' speaks';
};
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.bark = function() {
return this.name + ' barks';
};
function makeAndBark() {
// inline new-expression receiver: (new Dog('Rex')).bark()
return (new Dog('Rex')).bark();
}
const d = new Dog('Buddy');
d.speak();
d.bark();
`,
};
let tmpDir: string;
beforeAll(async () => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-1317-'));
for (const [rel, content] of Object.entries(FIXTURE)) {
fs.writeFileSync(path.join(tmpDir, rel), content);
}
// TODO(#1381): pinned to WASM because the published native binary predates the
// prototype-method fixes landed in #1331. Remove the pin (or add a dual-engine
// variant) once the native binary ships the corresponding Rust-side extraction.
await buildGraph(tmpDir, { incremental: false, skipRegistry: true, engine: 'wasm' });
});
afterAll(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
function readCallEdges(dbPath: string) {
const db = new Database(dbPath, { readonly: true });
try {
return db
.prepare(
`SELECT n1.name AS src, n2.name AS tgt
FROM edges e
JOIN nodes n1 ON e.source_id = n1.id
JOIN nodes n2 ON e.target_id = n2.id
WHERE e.kind = 'calls'
ORDER BY n1.name, n2.name`,
)
.all() as Array<{ src: string; tgt: string }>;
} finally {
db.close();
}
}
describe('prototype method resolution (#1317)', () => {
it('emits Dog.bark as a method definition', () => {
const dbPath = path.join(tmpDir, '.codegraph', 'graph.db');
const db = new Database(dbPath, { readonly: true });
try {
const node = db.prepare(`SELECT name, kind FROM nodes WHERE name = 'Dog.bark'`).get() as
| { name: string; kind: string }
| undefined;
expect(node).toBeDefined();
expect(node?.kind).toBe('method');
} finally {
db.close();
}
});
it('resolves d.bark() call to Dog.bark via typeMap receiver type', () => {
const dbPath = path.join(tmpDir, '.codegraph', 'graph.db');
const edges = readCallEdges(dbPath);
const barkEdge = edges.find((e) => e.tgt === 'Dog.bark');
expect(barkEdge).toBeDefined();
});
it('resolves (new Dog(...)).bark() inline-new receiver call to Dog.bark', () => {
const dbPath = path.join(tmpDir, '.codegraph', 'graph.db');
const edges = readCallEdges(dbPath);
// makeAndBark calls (new Dog('Rex')).bark() — inline new receiver
const inlineNewEdge = edges.find((e) => e.src === 'makeAndBark' && e.tgt === 'Dog.bark');
expect(inlineNewEdge).toBeDefined();
});
});