Skip to content

Commit 3f50498

Browse files
committed
Merge branch '@invertase/feat-add-retry-for-storage-firestore-database' of https://github.com/firebase/firebase-functions-python into @invertase/feat-add-retry-for-storage-firestore-database
2 parents 158cdc8 + 4d343c0 commit 3f50498

3 files changed

Lines changed: 111 additions & 4 deletions

File tree

src/firebase_functions/options.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,11 @@ class RuntimeOptions:
254254
to the value "gcf_gen1"
255255
"""
256256

257-
vpc_connector: str | _util.Sentinel | None = None
257+
vpc_connector: str | Expression[str] | _util.Sentinel | None = None
258258
"""
259259
Connect function to specified VPC connector.
260-
A value of ``RESET_VALUE`` removes the VPC connector.
260+
Accepts a plain string, an ``Expression[str]`` (e.g. a ``StringParam`` or
261+
ternary expression), or ``RESET_VALUE`` to remove the VPC connector.
261262
"""
262263

263264
vpc_connector_egress_settings: VpcEgressSetting | _util.Sentinel | None = None
@@ -1239,8 +1240,8 @@ def set_global_options(
12391240
max_instances: int | Expression[int] | _util.Sentinel | None = None,
12401241
concurrency: int | Expression[int] | _util.Sentinel | None = None,
12411242
cpu: int | _typing.Literal["gcf_gen1"] | _util.Sentinel = "gcf_gen1",
1242-
vpc_connector: str | None = None,
1243-
vpc_connector_egress_settings: VpcEgressSetting | None = None,
1243+
vpc_connector: str | Expression[str] | _util.Sentinel | None = None,
1244+
vpc_connector_egress_settings: VpcEgressSetting | _util.Sentinel | None = None,
12441245
service_account: str | _util.Sentinel | None = None,
12451246
ingress: IngressSetting | _util.Sentinel | None = None,
12461247
labels: dict[str, str] | None = None,

tests/conftest.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2026 Google Inc.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""
15+
Pytest configuration and shared fixtures for the test suite.
16+
"""
17+
18+
import pytest
19+
20+
from firebase_functions import params
21+
22+
# pylint: disable=protected-access
23+
24+
25+
@pytest.fixture(autouse=True)
26+
def _cleanup_params():
27+
"""Clear the global params registry so each test runs with a clean state."""
28+
params._params.clear()
29+
yield
30+
params._params.clear()

tests/test_options.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
Options unit tests.
1616
"""
1717

18+
import pytest
1819
from pytest import raises
1920

2021
from firebase_functions import alerts_fn, https_fn, options, params
@@ -41,6 +42,15 @@ def asamplefunctionpreserved(_):
4142
return "hello world"
4243

4344

45+
@pytest.fixture(autouse=True)
46+
def _cleanup_global_options():
47+
"""Reset global options so each test runs with a clean state."""
48+
original_options = options._GLOBAL_OPTIONS
49+
options._GLOBAL_OPTIONS = options.RuntimeOptions()
50+
yield
51+
options._GLOBAL_OPTIONS = original_options
52+
53+
4454
def test_set_global_options():
4555
"""
4656
Testing if setting a global option internally change the values.
@@ -64,6 +74,30 @@ def test_global_options_merged_with_provider_options():
6474
)
6575

6676

77+
@pytest.mark.parametrize(
78+
"vpc_connector_expr_factory",
79+
[
80+
lambda: params.StringParam("VPC_CONNECTOR"),
81+
lambda: params.BoolParam("USE_VPC").equals(True).then("my-vpc", ""),
82+
],
83+
)
84+
def test_set_global_options_accepts_vpc_connector_expression(vpc_connector_expr_factory):
85+
vpc_connector_expr = vpc_connector_expr_factory()
86+
options.set_global_options(vpc_connector=vpc_connector_expr)
87+
88+
assert options._GLOBAL_OPTIONS.vpc_connector == vpc_connector_expr, (
89+
"global vpc_connector expression was not stored"
90+
)
91+
92+
https_options = options.HttpsOptions()
93+
endpoint = https_options._endpoint(func_name="test_vpc_global")
94+
95+
assert endpoint.vpc is not None, "vpc block was not set on endpoint"
96+
assert endpoint.vpc["connector"] == str(vpc_connector_expr), (
97+
"global vpc_connector Expression[str] was not applied to the endpoint"
98+
)
99+
100+
67101
def test_https_options_removes_cors():
68102
"""
69103
Testing _HttpsOptions strips out the 'cors' property when converted to a dict.
@@ -206,6 +240,48 @@ def test_invoker_with_no_element_throws():
206240
options.HttpsOptions(invoker=[])._endpoint(func_name="test")
207241

208242

243+
@pytest.mark.parametrize(
244+
"vpc_connector_expr_factory",
245+
[
246+
lambda: params.StringParam("VPC_CONNECTOR"),
247+
lambda: params.BoolParam("USE_VPC").equals(True).then("my-vpc", ""),
248+
],
249+
)
250+
def test_vpc_connector_accepts_expression(vpc_connector_expr_factory):
251+
vpc_connector_expr = vpc_connector_expr_factory()
252+
https_options = options.HttpsOptions(vpc_connector=vpc_connector_expr)
253+
https_options_dict = https_options._asdict_with_global_options()
254+
255+
# The options dict should contain the CEL string representation for the expression.
256+
assert https_options_dict["vpc_connector"] == str(vpc_connector_expr), (
257+
"vpc_connector expression was not converted to CEL string"
258+
)
259+
260+
# The generated endpoint should map the resolved vpc_connector into the vpc block.
261+
endpoint = https_options._endpoint(func_name="test_vpc")
262+
assert endpoint.vpc is not None, "vpc block was not set on endpoint"
263+
assert endpoint.vpc["connector"] == str(vpc_connector_expr), (
264+
"vpc connector was not set from vpc_connector Expression[str]"
265+
)
266+
267+
268+
def test_vpc_connector_expression_with_egress_settings():
269+
vpc_connector_expr = params.StringParam("VPC_CONNECTOR")
270+
https_options = options.HttpsOptions(
271+
vpc_connector=vpc_connector_expr,
272+
vpc_connector_egress_settings=options.VpcEgressSetting.ALL_TRAFFIC,
273+
)
274+
275+
endpoint = https_options._endpoint(func_name="test_vpc_egress")
276+
assert endpoint.vpc is not None, "vpc block was not set on endpoint"
277+
assert endpoint.vpc["connector"] == str(vpc_connector_expr), (
278+
"vpc connector was not set from vpc_connector Expression[str]"
279+
)
280+
assert endpoint.vpc.get("egressSettings") == options.VpcEgressSetting.ALL_TRAFFIC.value, (
281+
"egressSettings was not set alongside an Expression[str] vpc_connector"
282+
)
283+
284+
209285
def _assert_alert_endpoint_options(endpoint, expected_alert_type, expect_app_id: str | None = None):
210286
assert endpoint.region == ["europe-west1"]
211287
assert endpoint.maxInstances == 1

0 commit comments

Comments
 (0)