diff --git a/src/commands/apphosting-backends-create.spec.ts b/src/commands/apphosting-backends-create.spec.ts new file mode 100644 index 00000000000..936e4e81617 --- /dev/null +++ b/src/commands/apphosting-backends-create.spec.ts @@ -0,0 +1,121 @@ +import { expect } from "chai"; +import * as sinon from "sinon"; +import * as nock from "nock"; +import { command } from "./apphosting-backends-create"; +import * as backend from "../apphosting/backend"; +import * as projectUtils from "../projectUtils"; +import * as requireAuthModule from "../requireAuth"; +import { FirebaseError } from "../error"; + +describe("apphosting:backends:create", () => { + const PROJECT_ID = "test-project"; + const WEB_APP_ID = "test-web-app"; + const BACKEND_ID = "test-backend"; + const REGION = "us-central1"; + const SERVICE_ACCOUNT = "test-sa"; + const ROOT_DIR = "."; + + let doSetupStub: sinon.SinonStub; + + before(() => { + nock.disableNetConnect(); + }); + + after(() => { + nock.enableNetConnect(); + }); + + beforeEach(() => { + doSetupStub = sinon.stub(backend, "doSetup").resolves(); + sinon.stub(projectUtils, "needProjectId").returns(PROJECT_ID); + sinon.stub(requireAuthModule, "requireAuth").resolves(); + + // Stub ensureApiEnabled calls + nock("https://serviceusage.googleapis.com") + .get(`/v1/projects/${PROJECT_ID}/services/firebaseapphosting.googleapis.com`) + .query(true) // match any query params + .reply(200, { state: "ENABLED" }); + + // Stub TOS acceptance check + nock("https://mobilesdk-pa.googleapis.com") + .get("/v1/accessmanagement/tos:getStatus") + .query(true) + .reply(200, { + perServiceStatus: [ + { + tosId: "APP_HOSTING_TOS", + serviceStatus: { + status: "ACCEPTED", + }, + }, + ], + }); + }); + + afterEach(() => { + sinon.restore(); + nock.cleanAll(); + }); + + [ + { + description: "missing required options", + options: { nonInteractive: true }, + }, + { + description: "just backend provided", + options: { nonInteractive: true, backend: BACKEND_ID }, + }, + { + description: "just region provided", + options: { nonInteractive: true, primaryRegion: REGION }, + }, + ].forEach(({ description, options }) => { + it(`should throw error if non-interactive and ${description}`, async () => { + await expect(command.runner()(options)).to.be.rejectedWith( + FirebaseError, + "requires --backend and --primary-region", + ); + }); + }); + + it("should call doSetup with correct arguments in interactive mode", async () => { + before(() => { + sinon.stub(process.stdin, "isTTY").value(true); + }); + const options = {}; + await command.runner()(options); + + expect(doSetupStub).to.have.been.calledWith( + PROJECT_ID, + undefined, // nonInteractive + undefined, // webAppId + undefined, // backendId + undefined, // serviceAccount + undefined, // primaryRegion + undefined, // rootDir + ); + }); + + it("should call doSetup with passed options in non-interactive mode", async () => { + const options = { + nonInteractive: true, + backend: BACKEND_ID, + primaryRegion: REGION, + app: WEB_APP_ID, + serviceAccount: SERVICE_ACCOUNT, + rootDir: ROOT_DIR, + }; + await command.runner()(options); + + expect(doSetupStub).to.have.been.calledWith( + PROJECT_ID, + true, + WEB_APP_ID, + BACKEND_ID, + SERVICE_ACCOUNT, + REGION, + ROOT_DIR, + ); + }); +}); diff --git a/src/commands/apphosting-backends-create.ts b/src/commands/apphosting-backends-create.ts index b09a02d585c..c3a264b79f5 100644 --- a/src/commands/apphosting-backends-create.ts +++ b/src/commands/apphosting-backends-create.ts @@ -28,7 +28,7 @@ export const command = new Command("apphosting:backends:create") "specify the primary region for the backend. Required with --non-interactive.", ) .option("--root-dir ", "specify the root directory for the backend.") - .before(requireAuth) + .before((options: Options) => requireAuth(options)) .before(ensureApiEnabled) .before(requireTosAcceptance(APPHOSTING_TOS_ID)) .action(async (options: Options) => {