Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
35 changes: 33 additions & 2 deletions .github/shared/src/sdk-types.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
/* v8 ignore start */
Copy link
Member Author

Choose a reason for hiding this comment

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

added test coverage, so we can remove ignore

import * as z from "zod";

const SdkNameSchema = z.enum([
"azure-sdk-for-go",
"azure-sdk-for-java",
"azure-sdk-for-js",
"azure-sdk-for-net",
"azure-sdk-for-python",
]);
/**
* @typedef {'azure-sdk-for-go' | 'azure-sdk-for-java' | 'azure-sdk-for-js' | 'azure-sdk-for-net' | 'azure-sdk-for-python'} SdkName
* @typedef {import("zod").infer<typeof SdkNameSchema>} SdkName
Copy link
Member Author

Choose a reason for hiding this comment

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

type SdkName is unchanged, but now has a schema

*/

/*
* Data for the API view request.
*/
const APIViewRequestDataSchema = z.object({ packageName: z.string(), filePath: z.string() });
/**
* @typedef {import("zod").infer<typeof APIViewRequestDataSchema>} APIViewRequestData
Copy link
Member Author

Choose a reason for hiding this comment

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

type APIViewRequestData should be the same as the copy removed from eng/tools/spec-gen-sdk-runner/src/types.ts

*/

/**
* Represents the result of the spec-gen-sdk generation process.
*/
export const SpecGenSdkArtifactInfoSchema = z.object({
language: SdkNameSchema,
result: z.string(),
headSha: z.string(),
prNumber: z.string().optional(),
labelAction: z.boolean().optional(),
isSpecGenSdkCheckRequired: z.boolean(),
apiViewRequestData: z.array(APIViewRequestDataSchema),
});
/**
* @typedef {import("zod").infer<typeof SpecGenSdkArtifactInfoSchema>} SpecGenSdkArtifactInfo
Copy link
Member Author

Choose a reason for hiding this comment

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

type SpecGenSdkArtifactInfo should be the same as the copy removed from eng/tools/spec-gen-sdk-runner/src/types.ts

*/

/**
Expand Down
21 changes: 21 additions & 0 deletions .github/shared/test/sdk-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @typedef {import("../src/sdk-types.js").SpecGenSdkArtifactInfo} SpecGenSdkArtifactInfo
*/

/**
* Create a mock SpecGenSdkArtifactInfo, filling unspecified properties with defaults.
* @param {Partial<import("../src/sdk-types.js").SpecGenSdkArtifactInfo>} [overrides]
* @returns {SpecGenSdkArtifactInfo}
*/
export function createMockSpecGenSdkArtifactInfo(overrides = {}) {
/** @type {SpecGenSdkArtifactInfo} */
const defaults = {
apiViewRequestData: [],
headSha: "abc123",
isSpecGenSdkCheckRequired: true,
language: "azure-sdk-for-go",
result: "test result",
};

return { ...defaults, ...overrides };
}
22 changes: 22 additions & 0 deletions .github/shared/test/sdk-types.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { describe, expect, it } from "vitest";
import { sdkLabels, SpecGenSdkArtifactInfoSchema } from "../src/sdk-types.js";
import { createMockSpecGenSdkArtifactInfo } from "./sdk-types.js";

describe("sdk-types", () => {
it("defines sdkLabels", () => {
// Ensures constant "sdkLabels" is considered "covered" by codecov
expect(sdkLabels).toBeDefined();
});

it("parses SpecGetSdkArtifactInfo", () => {
const artifactInfo = createMockSpecGenSdkArtifactInfo();

const json = JSON.stringify(artifactInfo);
expect(json).toMatchInlineSnapshot(
`"{"apiViewRequestData":[],"headSha":"abc123","isSpecGenSdkCheckRequired":true,"language":"azure-sdk-for-go","result":"test result"}"`,
);

const parsed = SpecGenSdkArtifactInfoSchema.parse(JSON.parse(json));
expect(parsed).toEqual(artifactInfo);
});
});
8 changes: 5 additions & 3 deletions .github/workflows/src/sdk-breaking-change-labels.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { sdkLabels } from "../../shared/src/sdk-types.js";
import { SpecGenSdkArtifactInfoSchema, sdkLabels } from "../../shared/src/sdk-types.js";
import { getAdoBuildInfoFromUrl, getAzurePipelineArtifact } from "./artifacts.js";
import { extractInputs } from "./context.js";
import { LabelAction } from "./label.js";
Expand Down Expand Up @@ -72,12 +72,14 @@
} else {
core.info(`Artifact content: ${result.artifactData}`);
// Parse the JSON data
const specGenSdkArtifactInfo = JSON.parse(result.artifactData);
const specGenSdkArtifactInfo = SpecGenSdkArtifactInfoSchema.parse(
JSON.parse(result.artifactData),
);
const labelActionText = specGenSdkArtifactInfo.labelAction;

head_sha = specGenSdkArtifactInfo.headSha;

issue_number = parseInt(specGenSdkArtifactInfo.prNumber, 10);
issue_number = parseInt(specGenSdkArtifactInfo.prNumber ?? "", 10);
Copy link
Member Author

Choose a reason for hiding this comment

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

parseInt() only accepts a string, not undefined. But parseInt("") returns NaN anyway, so it's a good replacement.

if (!issue_number) {
core.warning(
`No PR number found in the artifact '${artifactName}' with details_url:${details_url}.`,
Expand Down
9 changes: 7 additions & 2 deletions .github/workflows/src/spec-gen-sdk-status.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { CheckStatus, CommitStatusState, PER_PAGE_MAX } from "../../shared/src/github.js";
import { SpecGenSdkArtifactInfoSchema } from "../../shared/src/sdk-types.js";
import { getAdoBuildInfoFromUrl, getAzurePipelineArtifact } from "./artifacts.js";
import { extractInputs } from "./context.js";
import { writeToActionsSummary } from "./github.js";
Expand Down Expand Up @@ -122,7 +123,7 @@

/**
* @param {Object} params
* @param {Array<any>} params.checkRuns
* @param {import("./github.js").CheckRuns} params.checkRuns
* @param {typeof import("@actions/core")} params.core
* @returns {Promise<{state: CommitStatusState, description: string}>}
*/
Expand All @@ -139,6 +140,9 @@

for (const checkRun of checkRuns) {
core.info(`Processing check run: ${checkRun.name} (${checkRun.conclusion})`);
if (checkRun.details_url === null) {
throw new Error(`'details_url' is null in Check Run '${checkRun.name}'`);
}
const buildInfo = getAdoBuildInfoFromUrl(checkRun.details_url);
const ado_project_url = buildInfo.projectUrl;
const ado_build_id = buildInfo.buildId;
Expand All @@ -159,7 +163,8 @@
`Artifact '${artifactName}' not found in the build with details_url:${checkRun.details_url}`,
);
}
const artifactJsonObj = JSON.parse(result.artifactData);

const artifactJsonObj = SpecGenSdkArtifactInfoSchema.parse(JSON.parse(result.artifactData));
const language = artifactJsonObj.language;
const shortLanguageName = language.split("-").pop();
const executionResult = artifactJsonObj.result;
Expand Down
76 changes: 40 additions & 36 deletions .github/workflows/test/sdk-breaking-change-labels.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { sdkLabels } from "../../shared/src/sdk-types.js";
import { createMockSpecGenSdkArtifactInfo } from "../../shared/test/sdk-types.js";
import { LabelAction } from "../src/label.js";
import {
getLabelAndActionImpl,
Expand All @@ -12,9 +13,11 @@
extractInputs: vi.fn(),
}));

/** @type {import('vitest').Mock<(url: string) => Promise<Partial<Response>>>} */
const mockFetch = vi.fn();

// Mock global fetch
global.fetch = mockFetch;
global.fetch = /** @type {import('vitest').MockedFunction<typeof fetch>} */ (mockFetch);

const mockGithub = createMockGithub();
const mockContext = createMockContext();
Expand Down Expand Up @@ -61,23 +64,26 @@

// Second fetch - artifact content
const language = "azure-sdk-for-js";

const mockContentResponse = {
ok: true,
text: vi.fn().mockResolvedValue(
JSON.stringify({
labelAction: true,
language,
prNumber: "123",
}),
JSON.stringify(
createMockSpecGenSdkArtifactInfo({
labelAction: true,
language,
prNumber: "123",
}),
),
),
};

// Setup fetch to return different responses for each call
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
return Promise.resolve(mockArtifactResponse);
} else {
return mockContentResponse;
return Promise.resolve(mockContentResponse);
}
});

Expand All @@ -90,6 +96,7 @@

// Verify result
expect(result).toEqual({
headSha: "abc123",
labelName: sdkLabels[language].breakingChange,
labelAction: LabelAction.Add,
issueNumber: 123,
Expand Down Expand Up @@ -117,23 +124,24 @@
};

const language = "azure-sdk-for-js";

const mockContentResponse = {
ok: true,
text: vi.fn().mockResolvedValue(
JSON.stringify({
labelAction: false,
language,
prNumber: "123",
}),
),
text: vi
.fn()
.mockResolvedValue(
JSON.stringify(
createMockSpecGenSdkArtifactInfo({ labelAction: false, language, prNumber: "123" }),
),
),
};

// Setup fetch to return different responses for each call
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
return Promise.resolve(mockArtifactResponse);
} else {
return mockContentResponse;
return Promise.resolve(mockContentResponse);
}
});

Expand All @@ -146,6 +154,7 @@

// Verify result has Remove action
expect(result).toEqual({
headSha: "abc123",
labelName: sdkLabels[language].breakingChange,
labelAction: LabelAction.Remove,
issueNumber: 123,
Expand Down Expand Up @@ -176,20 +185,22 @@
const mockContentResponse = {
ok: true,
text: vi.fn().mockResolvedValue(
JSON.stringify({
labelAction: false,
language,
prNumber: "123",
}),
JSON.stringify(
createMockSpecGenSdkArtifactInfo({
labelAction: false,
language,
prNumber: "123",
}),
),
),
};

// Setup fetch to return different responses for each call
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
return Promise.resolve(mockArtifactResponse);
} else {
return mockContentResponse;
return Promise.resolve(mockContentResponse);
}
});

Expand All @@ -202,6 +213,7 @@

// Verify result has none action
expect(result).toEqual({
headSha: "abc123",
labelName: sdkLabels[language].breakingChange,
labelAction: LabelAction.None,
issueNumber: 123,
Expand Down Expand Up @@ -343,11 +355,7 @@
};

// Setup fetch to return different responses for each call
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
}
});
mockFetch.mockResolvedValue(mockArtifactResponse);

// Call function and expect it to throw
await expect(
Expand All @@ -374,11 +382,7 @@
};

// Setup fetch to return different responses for each call
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
}
});
mockFetch.mockResolvedValue(mockArtifactResponse);

// Call function and expect it to throw
await expect(
Expand Down Expand Up @@ -419,9 +423,9 @@
// Setup fetch to return different responses for each call
mockFetch.mockImplementation((url) => {
if (url.includes("artifacts?artifactName=")) {
return mockArtifactResponse;
return Promise.resolve(mockArtifactResponse);
} else {
return mockContentResponse;
return Promise.resolve(mockContentResponse);
}
});

Expand Down
Loading
Loading