test: aicommits (#146)

This commit is contained in:
hiroki osame
2023-03-10 06:12:27 -05:00
committed by GitHub
parent 09c2c98e05
commit ceb49507ed
5 changed files with 151 additions and 18 deletions

View File

@@ -40,6 +40,8 @@ jobs:
run: pnpm build
- name: Test
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
run: |
pnpm test
pnpm --use-node-version=14.21.3 test

View File

@@ -1,5 +1,6 @@
import { describe } from 'manten';
describe('aicommits', ({ runTestSuite }) => {
runTestSuite(import('./specs/cli.js'));
runTestSuite(import('./specs/config.js'));
});

76
tests/specs/cli.ts Normal file
View File

@@ -0,0 +1,76 @@
import { testSuite, expect } from 'manten';
import { createFixture } from 'fs-fixture';
import { createAicommits, createGit } from '../utils.js';
const { OPENAI_KEY } = process.env;
if (!OPENAI_KEY) {
throw new Error('process.env.OPENAI_KEY is necessary to run these tests');
}
export default testSuite(({ describe }) => {
if (process.platform === 'win32') {
// https://github.com/nodejs/node/issues/31409
console.warn('Skipping tests on Windows because Node.js spawn cant open TTYs');
return;
}
describe('CLI', async ({ test }) => {
const fixture = await createFixture({
'data.json': JSON.stringify({
firstName: 'Hiroki',
}),
});
const aicommits = createAicommits({
cwd: fixture.path,
home: fixture.path,
});
await test('Fails on non-Git project', async () => {
const { stdout, exitCode } = await aicommits([], { reject: false });
expect(exitCode).toBe(1);
expect(stdout).toMatch('The current directory must be a Git repository!');
});
const git = await createGit(fixture.path);
await test('Fails on no staged files', async () => {
const { stdout, exitCode } = await aicommits([], { reject: false });
expect(exitCode).toBe(1);
expect(stdout).toMatch('No staged changes found. Make sure to stage your changes with `git add`.');
});
await test('Commits', async () => {
await git('add', ['data.json']);
await aicommits([
'config',
'set',
`OPENAI_KEY=${OPENAI_KEY}`,
]);
const statusBefore = await git('status', ['--porcelain', '--untracked-files=no']);
expect(statusBefore.stdout).toBe('A data.json');
const committing = aicommits();
committing.stdout!.on('data', (buffer) => {
const data = buffer.toString();
if (data.match('Yes /')) {
committing.stdin!.write('y');
committing.stdin!.end();
}
});
await committing;
const statusAfter = await git('status', ['--porcelain', '--untracked-files=no']);
expect(statusAfter.stdout).toBe('');
const { stdout } = await git('log', ['--oneline']);
console.log('Commited with:', stdout);
});
await fixture.rm();
});
});

View File

@@ -2,26 +2,19 @@ import fs from 'fs/promises';
import path from 'path';
import { testSuite, expect } from 'manten';
import { createFixture } from 'fs-fixture';
import { execaNode } from 'execa';
const aicommitsPath = path.resolve('./dist/cli.mjs');
import { createAicommits } from '../utils.js';
export default testSuite(({ describe }) => {
describe('config', async ({ test }) => {
const fixture = await createFixture();
const env = {
// Linux
HOME: fixture.path,
// Windows
USERPROFILE: fixture.path,
};
const aicommits = createAicommits({
home: fixture.path,
});
const configPath = path.join(fixture.path, '.aicommits');
const openAiToken = 'OPENAI_KEY=sk-abc';
test('set unknown config file', async () => {
const { stderr } = await execaNode(aicommitsPath, ['config', 'set', 'UNKNOWN=1'], {
env,
const { stderr } = await aicommits(['config', 'set', 'UNKNOWN=1'], {
reject: false,
});
@@ -29,8 +22,7 @@ export default testSuite(({ describe }) => {
});
test('set invalid OPENAI_KEY', async () => {
const { stderr } = await execaNode(aicommitsPath, ['config', 'set', 'OPENAI_KEY=abc'], {
env,
const { stderr } = await aicommits(['config', 'set', 'OPENAI_KEY=abc'], {
reject: false,
});
@@ -38,14 +30,14 @@ export default testSuite(({ describe }) => {
});
await test('set config file', async () => {
await execaNode(aicommitsPath, ['config', 'set', openAiToken], { env });
await aicommits(['config', 'set', openAiToken]);
const configFile = await fs.readFile(configPath, 'utf8');
expect(configFile).toMatch(openAiToken);
});
await test('get config file', async () => {
const { stdout } = await execaNode(aicommitsPath, ['config', 'get', 'OPENAI_KEY'], { env });
const { stdout } = await aicommits(['config', 'get', 'OPENAI_KEY']);
expect(stdout).toBe(openAiToken);
});
@@ -53,8 +45,7 @@ export default testSuite(({ describe }) => {
await test('reading unknown config', async () => {
await fs.appendFile(configPath, 'UNKNOWN=1');
const { stdout, stderr } = await execaNode(aicommitsPath, ['config', 'get', 'UNKNOWN'], {
env,
const { stdout, stderr } = await aicommits(['config', 'get', 'UNKNOWN'], {
reject: false,
});

63
tests/utils.ts Normal file
View File

@@ -0,0 +1,63 @@
import path from 'path';
import { execa, execaNode, type Options } from 'execa';
const aicommitsPath = path.resolve('./dist/cli.mjs');
export const createAicommits = ({
cwd,
home,
}: {
cwd?: string;
home: string;
}) => {
const homeEnv = {
HOME: home, // Linux
USERPROFILE: home, // Windows
};
return (
args?: string[],
options?: Options,
) => execaNode(aicommitsPath, args, {
...options,
cwd,
extendEnv: false,
env: {
...homeEnv,
...options?.env,
},
// Block tsx nodeOptions
nodeOptions: [],
});
};
export const createGit = async (cwd: string) => {
const git = (
command: string,
args?: string[],
options?: Options,
) => (
execa(
'git',
[command, ...(args || [])],
{
cwd,
...options,
},
)
);
await git(
'init',
[
// In case of different default branch name
'--initial-branch=master',
],
);
await git('config', ['user.name', 'name']);
await git('config', ['user.email', 'email']);
return git;
};