Skip to content
Open
16 changes: 16 additions & 0 deletions src/gcp/cloudfunctionsv2.spec.ts
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ran this through gemini and it also suggested some additional test cases:

  1. updateFunction Mask Verification: updateFunction unit tests in src/gcp/cloudfunctionsv2.spec.ts currently only verify default environment variable injection. A test case should be added to verify that updating a function with a custom or null buildConfig.serviceAccount correctly populates the updateMask query parameter.
  2. endpointFromFunction Reverse Mapping Tests: While the forward transformation (functionFromEndpoint) is thoroughly tested, the inverse transformation (endpointFromFunction) should include test cases ensuring that when serviceAccountEmail is null or omitted in a GCF API response, endpoint.serviceAccount is correctly mapped to null or undefined.

Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@

const fullGcfFunction: cloudfunctionsv2.InputCloudFunction = {
...CLOUD_FUNCTION_V2,
buildConfig: {
...CLOUD_FUNCTION_V2.buildConfig,
serviceAccount: "projects/project/serviceAccounts/inlined@google.com",
},
labels: {
...CLOUD_FUNCTION_V2.labels,
foo: "bar",
Expand Down Expand Up @@ -332,6 +336,10 @@

const saGcfFunction: cloudfunctionsv2.InputCloudFunction = {
...CLOUD_FUNCTION_V2,
buildConfig: {
...CLOUD_FUNCTION_V2.buildConfig,
serviceAccount: "projects/project/serviceAccounts/sa@google.com",
},
eventTrigger: {
eventType: events.v2.DATABASE_EVENTS[0],
eventFilters: [
Expand Down Expand Up @@ -405,6 +413,10 @@
}),
).to.deep.equal({
...CLOUD_FUNCTION_V2,
buildConfig: {
...CLOUD_FUNCTION_V2.buildConfig,
serviceAccount: `projects/${ENDPOINT.project}/serviceAccounts/sa@${ENDPOINT.project}.iam.gserviceaccount.com`,
},
serviceConfig: {
...CLOUD_FUNCTION_V2.serviceConfig,
serviceAccountEmail: `sa@${ENDPOINT.project}.iam.gserviceaccount.com`,
Expand All @@ -421,6 +433,10 @@
}),
).to.deep.equal({
...CLOUD_FUNCTION_V2,
buildConfig: {
...CLOUD_FUNCTION_V2.buildConfig,
serviceAccount: null,
},
serviceConfig: {
...CLOUD_FUNCTION_V2.serviceConfig,
serviceAccountEmail: null,
Expand Down Expand Up @@ -835,15 +851,15 @@

const scope = nock(functionsV2Origin())
.post("/v2/projects/project/locations/region/functions", (body) => {
expect(body.serviceConfig.environmentVariables).to.have.property(

Check warning on line 854 in src/gcp/cloudfunctionsv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .serviceConfig on an `any` value
"LOG_EXECUTION_ID",
"true",
);
expect(body.serviceConfig.environmentVariables).to.have.property(

Check warning on line 858 in src/gcp/cloudfunctionsv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .serviceConfig on an `any` value
"FUNCTION_TARGET",
"function",
);
expect(body.buildConfig.environmentVariables).to.have.property(

Check warning on line 862 in src/gcp/cloudfunctionsv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .buildConfig on an `any` value
"GOOGLE_NODE_RUN_SCRIPTS",
"",
);
Expand All @@ -861,15 +877,15 @@
it("should set default environment variables", async () => {
const scope = nock(functionsV2Origin())
.patch("/v2/projects/project/locations/region/functions/id", (body) => {
expect(body.serviceConfig.environmentVariables).to.have.property(

Check warning on line 880 in src/gcp/cloudfunctionsv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .serviceConfig on an `any` value
"LOG_EXECUTION_ID",
"true",
);
expect(body.serviceConfig.environmentVariables).to.have.property(

Check warning on line 884 in src/gcp/cloudfunctionsv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .serviceConfig on an `any` value
"FUNCTION_TARGET",
"function",
);
expect(body.buildConfig.environmentVariables).to.have.property(

Check warning on line 888 in src/gcp/cloudfunctionsv2.spec.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .buildConfig on an `any` value
"GOOGLE_NODE_RUN_SCRIPTS",
"",
);
Expand Down
23 changes: 13 additions & 10 deletions src/gcp/cloudfunctionsv2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
source: Source;
sourceToken?: string;
environmentVariables: Record<string, string>;
serviceAccount?: string | null;

// Output only
build?: string;
Expand Down Expand Up @@ -215,10 +216,10 @@
* @param type Type of deployment - create, update, or delete.
* @param err The error returned from the operation.
*/
function functionsOpLogReject(func: InputCloudFunction, type: string, err: any): void {

Check warning on line 219 in src/gcp/cloudfunctionsv2.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unexpected any. Specify a different type
// Sniff for runtime validation errors and log a more user-friendly warning.
if (err?.message?.includes("Runtime validation errors")) {

Check warning on line 221 in src/gcp/cloudfunctionsv2.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe call of an `any` typed value

Check warning on line 221 in src/gcp/cloudfunctionsv2.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe member access .message on an `any` value
const capturedMessage = captureRuntimeValidationError(err.message);

Check warning on line 222 in src/gcp/cloudfunctionsv2.ts

View workflow job for this annotation

GitHub Actions / lint (20)

Unsafe argument of type `any` assigned to a parameter of type `string`
utils.logLabeledWarning("functions", capturedMessage + " for function " + func.name);
}
if (err?.message?.includes("maxScale may not exceed")) {
Expand Down Expand Up @@ -467,16 +468,18 @@
"ingressSettings",
"timeoutSeconds",
);
proto.convertIfPresent(
gcfFunction.serviceConfig,
endpoint,
"serviceAccountEmail",
"serviceAccount",
(from) =>
!from
? null
: proto.formatServiceAccount(from, endpoint.project, true /* removeTypePrefix */),
);

if (Object.prototype.hasOwnProperty.call(endpoint, "serviceAccount")) {
Copy link
Copy Markdown
Contributor

@wandamora wandamora May 13, 2026

Choose a reason for hiding this comment

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

nit: Can you leave a comment here on why we're not using convertIfPresent like how the rest of the function is using the proto API? This could help with readability.

const serviceAccount = endpoint.serviceAccount;
if (!serviceAccount) {
gcfFunction.buildConfig.serviceAccount = null;
gcfFunction.serviceConfig.serviceAccountEmail = null;
} else {
const email = proto.formatServiceAccount(serviceAccount, endpoint.project, true);
gcfFunction.buildConfig.serviceAccount = `projects/${endpoint.project}/serviceAccounts/${email}`;
gcfFunction.serviceConfig.serviceAccountEmail = email;
}
}
// Memory must be set because the default value of GCF gen 2 is Megabytes and
// we use mebibytes
const mem = endpoint.availableMemoryMb || backend.DEFAULT_MEMORY;
Expand Down
Loading