Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .changepacks/changepack_log_KuqHZb0J7TpXTO4Yn7W6O.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"changes":{"packages/fetch/package.json":"Patch"},"note":"Fix missing method issue","date":"2026-03-06T12:21:47.966654100Z"}
162 changes: 17 additions & 145 deletions packages/fetch/src/__tests__/url-map.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ test.each([
] as const)('getApiEndpointInfo returns default for non-existent key: %s -> %s', async (key, expected, envValue) => {
process.env.DEVUP_API_URL_MAP = envValue
const { getApiEndpointInfo } = await import(`../url-map`)
expect(getApiEndpointInfo(key, '')).toEqual(expected)
expect(getApiEndpointInfo(key, '', 'GET')).toEqual(expected)
})

test.each([
Expand All @@ -73,7 +73,7 @@ test.each([
] as const)('getApiEndpointInfo works with empty URL map: %s -> %s', async (key, expected, envValue) => {
process.env.DEVUP_API_URL_MAP = envValue
const { getApiEndpointInfo } = await import(`../url-map?t=${random}`)
expect(getApiEndpointInfo(key, 'foo').url).toBe(expected)
expect(getApiEndpointInfo(key, 'foo', 'GET').url).toBe(expected)
})

test.each([
Expand All @@ -82,7 +82,19 @@ test.each([
] as const)('getApiEndpointInfo works with empty URL map: %s -> %s', async (key, expected, envValue) => {
process.env.DEVUP_API_URL_MAP = envValue
const { getApiEndpointInfo } = await import(`../url-map?t=${random}`)
expect(getApiEndpointInfo(key, 'foo')).toEqual(expected)
expect(getApiEndpointInfo(key, 'foo', 'GET')).toEqual(expected)
})

test.each([
[['GET', 'anyKey'], '{}'],
[['POST', 'anyKey'], '{}'],
[['PUT', 'anyKey'], '{}'],
[['DELETE', 'anyKey'], '{}'],
[['PATCH', 'anyKey'], '{}'],
] as const)('getApiEndpointInfo works with empty URL map: %s -> %s', async (key, envValue) => {
process.env.DEVUP_API_URL_MAP = envValue
const { getApiEndpointInfo } = await import(`../url-map?t=${random}`)
expect(getApiEndpointInfo(key[1], 'foo', key[0]).method).toEqual(key[0])
})

test.each([
Expand All @@ -91,7 +103,7 @@ test.each([
] as const)('getApiEndpointInfo works when DEVUP_API_URL_MAP is not set: %s -> %s', async (key, expected) => {
delete process.env.DEVUP_API_URL_MAP
const { getApiEndpointInfo } = await import(`../url-map?t=${random}`)
expect(getApiEndpointInfo(key, 'foo').url).toBe(expected)
expect(getApiEndpointInfo(key, 'foo', 'GET').url).toBe(expected)
})

test.each([
Expand All @@ -100,145 +112,5 @@ test.each([
] as const)('getApiEndpointInfo works when DEVUP_API_URL_MAP is not set: %s -> %s', async (key, expected) => {
delete process.env.DEVUP_API_URL_MAP
const { getApiEndpointInfo } = await import(`../url-map?t=${random}`)
expect(getApiEndpointInfo(key, 'foo').url).toBe(expected)
expect(getApiEndpointInfo(key, 'foo', 'GET').url).toBe(expected)
})

// test.each([
// ['anyKey', { method: 'GET', url: 'anyKey' }],
// ['test', { method: 'GET', url: 'test' }],
// ] as const)('getApiEndpointInfo works when DEVUP_API_URL_MAP is not set: %s -> %s', async (key, expected) => {
// delete process.env.DEVUP_API_URL_MAP
// const { getApiEndpointInfo } = await import(`../url-map?t=1`)
// expect(getApiEndpointInfo(key, 'foo')).toEqual(expected)
// })

// test('getApiEndpointInfo handles key that exists but url property is missing', async () => {
// const urlMapWithoutUrl = {
// testKey: { method: 'GET' as const },
// }
// process.env.DEVUP_API_URL_MAP = JSON.stringify(urlMapWithoutUrl)
// const { getApiEndpointInfo } = await import(
// `../url-map`
// )
// // When url property is missing, optional chaining returns undefined, so key is returned
// expect(getApiEndpointInfo('testKey', 'foo').url).toBe('testKey')
// })

// test('DEVUP_API_URL_MAP constant is exported and accessible', async () => {
// const testUrlMap = {
// '': { testKey: { method: 'GET' as const, url: '/test' } },
// }
// process.env.DEVUP_API_URL_MAP = JSON.stringify(testUrlMap)
// const urlMapModule = await import(
// `../url-map`
// )
// expect(urlMapModule).toHaveProperty('DEVUP_API_URL_MAP')
// expect(typeof urlMapModule.DEVUP_API_URL_MAP).toBe('object')
// // Directly access the constant to ensure it's covered
// const urlMap = urlMapModule.DEVUP_API_URL_MAP
// expect(urlMap).toEqual(testUrlMap)
// // Verify it's used by getApiEndpointInfo function
// expect(urlMapModule.getApiEndpointInfo('testKey', 'foo').url).toBe('/test')
// })

// test('DEVUP_API_URL_MAP uses fallback when env var is undefined', async () => {
// delete process.env.DEVUP_API_URL_MAP
// const urlMapModule = await import(
// `../url-map`
// )
// // Directly access the constant to ensure the fallback path is covered
// const urlMap = urlMapModule.DEVUP_API_URL_MAP
// expect(urlMap).toEqual({})
// expect(urlMapModule.getApiEndpointInfo('anyKey', 'foo').url).toBe('anyKey')
// // Explicitly call getApiEndpointInfo to ensure it's covered
// const result = urlMapModule.getApiEndpointInfo('anyKey', 'foo')
// expect(result).toEqual({
// method: 'GET',
// url: 'anyKey',
// })
// // Also test that the function exists and is callable
// expect(typeof urlMapModule.getApiEndpointInfo).toBe('function')
// })

// test('DEVUP_API_URL_MAP uses fallback when env var is empty string', async () => {
// process.env.DEVUP_API_URL_MAP = ''
// const urlMapModule = await import(
// `../url-map`
// )
// // Directly access the constant to ensure the fallback path is covered
// const urlMap = urlMapModule.DEVUP_API_URL_MAP
// expect(urlMap).toEqual({})
// expect(urlMapModule.getApiEndpointInfo('anyKey', 'foo').url).toBe('anyKey')
// expect(urlMapModule.getApiEndpointInfo('anyKey', 'foo')).toEqual({
// method: 'GET',
// url: 'anyKey',
// })
// })

// test('getApiEndpointInfo handles key where DEVUP_API_URL_MAP[key] exists but url is undefined', async () => {
// const urlMapWithUndefinedUrl = {
// testKey: { method: 'GET' as const },
// }
// process.env.DEVUP_API_URL_MAP = JSON.stringify(urlMapWithUndefinedUrl)
// const { getApiEndpointInfo } = await import(
// `../url-map`
// )
// // When url property is missing, optional chaining returns undefined, so key is returned
// expect(getApiEndpointInfo('testKey', 'foo').url).toBe('testKey')
// })

// test('getApiEndpointInfo handles key where DEVUP_API_URL_MAP[key] exists but url is null', async () => {
// const urlMapWithNullUrl = {
// testKey: { method: 'GET' as const, url: null as unknown as string },
// }
// process.env.DEVUP_API_URL_MAP = JSON.stringify(urlMapWithNullUrl)
// const { getApiEndpointInfo } = await import(
// `../url-map`
// )
// // When url is null, optional chaining returns null, so key is returned
// expect(getApiEndpointInfo('testKey', 'foo').url).toBe('testKey')
// })

// test('getApiEndpointInfo handles key where DEVUP_API_URL_MAP[key] exists but url is empty string', async () => {
// const urlMapWithEmptyUrl = {
// testKey: { method: 'GET' as const, url: '' },
// }
// process.env.DEVUP_API_URL_MAP = JSON.stringify(urlMapWithEmptyUrl)
// const { getApiEndpointInfo } = await import(
// `../url-map`
// )
// // When url is empty string, it's falsy, so key is returned
// expect(getApiEndpointInfo('testKey', 'foo').url).toBe('testKey')
// })

// test('getApiEndpointInfo returns default when key does not exist in map (explicit coverage for line 10)', async () => {
// const urlMap = { existingKey: { method: 'POST' as const, url: '/existing' } }
// process.env.DEVUP_API_URL_MAP = JSON.stringify(urlMap)
// const { getApiEndpointInfo } = await import(
// `../url-map`
// )
// // Explicitly test the if(!result) branch to ensure line 10 is covered
// const result = getApiEndpointInfo('nonExistentKeyInMap', 'foo')
// expect(result).toEqual({ method: 'GET', url: 'nonExistentKeyInMap' })
// expect(result.method).toBe('GET')
// expect(result.url).toBe('nonExistentKeyInMap')
// })

// test('getApiEndpointInfo returns result when key exists with url (explicit coverage for lines 12-13)', async () => {
// const urlMap = {
// '': {
// testKey: { method: 'PUT' as const, url: '/test/url' },
// },
// }
// process.env.DEVUP_API_URL_MAP = JSON.stringify(urlMap)
// const { getApiEndpointInfo } = await import(
// `../url-map?t=${Date.now()+Math.random()}`
// )
// // Explicitly test the result.url ||= key and return result path (lines 12-13)
// const result = getApiEndpointInfo('testKey', 'foo')
// expect(result).toEqual({ method: 'PUT', url: '/test/url' })
// expect(result.method).toBe('PUT')
// expect(result.url).toBe('/test/url')
// // Verify that url was not changed (since it already exists)
// expect(result.url).not.toBe('testKey')
// })
7 changes: 6 additions & 1 deletion packages/fetch/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,12 @@ export class DevupApi<S extends ConditionalKeys<DevupApiServers>> {
): Promise<
DevupApiResponse<ExtractValue<O, 'response'>, ExtractValue<O, 'error'>>
> {
const { method, url, bodyType } = getApiEndpointInfo(path, this.serverName)
const { method, url, bodyType } = getApiEndpointInfo(
path,
this.serverName,
(options[0]?.method as 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH') ||
'GET',
)
const {
middleware = [],
query,
Expand Down
3 changes: 2 additions & 1 deletion packages/fetch/src/url-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ export const DEVUP_API_URL_MAP: Record<
export function getApiEndpointInfo(
key: string,
serverName: string,
method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH',
): UrlMapValue {
const result = DEVUP_API_URL_MAP[serverName]?.[key] ?? {
method: 'GET',
method,
url: key,
}
result.url ||= key
Expand Down