Skip to content

Commit e73f535

Browse files
authored
Add SAT test for no duplicates in enum properties (#19243)
* Make pytest aware of backward compatibility mark * Unit tests for duplicate values in enum properties * Add docs about airbyte enforcements to enum usage * Add docs info to test
1 parent e20773d commit e73f535

File tree

4 files changed

+85
-2
lines changed

4 files changed

+85
-2
lines changed

airbyte-integrations/bases/source-acceptance-test/pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ testpaths =
77
markers =
88
default_timeout
99
slow: marks tests as slow (deselect with '-m "not slow"')
10+
backward_compatibility

airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,18 @@ def test_docker_env(self, actual_connector_spec: ConnectorSpecification, docker_
9595
docker_runner.entry_point
9696
), "env should be equal to space-joined entrypoint"
9797

98+
def test_enum_usage(self, actual_connector_spec: ConnectorSpecification):
99+
"""Check that enum lists in specs contain distinct values."""
100+
docs_url = "https://docs.airbyte.io/connector-development/connector-specification-reference"
101+
docs_msg = f"See specification reference at {docs_url}."
102+
103+
schema_helper = JsonSchemaHelper(actual_connector_spec.connectionSpecification)
104+
enum_paths = schema_helper.find_nodes(keys=["enum"])
105+
106+
for path in enum_paths:
107+
enum_list = schema_helper.get_node(path)
108+
assert len(set(enum_list)) == len(enum_list), f"Enum lists should not contain duplicate values. Misconfigured enum array: {enum_list}. {docs_msg}"
109+
98110
def test_oneof_usage(self, actual_connector_spec: ConnectorSpecification):
99111
"""Check that if spec contains oneOf it follows the rules according to reference
100112
https://docs.airbyte.io/connector-development/connector-specification-reference

airbyte-integrations/bases/source-acceptance-test/unit_tests/test_spec.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,49 @@ def test_oneof_usage(connector_spec, should_fail):
350350
else:
351351
t.test_oneof_usage(actual_connector_spec=ConnectorSpecification(connectionSpecification=connector_spec))
352352

353+
@parametrize_test_case(
354+
{
355+
"test_id": "successful",
356+
"connector_spec": {
357+
"type": "object",
358+
"properties": {
359+
"property_with_options": {
360+
"title": "Property with options",
361+
"description": "A property in the form of an enumerated list",
362+
"type": "string",
363+
"default": "Option 1",
364+
"enum": ["Option 1", "Option 2", "Option 3"],
365+
}
366+
},
367+
},
368+
"should_fail": False,
369+
},
370+
{
371+
"test_id": "duplicate_values",
372+
"connector_spec": {
373+
"type": "object",
374+
"properties": {
375+
"property_with_options": {
376+
"title": "Property with options",
377+
"description": "A property in the form of an enumerated list",
378+
"type": "string",
379+
"default": "Option 1",
380+
"enum": ["Option 1", "Option 2", "Option 3", "Option 2"],
381+
}
382+
},
383+
},
384+
"should_fail": True,
385+
},
386+
)
387+
def test_enum_usage(connector_spec, should_fail):
388+
t = _TestSpec()
389+
if should_fail is True:
390+
with pytest.raises(AssertionError):
391+
t.test_enum_usage(actual_connector_spec=ConnectorSpecification(connectionSpecification=connector_spec))
392+
else:
393+
t.test_enum_usage(actual_connector_spec=ConnectorSpecification(connectionSpecification=connector_spec))
394+
395+
353396

354397
@pytest.mark.parametrize(
355398
"connector_spec, expected_error",

docs/connector-development/connector-specification-reference.md

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,14 @@ In this case, use the `"airbyte_hidden": true` keyword to hide that field from t
101101
}
102102
```
103103

104+
Results in the following form:
105+
104106
![hidden fields](../.gitbook/assets/spec_reference_hidden_field_screenshot.png)
105107

106-
Results in the following form:
107108

109+
## Airbyte Modifications to `jsonschema`
108110

109-
### Using `oneOf`s
111+
### Using `oneOf`
110112

111113
In some cases, a connector needs to accept one out of many options. For example, a connector might need to know the compression codec of the file it will read, which will render in the Airbyte UI as a list of the available codecs. In JSONSchema, this can be expressed using the [oneOf](https://json-schema.org/understanding-json-schema/reference/combining.html#oneof) keyword.
112114

@@ -183,3 +185,28 @@ In each item in the `oneOf` array, the `option_title` string field exists with t
183185
}
184186
```
185187
188+
### Using `enum`
189+
190+
In regular `jsonschema`, some drafts enforce that `enum` lists must contain distinct values, while others do not. For consistency, Airbyte enforces this restriction.
191+
192+
For example, this spec is invalid, since `a_format` is listed twice under the enumerated property `format`:
193+
194+
```javascript
195+
{
196+
"connection_specification": {
197+
"$schema": "http://json-schema.org/draft-07/schema#",
198+
"title": "File Source Spec",
199+
"type": "object",
200+
"required": ["format"],
201+
"properties": {
202+
"dataset_name": {
203+
...
204+
},
205+
"format": {
206+
type: "string",
207+
enum: ["a_format", "another_format", "a_format"]
208+
},
209+
}
210+
}
211+
}
212+
```

0 commit comments

Comments
 (0)