-
Notifications
You must be signed in to change notification settings - Fork 1
Add unit tests for adk registry and fetch-utils #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| /** | ||
| * ADK Fetch Utils Unit Tests | ||
| * | ||
| * Tests for fetch-utils.ts functions that handle fetch response normalization. | ||
| */ | ||
|
|
||
| import { describe, it, expect } from 'vitest'; | ||
| import { toText } from '../src/base/fetch-utils'; | ||
|
|
||
| describe('toText', () => { | ||
| it('should return string as-is', async () => { | ||
| const result = await toText('hello world'); | ||
| expect(result).toBe('hello world'); | ||
| }); | ||
|
|
||
| it('should handle Response-like object with text method', async () => { | ||
| const mockResponse = { | ||
| text: () => Promise.resolve('response text'), | ||
| }; | ||
| const result = await toText(mockResponse); | ||
| expect(result).toBe('response text'); | ||
| }); | ||
|
|
||
| it('should convert null to empty string', async () => { | ||
| const result = await toText(null); | ||
| expect(result).toBe(''); | ||
| }); | ||
|
|
||
| it('should convert undefined to empty string', async () => { | ||
| const result = await toText(undefined); | ||
| expect(result).toBe(''); | ||
| }); | ||
|
|
||
| it('should convert number to string', async () => { | ||
| const result = await toText(123); | ||
| expect(result).toBe('123'); | ||
| }); | ||
|
|
||
| it('should convert object to string', async () => { | ||
| const result = await toText({ key: 'value' }); | ||
| expect(result).toBe('[object Object]'); | ||
| }); | ||
|
|
||
| it('should handle object with text property but not a function', async () => { | ||
| const result = await toText({ text: 'not a function' }); | ||
| expect(result).toBe('[object Object]'); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,280 @@ | ||||||||||||||||||||||
| /** | ||||||||||||||||||||||
| * ADK Registry Unit Tests | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * Tests for registry.ts functions that handle ADT type to ADK kind mapping | ||||||||||||||||||||||
| * and object type registration/resolution. | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import { describe, it, expect, beforeEach } from 'vitest'; | ||||||||||||||||||||||
| import { | ||||||||||||||||||||||
| parseAdtType, | ||||||||||||||||||||||
| getMainType, | ||||||||||||||||||||||
| registerObjectType, | ||||||||||||||||||||||
| resolveType, | ||||||||||||||||||||||
| resolveKind, | ||||||||||||||||||||||
| getKindForType, | ||||||||||||||||||||||
| getTypeForKind, | ||||||||||||||||||||||
| isTypeRegistered, | ||||||||||||||||||||||
| getRegisteredTypes, | ||||||||||||||||||||||
| getRegisteredKinds, | ||||||||||||||||||||||
| getEndpointForType, | ||||||||||||||||||||||
| } from '../src/base/registry'; | ||||||||||||||||||||||
| import * as kinds from '../src/base/kinds'; | ||||||||||||||||||||||
| import type { AdkKind } from '../src/base/kinds'; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Mock AdkObject class for testing | ||||||||||||||||||||||
| class MockAdkObject { | ||||||||||||||||||||||
| constructor( | ||||||||||||||||||||||
| public ctx: unknown, | ||||||||||||||||||||||
| public nameOrData: string | unknown, | ||||||||||||||||||||||
|
Check warning on line 29 in packages/adk/tests/registry.test.ts
|
||||||||||||||||||||||
| ) {} | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('parseAdtType', () => { | ||||||||||||||||||||||
| it('should parse full type with sub type', () => { | ||||||||||||||||||||||
| const result = parseAdtType('DEVC/K'); | ||||||||||||||||||||||
| expect(result).toEqual({ | ||||||||||||||||||||||
| full: 'DEVC/K', | ||||||||||||||||||||||
| main: 'DEVC', | ||||||||||||||||||||||
| sub: 'K', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should parse main type without sub type', () => { | ||||||||||||||||||||||
| const result = parseAdtType('CLAS'); | ||||||||||||||||||||||
| expect(result).toEqual({ | ||||||||||||||||||||||
| full: 'CLAS', | ||||||||||||||||||||||
| main: 'CLAS', | ||||||||||||||||||||||
| sub: undefined, | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should handle lowercase input', () => { | ||||||||||||||||||||||
| const result = parseAdtType('tabl/ds'); | ||||||||||||||||||||||
| expect(result).toEqual({ | ||||||||||||||||||||||
| full: 'tabl/ds', | ||||||||||||||||||||||
| main: 'TABL', | ||||||||||||||||||||||
| sub: 'DS', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should handle empty sub type', () => { | ||||||||||||||||||||||
| const result = parseAdtType('TABL/'); | ||||||||||||||||||||||
| expect(result).toEqual({ | ||||||||||||||||||||||
| full: 'TABL/', | ||||||||||||||||||||||
| main: 'TABL', | ||||||||||||||||||||||
| sub: '', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('getMainType', () => { | ||||||||||||||||||||||
| it('should return main type from full type', () => { | ||||||||||||||||||||||
| expect(getMainType('DEVC/K')).toBe('DEVC'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return type as-is for main type', () => { | ||||||||||||||||||||||
| expect(getMainType('CLAS')).toBe('CLAS'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should handle lowercase input', () => { | ||||||||||||||||||||||
| expect(getMainType('prog')).toBe('PROG'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('registerObjectType', () => { | ||||||||||||||||||||||
| beforeEach(() => { | ||||||||||||||||||||||
| // Reset would be needed here but since we're using internal registry, | ||||||||||||||||||||||
| // we'll test in isolation by importing fresh functions | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should register a type with endpoint and nameTransform', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| registerObjectType('PROG', kinds.Program, mockConstructor, { | ||||||||||||||||||||||
| endpoint: 'abap/ programs', | ||||||||||||||||||||||
| nameTransform: 'preserve', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const entry = resolveType('PROG'); | ||||||||||||||||||||||
| expect(entry).toBeDefined(); | ||||||||||||||||||||||
| expect(entry?.kind).toBe(kinds.Program); | ||||||||||||||||||||||
| expect(entry?.endpoint).toBe('abap/ programs'); | ||||||||||||||||||||||
| expect(entry?.nameTransform).toBe('preserve'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should register without optional parameters', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| registerObjectType('TEST', 'TestType' as AdkKind, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const entry = resolveType('TEST'); | ||||||||||||||||||||||
| expect(entry).toBeDefined(); | ||||||||||||||||||||||
| expect(entry?.kind).toBe('TestType'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should handle case-insensitive registration', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| registerObjectType('prog', kinds.Program, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| expect(resolveType('PROG')).toBeDefined(); | ||||||||||||||||||||||
| expect(resolveType('prog')).toBeDefined(); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('resolveType', () => { | ||||||||||||||||||||||
| it('should resolve exact type match first', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| registerObjectType('MYTAB', kinds.Table, mockConstructor, { | ||||||||||||||||||||||
| endpoint: 'ddic/tables', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| registerObjectType( | ||||||||||||||||||||||
| 'MYTAB/DS', | ||||||||||||||||||||||
| kinds.Structure as AdkKind, | ||||||||||||||||||||||
| mockConstructor, | ||||||||||||||||||||||
| { endpoint: 'ddic/structs' }, | ||||||||||||||||||||||
| ); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const entry = resolveType('MYTAB/DS'); | ||||||||||||||||||||||
| expect(entry?.endpoint).toBe('ddic/structs'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should fall back to main type if full type not found', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| registerObjectType('ANOTAB', kinds.Table, mockConstructor, { | ||||||||||||||||||||||
| endpoint: 'ddic/tables', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const entry = resolveType('ANOTAB/DS'); | ||||||||||||||||||||||
| expect(entry?.endpoint).toBe('ddic/tables'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return undefined for unregistered type', () => { | ||||||||||||||||||||||
| const entry = resolveType('UNREGISTERED'); | ||||||||||||||||||||||
| expect(entry).toBeUndefined(); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('getKindForType', () => { | ||||||||||||||||||||||
| it('should return kind for registered type', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('CLAS', kinds.Class, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const kind = getKindForType('CLAS'); | ||||||||||||||||||||||
| expect(kind).toBe(kinds.Class); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return kind for full type', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('TABL', kinds.Table, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const kind = getKindForType('TABL/DS'); | ||||||||||||||||||||||
| expect(kind).toBe(kinds.Table); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return undefined for unregistered type', () => { | ||||||||||||||||||||||
| const kind = getKindForType('UNREG'); | ||||||||||||||||||||||
| expect(kind).toBeUndefined(); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('getTypeForKind', () => { | ||||||||||||||||||||||
| it('should return ADT type for registered kind', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('CLAS', kinds.Class, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const type = getTypeForKind(kinds.Class); | ||||||||||||||||||||||
| expect(type).toBe('CLAS'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return undefined for unregistered kind', () => { | ||||||||||||||||||||||
| const type = getTypeForKind('UnknownKind' as AdkKind); | ||||||||||||||||||||||
| expect(type).toBeUndefined(); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('isTypeRegistered', () => { | ||||||||||||||||||||||
| it('should return true for registered type', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('CLAS', kinds.Class, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| expect(isTypeRegistered('CLAS')).toBe(true); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return false for unregistered type', () => { | ||||||||||||||||||||||
| expect(isTypeRegistered('UNREG')).toBe(false); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should check main type only', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('TABL', kinds.Table, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| expect(isTypeRegistered('TABL/DS')).toBe(true); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('getRegisteredTypes', () => { | ||||||||||||||||||||||
| it('should return array of registered types', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('TYPE1', 'Type1' as AdkKind, mockConstructor); | ||||||||||||||||||||||
| registerObjectType('TYPE2', 'Type2' as AdkKind, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const types = getRegisteredTypes(); | ||||||||||||||||||||||
| expect(types).toContain('TYPE1'); | ||||||||||||||||||||||
| expect(types).toContain('TYPE2'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return empty array when nothing registered', () => { | ||||||||||||||||||||||
| // Note: This assumes fresh state - may need adjustment | ||||||||||||||||||||||
| const types = getRegisteredTypes(); | ||||||||||||||||||||||
| expect(Array.isArray(types)).toBe(true); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
Comment on lines
+230
to
+234
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The “empty array” test does not validate emptiness. Line 233 only checks array shape, not that it is empty. After state isolation is fixed, assert Suggested assertion update- expect(Array.isArray(types)).toBe(true);
+ expect(types).toEqual([]);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('getRegisteredKinds', () => { | ||||||||||||||||||||||
| it('should return array of registered kinds', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('TYPE1', 'Kind1' as AdkKind, mockConstructor); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const kinds_list = getRegisteredKinds(); | ||||||||||||||||||||||
| expect(kinds_list).toContain('Kind1'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('resolveKind', () => { | ||||||||||||||||||||||
| it('should resolve registered kind to entry', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('CLAS', kinds.Class, mockConstructor, { | ||||||||||||||||||||||
| endpoint: 'oo/classes', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const entry = resolveKind(kinds.Class); | ||||||||||||||||||||||
| expect(entry?.kind).toBe(kinds.Class); | ||||||||||||||||||||||
| expect(entry?.endpoint).toBe('oo/classes'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return undefined for unregistered kind', () => { | ||||||||||||||||||||||
| const entry = resolveKind('UnknownKind' as AdkKind); | ||||||||||||||||||||||
| expect(entry).toBeUndefined(); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| describe('getEndpointForType', () => { | ||||||||||||||||||||||
| it('should return endpoint for registered type', () => { | ||||||||||||||||||||||
| const mockConstructor = MockAdkObject as any; | ||||||||||||||||||||||
| registerObjectType('PROG', kinds.Program, mockConstructor, { | ||||||||||||||||||||||
| endpoint: 'abap/programs', | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| const endpoint = getEndpointForType('PROG'); | ||||||||||||||||||||||
| expect(endpoint).toBe('abap/programs'); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| it('should return undefined for unregistered type', () => { | ||||||||||||||||||||||
| const endpoint = getEndpointForType('UNREG'); | ||||||||||||||||||||||
| expect(endpoint).toBeUndefined(); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
| }); | ||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: abapify/adt-cli
Length of output: 1706
🏁 Script executed:
Repository: abapify/adt-cli
Length of output: 3570
🏁 Script executed:
Repository: abapify/adt-cli
Length of output: 2228
🏁 Script executed:
Repository: abapify/adt-cli
Length of output: 2206
🏁 Script executed:
Repository: abapify/adt-cli
Length of output: 2066
Reset registry state between tests to eliminate order-dependent behavior.
The module-level Maps (
registry,adtToKind,kindToAdt) are never reset between test cases. Tests that register the same types interfere with each other—particularly the test at line 230 which explicitly checks for "empty array" but assumes fresh state (as noted in its comment). ThebeforeEachhook is a no-op with a misleading comment; "importing fresh functions" does not create isolation because all tests share the same module-level mutable state. Export a reset function frompackages/adk/src/base/registry.tsand call it in thebeforeEachhook.🤖 Prompt for AI Agents