148 lines
5.8 KiB
TypeScript
148 lines
5.8 KiB
TypeScript
import * as NodeServices from "@effect/platform-node/NodeServices"
|
|
import { assert, describe, it } from "@effect/vitest"
|
|
import { Effect, Layer, Stdio, Stream } from "effect"
|
|
import * as Exit from "effect/Exit"
|
|
import { TestConsole } from "effect/testing"
|
|
import { CliOutput } from "effect/unstable/cli"
|
|
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
|
|
|
|
const makeLayer = (args: ReadonlyArray<string>) =>
|
|
Layer.mergeAll(
|
|
TestConsole.layer,
|
|
CliOutput.layer(CliOutput.defaultFormatter({ colors: false })),
|
|
NodeServices.layer,
|
|
Stdio.layerTest({ args: Effect.succeed(args) })
|
|
)
|
|
|
|
const fixturePath = (fileName: string) => `${import.meta.dirname}/fixtures/${fileName}`
|
|
const cliProcessPath = `${import.meta.dirname}/../src/bin.ts`
|
|
|
|
type CliMainModule = {
|
|
readonly run: Effect.Effect<void>
|
|
}
|
|
|
|
const runCli = Effect.fnUntraced(function*(args: ReadonlyArray<string>) {
|
|
const module = (yield* Effect.promise(
|
|
() => import(new URL("../src/main.ts", import.meta.url).href)
|
|
)) as CliMainModule
|
|
|
|
return yield* Effect.gen(function*() {
|
|
const exit = yield* Effect.exit(module.run)
|
|
const stdoutLines = yield* TestConsole.logLines
|
|
const stderrLines = yield* TestConsole.errorLines
|
|
const stdout = stdoutLines.length > 0 ? String(stdoutLines[stdoutLines.length - 1]) : ""
|
|
const stderr = stderrLines.map(String).join("\n")
|
|
return { exit, stdout, stderr } as const
|
|
}).pipe(Effect.provide(makeLayer(args)))
|
|
})
|
|
|
|
const runCliProcess = Effect.fnUntraced(function*(args: ReadonlyArray<string>) {
|
|
const handle = yield* ChildProcess.make("node", [cliProcessPath, ...args])
|
|
return yield* Effect.all({
|
|
exitCode: handle.exitCode,
|
|
stdout: Stream.mkString(Stream.decodeText(handle.stdout)),
|
|
stderr: Stream.mkString(Stream.decodeText(handle.stderr))
|
|
}, { concurrency: "unbounded" })
|
|
})
|
|
|
|
describe("openapigen CLI", () => {
|
|
it.effect("documents --format values and default in --help", () =>
|
|
Effect.gen(function*() {
|
|
const result = yield* runCli(["--help"])
|
|
|
|
assert.isTrue(Exit.isSuccess(result.exit))
|
|
assert.include(result.stdout, "--format")
|
|
assert.include(result.stdout, "httpclient")
|
|
assert.include(result.stdout, "httpclient-type-only")
|
|
assert.include(result.stdout, "httpapi")
|
|
assert.include(result.stdout, "default: httpclient")
|
|
assert.strictEqual(result.stderr, "")
|
|
}))
|
|
|
|
it.effect("routes --format values and defaults to httpclient", () =>
|
|
Effect.gen(function*() {
|
|
const spec = fixturePath("cli-basic-spec.json")
|
|
|
|
const defaultResult = yield* runCli(["--spec", spec, "--name", "CliClient"])
|
|
const httpclientResult = yield* runCli([
|
|
"--spec",
|
|
spec,
|
|
"--name",
|
|
"CliClient",
|
|
"--format",
|
|
"httpclient"
|
|
])
|
|
const typeOnlyResult = yield* runCli([
|
|
"--spec",
|
|
spec,
|
|
"--name",
|
|
"CliClient",
|
|
"--format",
|
|
"httpclient-type-only"
|
|
])
|
|
const httpapiResult = yield* runCli([
|
|
"--spec",
|
|
spec,
|
|
"--name",
|
|
"CliClient",
|
|
"--format",
|
|
"httpapi"
|
|
])
|
|
|
|
assert.isTrue(Exit.isSuccess(defaultResult.exit))
|
|
assert.isTrue(Exit.isSuccess(httpclientResult.exit))
|
|
assert.isTrue(Exit.isSuccess(typeOnlyResult.exit))
|
|
assert.isTrue(Exit.isSuccess(httpapiResult.exit))
|
|
|
|
assert.strictEqual(defaultResult.stderr, "")
|
|
assert.strictEqual(httpclientResult.stderr, "")
|
|
assert.strictEqual(typeOnlyResult.stderr, "")
|
|
assert.strictEqual(httpapiResult.stderr, "")
|
|
|
|
assert.strictEqual(defaultResult.stdout, httpclientResult.stdout)
|
|
assert.include(httpclientResult.stdout, "import * as Schema from \"effect/Schema\"")
|
|
assert.notInclude(typeOnlyResult.stdout, "import * as Schema from \"effect/Schema\"")
|
|
assert.include(typeOnlyResult.stdout, "import type * as HttpClient from \"effect/unstable/http/HttpClient\"")
|
|
assert.include(httpapiResult.stdout, "export class CliClient extends HttpApi.make(\"CliClient\")")
|
|
}))
|
|
|
|
it.effect("rejects legacy --type-only flag", () =>
|
|
Effect.gen(function*() {
|
|
const spec = fixturePath("cli-basic-spec.json")
|
|
const result = yield* runCli(["--spec", spec, "--name", "CliClient", "--type-only"])
|
|
|
|
assert.isTrue(Exit.isFailure(result.exit))
|
|
assert.include(result.stdout, "USAGE")
|
|
assert.include(result.stderr, "Unrecognized flag: --type-only")
|
|
}))
|
|
|
|
it.effect("writes warnings to stderr and keeps stdout as generated source", () =>
|
|
Effect.gen(function*() {
|
|
const spec = fixturePath("cli-warning-spec.json")
|
|
const result = yield* runCli(["--spec", spec, "--name", "CliClient"])
|
|
|
|
assert.isTrue(Exit.isSuccess(result.exit))
|
|
assert.include(result.stdout, "export const make = (")
|
|
assert.include(result.stderr, "WARNING [cookie-parameter-dropped]")
|
|
assert.include(result.stderr, "cookie-parameter-dropped")
|
|
assert.notInclude(result.stdout, "cookie-parameter-dropped")
|
|
assert.notInclude(result.stderr, "export const make = (")
|
|
}))
|
|
|
|
it.effect("separates generated source and warnings when spawned as a child process", () =>
|
|
Effect.gen(function*() {
|
|
const spec = fixturePath("cli-warning-spec.json")
|
|
const result = yield* runCliProcess(["--spec", spec, "--name", "CliClient"])
|
|
|
|
assert.strictEqual(result.exitCode, ChildProcessSpawner.ExitCode(0))
|
|
assert.include(result.stdout, "export const make = (")
|
|
assert.notInclude(result.stdout, "WARNING [")
|
|
assert.notInclude(result.stdout, "cookie-parameter-dropped")
|
|
assert.include(
|
|
result.stderr,
|
|
"WARNING [cookie-parameter-dropped] GET /users/{id} (getUser): Cookie parameter \"session\" was dropped because non-security cookie parameters are not supported."
|
|
)
|
|
assert.notInclude(result.stderr, "export const make = (")
|
|
}).pipe(Effect.scoped, Effect.provide(NodeServices.layer)))
|
|
})
|