test: aicommits (#146)
This commit is contained in:
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@@ -40,6 +40,8 @@ jobs:
|
|||||||
run: pnpm build
|
run: pnpm build
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
|
env:
|
||||||
|
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
|
||||||
run: |
|
run: |
|
||||||
pnpm test
|
pnpm test
|
||||||
pnpm --use-node-version=14.21.3 test
|
pnpm --use-node-version=14.21.3 test
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { describe } from 'manten';
|
import { describe } from 'manten';
|
||||||
|
|
||||||
describe('aicommits', ({ runTestSuite }) => {
|
describe('aicommits', ({ runTestSuite }) => {
|
||||||
|
runTestSuite(import('./specs/cli.js'));
|
||||||
runTestSuite(import('./specs/config.js'));
|
runTestSuite(import('./specs/config.js'));
|
||||||
});
|
});
|
||||||
|
|||||||
76
tests/specs/cli.ts
Normal file
76
tests/specs/cli.ts
Normal 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();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,26 +2,19 @@ import fs from 'fs/promises';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { testSuite, expect } from 'manten';
|
import { testSuite, expect } from 'manten';
|
||||||
import { createFixture } from 'fs-fixture';
|
import { createFixture } from 'fs-fixture';
|
||||||
import { execaNode } from 'execa';
|
import { createAicommits } from '../utils.js';
|
||||||
|
|
||||||
const aicommitsPath = path.resolve('./dist/cli.mjs');
|
|
||||||
|
|
||||||
export default testSuite(({ describe }) => {
|
export default testSuite(({ describe }) => {
|
||||||
describe('config', async ({ test }) => {
|
describe('config', async ({ test }) => {
|
||||||
const fixture = await createFixture();
|
const fixture = await createFixture();
|
||||||
const env = {
|
const aicommits = createAicommits({
|
||||||
// Linux
|
home: fixture.path,
|
||||||
HOME: fixture.path,
|
});
|
||||||
|
|
||||||
// Windows
|
|
||||||
USERPROFILE: fixture.path,
|
|
||||||
};
|
|
||||||
const configPath = path.join(fixture.path, '.aicommits');
|
const configPath = path.join(fixture.path, '.aicommits');
|
||||||
const openAiToken = 'OPENAI_KEY=sk-abc';
|
const openAiToken = 'OPENAI_KEY=sk-abc';
|
||||||
|
|
||||||
test('set unknown config file', async () => {
|
test('set unknown config file', async () => {
|
||||||
const { stderr } = await execaNode(aicommitsPath, ['config', 'set', 'UNKNOWN=1'], {
|
const { stderr } = await aicommits(['config', 'set', 'UNKNOWN=1'], {
|
||||||
env,
|
|
||||||
reject: false,
|
reject: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,8 +22,7 @@ export default testSuite(({ describe }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('set invalid OPENAI_KEY', async () => {
|
test('set invalid OPENAI_KEY', async () => {
|
||||||
const { stderr } = await execaNode(aicommitsPath, ['config', 'set', 'OPENAI_KEY=abc'], {
|
const { stderr } = await aicommits(['config', 'set', 'OPENAI_KEY=abc'], {
|
||||||
env,
|
|
||||||
reject: false,
|
reject: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,14 +30,14 @@ export default testSuite(({ describe }) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await test('set config file', async () => {
|
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');
|
const configFile = await fs.readFile(configPath, 'utf8');
|
||||||
expect(configFile).toMatch(openAiToken);
|
expect(configFile).toMatch(openAiToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
await test('get config file', async () => {
|
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);
|
expect(stdout).toBe(openAiToken);
|
||||||
});
|
});
|
||||||
@@ -53,8 +45,7 @@ export default testSuite(({ describe }) => {
|
|||||||
await test('reading unknown config', async () => {
|
await test('reading unknown config', async () => {
|
||||||
await fs.appendFile(configPath, 'UNKNOWN=1');
|
await fs.appendFile(configPath, 'UNKNOWN=1');
|
||||||
|
|
||||||
const { stdout, stderr } = await execaNode(aicommitsPath, ['config', 'get', 'UNKNOWN'], {
|
const { stdout, stderr } = await aicommits(['config', 'get', 'UNKNOWN'], {
|
||||||
env,
|
|
||||||
reject: false,
|
reject: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
63
tests/utils.ts
Normal file
63
tests/utils.ts
Normal 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;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user