mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-11 22:53:04 +00:00
Trimmed rebuild of 6ea5aa44 (fork PR #4): Move core (gitnexus/src/core/move/)
and Move tests, plus the functional minimum elsewhere.
Dropped from the original commit:
- .gitignore .codex/ entry (unrelated)
- cli/analyze.ts env-constant swap (cosmetic; same string literal)
- core/logger.ts warnRespectingProgressBar extraction and the
filesystem-walker.ts reuse of it (Move code never imports the helper)
- scope-resolution callable-value-flow warning aggregation and pipeline/run.ts
progress-warning formatting, with their two tests (warning-UX hardening,
not Move-functional)
- process-detection interface doc-comment retuning (DEFAULT_CONFIG runtime
values already match upstream; region left byte-identical to upstream)
Kept outside src/core/move/ and Move tests (one line each):
- gitnexus-shared/src/graph/types.ts: 'external' locationFidelity for
dependency symbols
- gitnexus-shared/src/lbug/schema-constants.ts: 'Type' node table registration
- core/lbug/schema.ts: Type table schema + Move rel-table endpoint pairs
- core/lbug/node-table-layout.ts: TYPE_LAYOUT CSV layout
- core/lbug/csv-generator.ts: 'Type' in MULTI_LANG_TYPES routing
- core/lbug/lbug-adapter.ts: deleteAllExternalNodes + shared delete-by-label
mechanics; Type backtick entry
- core/incremental/subgraph-extract.ts: external nodes get the
delete-all-then-rebuild treatment
- core/run-analyze.ts: Move consistency digest to meta.json;
deleteAllExternalNodes call; grouped Move meta; finally-based shutdown
- storage/repo-manager.ts: moveConsistency in RepoMeta;
INCREMENTAL_SCHEMA_VERSION 13 (Type/EnumVariant persistence)
- core/ingestion/pipeline.ts: generic TStandaloneIngest output threading
- types/pipeline.ts: PipelineResult.standaloneIngest replaces ingestWarnings
- core/ingestion/process-processor.ts: ENTRY_POINT_OF explicit graph roots
- tests: schema, node-table-layout, process-processor,
repo-manager-reconcile, call-summary-schema-version,
incremental-subgraph-extract, run-analyze-fts-repair (unit);
lbug-core-adapter (integration)
Gates: tsc clean; Move unit 185/185; full unit suite green except three
pre-existing Darwin-environment failures reproduced on the pristine base;
integration (move + lbug-core-adapter) 29/29.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
190 lines
6.2 KiB
TypeScript
190 lines
6.2 KiB
TypeScript
import path from 'node:path';
|
|
import { describe, expect, it } from 'vitest';
|
|
import type { MoveFactsMap } from '../../../src/core/move/compiler-facts.js';
|
|
import {
|
|
makeMoveFlowClientStub,
|
|
moveFunctionFact,
|
|
runMoveIngestPhaseWithGraph,
|
|
} from '../../helpers/move-ingest-harness.js';
|
|
|
|
const REPO_ROOT = path.resolve('/repo');
|
|
const SOURCE_FILE = path.join(REPO_ROOT, 'pkg/sources/spot.move');
|
|
|
|
const facts: MoveFactsMap = {
|
|
'0xa::spot_callbacks': {
|
|
file: SOURCE_FILE,
|
|
functions: [
|
|
moveFunctionFact('make_callbacks', {
|
|
file: SOURCE_FILE,
|
|
span: [1, 4],
|
|
visibility: 'public',
|
|
params: [],
|
|
returnTypes: ['|address|bool has copy + drop', '|address|bool has copy + drop'],
|
|
}),
|
|
moveFunctionFact('dispatch_funds', {
|
|
file: SOURCE_FILE,
|
|
span: [6, 8],
|
|
params: [],
|
|
}),
|
|
moveFunctionFact('local_callback_user', {
|
|
file: SOURCE_FILE,
|
|
span: [9, 9],
|
|
params: [],
|
|
}),
|
|
moveFunctionFact('withdrawal_done', {
|
|
file: SOURCE_FILE,
|
|
span: [10, 12],
|
|
params: [],
|
|
}),
|
|
moveFunctionFact('complete', {
|
|
file: SOURCE_FILE,
|
|
span: [14, 16],
|
|
params: [],
|
|
}),
|
|
moveFunctionFact('public_api', {
|
|
file: SOURCE_FILE,
|
|
span: [22, 25],
|
|
visibility: 'public',
|
|
params: [
|
|
{
|
|
name: 'object',
|
|
type: '0x1::object::Object<0x1::fungible_asset::Metadata>',
|
|
},
|
|
],
|
|
}),
|
|
moveFunctionFact('native_helper', {
|
|
file: SOURCE_FILE,
|
|
span: [27, 27],
|
|
isNative: true,
|
|
}),
|
|
],
|
|
structs: [],
|
|
constants: [],
|
|
},
|
|
};
|
|
|
|
async function ingestFixture() {
|
|
const client = makeMoveFlowClientStub({
|
|
facts: async () => facts,
|
|
callGraph: async () => ({
|
|
'0xa::spot_callbacks::make_callbacks': [],
|
|
'0xa::spot_callbacks::withdrawal_done': ['0xa::spot_callbacks::complete'],
|
|
'0xa::spot_callbacks::public_api': ['0x1::object::object_address'],
|
|
}),
|
|
functionUsage: async (_pkg, functionName) => {
|
|
if (functionName === 'spot_callbacks::local_callback_user') {
|
|
return {
|
|
called: [],
|
|
used: ['0xa::spot_callbacks::complete'],
|
|
};
|
|
}
|
|
if (functionName === 'spot_callbacks::withdrawal_done') {
|
|
// call_graph already carries withdrawal_done → complete; function_usage
|
|
// reporting it as used-not-called must not mint a second CALLS edge.
|
|
return { called: [], used: ['0xa::spot_callbacks::complete'] };
|
|
}
|
|
if (functionName !== 'spot_callbacks::make_callbacks') {
|
|
return { called: [], used: [] };
|
|
}
|
|
return {
|
|
called: [],
|
|
used: ['0xa::spot_callbacks::dispatch_funds', '0xa::spot_callbacks::withdrawal_done'],
|
|
};
|
|
},
|
|
});
|
|
const { output, graph } = await runMoveIngestPhaseWithGraph(client, REPO_ROOT, [
|
|
'pkg/Move.toml',
|
|
'pkg/sources/spot.move',
|
|
]);
|
|
const nodes = [...graph.iterNodes()];
|
|
const edges = [...graph.iterRelationships()];
|
|
const functionId = (qualifiedName: string) =>
|
|
nodes.find(
|
|
(node) => node.label === 'Function' && node.properties.qualifiedName === qualifiedName,
|
|
)?.id;
|
|
return { client, output, nodes, edges, functionId };
|
|
}
|
|
|
|
describe('Move graph quality regressions', () => {
|
|
it('links compiler-reported closure captures for all ordinary functions', async () => {
|
|
const { client, edges, functionId } = await ingestFixture();
|
|
const makeCallbacks = functionId('0xa::spot_callbacks::make_callbacks');
|
|
expect(
|
|
edges.filter(
|
|
(edge) =>
|
|
edge.sourceId === makeCallbacks &&
|
|
edge.type === 'CALLS' &&
|
|
edge.reason === 'move-compiler-closure-use',
|
|
),
|
|
).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
targetId: functionId('0xa::spot_callbacks::dispatch_funds'),
|
|
}),
|
|
expect.objectContaining({
|
|
targetId: functionId('0xa::spot_callbacks::withdrawal_done'),
|
|
}),
|
|
]),
|
|
);
|
|
expect(
|
|
edges.some(
|
|
(edge) =>
|
|
edge.sourceId === functionId('0xa::spot_callbacks::local_callback_user') &&
|
|
edge.targetId === functionId('0xa::spot_callbacks::complete') &&
|
|
edge.type === 'CALLS' &&
|
|
edge.reason === 'move-compiler-closure-use',
|
|
),
|
|
).toBe(true);
|
|
const queryable = (facts['0xa::spot_callbacks'].functions ?? []).filter(
|
|
(f) => !f.isNative && !f.isLambdaLifted,
|
|
);
|
|
expect(client.counts.functionUsage).toBe(queryable.length);
|
|
});
|
|
|
|
it('does not duplicate a call-graph edge the usage query classifies as a capture', async () => {
|
|
const { edges, functionId } = await ingestFixture();
|
|
const callEdges = edges.filter(
|
|
(edge) =>
|
|
edge.type === 'CALLS' &&
|
|
edge.sourceId === functionId('0xa::spot_callbacks::withdrawal_done') &&
|
|
edge.targetId === functionId('0xa::spot_callbacks::complete'),
|
|
);
|
|
|
|
expect(callEdges).toHaveLength(1);
|
|
expect(callEdges[0].reason).toBe('move-compiler-call-graph');
|
|
});
|
|
|
|
it('materializes external function and type targets', async () => {
|
|
const { output, nodes, edges, functionId } = await ingestFixture();
|
|
const externalFunction = nodes.find(
|
|
(node) =>
|
|
node.label === 'Function' &&
|
|
node.properties.qualifiedName === '0x1::object::object_address',
|
|
);
|
|
expect(externalFunction?.properties.locationFidelity).toBe('external');
|
|
expect(
|
|
edges.some(
|
|
(edge) =>
|
|
edge.type === 'CALLS' &&
|
|
edge.sourceId === functionId('0xa::spot_callbacks::public_api') &&
|
|
edge.targetId === externalFunction?.id,
|
|
),
|
|
).toBe(true);
|
|
|
|
const externalTypes = nodes.filter(
|
|
(node) => node.label === 'Type' && node.properties.locationFidelity === 'external',
|
|
);
|
|
expect(externalTypes.map((node) => node.properties.qualifiedName).sort()).toEqual([
|
|
'0x1::fungible_asset::Metadata',
|
|
'0x1::object::Object',
|
|
]);
|
|
expect(
|
|
edges.filter(
|
|
(edge) =>
|
|
edge.type === 'USES_TYPE' &&
|
|
edge.sourceId === functionId('0xa::spot_callbacks::public_api'),
|
|
),
|
|
).toHaveLength(2);
|
|
expect(output.droppedRefs.filter((d) => d.kind === 'type')).toEqual([]);
|
|
});
|
|
});
|