From 9708c2b7d757aaa25d7a84ca35af208d7e2fb75e Mon Sep 17 00:00:00 2001 From: Joanna Wang Date: Mon, 15 Jun 2026 16:05:16 +0000 Subject: [PATCH] test: verify Secret Manager API enablement for Developer Connect changes ### Description Ensures that the Firebase App Hosting onboarding flow explicitly enables the Secret Manager API alongside the Developer Connect API. Developer Connect is undergoing changes starting September 21, 2026, where it will no longer automatically enable the Secret Manager API. This PR adds unit tests to `backend.spec.ts` verifying that `ensureRequiredApisEnabled` and `createGitRepoLink` correctly and explicitly enable the Secret Manager API. The codebase already explicitly enables this API in these locations, so this change guarantees that the flow will not break when the dependency is removed. Fixes b/524212868 ### Scenarios Tested - Ran `npx mocha src/apphosting/backend.spec.ts` to verify tests pass. - Ran `npm run lint:changed-files` to ensure formatting/linting is clean. ### Sample Commands npx mocha src/apphosting/backend.spec.ts --- src/apphosting/backend.spec.ts | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/apphosting/backend.spec.ts b/src/apphosting/backend.spec.ts index 24892b8815c..6ff89319bf8 100644 --- a/src/apphosting/backend.spec.ts +++ b/src/apphosting/backend.spec.ts @@ -15,10 +15,22 @@ import { chooseBackends, getBackendForAmbiguousLocation, getBackend, + ensureRequiredApisEnabled, + createGitRepoLink, } from "./backend"; import * as deploymentTool from "../deploymentTool"; import { FirebaseError } from "../error"; import * as experiments from "../experiments"; +import * as ensureApiEnabled from "../ensureApiEnabled"; +import * as githubConnections from "./githubConnections"; +import { + developerConnectOrigin, + secretManagerOrigin, + cloudbuildOrigin, + cloudRunApiOrigin, + artifactRegistryDomain, + iamOrigin, +} from "../api"; describe("apphosting setup functions", () => { const projectId = "projectId"; @@ -35,6 +47,7 @@ describe("apphosting setup functions", () => { let createServiceAccountStub: sinon.SinonStub; let addServiceAccountToRolesStub: sinon.SinonStub; let testResourceIamPermissionsStub: sinon.SinonStub; + let ensureStub: sinon.SinonStub; beforeEach(() => { promptStub = sinon.stub(promptImport); @@ -67,6 +80,7 @@ describe("apphosting setup functions", () => { testResourceIamPermissionsStub = sinon .stub(iam, "testResourceIamPermissions") .throws("Unexpected testResourceIamPermissions call"); + ensureStub = sinon.stub(ensureApiEnabled, "ensure").resolves(); sinon.stub(experiments, "isEnabled").returns(false).withArgs("abiu").returns(true); }); @@ -509,4 +523,68 @@ describe("apphosting setup functions", () => { await expect(getBackend(projectId, "cow")).to.eventually.equal(backendCow); }); }); + + describe("ensureRequiredApisEnabled", () => { + it("should enable all required APIs including developerconnect and secretmanager", async () => { + await ensureRequiredApisEnabled(projectId); + expect(ensureStub).to.have.been.calledWith( + projectId, + developerConnectOrigin(), + "apphosting", + true, + ); + expect(ensureStub).to.have.been.calledWith( + projectId, + secretManagerOrigin(), + "apphosting", + true, + ); + expect(ensureStub).to.have.been.calledWith(projectId, cloudbuildOrigin(), "apphosting", true); + expect(ensureStub).to.have.been.calledWith( + projectId, + cloudRunApiOrigin(), + "apphosting", + true, + ); + expect(ensureStub).to.have.been.calledWith( + projectId, + artifactRegistryDomain(), + "apphosting", + true, + ); + expect(ensureStub).to.have.been.calledWith(projectId, iamOrigin(), "apphosting", true); + }); + }); + + describe("createGitRepoLink", () => { + it("should enable developerconnect, secretmanager and iam APIs", async () => { + const linkGitHubRepositoryStub = sinon + .stub(githubConnections, "linkGitHubRepository") + .resolves(); + listLocationsStub.resolves([{ name: location, locationId: location }]); + try { + await createGitRepoLink(projectId, location, "connectionId"); + expect(ensureStub).to.have.been.calledWith( + projectId, + developerConnectOrigin(), + "apphosting", + true, + ); + expect(ensureStub).to.have.been.calledWith( + projectId, + secretManagerOrigin(), + "apphosting", + true, + ); + expect(ensureStub).to.have.been.calledWith(projectId, iamOrigin(), "apphosting", true); + expect(linkGitHubRepositoryStub).to.have.been.calledWith( + projectId, + location, + "connectionId", + ); + } finally { + linkGitHubRepositoryStub.restore(); + } + }); + }); });