mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-08 22:22:52 +00:00
* feat(kotlin): bind Spring @Value and ConfigurationProperties consumers Kotlin sources were skipping the Java-only config-binding attach path, so mixed JVM apps under-reported blast radius for Kotlin placeholders. Capture from the live AST, serialize on the existing side channel, and reuse the shared binder. (U1-U4) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(review): require exact Spring annotation FQNs and skip raw-string escapes Reject similarly named third-party imports and leave Kotlin triple-quoted bodies undecoded so capture stays fail-closed. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(kotlin): use only grammar-valid Kotlin string and class-name nodes The coverage shard failed the #1920 literal gate on invented string node types and a Java-style name field. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(kotlin): fail closed on non-literal prefixes and persist unresolved config markers Reject constant and boolean @ConfigurationProperties arguments, scope nested Value shadows to their owner, and rewrite drifted consumer files when a config key is deleted. Co-authored-by: Cursor <cursoragent@cursor.com> * test(kotlin): add config-consumer capture benchmark and fix JVM feature stamps The Kotlin capture path had no performance or behavior guard: a file-wide lexical shadow regression silently dropped two of every three facts. The new bench arm fingerprints @Value / @ConfigurationProperties facts from an explicit-import control against a wildcard-import corpus whose files each declare a sibling nested `Value` type, so parity between the arms is the regression gate, and CI runs it with scaling + widening budgets. Broadening spring.config-bindings to .kt also means Kotlin-only JVM repos now stamp the feature, which the two exact-map orchestration expectations still denied. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(kotlin): let an explicit import shadow the Spring wildcard import importedAs accepted a star import of the Spring package even when the same simple name was explicitly bound to another type, so a file importing com.example.Value alongside the Spring annotation package emitted a false @Value consumer fact. Kotlin resolves the explicit import first, so the wildcard branch now only applies when the name is otherwise unbound. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Gergo Magyar <gergomagyar0@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
101 lines
2.8 KiB
TypeScript
101 lines
2.8 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import type { GraphNode } from 'gitnexus-shared';
|
|
import { createKnowledgeGraph } from '../../src/core/graph/graph.js';
|
|
import { collectSpringConfigConsumerDriftFiles } from '../../src/core/incremental/spring-config-drift.js';
|
|
|
|
function consumerNode(
|
|
id: string,
|
|
filePath: string,
|
|
description?: string,
|
|
label = 'Property',
|
|
): GraphNode {
|
|
return {
|
|
id,
|
|
label,
|
|
properties: {
|
|
name: id,
|
|
filePath,
|
|
...(description === undefined ? {} : { description }),
|
|
},
|
|
} as GraphNode;
|
|
}
|
|
|
|
describe('collectSpringConfigConsumerDriftFiles', () => {
|
|
it('finds consumers that become unresolved after a config key is deleted', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode(
|
|
consumerNode(
|
|
'property:timeout',
|
|
'src/main/kotlin/Service.kt',
|
|
'Property; Spring config unresolved: service.timeout',
|
|
),
|
|
);
|
|
|
|
expect(
|
|
collectSpringConfigConsumerDriftFiles(graph, [
|
|
{
|
|
id: 'property:timeout',
|
|
description: 'Property',
|
|
},
|
|
]),
|
|
).toEqual(new Set(['src/main/kotlin/Service.kt']));
|
|
});
|
|
|
|
it('finds newly unresolved consumers even when the persisted row is absent', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode(
|
|
consumerNode(
|
|
'property:timeout',
|
|
'src/main/kotlin/Service.kt',
|
|
'Spring config unresolved: service.timeout',
|
|
),
|
|
);
|
|
|
|
expect(collectSpringConfigConsumerDriftFiles(graph, [])).toEqual(
|
|
new Set(['src/main/kotlin/Service.kt']),
|
|
);
|
|
});
|
|
|
|
it('finds consumers that become resolved and ignores unrelated description changes', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode(
|
|
consumerNode('class:service', 'src/main/kotlin/Service.kt', 'Updated docs', 'Class'),
|
|
);
|
|
graph.addNode(
|
|
consumerNode('function:helper', 'src/main/kotlin/Helper.kt', undefined, 'Function'),
|
|
);
|
|
|
|
expect(
|
|
collectSpringConfigConsumerDriftFiles(graph, [
|
|
{
|
|
id: 'class:service',
|
|
description: 'Old docs; Spring config unresolved: service',
|
|
},
|
|
{
|
|
id: 'function:helper',
|
|
description: 'Spring config unresolved: ignored',
|
|
},
|
|
]),
|
|
).toEqual(new Set(['src/main/kotlin/Service.kt']));
|
|
});
|
|
|
|
it('does not rewrite consumers whose unresolved keys are unchanged', () => {
|
|
const graph = createKnowledgeGraph();
|
|
graph.addNode(
|
|
consumerNode(
|
|
'property:timeout',
|
|
'src/main/kotlin/Service.kt',
|
|
'Spring config unresolved: service.timeout; Existing docs',
|
|
),
|
|
);
|
|
|
|
expect(
|
|
collectSpringConfigConsumerDriftFiles(graph, [
|
|
{
|
|
id: 'property:timeout',
|
|
description: 'Existing docs; Spring config unresolved: service.timeout',
|
|
},
|
|
]),
|
|
).toEqual(new Set());
|
|
});
|
|
});
|