1
1
mirror of https://github.com/theoludwig/programming-challenges.git synced 2025-05-18 12:02:53 +02:00

feat(cli): add commands/run/solution

This commit is contained in:
Divlo
2022-08-30 15:48:07 +02:00
parent 6427b3d273
commit d6a6c706ce
8 changed files with 272 additions and 13 deletions

View File

@ -0,0 +1,148 @@
import { PassThrough } from 'node:stream'
import path from 'node:path'
import tap from 'tap'
import sinon from 'sinon'
import chalk from 'chalk'
import { cli } from '../../../cli.js'
const input = ['run', 'solution']
const challenge = 'hello-world'
const language = 'c'
const solution = 'function'
const inputPath = path.join(
process.cwd(),
'challenges',
challenge,
'test',
'1',
'input.txt'
)
const inputChallenge = `--challenge=${challenge}`
const inputLanguage = `--language=${language}`
const inputSolution = `--solution=${solution}`
const inputInputPath = `--input-path=${inputPath}`
await tap.test('programming-challenges run solution', async (t) => {
t.afterEach(() => {
sinon.restore()
})
await t.test('succeeds', async (t) => {
sinon.stub(console, 'log').value(() => {})
const consoleLogSpy = sinon.spy(console, 'log')
const stream = new PassThrough()
const exitCode = await cli.run(
[
...input,
inputChallenge,
inputSolution,
inputLanguage,
inputInputPath,
'--output'
],
{
stdin: process.stdin,
stdout: stream,
stderr: stream
}
)
stream.end()
t.equal(exitCode, 0)
t.equal(consoleLogSpy.calledWith(`Hello, world!`), true)
})
await t.test("fails with solution that doesn't exist", async (t) => {
sinon.stub(console, 'error').value(() => {})
const consoleErrorSpy = sinon.spy(console, 'error')
const stream = new PassThrough()
const invalidSolution = 'invalid'
const inputInvalidSolution = `--solution=${invalidSolution}`
const exitCode = await cli.run(
[
...input,
inputChallenge,
inputInvalidSolution,
inputLanguage,
inputInputPath
],
{
stdin: process.stdin,
stdout: stream,
stderr: stream
}
)
stream.end()
t.equal(exitCode, 1)
t.equal(
consoleErrorSpy.calledWith(
chalk.bold.red('Error:') + ' The solution was not found.'
),
true
)
})
await t.test('fails with invalid language', async (t) => {
sinon.stub(console, 'error').value(() => {})
const consoleErrorSpy = sinon.spy(console, 'error')
const stream = new PassThrough()
const invalidLanguage = 'invalid'
const inputInvalidLanguage = `--language=${invalidLanguage}`
const exitCode = await cli.run(
[
...input,
inputChallenge,
inputSolution,
inputInvalidLanguage,
inputInputPath
],
{
stdin: process.stdin,
stdout: stream,
stderr: stream
}
)
stream.end()
t.equal(exitCode, 1)
t.equal(
consoleErrorSpy.calledWith(
chalk.bold.red('Error:') +
' This programming language is not supported yet.'
),
true
)
})
await t.test('fails with invalid `input-path`', async (t) => {
sinon.stub(console, 'error').value(() => {})
const consoleErrorSpy = sinon.spy(console, 'error')
const stream = new PassThrough()
const invalidInputPath = 'invalid'
const inputInvalidInputPath = `--input-path=${invalidInputPath}`
const inputPath = path.resolve(process.cwd(), invalidInputPath)
const exitCode = await cli.run(
[
...input,
inputChallenge,
inputSolution,
inputLanguage,
inputInvalidInputPath
],
{
stdin: process.stdin,
stdout: stream,
stderr: stream
}
)
stream.end()
t.equal(exitCode, 1)
t.equal(
consoleErrorSpy.calledWith(
chalk.bold.red('Error:') +
` The \`input-path\` doesn't exist: ${inputPath}.`
),
true
)
})
})

View File

@ -0,0 +1,72 @@
import path from 'node:path'
import fs from 'node:fs'
import { Command, Option } from 'clipanion'
import * as typanion from 'typanion'
import chalk from 'chalk'
import { isExistingPath } from '../../utils/isExistingPath.js'
import { template } from '../../services/Template.js'
import { Solution } from '../../services/Solution.js'
export class RunSolutionCommand extends Command {
static paths = [['run', 'solution']]
static usage = {
description: 'Run the solution with the given `input.txt` file.'
}
public programmingLanguage = Option.String('--language', {
description: 'The programming language used to solve the challenge.',
required: true,
validator: typanion.isString()
})
public challenge = Option.String('--challenge', {
description: 'The challenge name where you want to run your solution.',
required: true,
validator: typanion.isString()
})
public solutionName = Option.String('--solution', {
description: 'The solution name to run.',
required: true,
validator: typanion.isString()
})
public inputPathUser = Option.String('--input-path', {
description: 'The input file path to use.',
required: true,
validator: typanion.isString()
})
public output = Option.Boolean('--output', false, {
description: 'Display the output of the solution.'
})
async execute(): Promise<number> {
console.log()
try {
await template.verifySupportedProgrammingLanguage(
this.programmingLanguage
)
const solution = await Solution.get({
name: this.solutionName,
challengeName: this.challenge,
programmingLanguageName: this.programmingLanguage
})
const inputPath = path.resolve(process.cwd(), this.inputPathUser)
if (!(await isExistingPath(inputPath))) {
throw new Error(`The \`input-path\` doesn't exist: ${inputPath}.`)
}
const input = await fs.promises.readFile(inputPath, { encoding: 'utf-8' })
await solution.run(input, this.output)
return 0
} catch (error) {
if (error instanceof Error) {
console.error(`${chalk.bold.red('Error:')} ${error.message}`)
}
return 1
}
}
}

View File

@ -26,7 +26,7 @@ export class RunTestCommand extends Command {
})
public solutionName = Option.String('--solution', {
description: 'solution',
description: 'The solution name to run.',
validator: typanion.isString()
})