From e362370d2822ad8a3758e647f17e8133e265fe76 Mon Sep 17 00:00:00 2001 From: liyipeng06 Date: Mon, 27 Apr 2026 20:44:22 +0800 Subject: [PATCH] fix(group): preserve grpc manifest service resolution --- .../group/extractors/manifest-extractor.ts | 4 +- .../unit/group/manifest-extractor.test.ts | 84 +++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) diff --git a/gitnexus/src/core/group/extractors/manifest-extractor.ts b/gitnexus/src/core/group/extractors/manifest-extractor.ts index 869537fd2..de2e772d3 100644 --- a/gitnexus/src/core/group/extractors/manifest-extractor.ts +++ b/gitnexus/src/core/group/extractors/manifest-extractor.ts @@ -234,7 +234,9 @@ export class ManifestExtractor { // → Class|Interface (no label match = no silent wrong hits on // File/Variable nodes that happen to share the name). const parts = link.contract.split('/'); - const serviceName = parts[0]?.trim().split('.').pop() ?? ''; + const rawServiceName = parts[0]?.trim() ?? ''; + const serviceName = + link.type === 'thrift' ? (rawServiceName.split('.').pop() ?? '') : rawServiceName; const methodName = parts[1]?.trim() ?? ''; if (methodName) { rows = await executor( diff --git a/gitnexus/test/unit/group/manifest-extractor.test.ts b/gitnexus/test/unit/group/manifest-extractor.test.ts index fb74eeeb6..e5551de63 100644 --- a/gitnexus/test/unit/group/manifest-extractor.test.ts +++ b/gitnexus/test/unit/group/manifest-extractor.test.ts @@ -169,6 +169,90 @@ describe('ManifestExtractor', () => { expect(provider?.symbolUid).toBe('uid-correct-login'); }); + it('resolves grpc package-qualified service-only manifest by full service name', async () => { + const links: GroupManifestLink[] = [ + { + from: 'platform/orders', + to: 'platform/auth', + type: 'grpc', + contract: 'auth.AuthService', + role: 'consumer', + }, + ]; + + let seenServiceName: string | undefined; + const dbExecutors = new Map< + string, + (cypher: string, params?: Record) => Promise[]> + >([ + [ + 'platform/auth', + async (_cypher, params) => { + seenServiceName = params?.serviceName as string; + if (params?.serviceName === 'auth.AuthService') { + return [ + { + uid: 'uid-auth-service', + name: 'auth.AuthService', + filePath: 'src/auth.proto', + }, + ]; + } + return []; + }, + ], + ['platform/orders', async () => []], + ]); + + const result = await extractor.extractFromManifest(links, dbExecutors); + + expect(seenServiceName).toBe('auth.AuthService'); + const provider = result.contracts.find((c) => c.role === 'provider'); + expect(provider?.symbolUid).toBe('uid-auth-service'); + }); + + it('resolves thrift package-qualified service-only manifest by simple service name', async () => { + const links: GroupManifestLink[] = [ + { + from: 'gateway', + to: 'orders', + type: 'thrift', + contract: 'billing.v1.OrderService', + role: 'consumer', + }, + ]; + + let seenServiceName: string | undefined; + const dbExecutors = new Map< + string, + (cypher: string, params?: Record) => Promise[]> + >([ + [ + 'orders', + async (_cypher, params) => { + seenServiceName = params?.serviceName as string; + if (params?.serviceName === 'OrderService') { + return [ + { + uid: 'uid-order-service', + name: 'OrderService', + filePath: 'idl/order.thrift', + }, + ]; + } + return []; + }, + ], + ['gateway', async () => []], + ]); + + const result = await extractor.extractFromManifest(links, dbExecutors); + + expect(seenServiceName).toBe('OrderService'); + const provider = result.contracts.find((c) => c.role === 'provider'); + expect(provider?.symbolUid).toBe('uid-order-service'); + }); + it('resolves lib manifest links by exact name only', async () => { const links: GroupManifestLink[] = [ {