diff --git a/lib/configManager.js b/lib/configManager.js index 7f1bb39b0..e9b172203 100644 --- a/lib/configManager.js +++ b/lib/configManager.js @@ -21,6 +21,7 @@ module.exports = class ConfigManager { const params = Object.assign(repo, { path: filePath, ref: this.ref }) const response = await this.context.octokit.rest.repos.getContent(params).catch(e => { this.log.error(`Error getting settings ${e}`) + throw e }) // Ignore in case path is a folder diff --git a/test/unit/lib/configManager.test.js b/test/unit/lib/configManager.test.js new file mode 100644 index 000000000..3db68dd7c --- /dev/null +++ b/test/unit/lib/configManager.test.js @@ -0,0 +1,100 @@ +/* eslint-disable no-undef */ +const ConfigManager = require('../../../lib/configManager') + +describe('configManager', () => { + let context + + beforeEach(() => { + context = { + repo: () => { return { owner: 'test-org', repo: 'admin' } }, + octokit: { + rest: { + repos: { + getContent: jest.fn() + } + } + }, + log: { + debug: jest.fn(), + info: jest.fn(), + error: jest.fn() + } + } + }) + + describe('loadYaml', () => { + it('returns the parsed YAML content when the file is fetched successfully', async () => { + const configManager = new ConfigManager(context, 'main') + context.octokit.rest.repos.getContent.mockResolvedValue({ + data: { content: Buffer.from('key: value').toString('base64') } + }) + + const result = await configManager.loadYaml('.github/settings.yml') + + expect(result).toEqual({ key: 'value' }) + expect(context.octokit.rest.repos.getContent).toHaveBeenCalledWith({ + owner: 'test-org', + repo: 'admin', + path: '.github/settings.yml', + ref: 'main' + }) + }) + + it('returns null when the path is a folder', async () => { + const configManager = new ConfigManager(context, 'main') + context.octokit.rest.repos.getContent.mockResolvedValue({ data: [] }) + + await expect(configManager.loadYaml('.github')).resolves.toBeNull() + }) + + it('returns undefined when the path is a symlink or submodule', async () => { + const configManager = new ConfigManager(context, 'main') + context.octokit.rest.repos.getContent.mockResolvedValue({ data: { content: null } }) + + await expect(configManager.loadYaml('.github/settings.yml')).resolves.toBeUndefined() + }) + + it('returns null when the file does not exist', async () => { + const configManager = new ConfigManager(context, 'main') + const notFound = new Error('Not Found') + notFound.status = 404 + context.octokit.rest.repos.getContent.mockRejectedValue(notFound) + + await expect(configManager.loadYaml('.github/settings.yml')).resolves.toBeNull() + }) + + it('propagates a non-404 error instead of masking it', async () => { + const configManager = new ConfigManager(context, 'main') + const serverError = new Error('Internal Server Error') + serverError.status = 500 + context.octokit.rest.repos.getContent.mockRejectedValue(serverError) + + await expect(configManager.loadYaml('.github/settings.yml')).rejects.toThrow('Internal Server Error') + }) + + it('propagates the original error object so its status is preserved', async () => { + const configManager = new ConfigManager(context, 'main') + const forbidden = new Error('Forbidden') + forbidden.status = 403 + context.octokit.rest.repos.getContent.mockRejectedValue(forbidden) + + await expect(configManager.loadYaml('.github/settings.yml')).rejects.toBe(forbidden) + }) + }) + + describe('loadGlobalSettingsYaml', () => { + it('loads the settings file from the configured config path', async () => { + const configManager = new ConfigManager(context, 'main') + context.octokit.rest.repos.getContent.mockResolvedValue({ + data: { content: Buffer.from('repository:\n has_wiki: false').toString('base64') } + }) + + const result = await configManager.loadGlobalSettingsYaml() + + expect(result).toEqual({ repository: { has_wiki: false } }) + expect(context.octokit.rest.repos.getContent).toHaveBeenCalledWith( + expect.objectContaining({ path: '.github/settings.yml' }) + ) + }) + }) +})