|
1 | 1 | import { expect } from "chai"; |
2 | | -import { AppHostingYamlConfig } from "./yaml"; |
3 | | - |
4 | | -describe("merge", () => { |
5 | | - it("merges incoming apphosting yaml config with precendence", () => { |
6 | | - const apphostingYaml = AppHostingYamlConfig.empty(); |
7 | | - apphostingYaml.env = { |
8 | | - ENV_1: { value: "env_1" }, |
9 | | - ENV_2: { value: "env_2" }, |
10 | | - SECRET: { secret: "secret_1" }, |
11 | | - }; |
12 | | - |
13 | | - const incomingAppHostingYaml = AppHostingYamlConfig.empty(); |
14 | | - incomingAppHostingYaml.env = { |
15 | | - ENV_1: { value: "incoming_env_1" }, |
16 | | - ENV_3: { value: "incoming_env_3" }, |
17 | | - SECRET_2: { value: "incoming_secret_2" }, |
18 | | - }; |
19 | | - |
20 | | - apphostingYaml.merge(incomingAppHostingYaml); |
21 | | - expect(apphostingYaml.env).to.deep.equal({ |
22 | | - ENV_1: { value: "incoming_env_1" }, |
23 | | - ENV_2: { value: "env_2" }, |
24 | | - ENV_3: { value: "incoming_env_3" }, |
25 | | - SECRET: { secret: "secret_1" }, |
26 | | - SECRET_2: { value: "incoming_secret_2" }, |
| 2 | +import * as sinon from "sinon"; |
| 3 | +import { AppHostingYamlConfig, toEnvMap, toEnvList } from "./yaml"; |
| 4 | +import * as configModule from "./config"; |
| 5 | +import * as utils from "../utils"; |
| 6 | +import * as fsutils from "../fsutils"; |
| 7 | +import { FirebaseError } from "../error"; |
| 8 | + |
| 9 | +describe("apphosting/yaml", () => { |
| 10 | + let fileExistsStub: sinon.SinonStub; |
| 11 | + let readFileStub: sinon.SinonStub; |
| 12 | + let storeStub: sinon.SinonStub; |
| 13 | + |
| 14 | + beforeEach(() => { |
| 15 | + fileExistsStub = sinon.stub(fsutils, "fileExistsSync"); |
| 16 | + readFileStub = sinon.stub(utils, "readFileFromDirectory"); |
| 17 | + storeStub = sinon.stub(configModule, "store"); |
| 18 | + }); |
| 19 | + |
| 20 | + afterEach(() => { |
| 21 | + sinon.restore(); |
| 22 | + }); |
| 23 | + |
| 24 | + describe("loadFromFile", () => { |
| 25 | + it("should successfully load configuration with env vars", async () => { |
| 26 | + fileExistsStub.returns(true); |
| 27 | + readFileStub.resolves({ source: "env:\n - variable: FOO\n value: bar" }); |
| 28 | + |
| 29 | + const res = await AppHostingYamlConfig.loadFromFile("apphosting.yaml"); |
| 30 | + |
| 31 | + expect(res.filename).to.equal("apphosting.yaml"); |
| 32 | + expect(res.env).to.deep.equal({ FOO: { value: "bar" } }); |
| 33 | + }); |
| 34 | + |
| 35 | + it("should throw if file does not exist", async () => { |
| 36 | + fileExistsStub.returns(false); |
| 37 | + |
| 38 | + await expect(AppHostingYamlConfig.loadFromFile("missing.yaml")).to.be.rejectedWith( |
| 39 | + FirebaseError, |
| 40 | + /Cannot load missing.yaml from given path/, |
| 41 | + ); |
| 42 | + }); |
| 43 | + |
| 44 | + it("should return empty env if file contains no env", async () => { |
| 45 | + fileExistsStub.returns(true); |
| 46 | + readFileStub.resolves({ source: "runConfig:\n cpu: 2" }); |
| 47 | + |
| 48 | + const res = await AppHostingYamlConfig.loadFromFile("apphosting.yaml"); |
| 49 | + |
| 50 | + expect(res.env).to.deep.equal({}); |
| 51 | + }); |
| 52 | + }); |
| 53 | + |
| 54 | + describe("empty", () => { |
| 55 | + it("should create an empty config", () => { |
| 56 | + const res = AppHostingYamlConfig.empty(); |
| 57 | + expect(res.env).to.deep.equal({}); |
27 | 58 | }); |
28 | 59 | }); |
29 | 60 |
|
30 | | - it("conditionally allows secrets to become plaintext", () => { |
31 | | - const apphostingYaml = AppHostingYamlConfig.empty(); |
32 | | - apphostingYaml.env = { |
33 | | - API_KEY: { secret: "api_key" }, |
34 | | - }; |
35 | | - |
36 | | - const incomingYaml = AppHostingYamlConfig.empty(); |
37 | | - incomingYaml.env = { |
38 | | - API_KEY: { value: "plaintext" }, |
39 | | - }; |
40 | | - |
41 | | - expect(() => |
42 | | - apphostingYaml.merge(incomingYaml, /* alllowSecretsToBecomePlaintext */ false), |
43 | | - ).to.throw("Cannot convert secret to plaintext in apphosting yaml"); |
44 | | - |
45 | | - expect(() => |
46 | | - apphostingYaml.merge(incomingYaml, /* alllowSecretsToBecomePlaintext */ true), |
47 | | - ).to.not.throw(); |
48 | | - expect(apphostingYaml.env).to.deep.equal({ |
49 | | - API_KEY: { value: "plaintext" }, |
| 61 | + describe("merge", () => { |
| 62 | + it("should override variables from incoming config", () => { |
| 63 | + const base = AppHostingYamlConfig.empty(); |
| 64 | + base.env = { FOO: { value: "1" }, BAR: { secret: "sec" } }; |
| 65 | + |
| 66 | + const other = AppHostingYamlConfig.empty(); |
| 67 | + other.env = { FOO: { value: "2" }, BAZ: { value: "3" } }; |
| 68 | + |
| 69 | + base.merge(other, true); |
| 70 | + |
| 71 | + expect(base.env).to.deep.equal({ |
| 72 | + FOO: { value: "2" }, |
| 73 | + BAR: { secret: "sec" }, |
| 74 | + BAZ: { value: "3" }, |
| 75 | + }); |
| 76 | + }); |
| 77 | + |
| 78 | + it("should throw when a secret turns into plaintext and allowSecretsToBecomePlaintext is false", () => { |
| 79 | + const base = AppHostingYamlConfig.empty(); |
| 80 | + base.env = { DB_PASS: { secret: "my-secret" } }; |
| 81 | + |
| 82 | + const other = AppHostingYamlConfig.empty(); |
| 83 | + other.env = { DB_PASS: { value: "plaintext" } }; |
| 84 | + |
| 85 | + expect(() => base.merge(other, false)).to.throw(/Cannot convert secret to plaintext/); |
| 86 | + }); |
| 87 | + }); |
| 88 | + |
| 89 | + describe("utilities", () => { |
| 90 | + it("toEnvMap", () => { |
| 91 | + const list = [{ variable: "FOO", value: "bar" }]; |
| 92 | + const map = toEnvMap(list); |
| 93 | + expect(map).to.deep.equal({ FOO: { value: "bar" } }); |
| 94 | + }); |
| 95 | + |
| 96 | + it("toEnvList", () => { |
| 97 | + const map = { FOO: { value: "bar" } }; |
| 98 | + const list = toEnvList(map); |
| 99 | + expect(list).to.deep.equal([{ variable: "FOO", value: "bar" }]); |
| 100 | + }); |
| 101 | + }); |
| 102 | + |
| 103 | + describe("upsertFile", () => { |
| 104 | + it("should parse, merge, and store successfully", async () => { |
| 105 | + fileExistsStub.returns(true); |
| 106 | + readFileStub.resolves({ source: "env:\n - variable: FOO\n value: bar" }); |
| 107 | + |
| 108 | + const conf = AppHostingYamlConfig.empty(); |
| 109 | + conf.env = { BAZ: { value: "qux" } }; |
| 110 | + |
| 111 | + await conf.upsertFile("apphosting.yaml"); |
| 112 | + |
| 113 | + expect(storeStub).to.have.been.calledOnce; |
50 | 114 | }); |
51 | 115 | }); |
52 | 116 | }); |
0 commit comments