Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions base_operations.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3348,6 +3348,12 @@ paths:
link_rows: {}
summaries: {}
colors: {}
"400":
description: A view with this name already exists in the table.
content:
application/json:
example:
error_msg: params invalid
/api-gateway/api/v2/dtables/{base_uuid}/views/{view_name}/:
get:
tags:
Expand Down Expand Up @@ -3549,6 +3555,12 @@ paths:
link_rows: {}
summaries: {}
colors: {}
"400":
description: The target view name already exists in the table.
content:
application/json:
example:
error_msg: params invalid
delete:
tags:
- Views
Expand Down Expand Up @@ -3702,6 +3714,11 @@ paths:
conditions: []
permission_type: ""
permitted_users: []
"400":
description: A column with this name already exists in the table.
content:
application/json:
example: Column ColA exists.
put:
tags:
- Columns
Expand Down Expand Up @@ -3773,6 +3790,13 @@ paths:
data: null
permission_type: ""
permitted_users: []
"400":
description: A column with this name already exists in the table.
content:
application/json:
example:
error_type: column_already_exists
error_message: column name already exists
delete:
tags:
- Columns
Expand Down
64 changes: 64 additions & 0 deletions tests/test_columns.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,3 +378,67 @@ def test_deleteColumn(base: Base):
column_names = [c['name'] for c in response.json()['columns']]
assert 'keep' in column_names
assert 'delete-me' not in column_names


def test_insertColumn_duplicate_name_returns_400(base: Base):
table_name = 'test_insertColumn_duplicate'
create_table(base, table_name, [{'column_name': 'existing', 'column_type': 'text'}])

path_parameters = {'base_uuid': base.uuid}
headers = {'Authorization': f'Bearer {base.token}'}
body = {'table_name': table_name, 'column_name': 'existing', 'column_type': 'text'}

# Inserting a column whose name collides with an existing column is rejected
case: Case = base_operations_schema.find_operation_by_id('insertColumn') \
.Case(path_parameters=path_parameters, body=body, headers=headers)
response = case.call()

assert response.status_code == 400
# application/json is also enforced by the conformance hook against the documented 400
assert response.headers['content-type'][0].startswith('application/json')


@pytest.mark.xfail(
reason="insertColumn returns the plain-text body 'Column ColA exists.' with an "
"application/json content-type on a duplicate-column 400 (not valid JSON)",
)
def test_insertColumn_duplicate_name_returns_json(base: Base):
table_name = 'test_insertColumn_duplicate_json'
create_table(base, table_name, [{'column_name': 'existing', 'column_type': 'text'}])

path_parameters = {'base_uuid': base.uuid}
headers = {'Authorization': f'Bearer {base.token}'}
body = {'table_name': table_name, 'column_name': 'existing', 'column_type': 'text'}

case: Case = base_operations_schema.find_operation_by_id('insertColumn') \
.Case(path_parameters=path_parameters, body=body, headers=headers)
response = case.call()

assert response.status_code == 400
# The body should be valid JSON; currently raises JSONDecodeError (plain text)
response.json()


def test_updateColumn_rename_collision_returns_400(base: Base):
table_name = 'test_updateColumn_collision'
create_table(base, table_name, [
{'column_name': 'col-a', 'column_type': 'text'},
{'column_name': 'col-b', 'column_type': 'text'},
])

path_parameters = {'base_uuid': base.uuid}
headers = {'Authorization': f'Bearer {base.token}'}
body = {
'op_type': 'rename_column',
'table_name': table_name,
'column': 'col-b',
'new_column_name': 'col-a',
}

# Renaming a column to collide with an existing column name is rejected
case: Case = base_operations_schema.find_operation_by_id('updateColumn') \
.Case(path_parameters=path_parameters, body=body, headers=headers)
response = case.call()

assert response.status_code == 400
assert response.headers['content-type'][0].startswith('application/json')
51 changes: 51 additions & 0 deletions tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,3 +153,54 @@ def test_deleteView(base: Base):
assert response.status_code == 200
view_names = [v['name'] for v in response.json()['views']]
assert 'View To Delete' not in view_names


def test_createView_duplicate_name_returns_400(base: Base):
table_name = 'test_createView_duplicate'
create_table(base, table_name, SIMPLE_COLUMNS)

path_parameters = {'base_uuid': base.uuid}
query = {'table_name': table_name}
headers = {'Authorization': f'Bearer {base.token}'}
body = {'name': 'Collision View', 'type': 'table'}

# Create the view once
case: Case = base_operations_schema.find_operation_by_id('createView') \
.Case(path_parameters=path_parameters, query=query, body=body, headers=headers)
response = case.call()
assert response.status_code == 200

# Creating a second view with the same name is rejected
case = base_operations_schema.find_operation_by_id('createView') \
.Case(path_parameters=path_parameters, query=query, body=body, headers=headers)
response = case.call()

assert response.status_code == 400
# application/json is also enforced by the conformance hook against the documented 400
assert response.headers['content-type'][0].startswith('application/json')


def test_updateView_rename_collision_returns_400(base: Base):
table_name = 'test_updateView_collision'
create_table(base, table_name, SIMPLE_COLUMNS)

path_parameters = {'base_uuid': base.uuid}
query = {'table_name': table_name}
headers = {'Authorization': f'Bearer {base.token}'}

# Create a second view (the table already has a "Default View")
body = {'name': 'Second View', 'type': 'table'}
case: Case = base_operations_schema.find_operation_by_id('createView') \
.Case(path_parameters=path_parameters, query=query, body=body, headers=headers)
response = case.call()
assert response.status_code == 200

# Renaming it to collide with the existing "Default View" is rejected
rename_path = {'base_uuid': base.uuid, 'view_name': 'Second View'}
rename_body = {'name': 'Default View'}
case = base_operations_schema.find_operation_by_id('updateView') \
.Case(path_parameters=rename_path, query=query, body=rename_body, headers=headers)
response = case.call()

assert response.status_code == 400
assert response.headers['content-type'][0].startswith('application/json')