From fcb82bae8e572e4da3d0b8d921e6891f8cfc2bcf Mon Sep 17 00:00:00 2001 From: Emily Rockman Date: Fri, 22 Sep 2023 10:56:57 -0500 Subject: [PATCH] Backport #8646: Support labels for semantic_models, dimensions, measures and entities --- .../unreleased/Features-20230914-074429.yaml | 7 + core/dbt/contracts/graph/nodes.py | 1 + core/dbt/contracts/graph/semantic_models.py | 3 + core/dbt/contracts/graph/unparsed.py | 4 + core/dbt/parser/schema_yaml_readers.py | 4 + tests/functional/semantic_models/fixtures.py | 246 ++++++++++++ ..._semantic_layer_nodes_satisfy_protocols.py | 366 +++++++++++++++--- 7 files changed, 578 insertions(+), 53 deletions(-) create mode 100644 .changes/unreleased/Features-20230914-074429.yaml create mode 100644 tests/functional/semantic_models/fixtures.py diff --git a/.changes/unreleased/Features-20230914-074429.yaml b/.changes/unreleased/Features-20230914-074429.yaml new file mode 100644 index 00000000000..980f932b99c --- /dev/null +++ b/.changes/unreleased/Features-20230914-074429.yaml @@ -0,0 +1,7 @@ +kind: Features +body: Add support for optional label in semantic_models, measures, dimensions and + entities. +time: 2023-09-14T07:44:29.828199-05:00 +custom: + Author: emmyoop + Issue: "8595" diff --git a/core/dbt/contracts/graph/nodes.py b/core/dbt/contracts/graph/nodes.py index 3d1fc60a4d1..039f3ff3641 100644 --- a/core/dbt/contracts/graph/nodes.py +++ b/core/dbt/contracts/graph/nodes.py @@ -1502,6 +1502,7 @@ class SemanticModel(GraphNode): model: str node_relation: Optional[NodeRelation] description: Optional[str] = None + label: Optional[str] = None defaults: Optional[Defaults] = None entities: Sequence[Entity] = field(default_factory=list) measures: Sequence[Measure] = field(default_factory=list) diff --git a/core/dbt/contracts/graph/semantic_models.py b/core/dbt/contracts/graph/semantic_models.py index 2bb75382c46..bdcf1effbce 100644 --- a/core/dbt/contracts/graph/semantic_models.py +++ b/core/dbt/contracts/graph/semantic_models.py @@ -66,6 +66,7 @@ class Dimension(dbtClassMixin): name: str type: DimensionType description: Optional[str] = None + label: Optional[str] = None is_partition: bool = False type_params: Optional[DimensionTypeParams] = None expr: Optional[str] = None @@ -100,6 +101,7 @@ class Entity(dbtClassMixin): name: str type: EntityType description: Optional[str] = None + label: Optional[str] = None role: Optional[str] = None expr: Optional[str] = None @@ -136,6 +138,7 @@ class Measure(dbtClassMixin): name: str agg: AggregationType description: Optional[str] = None + label: Optional[str] = None create_metric: bool = False expr: Optional[str] = None agg_params: Optional[MeasureAggregationParameters] = None diff --git a/core/dbt/contracts/graph/unparsed.py b/core/dbt/contracts/graph/unparsed.py index 2e976db3a89..2fe3276b8c3 100644 --- a/core/dbt/contracts/graph/unparsed.py +++ b/core/dbt/contracts/graph/unparsed.py @@ -676,6 +676,7 @@ class UnparsedEntity(dbtClassMixin): name: str type: str # EntityType enum description: Optional[str] = None + label: Optional[str] = None role: Optional[str] = None expr: Optional[str] = None @@ -692,6 +693,7 @@ class UnparsedMeasure(dbtClassMixin): name: str agg: str # actually an enum description: Optional[str] = None + label: Optional[str] = None expr: Optional[Union[str, bool, int]] = None agg_params: Optional[MeasureAggregationParameters] = None non_additive_dimension: Optional[UnparsedNonAdditiveDimension] = None @@ -709,6 +711,7 @@ class UnparsedDimension(dbtClassMixin): name: str type: str # actually an enum description: Optional[str] = None + label: Optional[str] = None is_partition: bool = False type_params: Optional[UnparsedDimensionTypeParams] = None expr: Optional[str] = None @@ -719,6 +722,7 @@ class UnparsedSemanticModel(dbtClassMixin): name: str model: str # looks like "ref(...)" description: Optional[str] = None + label: Optional[str] = None defaults: Optional[Defaults] = None entities: List[UnparsedEntity] = field(default_factory=list) measures: List[UnparsedMeasure] = field(default_factory=list) diff --git a/core/dbt/parser/schema_yaml_readers.py b/core/dbt/parser/schema_yaml_readers.py index 2f2a3eb18e6..145e6485ffe 100644 --- a/core/dbt/parser/schema_yaml_readers.py +++ b/core/dbt/parser/schema_yaml_readers.py @@ -456,6 +456,7 @@ def _get_dimensions(self, unparsed_dimensions: List[UnparsedDimension]) -> List[ name=unparsed.name, type=DimensionType(unparsed.type), description=unparsed.description, + label=unparsed.label, is_partition=unparsed.is_partition, type_params=self._get_dimension_type_params(unparsed=unparsed.type_params), expr=unparsed.expr, @@ -472,6 +473,7 @@ def _get_entities(self, unparsed_entities: List[UnparsedEntity]) -> List[Entity] name=unparsed.name, type=EntityType(unparsed.type), description=unparsed.description, + label=unparsed.label, role=unparsed.role, expr=unparsed.expr, ) @@ -499,6 +501,7 @@ def _get_measures(self, unparsed_measures: List[UnparsedMeasure]) -> List[Measur name=unparsed.name, agg=AggregationType(unparsed.agg), description=unparsed.description, + label=unparsed.label, expr=str(unparsed.expr) if unparsed.expr is not None else None, agg_params=unparsed.agg_params, non_additive_dimension=self._get_non_additive_dimension( @@ -519,6 +522,7 @@ def parse_semantic_model(self, unparsed: UnparsedSemanticModel): parsed = SemanticModel( description=unparsed.description, + label=unparsed.label, fqn=fqn, model=unparsed.model, name=unparsed.name, diff --git a/tests/functional/semantic_models/fixtures.py b/tests/functional/semantic_models/fixtures.py new file mode 100644 index 00000000000..d19ba42f1a6 --- /dev/null +++ b/tests/functional/semantic_models/fixtures.py @@ -0,0 +1,246 @@ +simple_metricflow_time_spine_sql = """ +SELECT to_date('02/20/2023', 'mm/dd/yyyy') as date_day +""" + +models_people_sql = """ +select 1 as id, 'Drew' as first_name, 'Banin' as last_name, 'yellow' as favorite_color, true as loves_dbt, 5 as tenure, current_timestamp as created_at +union all +select 2 as id, 'Jeremy' as first_name, 'Cohen' as last_name, 'indigo' as favorite_color, true as loves_dbt, 4 as tenure, current_timestamp as created_at +union all +select 3 as id, 'Callum' as first_name, 'McCann' as last_name, 'emerald' as favorite_color, true as loves_dbt, 0 as tenure, current_timestamp as created_at +""" + +groups_yml = """ +version: 2 + +groups: + - name: some_group + owner: + email: me@gmail.com + - name: some_other_group + owner: + email: me@gmail.com +""" + +models_people_metrics_yml = """ +version: 2 + +metrics: + - name: number_of_people + label: "Number of people" + description: Total count of people + type: simple + type_params: + measure: people + meta: + my_meta: 'testing' +""" + +disabled_models_people_metrics_yml = """ +version: 2 + +metrics: + - name: number_of_people + config: + enabled: false + group: some_group + label: "Number of people" + description: Total count of people + type: simple + type_params: + measure: people + meta: + my_meta: 'testing' +""" + +semantic_model_people_yml = """ +version: 2 + +semantic_models: + - name: semantic_people + label: "Semantic People" + model: ref('people') + dimensions: + - name: favorite_color + label: "Favorite Color" + type: categorical + - name: created_at + label: "Created At" + type: TIME + type_params: + time_granularity: day + measures: + - name: years_tenure + label: "Years Tenure" + agg: SUM + expr: tenure + - name: people + label: "People" + agg: count + expr: id + entities: + - name: id + label: "Primary ID" + type: primary + defaults: + agg_time_dimension: created_at +""" + +enabled_semantic_model_people_yml = """ +version: 2 + +semantic_models: + - name: semantic_people + label: "Semantic People" + model: ref('people') + config: + enabled: true + group: some_group + dimensions: + - name: favorite_color + type: categorical + - name: created_at + type: TIME + type_params: + time_granularity: day + measures: + - name: years_tenure + agg: SUM + expr: tenure + - name: people + agg: count + expr: id + entities: + - name: id + type: primary + defaults: + agg_time_dimension: created_at +""" + +disabled_semantic_model_people_yml = """ +version: 2 + +semantic_models: + - name: semantic_people + label: "Semantic People" + model: ref('people') + config: + enabled: false + dimensions: + - name: favorite_color + type: categorical + - name: created_at + type: TIME + type_params: + time_granularity: day + measures: + - name: years_tenure + agg: SUM + expr: tenure + - name: people + agg: count + expr: id + entities: + - name: id + type: primary + defaults: + agg_time_dimension: created_at +""" + +schema_yml = """models: + - name: fct_revenue + description: This is the model fct_revenue. It should be able to use doc blocks + +semantic_models: + - name: revenue + description: This is the revenue semantic model. It should be able to use doc blocks + model: ref('fct_revenue') + + defaults: + agg_time_dimension: ds + + measures: + - name: txn_revenue + expr: revenue + agg: sum + agg_time_dimension: ds + create_metric: true + - name: sum_of_things + expr: 2 + agg: sum + agg_time_dimension: ds + - name: has_revenue + expr: true + agg: sum_boolean + agg_time_dimension: ds + - name: discrete_order_value_p99 + expr: order_total + agg: percentile + agg_time_dimension: ds + agg_params: + percentile: 0.99 + use_discrete_percentile: True + use_approximate_percentile: False + - name: test_agg_params_optional_are_empty + expr: order_total + agg: percentile + agg_time_dimension: ds + agg_params: + percentile: 0.99 + - name: test_non_additive + expr: txn_revenue + agg: sum + non_additive_dimension: + name: ds + window_choice: max + + dimensions: + - name: ds + type: time + expr: created_at + type_params: + time_granularity: day + + entities: + - name: user + type: foreign + expr: user_id + - name: id + type: primary + +metrics: + - name: simple_metric + label: Simple Metric + type: simple + type_params: + measure: sum_of_things +""" + +schema_without_semantic_model_yml = """models: + - name: fct_revenue + description: This is the model fct_revenue. It should be able to use doc blocks +""" + +fct_revenue_sql = """select + 1 as id, + 10 as user_id, + 1000 as revenue, + current_timestamp as created_at""" + +metricflow_time_spine_sql = """ +with days as ( + {{dbt_utils.date_spine('day' + , "to_date('01/01/2000','mm/dd/yyyy')" + , "to_date('01/01/2027','mm/dd/yyyy')" + ) + }} +), + +final as ( + select cast(date_day as date) as date_day + from days +) + +select * +from final +""" diff --git a/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py b/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py index 083924fb428..7325a41da19 100644 --- a/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py +++ b/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py @@ -1,7 +1,11 @@ +import pytest +import copy + from dbt.contracts.graph.nodes import ( Metric, MetricInput, MetricInputMeasure, + MetricTimeWindow, MetricTypeParams, NodeRelation, SemanticModel, @@ -10,24 +14,24 @@ from dbt.contracts.graph.semantic_models import ( Dimension, DimensionTypeParams, + DimensionValidityParams, + Defaults, Entity, + FileSlice, Measure, + MeasureAggregationParameters, NonAdditiveDimension, + SourceFileMetadata, ) from dbt.node_types import NodeType from dbt_semantic_interfaces.protocols import ( - Dimension as DSIDimension, - Entity as DSIEntitiy, - Measure as DSIMeasure, - Metric as DSIMetric, - MetricInput as DSIMetricInput, - MetricInputMeasure as DSIMetricInputMeasure, - MetricTypeParams as DSIMetricTypeParams, - SemanticModel as DSISemanticModel, - WhereFilter as DSIWhereFilter, -) -from dbt_semantic_interfaces.protocols.measure import ( - NonAdditiveDimensionParameters as DSINonAdditiveDimensionParameters, + dimension as DimensionProtocols, + entity as EntityProtocols, + measure as MeasureProtocols, + metadata as MetadataProtocols, + metric as MetricProtocols, + semantic_model as SemanticModelProtocols, + WhereFilter as WhereFilterProtocol, ) from dbt_semantic_interfaces.type_enums import ( AggregationType, @@ -40,59 +44,262 @@ @runtime_checkable -class RuntimeCheckableSemanticModel(DSISemanticModel, Protocol): +class RuntimeCheckableSemanticModel(SemanticModelProtocols.SemanticModel, Protocol): + pass + + +@runtime_checkable +class RuntimeCheckableDimension(DimensionProtocols.Dimension, Protocol): + pass + + +@runtime_checkable +class RuntimeCheckableEntity(EntityProtocols.Entity, Protocol): + pass + + +@runtime_checkable +class RuntimeCheckableMeasure(MeasureProtocols.Measure, Protocol): + pass + + +@runtime_checkable +class RuntimeCheckableMetric(MetricProtocols.Metric, Protocol): pass @runtime_checkable -class RuntimeCheckableDimension(DSIDimension, Protocol): +class RuntimeCheckableMetricInput(MetricProtocols.MetricInput, Protocol): pass @runtime_checkable -class RuntimeCheckableEntity(DSIEntitiy, Protocol): +class RuntimeCheckableMetricInputMeasure(MetricProtocols.MetricInputMeasure, Protocol): pass @runtime_checkable -class RuntimeCheckableMeasure(DSIMeasure, Protocol): +class RuntimeCheckableMetricTypeParams(MetricProtocols.MetricTypeParams, Protocol): pass @runtime_checkable -class RuntimeCheckableMetric(DSIMetric, Protocol): +class RuntimeCheckableWhereFilter(WhereFilterProtocol, Protocol): pass @runtime_checkable -class RuntimeCheckableMetricInput(DSIMetricInput, Protocol): +class RuntimeCheckableNonAdditiveDimension( + MeasureProtocols.NonAdditiveDimensionParameters, Protocol +): pass @runtime_checkable -class RuntimeCheckableMetricInputMeasure(DSIMetricInputMeasure, Protocol): +class RuntimeCheckableFileSlice(MetadataProtocols.FileSlice, Protocol): pass @runtime_checkable -class RuntimeCheckableMetricTypeParams(DSIMetricTypeParams, Protocol): +class RuntimeCheckableSourceFileMetadata(MetadataProtocols.Metadata, Protocol): pass @runtime_checkable -class RuntimeCheckableWhereFilter(DSIWhereFilter, Protocol): +class RuntimeCheckableSemanticModelDefaults( + SemanticModelProtocols.SemanticModelDefaults, Protocol +): pass @runtime_checkable -class RuntimeCheckableNonAdditiveDimension(DSINonAdditiveDimensionParameters, Protocol): +class RuntimeCheckableDimensionValidityParams( + DimensionProtocols.DimensionValidityParams, Protocol +): pass -def test_semantic_model_node_satisfies_protocol(): +@runtime_checkable +class RuntimeCheckableDimensionTypeParams(DimensionProtocols.DimensionTypeParams, Protocol): + pass + + +@runtime_checkable +class RuntimeCheckableMeasureAggregationParams( + MeasureProtocols.MeasureAggregationParameters, Protocol +): + pass + + +@runtime_checkable +class RuntimeCheckableMetricTimeWindow(MetricProtocols.MetricTimeWindow, Protocol): + pass + + +@pytest.fixture(scope="session") +def file_slice() -> FileSlice: + return FileSlice( + filename="test_filename", content="test content", start_line_number=0, end_line_number=1 + ) + + +@pytest.fixture(scope="session") +def source_file_metadata(file_slice) -> SourceFileMetadata: + return SourceFileMetadata( + repo_file_path="test/file/path.yml", + file_slice=file_slice, + ) + + +@pytest.fixture(scope="session") +def semantic_model_defaults() -> Defaults: + return Defaults(agg_time_dimension="test_time_dimension") + + +@pytest.fixture(scope="session") +def dimension_validity_params() -> DimensionValidityParams: + return DimensionValidityParams() + + +@pytest.fixture(scope="session") +def dimension_type_params() -> DimensionTypeParams: + return DimensionTypeParams(time_granularity=TimeGranularity.DAY) + + +@pytest.fixture(scope="session") +def measure_agg_params() -> MeasureAggregationParameters: + return MeasureAggregationParameters() + + +@pytest.fixture(scope="session") +def non_additive_dimension() -> NonAdditiveDimension: + return NonAdditiveDimension( + name="dimension_name", + window_choice=AggregationType.MIN, + window_groupings=["entity_name"], + ) + + +@pytest.fixture(scope="session") +def where_filter() -> WhereFilter: + return WhereFilter( + where_sql_template="{{ Dimension('enity_name__dimension_name') }} AND {{ TimeDimension('entity_name__time_dimension_name', 'month') }} AND {{ Entity('entity_name') }}" + ) + + +@pytest.fixture(scope="session") +def metric_time_window() -> MetricTimeWindow: + return MetricTimeWindow(count=1, granularity=TimeGranularity.DAY) + + +@pytest.fixture(scope="session") +def simple_metric_input() -> MetricInput: + return MetricInput(name="test_simple_metric_input") + + +@pytest.fixture(scope="session") +def complex_metric_input(metric_time_window, where_filter) -> MetricInput: + return MetricInput( + name="test_complex_metric_input", + filter=where_filter, + alias="aliased_metric_input", + offset_window=metric_time_window, + offset_to_grain=TimeGranularity.DAY, + ) + + +@pytest.fixture(scope="session") +def simple_metric_input_measure() -> MetricInputMeasure: + return MetricInputMeasure(name="test_simple_metric_input_measure") + + +@pytest.fixture(scope="session") +def complex_metric_input_measure(where_filter) -> MetricInputMeasure: + return MetricInputMeasure( + name="test_complex_metric_input_measure", filter=where_filter, alias="complex_alias" + ) + + +@pytest.fixture(scope="session") +def complex_metric_type_params( + metric_time_window, simple_metric_input, simple_metric_input_measure +) -> MetricTypeParams: + return MetricTypeParams( + measure=simple_metric_input_measure, + numerator=simple_metric_input, + denominator=simple_metric_input, + expr="1 = 1", + window=metric_time_window, + grain_to_date=TimeGranularity.DAY, + metrics=[simple_metric_input], + ) + + +def test_file_slice_obj_satisfies_protocol(file_slice): + assert isinstance(file_slice, RuntimeCheckableFileSlice) + + +def test_metadata_obj_satisfies_protocol(source_file_metadata): + assert isinstance(source_file_metadata, RuntimeCheckableSourceFileMetadata) + + +def test_defaults_obj_satisfies_protocol(semantic_model_defaults): + assert isinstance(semantic_model_defaults, RuntimeCheckableSemanticModelDefaults) + assert isinstance(Defaults(), RuntimeCheckableSemanticModelDefaults) + + +def test_dimension_validity_params_satisfies_protocol(dimension_validity_params): + assert isinstance(dimension_validity_params, RuntimeCheckableDimensionValidityParams) + + +def test_dimension_type_params_satisfies_protocol( + dimension_type_params, dimension_validity_params +): + assert isinstance(dimension_type_params, RuntimeCheckableDimensionTypeParams) + + # check with validity params specified + optionals_specified_type_params = copy.deepcopy(dimension_type_params) + optionals_specified_type_params.validity_params = dimension_validity_params + assert isinstance(optionals_specified_type_params, RuntimeCheckableDimensionTypeParams) + + +def test_measure_aggregation_params_satisfies_protocol(measure_agg_params): + assert isinstance(measure_agg_params, RuntimeCheckableMeasureAggregationParams) + + # check with optionals specified + optionals_specified_measure_agg_params = copy.deepcopy(measure_agg_params) + optionals_specified_measure_agg_params.percentile = 0.5 + assert isinstance( + optionals_specified_measure_agg_params, RuntimeCheckableMeasureAggregationParams + ) + + +def test_semantic_model_node_satisfies_protocol_optionals_unspecified(): + test_semantic_model = SemanticModel( + name="test_semantic_model", + resource_type=NodeType.SemanticModel, + package_name="package_name", + path="path.to.semantic_model", + original_file_path="path/to/file", + unique_id="not_like_the_other_semantic_models", + fqn=["fully", "qualified", "name"], + model="ref('a_model')", + # Technically NodeRelation is optional on our SemanticModel implementation + # however, it's functionally always loaded, it's just delayed. + # This will type/state mismatch will likely bite us at some point + node_relation=NodeRelation( + alias="test_alias", + schema_name="test_schema_name", + ), + ) + assert isinstance(test_semantic_model, RuntimeCheckableSemanticModel) + + +def test_semantic_model_node_satisfies_protocol_optionals_specified( + semantic_model_defaults, source_file_metadata +): test_semantic_model = SemanticModel( name="test_semantic_model", - description="a test semantic_model", resource_type=NodeType.SemanticModel, package_name="package_name", path="path.to.semantic_model", @@ -104,29 +311,51 @@ def test_semantic_model_node_satisfies_protocol(): alias="test_alias", schema_name="test_schema_name", ), - entities=[], - measures=[], - dimensions=[], + description="test_description", + label="test label", + defaults=semantic_model_defaults, + metadata=source_file_metadata, + primary_entity="test_primary_entity", ) assert isinstance(test_semantic_model, RuntimeCheckableSemanticModel) -def test_dimension_satisfies_protocol(): +def test_dimension_satisfies_protocol_optionals_unspecified(): dimension = Dimension( name="test_dimension", - description="a test dimension", type=DimensionType.TIME, - type_params=DimensionTypeParams( - time_granularity=TimeGranularity.DAY, - ), ) assert isinstance(dimension, RuntimeCheckableDimension) -def test_entity_satisfies_protocol(): +def test_dimension_satisfies_protocol_optionals_specified( + dimension_type_params, source_file_metadata +): + dimension = Dimension( + name="test_dimension", + type=DimensionType.TIME, + description="test_description", + label="test_label", + type_params=dimension_type_params, + expr="1", + metadata=source_file_metadata, + ) + assert isinstance(dimension, RuntimeCheckableDimension) + + +def test_entity_satisfies_protocol_optionals_unspecified(): + entity = Entity( + name="test_entity", + type=EntityType.PRIMARY, + ) + assert isinstance(entity, RuntimeCheckableEntity) + + +def test_entity_satisfies_protocol_optionals_specified(): entity = Entity( name="test_entity", description="a test entity", + label="A test entity", type=EntityType.PRIMARY, expr="id", role="a_role", @@ -134,19 +363,32 @@ def test_entity_satisfies_protocol(): assert isinstance(entity, RuntimeCheckableEntity) -def test_measure_satisfies_protocol(): +def test_measure_satisfies_protocol_optionals_unspecified(): + measure = Measure( + name="test_measure", + agg="sum", + ) + assert isinstance(measure, RuntimeCheckableMeasure) + + +def test_measure_satisfies_protocol_optionals_specified( + measure_agg_params, non_additive_dimension +): measure = Measure( name="test_measure", description="a test measure", + label="A test measure", agg="sum", create_metric=True, expr="amount", + agg_params=measure_agg_params, + non_additive_dimension=non_additive_dimension, agg_time_dimension="a_time_dimension", ) assert isinstance(measure, RuntimeCheckableMeasure) -def test_metric_node_satisfies_protocol(): +def test_metric_node_satisfies_protocol_optionals_unspecified(): metric = Metric( name="a_metric", resource_type=NodeType.Metric, @@ -167,32 +409,50 @@ def test_metric_node_satisfies_protocol(): assert isinstance(metric, RuntimeCheckableMetric) -def test_where_filter_satisfies_protocol(): - where_filter = WhereFilter( - where_sql_template="{{ Dimension('enity_name__dimension_name') }} AND {{ TimeDimension('entity_name__time_dimension_name', 'month') }} AND {{ Entity('entity_name') }}" +def test_metric_node_satisfies_protocol_optionals_specified( + complex_metric_type_params, source_file_metadata, where_filter +): + metric = Metric( + name="a_metric", + resource_type=NodeType.Metric, + package_name="package_name", + path="path.to.semantic_model", + original_file_path="path/to/file", + unique_id="not_like_the_other_semantic_models", + fqn=["fully", "qualified", "name"], + description="a test metric", + label="A test metric", + type=MetricType.SIMPLE, + type_params=complex_metric_type_params, + filter=where_filter, + metadata=source_file_metadata, + group="test_group", ) + assert isinstance(metric, RuntimeCheckableMetric) + + +def test_where_filter_satisfies_protocol(where_filter): assert isinstance(where_filter, RuntimeCheckableWhereFilter) -def test_metric_input(): - metric_input = MetricInput(name="a_metric_input") - assert isinstance(metric_input, RuntimeCheckableMetricInput) +def test_metric_time_window(metric_time_window): + assert isinstance(metric_time_window, RuntimeCheckableMetricTimeWindow) -def test_metric_input_measure(): - metric_input_measure = MetricInputMeasure(name="a_metric_input_measure") - assert isinstance(metric_input_measure, RuntimeCheckableMetricInputMeasure) +def test_metric_input(simple_metric_input, complex_metric_input): + assert isinstance(simple_metric_input, RuntimeCheckableMetricInput) + assert isinstance(complex_metric_input, RuntimeCheckableMetricInput) -def test_metric_type_params_satisfies_protocol(): - type_params = MetricTypeParams() - assert isinstance(type_params, RuntimeCheckableMetricTypeParams) +def test_metric_input_measure(simple_metric_input_measure, complex_metric_input_measure): + assert isinstance(simple_metric_input_measure, RuntimeCheckableMetricInputMeasure) + assert isinstance(complex_metric_input_measure, RuntimeCheckableMetricInputMeasure) -def test_non_additive_dimension_satisfies_protocol(): - non_additive_dimension = NonAdditiveDimension( - name="dimension_name", - window_choice=AggregationType.MIN, - window_groupings=["entity_name"], - ) +def test_metric_type_params_satisfies_protocol(complex_metric_type_params): + assert isinstance(MetricTypeParams(), RuntimeCheckableMetricTypeParams) + assert isinstance(complex_metric_type_params, RuntimeCheckableMetricTypeParams) + + +def test_non_additive_dimension_satisfies_protocol(non_additive_dimension): assert isinstance(non_additive_dimension, RuntimeCheckableNonAdditiveDimension)