diff --git a/.github/workflows/_reusable-eng-tools-test.yaml b/.github/workflows/_reusable-eng-tools-test.yaml index bf2b2d80b7b6..63e6859de05d 100644 --- a/.github/workflows/_reusable-eng-tools-test.yaml +++ b/.github/workflows/_reusable-eng-tools-test.yaml @@ -8,6 +8,9 @@ on: description: Name of package under eng/tools required: true type: string + sparse-checkout-paths: + description: Paths for sparse checkout + type: string jobs: test: @@ -24,9 +27,15 @@ jobs: runs-on: ${{ matrix.os }} steps: + - if: runner.os == 'Windows' + run: git config --global core.longpaths true + shell: pwsh + - uses: actions/checkout@v4 with: - sparse-checkout: eng + sparse-checkout: | + eng + ${{ inputs.sparse-checkout-paths }} - name: Use Node ${{ matrix.node-version }}.x uses: actions/setup-node@v4 diff --git a/.github/workflows/tsp-client-test.yaml b/.github/workflows/tsp-client-test.yaml new file mode 100644 index 000000000000..a8b7a7e45b6f --- /dev/null +++ b/.github/workflows/tsp-client-test.yaml @@ -0,0 +1,28 @@ +name: tsp-client - Test + +on: + push: + branches: + - main + pull_request: + paths: + - package-lock.json + - package.json + - tsconfig.json + - .github/workflows/_reusable-eng-tools-test.yaml + - .github/workflows/tsp-client-test.yaml + - eng/tools/package.json + - eng/tools/tsconfig.json + - eng/tools/tsp-client/** + - specification/keyvault + - specification/sphere + +jobs: + tsp-client: + uses: ./.github/workflows/_reusable-eng-tools-test.yaml + with: + package: tsp-client-tests + sparse-checkout-paths: | + specification/common-types + specification/keyvault + specification/sphere diff --git a/eng/tools/package.json b/eng/tools/package.json index 1c69ffb902f1..88119fc74c3b 100644 --- a/eng/tools/package.json +++ b/eng/tools/package.json @@ -2,6 +2,7 @@ "name": "azure-rest-api-specs-eng-tools", "devDependencies": { "@azure-tools/suppressions": "file:suppressions", + "@azure-tools/tsp-client-tests": "file:tsp-client-tests", "@azure-tools/typespec-requirement": "file:typespec-requirement", "@azure-tools/typespec-validation": "file:typespec-validation" }, diff --git a/eng/tools/tsp-client-tests/package.json b/eng/tools/tsp-client-tests/package.json new file mode 100644 index 000000000000..3a56644a56c0 --- /dev/null +++ b/eng/tools/tsp-client-tests/package.json @@ -0,0 +1,20 @@ +{ + "name": "@azure-tools/tsp-client-tests", + "private": true, + "type": "module", + "devDependencies": { + "@types/node": "^18.19.31", + "execa": "^9.3.0", + "typescript": "~5.4.5", + "vitest": "^1.6.0" + }, + "scripts": { + "build": "tsc", + "postinstall": "npm run build", + "test": "vitest", + "test:ci": "vitest run --reporter=verbose" + }, + "engines": { + "node": ">= 18.0.0" + } +} diff --git a/eng/tools/tsp-client-tests/test/tsp-client.test.ts b/eng/tools/tsp-client-tests/test/tsp-client.test.ts new file mode 100644 index 000000000000..501ceb2481a7 --- /dev/null +++ b/eng/tools/tsp-client-tests/test/tsp-client.test.ts @@ -0,0 +1,72 @@ +import { execa } from "execa"; +import { access, constants, mkdir, rm } from "fs/promises"; +import { dirname, join } from "path"; +import { ExpectStatic, test } from "vitest"; + +const repoRoot = join(__dirname, "..", "..", "..", ".."); + +async function tspClient(...args: string[]) { + const allArgs = ["exec", "--no", "--", "tsp-client"].concat(args); + + console.log(`${repoRoot}$ npm ${allArgs.join(" ")}`); + + return await execa("npm", allArgs, { all: true, cwd: repoRoot, reject: false }); +} + +async function convert(expect: ExpectStatic, readme: string) { + const specFolder = dirname(dirname(join(repoRoot, readme))); + const outputFolder = join(specFolder, "Test.TspClientConvert"); + + try { + await mkdir(outputFolder); + } catch { + // Delete and retry + await rm(outputFolder, { recursive: true, force: true }); + await mkdir(outputFolder); + } + + try { + const { stdout, all, exitCode } = await tspClient( + "convert", + "--no-prompt", + "--swagger-readme", + readme, + "-o", + outputFolder, + readme.includes("resource-manager") ? "--arm" : "", + ); + + expect(stdout).toContain("Converting"); + expect(exitCode, all).toBe(0); + + const tspConfigYaml = join(outputFolder, "tspconfig.yaml"); + await access(tspConfigYaml, constants.R_OK); + console.log(`File exists: ${tspConfigYaml}`); + + const mainTsp = join(outputFolder, "main.tsp"); + await access(mainTsp, constants.R_OK); + console.log(`File exists: ${mainTsp}`); + } finally { + await rm(outputFolder, { recursive: true, force: true }); + } + + // Ensure outputFolder is deleted + expect(() => access(outputFolder)).rejects.toThrowError(); +} + +// TODO: Convert to `test.concurrent()` once Azure/azure-sdk-tools#8610 is merged, +// which should fix race condition bug calling `npx autorest`. +test("Usage", async ({ expect }) => { + const { stdout, exitCode } = await tspClient(); + + expect(stdout).toContain("Usage"); + expect(exitCode).not.toBe(0); +}); + +test("Convert keyvault/data-plane", async ({ expect }) => { + await convert(expect, "specification/keyvault/data-plane/readme.md"); +}); + +test("Convert sphere/resource-manager", async ({ expect }) => { + await convert(expect, "specification/sphere/resource-manager/readme.md"); +}); diff --git a/eng/tools/tsp-client-tests/tsconfig.json b/eng/tools/tsp-client-tests/tsconfig.json new file mode 100644 index 000000000000..ec6d6640928a --- /dev/null +++ b/eng/tools/tsp-client-tests/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + } +} diff --git a/eng/tools/tsp-client-tests/vite.config.ts b/eng/tools/tsp-client-tests/vite.config.ts new file mode 100644 index 000000000000..56e16cce625c --- /dev/null +++ b/eng/tools/tsp-client-tests/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vite"; + +export default defineConfig({ + test: { + testTimeout: 240000, + }, +}); diff --git a/eng/tools/typespec-requirement/package.json b/eng/tools/typespec-requirement/package.json index e82d0d39da50..6872c5db6c19 100644 --- a/eng/tools/typespec-requirement/package.json +++ b/eng/tools/typespec-requirement/package.json @@ -4,7 +4,6 @@ "type": "module", "devDependencies": { "@types/node": "^18.19.31", - "@vitest/coverage-v8": "^1.6.0", "execa": "^9.3.0", "typescript": "~5.4.5", "vitest": "^1.6.0" @@ -13,7 +12,7 @@ "build": "tsc", "postinstall": "npm run build", "test": "vitest", - "test:ci": "vitest run --coverage --reporter=verbose" + "test:ci": "vitest run --reporter=verbose" }, "engines": { "node": ">= 18.0.0" diff --git a/package-lock.json b/package-lock.json index d4cafb68ee5d..8e61301e8ffa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "dev": true, "devDependencies": { "@azure-tools/suppressions": "file:suppressions", + "@azure-tools/tsp-client-tests": "file:tsp-client-tests", "@azure-tools/typespec-requirement": "file:typespec-requirement", "@azure-tools/typespec-validation": "file:typespec-validation" } @@ -307,13 +308,26 @@ "node": ">= 18.0.0" } }, + "eng/tools/tsp-client-tests": { + "name": "@azure-tools/tsp-client-tests", + "dev": true, + "hasInstallScript": true, + "devDependencies": { + "@types/node": "^18.19.31", + "execa": "^9.3.0", + "typescript": "~5.4.5", + "vitest": "^1.6.0" + }, + "engines": { + "node": ">= 18.0.0" + } + }, "eng/tools/typespec-requirement": { "name": "@azure-tools/typespec-requirement", "dev": true, "hasInstallScript": true, "devDependencies": { "@types/node": "^18.19.31", - "@vitest/coverage-v8": "^1.6.0", "execa": "^9.3.0", "typescript": "~5.4.5", "vitest": "^1.6.0" @@ -490,6 +504,10 @@ "resolved": "eng/tools/suppressions", "link": true }, + "node_modules/@azure-tools/tsp-client-tests": { + "resolved": "eng/tools/tsp-client-tests", + "link": true + }, "node_modules/@azure-tools/typespec-apiview": { "version": "0.4.9", "resolved": "https://registry.npmjs.org/@azure-tools/typespec-apiview/-/typespec-apiview-0.4.9.tgz",