Skip to content

Commit fe4a140

Browse files
committed
test: add registry-level cross-project name conflict regression test
Replace the FeatureStore-level test (which fought parquet inference and dill source extraction) with a direct Registry-level test mirroring the pattern in test_sql_registry.py. Two tests in tests/unit/infra/registry/test_file_registry.py: - test_same_project_name_conflict_batch_vs_stream: confirms that a FeatureView and StreamFeatureView with the same name in the same project still raise ConflictingFeatureViewNames (existing behaviour) - test_cross_project_name_does_not_conflict_batch_vs_stream: confirms that the same name across different projects and different view types no longer raises (the bug fixed by this PR) Signed-off-by: Abhishek8108 <87538407+Abhishek8108@users.noreply.github.com>
1 parent c9da132 commit fe4a140

2 files changed

Lines changed: 94 additions & 53 deletions

File tree

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import tempfile
2+
from datetime import timedelta
3+
4+
import pytest
5+
6+
from feast import Field
7+
from feast.data_source import PushSource
8+
from feast.entity import Entity
9+
from feast.errors import ConflictingFeatureViewNames
10+
from feast.feature_view import FeatureView
11+
from feast.infra.offline_stores.file_source import FileSource
12+
from feast.infra.registry.registry import Registry
13+
from feast.repo_config import RegistryConfig
14+
from feast.stream_feature_view import StreamFeatureView
15+
from feast.types import Float32
16+
from feast.value_type import ValueType
17+
18+
19+
@pytest.fixture
20+
def file_registry():
21+
fd, registry_path = tempfile.mkstemp()
22+
config = RegistryConfig(path=registry_path)
23+
registry = Registry("test_project", config, None)
24+
yield registry
25+
26+
27+
def _make_sources():
28+
file_source = FileSource(
29+
path="driver_stats.parquet",
30+
timestamp_field="event_timestamp",
31+
created_timestamp_column="created",
32+
)
33+
push_source = PushSource(name="driver_push", batch_source=file_source)
34+
return file_source, push_source
35+
36+
37+
def test_same_project_name_conflict_batch_vs_stream(file_registry):
38+
"""A FeatureView and StreamFeatureView with the same name in the same project must raise ConflictingFeatureViewNames."""
39+
entity = Entity(name="driver", value_type=ValueType.STRING, join_keys=["driver_id"])
40+
file_registry.apply_entity(entity, "test_project")
41+
42+
file_source, push_source = _make_sources()
43+
44+
batch_view = FeatureView(
45+
name="driver_activity",
46+
entities=[entity],
47+
ttl=timedelta(days=1),
48+
schema=[Field(name="conv_rate", dtype=Float32)],
49+
source=file_source,
50+
)
51+
file_registry.apply_feature_view(batch_view, "test_project")
52+
53+
stream_view = StreamFeatureView(
54+
name="driver_activity",
55+
source=push_source,
56+
entities=[entity],
57+
schema=[Field(name="conv_rate", dtype=Float32)],
58+
timestamp_field="event_timestamp",
59+
)
60+
with pytest.raises(ConflictingFeatureViewNames):
61+
file_registry.apply_feature_view(stream_view, "test_project")
62+
63+
64+
def test_cross_project_name_does_not_conflict_batch_vs_stream(file_registry):
65+
"""A FeatureView in project_a and a StreamFeatureView with the same name in project_b
66+
must not raise ConflictingFeatureViewNames.
67+
68+
Before the fix, _existing_feature_view_names_to_fvs scanned all projects,
69+
so the type mismatch between the two projects triggered a spurious error.
70+
"""
71+
entity = Entity(name="driver", value_type=ValueType.STRING, join_keys=["driver_id"])
72+
file_registry.apply_entity(entity, "project_a")
73+
file_registry.apply_entity(entity, "project_b")
74+
75+
file_source, push_source = _make_sources()
76+
77+
batch_view = FeatureView(
78+
name="driver_activity",
79+
entities=[entity],
80+
ttl=timedelta(days=1),
81+
schema=[Field(name="conv_rate", dtype=Float32)],
82+
source=file_source,
83+
)
84+
file_registry.apply_feature_view(batch_view, "project_a")
85+
86+
stream_view = StreamFeatureView(
87+
name="driver_activity",
88+
source=push_source,
89+
entities=[entity],
90+
schema=[Field(name="conv_rate", dtype=Float32)],
91+
timestamp_field="event_timestamp",
92+
)
93+
# Must not raise — same name, different project, different type.
94+
file_registry.apply_feature_view(stream_view, "project_b")

sdk/python/tests/unit/local_feast_tests/test_local_feature_store.py

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -543,59 +543,6 @@ def test_apply_conflicting_feature_view_names(feature_store_with_local_registry)
543543
feature_store_with_local_registry.teardown()
544544

545545

546-
def test_cross_project_feature_view_names_do_not_conflict():
547-
"""Feature views with the same name in different projects must not raise ConflictingFeatureViewNames."""
548-
fd, registry_path = mkstemp()
549-
fd, online_store_path = mkstemp()
550-
551-
def make_store(project: str) -> FeatureStore:
552-
return FeatureStore(
553-
config=RepoConfig(
554-
registry=registry_path,
555-
project=project,
556-
provider="local",
557-
online_store=SqliteOnlineStoreConfig(path=online_store_path),
558-
entity_key_serialization_version=3,
559-
)
560-
)
561-
562-
store_a = make_store("project_a")
563-
store_b = make_store("project_b")
564-
565-
entity = Entity(name="driver", join_keys=["driver_id"])
566-
df = pd.DataFrame(
567-
{
568-
"driver_id": [1, 2],
569-
"ts": pd.to_datetime(["2024-01-01", "2024-01-02"], utc=True),
570-
}
571-
)
572-
573-
with prep_file_source(df=df, timestamp_field="ts") as source:
574-
fv_a = FeatureView(
575-
name="driver_stats",
576-
entities=[entity],
577-
schema=[Field(name="driver_id", dtype=Int64)],
578-
ttl=timedelta(seconds=10),
579-
online=False,
580-
source=source,
581-
)
582-
store_a.apply([entity, fv_a])
583-
584-
fv_b = FeatureView(
585-
name="driver_stats",
586-
entities=[entity],
587-
schema=[Field(name="driver_id", dtype=Int64)],
588-
ttl=timedelta(seconds=10),
589-
online=False,
590-
source=source,
591-
)
592-
# Must not raise ConflictingFeatureViewNames — same name but different project.
593-
store_b.apply([entity, fv_b])
594-
595-
store_a.teardown()
596-
store_b.teardown()
597-
598-
599546
@pytest.mark.parametrize(
600547
"test_feature_store",
601548
[lazy_fixture("feature_store_with_local_registry")],

0 commit comments

Comments
 (0)