1
0
Fork 0
mirror of synced 2026-06-05 11:08:19 +00:00
configure-aws-credentials/test/profileManager.test.ts
2025-12-21 11:28:36 +01:00

342 lines
12 KiB
TypeScript

import * as core from '@actions/core';
import { fs, vol } from 'memfs';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import * as ini from 'ini';
import {
ensureAwsDirectoryExists,
getProfileFilePaths,
mergeProfileSection,
validateProfileName,
writeProfileFiles,
} from '../src/profileManager';
describe('Profile Manager', {}, () => {
beforeEach(() => {
vi.restoreAllMocks();
vi.mock('node:fs');
vol.reset();
vi.spyOn(core, 'debug').mockImplementation(() => {});
vi.spyOn(core, 'info').mockImplementation(() => {});
});
describe('validateProfileName', {}, () => {
it('accepts valid profile names', {}, () => {
expect(() => validateProfileName('dev')).not.toThrow();
expect(() => validateProfileName('production')).not.toThrow();
expect(() => validateProfileName('my-profile-123')).not.toThrow();
expect(() => validateProfileName('default')).not.toThrow();
});
it('rejects empty profile names', {}, () => {
expect(() => validateProfileName('')).toThrow('aws-profile must not be empty');
expect(() => validateProfileName(' ')).toThrow('aws-profile must not be empty');
});
it('rejects profile names with whitespace', {}, () => {
expect(() => validateProfileName('my profile')).toThrow('aws-profile must not contain whitespace');
expect(() => validateProfileName('dev\ntest')).toThrow('aws-profile must not contain whitespace');
expect(() => validateProfileName('prod\tenv')).toThrow('aws-profile must not contain whitespace');
});
it('rejects profile names with brackets', {}, () => {
expect(() => validateProfileName('dev[test]')).toThrow('aws-profile must not contain brackets');
expect(() => validateProfileName('[profile]')).toThrow('aws-profile must not contain brackets');
});
it('rejects profile names with path separators', {}, () => {
expect(() => validateProfileName('dev/test')).toThrow('aws-profile must not contain path separators');
expect(() => validateProfileName('dev\\test')).toThrow('aws-profile must not contain path separators');
expect(() => validateProfileName('../etc/passwd')).toThrow('aws-profile must not contain path separators');
});
});
describe('getProfileFilePaths', {}, () => {
it('returns default paths when env vars not set', {}, () => {
delete process.env.AWS_SHARED_CREDENTIALS_FILE;
delete process.env.AWS_CONFIG_FILE;
const paths = getProfileFilePaths();
expect(paths.credentials).toMatch(/\.aws\/credentials$/);
expect(paths.config).toMatch(/\.aws\/config$/);
});
it('respects AWS_SHARED_CREDENTIALS_FILE env var', {}, () => {
process.env.AWS_SHARED_CREDENTIALS_FILE = '/custom/path/credentials';
process.env.AWS_CONFIG_FILE = '/custom/path/config';
const paths = getProfileFilePaths();
expect(paths.credentials).toBe('/custom/path/credentials');
expect(paths.config).toBe('/custom/path/config');
});
});
describe('ensureAwsDirectoryExists', {}, () => {
it('creates directory if it does not exist', {}, () => {
const filePath = '/home/runner/.aws/credentials';
ensureAwsDirectoryExists(filePath);
expect(fs.existsSync('/home/runner/.aws')).toBe(true);
});
it('does not error if directory already exists', {}, () => {
const filePath = '/home/runner/.aws/credentials';
fs.mkdirSync('/home/runner/.aws', { recursive: true });
expect(() => ensureAwsDirectoryExists(filePath)).not.toThrow();
});
it('creates nested directories', {}, () => {
const filePath = '/home/runner/custom/path/.aws/credentials';
ensureAwsDirectoryExists(filePath);
expect(fs.existsSync('/home/runner/custom/path/.aws')).toBe(true);
});
});
describe('mergeProfileSection', {}, () => {
it('creates new file with profile section', {}, () => {
const filePath = '/home/runner/.aws/credentials';
fs.mkdirSync('/home/runner/.aws', { recursive: true });
mergeProfileSection(filePath, 'dev', {
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
aws_secret_access_key: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
});
const content = fs.readFileSync(filePath, 'utf-8');
const parsed = ini.parse(content);
expect(parsed.dev).toBeDefined();
expect(parsed.dev.aws_access_key_id).toBe('AKIAIOSFODNN7EXAMPLE');
expect(parsed.dev.aws_secret_access_key).toBe('wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY');
});
it('merges with existing profiles', {}, () => {
const filePath = '/home/runner/.aws/credentials';
fs.mkdirSync('/home/runner/.aws', { recursive: true });
// Create initial profile
mergeProfileSection(filePath, 'dev', {
aws_access_key_id: 'AKIAIOSFODNN7EXAMPLE',
aws_secret_access_key: 'devSecretKey',
});
// Add second profile
mergeProfileSection(filePath, 'prod', {
aws_access_key_id: 'AKIAPRODEXAMPLE',
aws_secret_access_key: 'prodSecretKey',
});
const content = fs.readFileSync(filePath, 'utf-8');
const parsed = ini.parse(content);
expect(parsed.dev).toBeDefined();
expect(parsed.dev.aws_access_key_id).toBe('AKIAIOSFODNN7EXAMPLE');
expect(parsed.prod).toBeDefined();
expect(parsed.prod.aws_access_key_id).toBe('AKIAPRODEXAMPLE');
});
it('overwrites existing profile with same name', {}, () => {
const filePath = '/home/runner/.aws/credentials';
fs.mkdirSync('/home/runner/.aws', { recursive: true });
// Create initial profile
mergeProfileSection(filePath, 'dev', {
aws_access_key_id: 'OLD_KEY',
aws_secret_access_key: 'oldSecretKey',
});
// Overwrite with new credentials
mergeProfileSection(filePath, 'dev', {
aws_access_key_id: 'NEW_KEY',
aws_secret_access_key: 'newSecretKey',
aws_session_token: 'sessionToken',
});
const content = fs.readFileSync(filePath, 'utf-8');
const parsed = ini.parse(content);
expect(parsed.dev.aws_access_key_id).toBe('NEW_KEY');
expect(parsed.dev.aws_secret_access_key).toBe('newSecretKey');
expect(parsed.dev.aws_session_token).toBe('sessionToken');
});
});
describe('writeProfileFiles', {}, () => {
beforeEach(() => {
delete process.env.AWS_SHARED_CREDENTIALS_FILE;
delete process.env.AWS_CONFIG_FILE;
});
it('writes credentials and config for new profile', {}, () => {
writeProfileFiles(
'dev',
{
AccessKeyId: 'AKIAIOSFODNN7EXAMPLE',
SecretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
SessionToken: 'FwoGZXIvYXdzEBYaDEXAMPLE',
},
'us-east-1',
);
// Check credentials file
const credsPath = getProfileFilePaths().credentials;
const credContent = fs.readFileSync(credsPath, 'utf-8');
const credParsed = ini.parse(credContent);
expect(credParsed.dev).toBeDefined();
expect(credParsed.dev.aws_access_key_id).toBe('AKIAIOSFODNN7EXAMPLE');
expect(credParsed.dev.aws_secret_access_key).toBe('wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY');
expect(credParsed.dev.aws_session_token).toBe('FwoGZXIvYXdzEBYaDEXAMPLE');
// Check config file
const configPath = getProfileFilePaths().config;
const configContent = fs.readFileSync(configPath, 'utf-8');
const configParsed = ini.parse(configContent);
expect(configParsed['profile dev']).toBeDefined();
expect(configParsed['profile dev'].region).toBe('us-east-1');
});
it('uses correct section naming for default profile', {}, () => {
writeProfileFiles(
'default',
{
AccessKeyId: 'AKIAIOSFODNN7EXAMPLE',
SecretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
},
'us-west-2',
);
// Check credentials file uses [default]
const credsPath = getProfileFilePaths().credentials;
const credContent = fs.readFileSync(credsPath, 'utf-8');
const credParsed = ini.parse(credContent);
expect(credParsed.default).toBeDefined();
expect(credParsed['profile default']).toBeUndefined();
// Check config file uses [default] (not [profile default])
const configPath = getProfileFilePaths().config;
const configContent = fs.readFileSync(configPath, 'utf-8');
const configParsed = ini.parse(configContent);
expect(configParsed.default).toBeDefined();
expect(configParsed['profile default']).toBeUndefined();
});
it('supports multiple profiles', {}, () => {
// Write first profile
writeProfileFiles(
'dev',
{
AccessKeyId: 'AKIADEV',
SecretAccessKey: 'devSecret',
},
'us-east-1',
);
// Write second profile
writeProfileFiles(
'prod',
{
AccessKeyId: 'AKIAPROD',
SecretAccessKey: 'prodSecret',
SessionToken: 'prodToken',
},
'us-west-2',
);
// Verify both profiles exist
const credsPath = getProfileFilePaths().credentials;
const credContent = fs.readFileSync(credsPath, 'utf-8');
const credParsed = ini.parse(credContent);
expect(credParsed.dev).toBeDefined();
expect(credParsed.prod).toBeDefined();
expect(credParsed.dev.aws_access_key_id).toBe('AKIADEV');
expect(credParsed.prod.aws_access_key_id).toBe('AKIAPROD');
});
it('handles credentials without session token', {}, () => {
writeProfileFiles(
'dev',
{
AccessKeyId: 'AKIAIOSFODNN7EXAMPLE',
SecretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
},
'us-east-1',
);
const credsPath = getProfileFilePaths().credentials;
const credContent = fs.readFileSync(credsPath, 'utf-8');
const credParsed = ini.parse(credContent);
expect(credParsed.dev.aws_access_key_id).toBe('AKIAIOSFODNN7EXAMPLE');
expect(credParsed.dev.aws_secret_access_key).toBe('wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY');
expect(credParsed.dev.aws_session_token).toBeUndefined();
});
it('throws error for invalid profile name', {}, () => {
expect(() =>
writeProfileFiles(
'invalid profile',
{
AccessKeyId: 'AKIA',
SecretAccessKey: 'secret',
},
'us-east-1',
),
).toThrow('Failed to write AWS profile');
expect(() =>
writeProfileFiles(
'invalid profile',
{
AccessKeyId: 'AKIA',
SecretAccessKey: 'secret',
},
'us-east-1',
),
).toThrow('whitespace');
});
it('respects custom file paths from env vars', {}, () => {
process.env.AWS_SHARED_CREDENTIALS_FILE = '/custom/credentials';
process.env.AWS_CONFIG_FILE = '/custom/config';
fs.mkdirSync('/custom', { recursive: true });
writeProfileFiles(
'dev',
{
AccessKeyId: 'AKIA',
SecretAccessKey: 'secret',
},
'us-east-1',
);
expect(fs.existsSync('/custom/credentials')).toBe(true);
expect(fs.existsSync('/custom/config')).toBe(true);
});
it('logs info messages', {}, () => {
writeProfileFiles(
'dev',
{
AccessKeyId: 'AKIA',
SecretAccessKey: 'secret',
},
'us-east-1',
);
expect(core.info).toHaveBeenCalledWith('Writing credentials to profile: dev');
expect(core.info).toHaveBeenCalledWith('Writing config to profile: dev');
expect(core.info).toHaveBeenCalledWith('✓ Successfully configured AWS profile: dev');
});
});
});