Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
40c87b0
[vitest.config.js] Fix type errors
mikeharder Nov 4, 2025
6f948c2
arm-auto-signoff.test.js
mikeharder Nov 4, 2025
1b24d8b
mocks.js
mikeharder Nov 4, 2025
ed2dd18
arm-incremental-typespec.test.js
mikeharder Nov 4, 2025
8acfccc
artifacts.test.js
mikeharder Nov 4, 2025
00d4721
avocado-code.test.js
mikeharder Nov 4, 2025
e740ae1
breaking-change-add-label-artifacts.test.js
mikeharder Nov 4, 2025
9aa5b03
type mock helpers as intersection
mikeharder Nov 4, 2025
72fca67
remove asCore()
mikeharder Nov 4, 2025
44757a6
remove ts-expect-error
mikeharder Nov 4, 2025
30fc264
context.test.js
mikeharder Nov 4, 2025
c79d61d
remove returns
mikeharder Nov 4, 2025
cba7e74
remove asAsyncFunctionArguments
mikeharder Nov 4, 2025
edc4820
keep default import
mikeharder Nov 4, 2025
cdf887f
avocado-code
mikeharder Nov 4, 2025
51d648d
breaking-change-add-label-artifacts
mikeharder Nov 4, 2025
d1ddb4d
make avocado load
mikeharder Nov 4, 2025
652c25a
fix test
mikeharder Nov 4, 2025
93b1909
simplify type fix
mikeharder Nov 4, 2025
51af728
simplify
mikeharder Nov 4, 2025
49480c5
load tests
mikeharder Nov 4, 2025
5e989a0
restore null in tests
mikeharder Nov 4, 2025
6b324e9
message.test
mikeharder Nov 4, 2025
c49945d
ndjson
mikeharder Nov 4, 2025
3654334
retries
mikeharder Nov 4, 2025
84f05ec
sdk-breaking-change-labels
mikeharder Nov 4, 2025
46f9a2d
verify-run-status
mikeharder Nov 4, 2025
83aecae
update-labels
mikeharder Nov 4, 2025
37a3159
execFile: make args optional, matching builtin API
mikeharder Nov 4, 2025
be090f5
summarize-checks
mikeharder Nov 4, 2025
bb155a9
labelling
mikeharder Nov 4, 2025
c1a984b
cast instead of expect error
mikeharder Nov 4, 2025
d546486
mocks
mikeharder Nov 4, 2025
d32579e
set-status
mikeharder Nov 4, 2025
a138b3a
spec-gen-sdk-status
mikeharder Nov 4, 2025
350103a
github.test
mikeharder Nov 4, 2025
026aa2f
fix summary.addRaw()
mikeharder Nov 4, 2025
09505a7
Merge branch 'main' into github-typecheck-tests
mikeharder Nov 4, 2025
3fa4bf9
arm-incremental-typespec
mikeharder Nov 5, 2025
e15d0e6
Merge branch 'github-typecheck-tests' of https://github.com/mikeharde…
mikeharder Nov 5, 2025
8e29b4a
remove ts-expect-errors
mikeharder Nov 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/shared/src/exec.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import child_process from "child_process";
import { dirname, join } from "path";
import { promisify } from "util";
Expand Down Expand Up @@ -36,7 +36,7 @@
* Wraps `child_process.execFile()`, adding logging and a larger default maxBuffer.
*
* @param {string} file
* @param {string[]} args
* @param {string[]} [args]
Copy link
Member Author

@mikeharder mikeharder Nov 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wrapped API also allows undefined

* @param {ExecOptions} [options]
* @returns {Promise<ExecResult>}
* @throws {ExecError}
Expand Down
4 changes: 0 additions & 4 deletions .github/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// Only used for type-checking of JavaScript sources. Folder should contain no TypeScript sources.
{
"extends": "@tsconfig/node20/tsconfig.json",
Expand All @@ -8,8 +8,4 @@
"incremental": false,
"noEmit": true,
},
"include": [
// Only check runtime sources. Tests currently have too many errors.
"**/src/**/*.js",
],
}
3 changes: 2 additions & 1 deletion .github/vitest.config.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { configDefaults, defineConfig } from "vitest/config";

export default defineConfig({
esbuild: {
// Ignore tsconfig.json, since it's only used for type checking, and causes
// a warning if vitest tries to load it
// @ts-expect-error: tsConfig' does not exist in type 'ESBuildOptions'
tsConfig: false,
},

test: {
coverage: {
exclude: [
...configDefaults.coverage.exclude,
...(configDefaults.coverage.exclude ?? []),

// Not worth testing CLI code
"**/cmd/**",
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/test/arm-auto-signoff.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { describe, expect, it } from "vitest";
import { CommitStatusState } from "../../shared/src/github.js";
import { getLabelActionImpl } from "../src/arm-auto-signoff.js";
Expand Down Expand Up @@ -43,7 +43,9 @@

describe("getLabelActionImpl", () => {
it("throws if inputs null", async () => {
await expect(getLabelActionImpl({})).rejects.toThrow();
await expect(
getLabelActionImpl(/** @type {Parameters<typeof getLabelActionImpl>[0]} */ ({})),
).rejects.toThrow();
});

it("throws if no artifact from incremental typespec", async () => {
Expand Down
75 changes: 62 additions & 13 deletions .github/workflows/test/arm-incremental-typespec.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { relative, resolve } from "path";
import { afterEach, describe, expect, it, vi } from "vitest";
import { repoRoot } from "../../shared/test/repo.js";
Expand All @@ -16,11 +16,20 @@
swaggerHandWritten,
swaggerTypeSpecGenerated,
} from "../../shared/test/examples.js";
import incrementalTypeSpec from "../src/arm-incremental-typespec.js";
import incrementalTypeSpecImpl from "../src/arm-incremental-typespec.js";
import { createMockCore } from "./mocks.js";

const core = createMockCore();

/**
* @param {unknown} asyncFunctionArgs
*/
function incrementalTypeSpec(asyncFunctionArgs) {
return incrementalTypeSpecImpl(
/** @type {import("@actions/github-script").AsyncFunctionArguments} */ (asyncFunctionArgs),
);
}

describe("incrementalTypeSpec", () => {
afterEach(() => {
vi.clearAllMocks();
Expand Down Expand Up @@ -131,8 +140,27 @@

const showSpy = vi
.mocked(simpleGit.simpleGit().show)
.mockImplementation(async ([treePath]) =>
treePath.split(":")[0] == "HEAD" ? swaggerTypeSpecGenerated : swaggerHandWritten,
.mockImplementation(
(
/** @type {import("simple-git").SimpleGitTaskCallback|string|string[]|undefined} */ optionsOrCallback,
) => {
/** @type {string} */
let option;

if (Array.isArray(optionsOrCallback)) {
option = optionsOrCallback[0];
} else if (typeof optionsOrCallback === "string") {
option = optionsOrCallback;
} else {
throw new Error(`Unexpected argument: ${optionsOrCallback}`);
}

return /** @type {import("simple-git").Response<string>} */ (
Promise.resolve(
option?.split(":")[0] == "HEAD" ? swaggerTypeSpecGenerated : swaggerHandWritten,
)
);
},
);

const lsTreeSpy = vi.mocked(simpleGit.simpleGit().raw).mockResolvedValue(swaggerPath);
Expand Down Expand Up @@ -199,16 +227,37 @@

vi.spyOn(changedFiles, "getChangedFiles").mockResolvedValue([readmePath]);

const showSpy = vi.mocked(simpleGit.simpleGit().show).mockImplementation(async ([treePath]) => {
const path = treePath.split(":")[1];
if (path === swaggerPath) {
return swaggerTypeSpecGenerated;
} else if (path === readmePath) {
return contosoReadme;
} else {
throw new Error("does not exist");
}
});
const showSpy = vi
.mocked(simpleGit.simpleGit().show)
.mockImplementation(
(
/** @type {import("simple-git").SimpleGitTaskCallback|string|string[]|undefined} */ optionsOrCallback,
) => {
/** @type {string} */
let option;

if (Array.isArray(optionsOrCallback)) {
option = optionsOrCallback[0];
} else if (typeof optionsOrCallback === "string") {
option = optionsOrCallback;
} else {
throw new Error(`Unexpected argument: ${optionsOrCallback}`);
}

const path = option.split(":")[1];
if (path === swaggerPath) {
return /** @type {import("simple-git").Response<string>} */ (
Promise.resolve(swaggerTypeSpecGenerated)
);
} else if (path === readmePath) {
return /** @type {import("simple-git").Response<string>} */ (
Promise.resolve(contosoReadme)
);
} else {
throw new Error("does not exist");
}
},
);

const lsTreeSpy = vi.mocked(simpleGit.simpleGit().raw).mockResolvedValue(swaggerPath);

Expand Down
33 changes: 17 additions & 16 deletions .github/workflows/test/artifacts.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
fetchFailedArtifact,
Expand All @@ -12,7 +12,8 @@
}));

// Mock global fetch
global.fetch = vi.fn();
const mockFetch = vi.fn();
global.fetch = mockFetch;
const mockCore = createMockCore();

describe("getAzurePipelineArtifact function", () => {
Expand Down Expand Up @@ -78,7 +79,7 @@
};

// Setup fetch to capture headers and return appropriate responses
global.fetch.mockImplementation((url, options) => {
mockFetch.mockImplementation((url, options) => {
// For all calls, verify the headers include the authorization token
if (options && options.headers) {
expect(options.headers).toEqual(expectedHeaders);
Expand Down Expand Up @@ -152,7 +153,7 @@
};

// Setup fetch with a spy to capture the headers
global.fetch.mockImplementation((url, options) => {
mockFetch.mockImplementation((url, options) => {
if (url.includes("artifacts?artifactName=")) {
// Verify headers contain Authorization
expect(options.headers).toHaveProperty("Authorization", `Bearer ${testToken}`);
Expand Down Expand Up @@ -189,7 +190,7 @@

it("should handle API failure", async () => {
// Mock fetch failure
global.fetch.mockResolvedValue({
mockFetch.mockResolvedValue({
ok: false,
status: 500,
statusText: "Server Error",
Expand All @@ -216,7 +217,7 @@

it("should complete without op when artifact does not exist", async () => {
// Mock fetch failure
global.fetch.mockResolvedValue({
mockFetch.mockResolvedValue({
ok: false,
status: 404,
statusText: "Not Found",
Expand Down Expand Up @@ -289,7 +290,7 @@
};

// Setup fetch to return different responses based on the URL
global.fetch.mockImplementation((url) => {
mockFetch.mockImplementation((url) => {
// First attempted artifact request with 404
if (url.includes(`artifacts?artifactName=${inputs.artifactName}&api-version=7.0`)) {
return mockInitialResponse;
Expand Down Expand Up @@ -340,7 +341,7 @@
};

// Setup fetch to return different responses for each call
global.fetch.mockImplementation((url) => {
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
}
Expand Down Expand Up @@ -368,7 +369,7 @@
};

// Setup fetch to return different responses for each call
global.fetch.mockImplementation((url) => {
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
}
Expand Down Expand Up @@ -408,7 +409,7 @@
};

// Setup fetch to return different responses for each call
global.fetch.mockImplementation((url) => {
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
} else {
Expand All @@ -430,7 +431,7 @@

it("should handle exception during processing", async () => {
// Mock fetch to throw an error
global.fetch.mockImplementation(() => {
mockFetch.mockImplementation(() => {
throw new Error("Network error");
});

Expand Down Expand Up @@ -473,7 +474,7 @@
};

// Setup fetch to return different responses for each call
global.fetch.mockImplementation((url) => {
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
} else {
Expand Down Expand Up @@ -587,7 +588,7 @@
};

// Setup fetch with a spy to capture the headers
global.fetch.mockImplementation((url, options) => {
mockFetch.mockImplementation((url, options) => {
// Verify that the custom headers are included in all requests
expect(options.headers).toEqual(customHeaders);

Expand Down Expand Up @@ -654,7 +655,7 @@
};

// Setup fetch to return different responses for each call
global.fetch.mockImplementation((url) => {
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?api-version")) {
return mockListResponse;
} else if (url.includes("artifactName=spec-gen-sdk-artifact-FailedAttempt2")) {
Expand Down Expand Up @@ -689,7 +690,7 @@
statusText: "OK",
};

global.fetch.mockResolvedValue(mockListResponse);
mockFetch.mockResolvedValue(mockListResponse);

// Call the function and expect it to return a 404 response
const response = await fetchFailedArtifact(defaultParams);
Expand All @@ -708,7 +709,7 @@
statusText: "Internal Server Error",
};

global.fetch.mockResolvedValue(mockErrorResponse);
mockFetch.mockResolvedValue(mockErrorResponse);

// Call the function and expect it to throw
await expect(fetchFailedArtifact(defaultParams)).rejects.toThrow(
Expand Down Expand Up @@ -751,7 +752,7 @@
};

// Setup fetch to return different responses for each call
global.fetch.mockImplementation((url) => {
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?api-version")) {
return mockListResponse;
} else if (url.includes("artifactName=spec-gen-sdk-artifact-FailedAttempt3")) {
Expand Down
22 changes: 16 additions & 6 deletions .github/workflows/test/avocado-code.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { beforeEach, describe, expect, it, vi } from "vitest";

// Mock fs/promises before imports
Expand All @@ -5,10 +5,9 @@

import * as fs from "fs/promises";

/** @type {import("vitest").Mock} */
const readFileMock = fs.readFile;
const readFileMock = /** @type {import("vitest").Mock} */ (fs.readFile);

import generateJobSummary from "../src/avocado-code.js";
import generateJobSummaryImpl from "../src/avocado-code.js";
import { MessageLevel, MessageType } from "../src/message.js";
import { stringify } from "../src/ndjson.js";
import { createMockCore } from "./mocks.js";
Expand All @@ -17,6 +16,15 @@
const outputFile = "avocado.ndjson";

describe("generateJobSummary", () => {
/**
* @param {unknown} asyncFunctionArgs
*/
function generateJobSummary(asyncFunctionArgs) {
return generateJobSummaryImpl(
/** @type {import("@actions/github-script").AsyncFunctionArguments} */ (asyncFunctionArgs),
);
}

beforeEach(() => {
vi.stubEnv("AVOCADO_OUTPUT_FILE", outputFile);
readFileMock.mockReset();
Expand All @@ -31,7 +39,9 @@
`[Error: Env var AVOCADO_OUTPUT_FILE must be set]`,
);

expect(core.info.mock.calls.at(-1)[0]).toMatchInlineSnapshot(`"avocadoOutputFile: undefined"`);
expect(core.info.mock.calls.at(-1)?.[0]).toMatchInlineSnapshot(
`"avocadoOutputFile: undefined"`,
);
});

it("no-ops if file cannot be read", async () => {
Expand All @@ -41,7 +51,7 @@

await expect(generateJobSummary({ core })).resolves.toBeUndefined();

expect(core.info.mock.calls.at(-1)[0]).toMatchInlineSnapshot(
expect(core.info.mock.calls.at(-1)?.[0]).toMatchInlineSnapshot(
`"Error reading 'avocado.ndjson': Error: ENOENT: no such file or directory, open 'avocado.ndjson'"`,
);
});
Expand All @@ -51,7 +61,7 @@

await expect(generateJobSummary({ core })).resolves.toBeUndefined();

expect(core.notice.mock.calls.at(-1)[0]).toMatchInlineSnapshot(
expect(core.notice.mock.calls.at(-1)?.[0]).toMatchInlineSnapshot(
`"No messages in 'avocado.ndjson'"`,
);
});
Expand Down
Loading