mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-08-28 05:25:25 +00:00
Merge e8f56e6dc5 into 31c9d9223e
This commit is contained in:
commit
86fa6745d8
3 changed files with 116 additions and 2 deletions
|
|
@ -619,12 +619,20 @@ function foldOperands(
|
|||
/**
|
||||
* Fold an inline operand list (e.g. `API_CIS_V1 + "summary/save"`) against
|
||||
* `fileKey`, or null when any piece is unresolvable (skip floor).
|
||||
*
|
||||
* An empty result is a SUCCESS, not a skip. `static final String ROOT = "";`
|
||||
* folds to `""`, which `joinPath` then resolves against the type-level prefix
|
||||
* exactly as it resolves the literal `@GetMapping("")`. Collapsing it into
|
||||
* `null` would make a resolved-empty path indistinguishable from an
|
||||
* unresolvable one — the skip floor is reserved for "could not fold", and
|
||||
* nothing else in the resolver conflates the two: `resolveOperands` in the
|
||||
* shared core returns `foldExpr` unfiltered, and `resolveJavaConstant` returns
|
||||
* `""` for an empty constant.
|
||||
*/
|
||||
export function foldJavaOperands(
|
||||
fileKey: string,
|
||||
operands: readonly Operand[],
|
||||
repo: RepoConstants,
|
||||
): string | null {
|
||||
const out = foldOperands(fileKey, operands, newFoldState(repo), 0);
|
||||
return out === '' ? null : out;
|
||||
return foldOperands(fileKey, operands, newFoldState(repo), 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,3 +211,46 @@ public class OrderController {
|
|||
expect(ingestionRoutes(files)).toEqual(['POST /api/v1/orders']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('an empty-valued constant route matches its literal spelling', () => {
|
||||
// Before this fix the two spellings diverged: `@GetMapping("")` was kept and
|
||||
// `@GetMapping(ApiPaths.ROOT)` with `ROOT = ""` was dropped, because the fold
|
||||
// collapsed a resolved-empty result into the skip floor. The class-level
|
||||
// prefix is what makes an empty method path meaningful, so the parity is
|
||||
// asserted with one present.
|
||||
const ROOT_CONSTS = `package com.example;
|
||||
public class ApiPaths {
|
||||
public static final String ROOT = "";
|
||||
}`;
|
||||
|
||||
const controller = (mapping: string): string => `package com.example;
|
||||
import com.example.ApiPaths;
|
||||
@RequestMapping("/api/v1")
|
||||
public class RootController {
|
||||
@GetMapping(${mapping})
|
||||
public void root() {}
|
||||
}`;
|
||||
|
||||
it('resolves the constant spelling to the same path as the literal one', () => {
|
||||
const literal = { [CTL]: controller('""') };
|
||||
const constant = { [CONSTS]: ROOT_CONSTS, [CTL]: controller('ApiPaths.ROOT') };
|
||||
|
||||
expect(groupProviders(constant)).toEqual(groupProviders(literal));
|
||||
expect(ingestionRoutes(constant)).toEqual(ingestionRoutes(literal));
|
||||
});
|
||||
|
||||
it('emits the route on both sides rather than dropping it', () => {
|
||||
// Deliberately NOT asserting the two sides produce the same string here.
|
||||
// They do not, and they did not before this change either: measured on the
|
||||
// LITERAL spelling, which no part of this change touches, the group side
|
||||
// emits `/api/v1/` (`joinPath` appends `/` before an empty method path)
|
||||
// while ingestion emits `/api/v1`. That trailing-slash divergence is a
|
||||
// separate pre-existing defect; the fix here only makes the constant
|
||||
// spelling reach it too, instead of silently dropping the route. The test
|
||||
// above is what pins the parity that this change is responsible for —
|
||||
// constant behaves as literal, on each side.
|
||||
const files = { [CONSTS]: ROOT_CONSTS, [CTL]: controller('ApiPaths.ROOT') };
|
||||
expect(groupProviders(files)).toEqual(['GET /api/v1/']);
|
||||
expect(ingestionRoutes(files)).toEqual(['GET /api/v1']);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -792,3 +792,66 @@ describe('text blocks keep the skip floor', () => {
|
|||
expect(extractJavaModuleConstants(parse(src)).literals.has('X')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('an empty fold is a success, not a skip', () => {
|
||||
// `null` from this fold is the SKIP FLOOR: it means "a piece was
|
||||
// unresolvable", and every caller acts on it by dropping the route. An
|
||||
// empty-valued constant is not that case — it resolved, to the empty string.
|
||||
// Spring reads `@GetMapping(ROOT)` with `ROOT = ""` exactly as it reads
|
||||
// `@GetMapping("")`, and the group extractor's literal branch already keeps
|
||||
// the latter. Collapsing the two silently lost the route for the constant
|
||||
// spelling alone.
|
||||
const EMPTY = 'src/main/java/com/example/Paths.java';
|
||||
|
||||
it('folds a constant whose value is the empty string', () => {
|
||||
const repo = repoOf({
|
||||
[EMPTY]: `package com.example;
|
||||
public class Paths { public static final String ROOT = ""; }`,
|
||||
});
|
||||
expect(foldJavaOperands(EMPTY, [{ kind: 'ref', name: 'ROOT' }], repo)).toBe('');
|
||||
});
|
||||
|
||||
it('concatenating empty constants folds to the empty string', () => {
|
||||
const repo = repoOf({
|
||||
[EMPTY]: `package com.example;
|
||||
public class Paths {
|
||||
public static final String A = "";
|
||||
public static final String B = "";
|
||||
}`,
|
||||
});
|
||||
expect(
|
||||
foldJavaOperands(
|
||||
EMPTY,
|
||||
[
|
||||
{ kind: 'ref', name: 'A' },
|
||||
{ kind: 'ref', name: 'B' },
|
||||
],
|
||||
repo,
|
||||
),
|
||||
).toBe('');
|
||||
});
|
||||
|
||||
it('still floors to skip when a piece is genuinely unresolvable', () => {
|
||||
// The control: the two outcomes have to stay distinguishable, or the fix
|
||||
// would trade one conflation for another.
|
||||
const repo = repoOf({
|
||||
[EMPTY]: `package com.example;
|
||||
public class Paths { public static final String ROOT = compute(); }`,
|
||||
});
|
||||
expect(foldJavaOperands(EMPTY, [{ kind: 'ref', name: 'ROOT' }], repo)).toBeNull();
|
||||
});
|
||||
|
||||
it('agrees with resolveJavaConstant, which never collapsed empty to null', () => {
|
||||
// The binding used to disagree with itself: the single-constant resolver
|
||||
// returns `""` (pinned by the 30-level DAG case above, whose every
|
||||
// intermediate value is empty), and `resolveOperands` in the shared
|
||||
// language-agnostic core returns `foldExpr` unfiltered. Only this fold
|
||||
// collapsed.
|
||||
const repo = repoOf({
|
||||
[EMPTY]: `package com.example;
|
||||
public class Paths { public static final String ROOT = ""; }`,
|
||||
});
|
||||
expect(resolveJavaConstant(EMPTY, 'ROOT', repo)).toBe('');
|
||||
expect(foldJavaOperands(EMPTY, [{ kind: 'ref', name: 'ROOT' }], repo)).toBe('');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue