fix: rename binding to createOctokit and harden option merging

- Rename context binding from getOctokit to createOctokit to avoid
  SyntaxError when users write const { getOctokit } = require(...)
  in their scripts (~10 public workflows affected)
- Strip undefined values from user options to prevent clobbering
  defaults (e.g. GHES baseUrl)
- Deep-merge retry options alongside request options
- Use nullish coalescing (??) instead of logical OR (||)
- Shallow-copy opts to prevent shared reference mutation
- Add tests: undefined stripping, retry merge, falsy value preservation,
  no mutation of defaults
- 32 tests passing, lint clean, dist rebuilt
This commit is contained in:
Salman Chishti 2026-04-08 21:39:12 +00:00 committed by GitHub
parent 95933befc0
commit f9d72d3f45
8 changed files with 169 additions and 63 deletions

View file

@ -76,6 +76,23 @@ describe('createConfiguredGetOctokit', () => {
)
})
test('deep-merges retry so partial overrides preserve existing settings', () => {
const raw = makeMockGetOctokit()
const defaults = {
retry: {enabled: true, retries: 3}
}
const wrapped = createConfiguredGetOctokit(raw as any, defaults)
wrapped('tok' as any, {retry: {retries: 5}} as any)
expect(raw).toHaveBeenCalledWith(
'tok',
expect.objectContaining({
retry: {enabled: true, retries: 5}
})
)
})
test('user can override request.retries explicitly', () => {
const raw = makeMockGetOctokit()
const defaults = {request: {retries: 3}}
@ -158,15 +175,35 @@ describe('createConfiguredGetOctokit', () => {
test('baseUrl: undefined from user does not clobber default', () => {
const raw = makeMockGetOctokit()
const defaults = {baseUrl: 'https://api.github.com'}
const defaults = {baseUrl: 'https://ghes.example.com/api/v3'}
const wrapped = createConfiguredGetOctokit(raw as any, defaults)
wrapped('tok' as any, {baseUrl: undefined} as any)
// undefined spread still overwrites — this documents current behavior.
// The `baseUrl` key is present but value is undefined.
const calledOpts = raw.mock.calls[0][1]
expect(calledOpts).toHaveProperty('baseUrl')
expect(calledOpts.baseUrl).toBe('https://ghes.example.com/api/v3')
})
test('undefined values in nested request are stripped', () => {
const raw = makeMockGetOctokit()
const defaults = {request: {retries: 3, agent: 'proxy'}}
const wrapped = createConfiguredGetOctokit(raw as any, defaults)
wrapped('tok' as any, {request: {retries: undefined, timeout: 5000}} as any)
const calledOpts = raw.mock.calls[0][1]
expect(calledOpts.request).toEqual({retries: 3, agent: 'proxy', timeout: 5000})
})
test('undefined values in nested retry are stripped', () => {
const raw = makeMockGetOctokit()
const defaults = {retry: {enabled: true, retries: 3}}
const wrapped = createConfiguredGetOctokit(raw as any, defaults)
wrapped('tok' as any, {retry: {enabled: undefined, retries: 5}} as any)
const calledOpts = raw.mock.calls[0][1]
expect(calledOpts.retry).toEqual({enabled: true, retries: 5})
})
test('each call creates an independent client', () => {
@ -183,4 +220,39 @@ describe('createConfiguredGetOctokit', () => {
expect(b).toBe('client-b')
expect(raw).toHaveBeenCalledTimes(2)
})
test('does not mutate defaultOptions between calls', () => {
const raw = makeMockGetOctokit()
const defaults = {
request: {retries: 3},
retry: {enabled: true}
}
const originalDefaults = JSON.parse(JSON.stringify(defaults))
const wrapped = createConfiguredGetOctokit(raw as any, defaults)
wrapped('tok' as any, {request: {timeout: 5000}, retry: {retries: 10}} as any)
wrapped('tok' as any, {request: {timeout: 9000}} as any)
expect(defaults).toEqual(originalDefaults)
})
test('falsy-but-valid values are preserved, only undefined is stripped', () => {
const raw = makeMockGetOctokit()
const defaults = {baseUrl: 'https://ghes.example.com/api/v3'}
const wrapped = createConfiguredGetOctokit(raw as any, defaults)
wrapped('tok' as any, {
log: null,
retries: 0,
debug: false,
userAgent: ''
} as any)
const calledOpts = raw.mock.calls[0][1]
expect(calledOpts.log).toBeNull()
expect(calledOpts.retries).toBe(0)
expect(calledOpts.debug).toBe(false)
expect(calledOpts.userAgent).toBe('')
expect(calledOpts.baseUrl).toBe('https://ghes.example.com/api/v3')
})
})