Skip to content

Commit 5dba222

Browse files
ckairenzman-ms
authored andcommitted
[TypeSpec Validation] Add unit test framework (#26166)
1 parent ba3fab7 commit 5dba222

19 files changed

+839
-37
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
spec: "dist/test/**/*.js"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env node
22

3-
import { main } from "../dist/index.js"
3+
import { main } from "../dist/src/index.js"
44

55
await main();

eng/tools/TypeSpecValidation/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,15 @@
1414
},
1515
"devDependencies": {
1616
"globby": "^13.1.1",
17+
"mocha": "^10.2.0",
1718
"@types/debug": "^4.1.8",
19+
"@types/mocha": "^10.0.2",
1820
"@types/node": "^18.16.18",
1921
"typescript": "~5.1.3"
2022
},
2123
"scripts": {
22-
"postinstall": "tsc"
24+
"postinstall": "tsc",
25+
"test": "mocha --recursive --exit"
2326
},
2427
"engines": {
2528
"node": "^16.17.0 || ^18.3.0 || ^20.0.0"

eng/tools/TypeSpecValidation/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { FormatRule } from "./rules/format.js";
55
import { GitDiffRule } from "./rules/git-diff.js";
66
import { LinterRulesetRule } from "./rules/linter-ruleset.js";
77
import { NpmPrefixRule } from "./rules/npm-prefix.js";
8+
import { TsvRunnerHost } from "./tsv-runner-host.js";
89

910
export async function main() {
1011
const args = process.argv.slice(2);
@@ -30,7 +31,7 @@ export async function main() {
3031
for (let i = 0; i < rules.length; i++) {
3132
const rule = rules[i];
3233
console.log("\nExecuting rule: " + rule.name);
33-
const result = await rule.execute(folder);
34+
const result = await rule.execute(TsvRunnerHost, folder);
3435
if (result.stdOutput) console.log(result.stdOutput);
3536
if (!result.success) {
3637
success = false;
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { RuleResult } from "./rule-result.js";
2+
import { TsvHost } from "./tsv-host.js";
23

34
export interface Rule {
45
readonly name: string;
56
readonly description: string;
6-
execute(folder?: string): Promise<RuleResult>;
7+
execute(host?: TsvHost, folder?: string): Promise<RuleResult>;
78
}

eng/tools/TypeSpecValidation/src/rules/compile.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
11
import path from "path";
2-
import { runCmd, checkFileExists } from "../utils.js";
32
import { Rule } from "../rule.js";
43
import { RuleResult } from "../rule-result.js";
4+
import { TsvHost } from "../tsv-host.js";
55

66
export class CompileRule implements Rule {
77
readonly name = "Compile";
88
readonly description = "Compile TypeSpec";
99

10-
async execute(folder: string): Promise<RuleResult> {
10+
async execute(host: TsvHost, folder: string): Promise<RuleResult> {
1111
let success = true;
1212
let stdOutput = "";
1313
let errorOutput = "";
1414

15-
if (await checkFileExists(path.join(folder, "main.tsp"))) {
16-
let [err, stdout, stderr] = await runCmd(`npx --no tsp compile . --warn-as-error`, folder);
15+
if (await host.checkFileExists(path.join(folder, "main.tsp"))) {
16+
let [err, stdout, stderr] = await host.runCmd(
17+
`npx --no tsp compile . --warn-as-error`,
18+
folder,
19+
);
1720
if (err) {
1821
success = false;
1922
errorOutput += err.message;
2023
}
2124
stdOutput += stdout;
2225
errorOutput += stderr;
2326
}
24-
if (await checkFileExists(path.join(folder, "client.tsp"))) {
25-
let [err, stdout, stderr] = await runCmd(
27+
if (await host.checkFileExists(path.join(folder, "client.tsp"))) {
28+
let [err, stdout, stderr] = await host.runCmd(
2629
`npx --no tsp compile client.tsp --no-emit --warn-as-error`,
27-
folder
30+
folder,
2831
);
2932
if (err) {
3033
success = false;

eng/tools/TypeSpecValidation/src/rules/folder-structure.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,18 @@ import { globby } from "globby";
22
import path from "path";
33
import { Rule } from "../rule.js";
44
import { RuleResult } from "../rule-result.js";
5-
import { checkFileExists } from "../utils.js";
5+
import { TsvHost } from "../tsv-host.js";
66

77
export class FolderStructureRule implements Rule {
88
readonly name = "FolderStructure";
99
readonly description = "Verify spec directory's folder structure and naming conventions.";
10-
async execute(folder: string): Promise<RuleResult> {
10+
async execute(host: TsvHost, folder: string): Promise<RuleResult> {
1111
let success = true;
1212
let stdOutput = "";
1313
let errorOutput = "";
1414

1515
stdOutput += `folder: ${folder}\n`;
16-
if (!(await checkFileExists(folder))) {
16+
if (!(await host.checkFileExists(folder))) {
1717
return {
1818
success: false,
1919
stdOutput: stdOutput,
@@ -64,17 +64,17 @@ export class FolderStructureRule implements Rule {
6464

6565
// Verify tspconfig, main.tsp, examples/
6666
let containsMinStruct =
67-
(await checkFileExists(path.join(folder, "main.tsp"))) ||
68-
(await checkFileExists(path.join(folder, "client.tsp")));
67+
(await host.checkFileExists(path.join(folder, "main.tsp"))) ||
68+
(await host.checkFileExists(path.join(folder, "client.tsp")));
6969

70-
if (await checkFileExists(path.join(folder, "main.tsp"))) {
70+
if (await host.checkFileExists(path.join(folder, "main.tsp"))) {
7171
containsMinStruct =
72-
containsMinStruct && (await checkFileExists(path.join(folder, "examples")));
72+
containsMinStruct && (await host.checkFileExists(path.join(folder, "examples")));
7373
}
7474

7575
if (!packageFolder.includes("Shared")) {
7676
containsMinStruct =
77-
containsMinStruct && (await checkFileExists(path.join(folder, "tspconfig.yaml")));
77+
containsMinStruct && (await host.checkFileExists(path.join(folder, "tspconfig.yaml")));
7878
}
7979
if (!containsMinStruct) {
8080
success = false;

eng/tools/TypeSpecValidation/src/rules/format.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
import { Rule } from "../rule.js";
22
import { RuleResult } from "../rule-result.js";
3-
import { runCmd } from "../utils.js";
3+
import { TsvHost } from "../tsv-host.js";
44

55
export class FormatRule implements Rule {
66
readonly name = "Format";
77
readonly description = "Format TypeSpec";
88

9-
async execute(folder: string): Promise<RuleResult> {
9+
async execute(host: TsvHost, folder: string): Promise<RuleResult> {
1010
// Format parent folder to include shared files
1111

12-
let [err, stdOutput, errorOutput] = await runCmd(`npx tsp format "../**/*.tsp"`, folder);
12+
let [err, stdOutput, errorOutput] = await host.runCmd(`npx tsp format "../**/*.tsp"`, folder);
1313
// Failing on both err and errorOutput because of known bug in tsp format where it returns 0 on failed formatting
1414
// https://github.com/microsoft/typespec/issues/2323
1515
let success = !err && !errorOutput;

eng/tools/TypeSpecValidation/src/rules/git-diff.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
import { simpleGit } from "simple-git";
21
import { Rule } from "../rule.js";
32
import { RuleResult } from "../rule-result.js";
3+
import { TsvHost } from "../tsv-host.js";
44

55
export class GitDiffRule implements Rule {
66
readonly name = "GitDiff";
77
readonly description = "Checks if previous rules resulted in a git diff";
88

9-
async execute(folder: string): Promise<RuleResult> {
10-
const git = simpleGit(folder);
9+
async execute(host: TsvHost, folder: string): Promise<RuleResult> {
10+
const git = host.gitOperation(folder);
1111
let gitStatusIsClean = (await git.status(["--porcelain"])).isClean();
1212

1313
let success = true;

eng/tools/TypeSpecValidation/src/rules/linter-ruleset.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import { join } from "path";
33
import { parse as yamlParse } from "yaml";
44
import { Rule } from "../rule.js";
55
import { RuleResult } from "../rule-result.js";
6-
import { checkFileExists } from "../utils.js";
6+
import { TsvHost } from "../tsv-host.js";
77

88
export class LinterRulesetRule implements Rule {
99
readonly name = "LinterRuleset";
1010

1111
readonly description =
1212
"Ensures each spec includes the correct linter ruleset (data-plane or management-plane)";
1313

14-
async execute(folder: string): Promise<RuleResult> {
14+
async execute(host: TsvHost, folder: string): Promise<RuleResult> {
1515
let success = true;
1616
let stdOutput = "";
1717
let errorOutput = "";
@@ -24,8 +24,8 @@ export class LinterRulesetRule implements Rule {
2424
config.options?.["@azure-tools/typespec-autorest"]?.["azure-resource-provider-folder"];
2525
stdOutput += `azure-resource-provider-folder: ${JSON.stringify(rpFolder)}\n`;
2626

27-
const mainTspExists = await checkFileExists(join(folder, "main.tsp"));
28-
const clientTspExists = await checkFileExists(join(folder, "client.tsp"));
27+
const mainTspExists = await host.checkFileExists(join(folder, "main.tsp"));
28+
const clientTspExists = await host.checkFileExists(join(folder, "client.tsp"));
2929
let files = [];
3030
if (mainTspExists) {
3131
files.push("main.tsp");

0 commit comments

Comments
 (0)