/**
* GitHub Tools Tests
*/
import { jest } from '@jest/globals';
import {
githubSecretsList,
githubSecretsSet,
setCommandRunner
} from '../src/tools.js';
describe('GitHub Secrets Tools', () => {
let mockRunner;
beforeEach(() => {
mockRunner = jest.fn();
setCommandRunner(mockRunner);
});
describe('githubSecretsList', () => {
test('should list repository secrets', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: 'DOCKER_USERNAME Updated 2024-01-15\nDOCKER_PASSWORD Updated 2024-01-15\nSONA_TOKEN Updated 2024-01-10',
stderr: ''
});
const result = await githubSecretsList({ repo: '' });
expect(result.content[0].text).toContain('REPOSITORY SECRETS');
expect(result.content[0].text).toContain('DOCKER_USERNAME');
expect(result.content[0].text).toContain('DOCKER_PASSWORD');
});
test('should show message when no secrets configured', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: '',
stderr: ''
});
const result = await githubSecretsList({ repo: '' });
expect(result.content[0].text).toContain('No repository secrets configured');
});
test('should handle authentication error', async () => {
mockRunner.mockResolvedValue({
success: false,
error: 'gh: not logged in',
stdout: '',
stderr: 'gh: not logged in. Run: gh auth login'
});
const result = await githubSecretsList({ repo: '' });
expect(result.content[0].text).toContain('Error');
expect(result.content[0].text).toContain('gh auth login');
});
test('should list secrets for specific repo', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: 'API_KEY Updated 2024-01-20',
stderr: ''
});
const result = await githubSecretsList({ repo: 'owner/repo' });
expect(mockRunner).toHaveBeenCalledWith('gh secret list -R owner/repo');
expect(result.content[0].text).toContain('API_KEY');
});
});
describe('githubSecretsSet', () => {
test('should reject invalid secret name - lowercase', async () => {
const result = await githubSecretsSet({
name: 'api_key',
value: 'secret123',
repo: '',
env: ''
});
expect(result.content[0].text).toContain('Invalid secret name');
expect(result.content[0].text).toContain('Must be UPPERCASE');
});
test('should reject invalid secret name - starts with number', async () => {
const result = await githubSecretsSet({
name: '123_KEY',
value: 'secret123',
repo: '',
env: ''
});
expect(result.content[0].text).toContain('Invalid secret name');
expect(result.content[0].text).toContain('Must start with a letter');
});
test('should reject invalid secret name - special characters', async () => {
const result = await githubSecretsSet({
name: 'API-KEY',
value: 'secret123',
repo: '',
env: ''
});
expect(result.content[0].text).toContain('Invalid secret name');
expect(result.content[0].text).toContain('A-Z, 0-9, and _');
});
test('should accept valid secret name and set secret', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: '✓ Set secret API_KEY',
stderr: ''
});
const result = await githubSecretsSet({
name: 'API_KEY',
value: 'my-secret-value-123',
repo: '',
env: ''
});
expect(result.content[0].text).toContain('Secret set successfully');
expect(result.content[0].text).toContain('API_KEY');
expect(result.content[0].text).toContain('my-***123'); // Masked value
expect(result.content[0].text).toContain('repository');
});
test('should set secret for specific environment', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: '✓ Set secret DATABASE_URL',
stderr: ''
});
const result = await githubSecretsSet({
name: 'DATABASE_URL',
value: 'postgres://user:pass@host:5432/db',
repo: '',
env: 'production'
});
expect(result.content[0].text).toContain('Secret set successfully');
expect(result.content[0].text).toContain('environment "production"');
expect(mockRunner).toHaveBeenCalledWith(
expect.stringContaining('--env production'),
expect.anything()
);
});
test('should set secret for specific repo', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: '✓ Set secret DEPLOY_KEY',
stderr: ''
});
const result = await githubSecretsSet({
name: 'DEPLOY_KEY',
value: 'ssh-rsa AAAAB3...',
repo: 'myorg/myrepo',
env: ''
});
expect(result.content[0].text).toContain('Secret set successfully');
expect(result.content[0].text).toContain('myorg/myrepo');
expect(mockRunner).toHaveBeenCalledWith(
expect.stringContaining('-R myorg/myrepo'),
expect.anything()
);
});
test('should handle authentication failure', async () => {
mockRunner.mockResolvedValue({
success: false,
error: 'HTTP 401: unauthorized',
stdout: '',
stderr: 'HTTP 401: unauthorized'
});
const result = await githubSecretsSet({
name: 'API_KEY',
value: 'secret',
repo: '',
env: ''
});
expect(result.content[0].text).toContain('Failed to set secret');
});
test('should handle repo not found', async () => {
mockRunner.mockResolvedValue({
success: false,
error: 'HTTP 404: not found',
stdout: '',
stderr: 'HTTP 404: repository not found'
});
const result = await githubSecretsSet({
name: 'API_KEY',
value: 'secret',
repo: 'nonexistent/repo',
env: ''
});
expect(result.content[0].text).toContain('Failed to set secret');
});
test('should mask short secrets properly', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: '✓ Set secret',
stderr: ''
});
const result = await githubSecretsSet({
name: 'SHORT_KEY',
value: 'abc123',
repo: '',
env: ''
});
expect(result.content[0].text).toContain('abc***123');
});
test('should accept complex valid secret names', async () => {
mockRunner.mockResolvedValue({
success: true,
stdout: '✓ Set secret',
stderr: ''
});
const result = await githubSecretsSet({
name: 'AWS_ACCESS_KEY_ID_2024',
value: 'AKIAIOSFODNN7EXAMPLE',
repo: '',
env: ''
});
expect(result.content[0].text).toContain('Secret set successfully');
expect(result.content[0].text).toContain('AWS_ACCESS_KEY_ID_2024');
});
});
});