Skip to content

Commit 3eaefb4

Browse files
committed
Replicate "too many feature flags" error in test
1 parent b37e7e2 commit 3eaefb4

File tree

2 files changed

+62
-10
lines changed

2 files changed

+62
-10
lines changed

src/feature-flags.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
mockFeatureFlagApiEndpoint,
2222
setupActionsVars,
2323
setupTests,
24+
stubFeatureFlagApiEndpoint,
2425
} from "./testing-utils";
2526
import { ToolsFeature } from "./tools-features";
2627
import * as util from "./util";
@@ -132,6 +133,35 @@ test("Features use default value if they're not returned in API response", async
132133
});
133134
});
134135

136+
test("Include no more than 25 features in each API request", async (t) => {
137+
await withTmpDir(async (tmpDir) => {
138+
const features = setUpFeatureFlagTests(tmpDir);
139+
140+
stubFeatureFlagApiEndpoint((request) => {
141+
const requestedFeatures = (request.features as string).split(",");
142+
return {
143+
status: requestedFeatures.length <= 25 ? 200 : 400,
144+
messageIfError: "Can request a maximum of 25 features.",
145+
data: {},
146+
};
147+
});
148+
149+
// We only need to call getValue once, and it does not matter which feature
150+
// we ask for. Under the hood, the features library will request all features
151+
// from the API.
152+
const feature = Object.values(Feature)[0];
153+
// TODO: change to `t.notThrowsAsync` once we implement request chunking.
154+
await t.throwsAsync(
155+
async () => features.getValue(feature, includeCodeQlIfRequired(feature)),
156+
{
157+
message:
158+
"Encountered an error while trying to determine feature enablement: " +
159+
"Error: Can request a maximum of 25 features.",
160+
},
161+
);
162+
});
163+
});
164+
135165
test("Feature flags exception is propagated if the API request errors", async (t) => {
136166
await withTmpDir(async (tmpDir) => {
137167
const features = setUpFeatureFlagTests(tmpDir);

src/testing-utils.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,21 @@ export function getRecordingLogger(messages: LoggedMessage[]): Logger {
180180
export function mockFeatureFlagApiEndpoint(
181181
responseStatusCode: number,
182182
response: { [flagName: string]: boolean },
183+
) {
184+
stubFeatureFlagApiEndpoint(() => ({
185+
status: responseStatusCode,
186+
messageIfError: "some error message",
187+
data: response,
188+
}));
189+
}
190+
191+
/** Stub the HTTP request to the feature flags enablement API endpoint. */
192+
export function stubFeatureFlagApiEndpoint(
193+
responseFunction: (params: any) => {
194+
status: number;
195+
messageIfError?: string;
196+
data: { [flagName: string]: boolean };
197+
},
183198
) {
184199
// Passing an auth token is required, so we just use a dummy value
185200
const client = github.getOctokit("123");
@@ -189,16 +204,23 @@ export function mockFeatureFlagApiEndpoint(
189204
const optInSpy = requestSpy.withArgs(
190205
"GET /repos/:owner/:repo/code-scanning/codeql-action/features",
191206
);
192-
if (responseStatusCode < 300) {
193-
optInSpy.resolves({
194-
status: responseStatusCode,
195-
data: response,
196-
headers: {},
197-
url: "GET /repos/:owner/:repo/code-scanning/codeql-action/features",
198-
});
199-
} else {
200-
optInSpy.throws(new HTTPError("some error message", responseStatusCode));
201-
}
207+
208+
optInSpy.callsFake((_route, params) => {
209+
const response = responseFunction(params);
210+
if (response.status < 300) {
211+
return Promise.resolve({
212+
status: response.status,
213+
data: response.data,
214+
headers: {},
215+
url: "GET /repos/:owner/:repo/code-scanning/codeql-action/features",
216+
});
217+
} else {
218+
throw new HTTPError(
219+
response.messageIfError || "default stub error message",
220+
response.status,
221+
);
222+
}
223+
});
202224

203225
sinon.stub(apiClient, "getApiClient").value(() => client);
204226
}

0 commit comments

Comments
 (0)