Skip to content

Commit 7af4097

Browse files
committed
fix: propagate process.env to Python runtime serveAdmin environment
1 parent 3170b27 commit 7af4097

File tree

2 files changed

+125
-0
lines changed

2 files changed

+125
-0
lines changed

src/deploy/functions/runtimes/python/index.spec.ts

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { expect } from "chai";
22
import * as sinon from "sinon";
33

44
import * as python from ".";
5+
import * as pythonFunctions from "../../../../functions/python";
56

67
const PROJECT_ID = "test-project";
78
const SOURCE_DIR = "/some/path/fns";
@@ -34,4 +35,127 @@ describe("PythonDelegate", () => {
3435
expect(delegate.getPythonBinary()).to.equal("python.exe");
3536
});
3637
});
38+
39+
describe("serveAdmin", () => {
40+
let delegate: python.Delegate;
41+
let runWithVirtualEnvStub: sinon.SinonStub;
42+
let originalEnv: NodeJS.ProcessEnv;
43+
44+
beforeEach(() => {
45+
delegate = new python.Delegate(PROJECT_ID, SOURCE_DIR, "python310");
46+
47+
// Mock the modulesDir method
48+
sinon.stub(delegate, "modulesDir" as any).resolves("/mock/modules/dir");
49+
50+
// Mock runWithVirtualEnv from the imported module
51+
runWithVirtualEnvStub = sinon.stub(pythonFunctions, "runWithVirtualEnv");
52+
53+
// Store original environment
54+
originalEnv = { ...process.env };
55+
});
56+
57+
afterEach(() => {
58+
// Restore original environment
59+
process.env = originalEnv;
60+
sinon.restore();
61+
});
62+
63+
it("should propagate process.env including GOOGLE_APPLICATION_CREDENTIALS", async () => {
64+
// Set up test environment variables
65+
process.env.GOOGLE_APPLICATION_CREDENTIALS = "/path/to/service-account.json";
66+
process.env.CUSTOM_VAR = "custom_value";
67+
68+
const testEnvs = {
69+
FIREBASE_PROJECT_ID: PROJECT_ID,
70+
FUNCTION_TARGET: "test_function",
71+
};
72+
73+
// Mock the runWithVirtualEnv function
74+
const mockChildProcess = {
75+
stdout: { on: sinon.stub() },
76+
stderr: { on: sinon.stub() },
77+
killed: false,
78+
kill: sinon.stub(),
79+
once: sinon.stub(),
80+
};
81+
runWithVirtualEnvStub.returns(mockChildProcess);
82+
83+
await delegate.serveAdmin(8080, testEnvs);
84+
85+
// Verify runWithVirtualEnv was called with correct environment
86+
expect(runWithVirtualEnvStub.calledOnce).to.be.true;
87+
const callArgs = runWithVirtualEnvStub.getCall(0).args;
88+
const passedEnvs = callArgs[2]; // Third argument is the environment
89+
90+
// Verify process.env variables are included
91+
expect(passedEnvs.GOOGLE_APPLICATION_CREDENTIALS).to.equal("/path/to/service-account.json");
92+
expect(passedEnvs.CUSTOM_VAR).to.equal("custom_value");
93+
94+
// Verify provided envs are included
95+
expect(passedEnvs.FIREBASE_PROJECT_ID).to.equal(PROJECT_ID);
96+
expect(passedEnvs.FUNCTION_TARGET).to.equal("test_function");
97+
98+
// Verify ADMIN_PORT is set
99+
expect(passedEnvs.ADMIN_PORT).to.equal("8080");
100+
});
101+
102+
it("should preserve process.env when no additional envs are provided", async () => {
103+
// Set up test environment variables
104+
process.env.GOOGLE_APPLICATION_CREDENTIALS = "/path/to/service-account.json";
105+
(process.env as any).NODE_ENV = "test";
106+
107+
const mockChildProcess = {
108+
stdout: { on: sinon.stub() },
109+
stderr: { on: sinon.stub() },
110+
killed: false,
111+
kill: sinon.stub(),
112+
once: sinon.stub(),
113+
};
114+
runWithVirtualEnvStub.returns(mockChildProcess);
115+
116+
await delegate.serveAdmin(8080, {});
117+
118+
// Verify runWithVirtualEnv was called with correct environment
119+
expect(runWithVirtualEnvStub.calledOnce).to.be.true;
120+
const callArgs = runWithVirtualEnvStub.getCall(0).args;
121+
const passedEnvs = callArgs[2];
122+
123+
// Verify process.env variables are preserved
124+
expect(passedEnvs.GOOGLE_APPLICATION_CREDENTIALS).to.equal("/path/to/service-account.json");
125+
expect(passedEnvs.NODE_ENV).to.equal("test");
126+
expect(passedEnvs.ADMIN_PORT).to.equal("8080");
127+
});
128+
129+
it("should override process.env with provided envs when there are conflicts", async () => {
130+
// Set up conflicting environment variables
131+
process.env.GOOGLE_APPLICATION_CREDENTIALS = "/original/path.json";
132+
process.env.FIREBASE_PROJECT_ID = "original-project";
133+
134+
const testEnvs = {
135+
GOOGLE_APPLICATION_CREDENTIALS: "/new/path.json",
136+
FIREBASE_PROJECT_ID: PROJECT_ID,
137+
};
138+
139+
const mockChildProcess = {
140+
stdout: { on: sinon.stub() },
141+
stderr: { on: sinon.stub() },
142+
killed: false,
143+
kill: sinon.stub(),
144+
once: sinon.stub(),
145+
};
146+
runWithVirtualEnvStub.returns(mockChildProcess);
147+
148+
await delegate.serveAdmin(8080, testEnvs);
149+
150+
// Verify runWithVirtualEnv was called with correct environment
151+
expect(runWithVirtualEnvStub.calledOnce).to.be.true;
152+
const callArgs = runWithVirtualEnvStub.getCall(0).args;
153+
const passedEnvs = callArgs[2];
154+
155+
// Verify provided envs override process.env
156+
expect(passedEnvs.GOOGLE_APPLICATION_CREDENTIALS).to.equal("/new/path.json");
157+
expect(passedEnvs.FIREBASE_PROJECT_ID).to.equal(PROJECT_ID);
158+
expect(passedEnvs.ADMIN_PORT).to.equal("8080");
159+
});
160+
});
37161
});

src/deploy/functions/runtimes/python/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ export class Delegate implements runtimes.RuntimeDelegate {
151151
async serveAdmin(port: number, envs: backend.EnvironmentVariables) {
152152
const modulesDir = await this.modulesDir();
153153
const envWithAdminPort = {
154+
...process.env,
154155
...envs,
155156
ADMIN_PORT: port.toString(),
156157
};

0 commit comments

Comments
 (0)