diff --git a/.chronus/changes/add_e2e_test-2024-4-10-13-19-5.md b/.chronus/changes/add_e2e_test-2024-4-10-13-19-5.md new file mode 100644 index 0000000000..f545abb605 --- /dev/null +++ b/.chronus/changes/add_e2e_test-2024-4-10-13-19-5.md @@ -0,0 +1,7 @@ +--- +changeKind: fix +packages: + - "@azure-tools/typespec-client-generator-core" +--- + +Fix: Crash due to using api from next version of the compiler diff --git a/eng/pipelines/jobs/e2e-job.yml b/eng/pipelines/jobs/e2e-job.yml index 86a2592323..cafeb94bf8 100644 --- a/eng/pipelines/jobs/e2e-job.yml +++ b/eng/pipelines/jobs/e2e-job.yml @@ -24,4 +24,4 @@ jobs: displayName: Cadl Ranch e2e tests - script: pnpm test:e2e - displayName: UI Tests + displayName: E2E Tests diff --git a/packages/typespec-client-generator-core/e2e/basic-latest/main.tsp b/packages/typespec-client-generator-core/e2e/basic-latest/main.tsp new file mode 100644 index 0000000000..5a7a1c3e52 --- /dev/null +++ b/packages/typespec-client-generator-core/e2e/basic-latest/main.tsp @@ -0,0 +1,4 @@ +import "@typespec/rest"; +import "@azure-tools/typespec-client-generator-core"; + +op ping(): void; diff --git a/packages/typespec-client-generator-core/e2e/basic-latest/package.json b/packages/typespec-client-generator-core/e2e/basic-latest/package.json new file mode 100644 index 0000000000..33e90c1a8a --- /dev/null +++ b/packages/typespec-client-generator-core/e2e/basic-latest/package.json @@ -0,0 +1,15 @@ +{ + "name": "@azure-tools/tcgc-test-basic-latest", + "dependencies": { + "@typespec/compiler": "latest", + "@typespec/rest": "latest", + "@typespec/http": "latest", + "@typespec/versioning": "latest", + "@typespec/openapi": "latest", + "@typespec/openapi3": "latest", + "@azure-tools/typespec-azure-core": "latest", + "@azure-tools/typespec-autorest": "latest", + "@azure-tools/typespec-azure-resource-manager": "latest" + }, + "private": true +} diff --git a/packages/typespec-client-generator-core/e2e/check-latest.e2e.ts b/packages/typespec-client-generator-core/e2e/check-latest.e2e.ts new file mode 100644 index 0000000000..ed4baee80f --- /dev/null +++ b/packages/typespec-client-generator-core/e2e/check-latest.e2e.ts @@ -0,0 +1,84 @@ +import { findTestPackageRoot } from "@typespec/compiler/testing"; +import { SpawnOptions, spawn } from "child_process"; +import { cp, readdir, rm } from "fs/promises"; +import { join, resolve } from "path"; +import { beforeAll, it } from "vitest"; + +const packageRoot = await findTestPackageRoot(import.meta.url); +const tempDir = join(packageRoot, "temp/e2e"); + +let tgzFile: string; +beforeAll(async () => { + await rm(tempDir, { recursive: true, force: true }); + + await execSuccessAsync("pnpm", ["pack", "--pack-destination", tempDir]); + const files = await readdir(tempDir); + + const filename = files.find((x) => x.startsWith("azure-tools-typespec-client-generator-core-")); + if (filename === undefined) { + throw new Error( + `Cannot resolve package starting with "azure-tools-typespec-client-generator-core-"` + ); + } + tgzFile = join(tempDir, filename); +}); + +// Make sure it works with the latest version of dependencies and not just the local build. +it("works with latest version of packages", async () => { + const dir = await setupScenario("basic-latest"); + await execSuccessAsync("npm", ["install", tgzFile], { cwd: dir }); + await execSuccessAsync("npx", ["tsp", "compile", "."], { cwd: dir }); +}); + +async function setupScenario(name: string): Promise { + const target = resolve(tempDir, name); + await cp(resolve(packageRoot, "e2e", name), target, { + recursive: true, + }); + return target; +} + +async function execSuccessAsync(command: string, args: string[] = [], options: SpawnOptions = {}) { + const result = await execAsync(command, args, options); + if (result.exitCode !== 0) { + throw new Error( + `Command '${command} ${args.join(" ")}' failed with exit code ${result.exitCode}\n` + + result.stdio + ); + } + return result; +} +async function execAsync( + command: string, + args: string[] = [], + options: SpawnOptions = {} +): Promise<{ exitCode: number; stdio: string; stdout: string; stderr: string; proc: any }> { + const child = spawn(command, args, options); + + return new Promise((resolve, reject) => { + child.on("error", (error) => { + reject(error); + }); + const stdio: Buffer[] = []; + const stdout: Buffer[] = []; + const stderr: Buffer[] = []; + child.stdout?.on("data", (data) => { + stdout.push(data); + stdio.push(data); + }); + child.stderr?.on("data", (data) => { + stderr.push(data); + stdio.push(data); + }); + + child.on("exit", (exitCode) => { + resolve({ + exitCode: exitCode ?? -1, + stdio: Buffer.concat(stdio).toString(), + stdout: Buffer.concat(stdout).toString(), + stderr: Buffer.concat(stderr).toString(), + proc: child, + }); + }); + }); +} diff --git a/packages/typespec-client-generator-core/package.json b/packages/typespec-client-generator-core/package.json index 1de18a823d..aeb2aacb86 100644 --- a/packages/typespec-client-generator-core/package.json +++ b/packages/typespec-client-generator-core/package.json @@ -43,6 +43,7 @@ "test:watch": "vitest -w", "test:ui": "vitest --ui", "test:ci": "vitest run --coverage --reporter=junit --reporter=default", + "test:e2e": "vitest run --config ./vitest.config.e2e.ts", "lint": "eslint . --max-warnings=0", "lint:fix": "eslint . --fix ", "regen-docs": "tspd doc . --enable-experimental --output-dir ../../docs/libraries/typespec-client-generator-core/reference" diff --git a/packages/typespec-client-generator-core/src/public-utils.ts b/packages/typespec-client-generator-core/src/public-utils.ts index dc31fa009d..31b22e5cbd 100644 --- a/packages/typespec-client-generator-core/src/public-utils.ts +++ b/packages/typespec-client-generator-core/src/public-utils.ts @@ -15,7 +15,6 @@ import { getProjectedName, ignoreDiagnostics, isErrorModel, - isType, listServices, resolveEncodedName, } from "@typespec/compiler"; @@ -196,7 +195,7 @@ export function getLibraryName( type.templateMapper.args .filter( (arg): arg is Model | Enum => - isType(arg) && (arg.kind === "Model" || arg.kind === "Enum") && arg.name.length > 0 + "kind" in arg && (arg.kind === "Model" || arg.kind === "Enum") && arg.name.length > 0 ) .map((arg) => pascalCase(arg.name)) .join("") diff --git a/packages/typespec-client-generator-core/src/types.ts b/packages/typespec-client-generator-core/src/types.ts index 09cabdc7b5..3de24fdc41 100644 --- a/packages/typespec-client-generator-core/src/types.ts +++ b/packages/typespec-client-generator-core/src/types.ts @@ -28,7 +28,6 @@ import { ignoreDiagnostics, isErrorModel, isNeverType, - isType, } from "@typespec/compiler"; import { Authentication, @@ -1147,7 +1146,7 @@ function checkAndGetClientType( if (context.filterOutCoreModels && isAzureCoreModel(effectivePayloadType)) { if (effectivePayloadType.templateMapper && effectivePayloadType.name) { effectivePayloadType.templateMapper.args - .filter(isType) + .filter((arg): arg is Type => "kind" in arg) .filter((arg) => arg.kind === "Model" && arg.name) .forEach((arg) => { retval.push(...diagnostics.pipe(checkAndGetClientType(context, arg, operation))); diff --git a/packages/typespec-client-generator-core/tsconfig.json b/packages/typespec-client-generator-core/tsconfig.json index 691e8fb04d..1aef16e920 100644 --- a/packages/typespec-client-generator-core/tsconfig.json +++ b/packages/typespec-client-generator-core/tsconfig.json @@ -10,5 +10,5 @@ "rootDir": ".", "tsBuildInfoFile": "temp/tsconfig.tsbuildinfo" }, - "include": ["src/**/*.ts", "test/**/*.ts"] + "include": ["src/**/*.ts", "test/**/*.ts", "e2e/**/*.ts"] } diff --git a/packages/typespec-client-generator-core/vitest.config.e2e.ts b/packages/typespec-client-generator-core/vitest.config.e2e.ts new file mode 100644 index 0000000000..1fa434e6aa --- /dev/null +++ b/packages/typespec-client-generator-core/vitest.config.e2e.ts @@ -0,0 +1,17 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + environment: "node", + testTimeout: 60_000, + isolate: false, + coverage: { + reporter: ["cobertura", "json", "text"], + }, + outputFile: { + junit: "./test-results.xml", + }, + + include: ["e2e/**/*.e2e.ts"], + }, +});