bmc_hub/tests/test_workflow_api.py

114 lines
5.1 KiB
Python
Raw Permalink Normal View History

from contextlib import contextmanager
from copy import deepcopy
import pytest
from fastapi import FastAPI
from fastapi.testclient import TestClient
from app.workflows import router as workflow_api
DEFINITION = {'trigger': 'time.created', 'branches': [{'condition': {'field': 'description', 'operator': 'contains', 'value': 'Excel'}, 'actions': [{'type': 'set_billing', 'method': 'subscription'}]}], 'metadata': {'name': 'Excel', 'priority': 10, 'customer_id': '24'}}
@pytest.fixture
def api(monkeypatch):
statements = []
workflow = {'id': 1, 'name': 'Excel', 'customer_id': '24', 'priority': 10, 'enabled': False, 'active_version_id': None}
version = {'id': 7, 'workflow_id': 1, 'version': 1, 'definition': deepcopy(DEFINITION)}
class Cursor:
def execute(self, sql, params=None):
statements.append((sql, params))
self.sql = sql
def fetchone(self):
if 'bmc_workflow_versions' in self.sql:
return deepcopy(version)
return deepcopy(workflow)
def fetchall(self):
return []
@contextmanager
def transaction():
yield Cursor()
monkeypatch.setattr(workflow_api.service, 'transaction', transaction)
app = FastAPI()
app.include_router(workflow_api.router, prefix='/api/v1')
for dep in (workflow_api.view_access, workflow_api.manage_access, workflow_api.activate_access):
app.dependency_overrides[dep] = lambda: {'id': 4}
return TestClient(app), statements, version
def test_simulator_is_read_only_and_returns_trace(api):
client, statements, _ = api
result = client.post('/api/v1/workflows/1/simulate', json={'version_id': 7, 'context': {'customer_id': '24', 'description': 'Excel', 'agreements': [{'id': 3}]}})
assert result.status_code == 200
assert result.json()['result']['method'] == 'subscription'
assert result.json()['result']['trace'][0]['matched'] is True
assert all(sql.startswith('SELECT') for sql, _ in statements)
assert result.json()['simulation_receipt']
def test_activation_requires_valid_simulation_receipt(api):
client, statements, _ = api
response = client.post('/api/v1/workflows/1/activate', json={'version_id': 7, 'simulation_receipt': 'forged'})
assert response.status_code == 409
assert all(sql.startswith('SELECT') for sql, _ in statements)
def test_tested_version_activation_never_updates_definition(api):
client, statements, _ = api
simulation = client.post('/api/v1/workflows/1/simulate', json={'version_id': 7, 'context': {}}).json()
response = client.post('/api/v1/workflows/1/activate', json={'version_id': 7, 'simulation_receipt': simulation['simulation_receipt']})
assert response.status_code == 200
assert any(sql.startswith('INSERT INTO bmc_workflow_activation_history') for sql, _ in statements)
assert not any(sql.startswith('UPDATE bmc_workflow_versions') for sql, _ in statements)
def test_receipt_bound_to_immutable_definition_and_actor(api):
_, _, version = api
token = workflow_api._receipt(1, version, 4)
with pytest.raises(Exception) as changed_actor:
workflow_api._verify_receipt(token, 1, version, 5)
assert changed_actor.value.status_code == 409
version['definition']['branches'][0]['actions'][0]['method'] = 'invoice'
with pytest.raises(Exception) as changed_definition:
workflow_api._verify_receipt(token, 1, version, 4)
assert changed_definition.value.status_code == 409
def test_invalid_definition_rejected_before_writes(api):
client, statements, _ = api
result = client.post('/api/v1/workflows', json={'name': 'SQL', 'definition': {'trigger': 'time.created', 'branches': [{'condition': {'field': 'password', 'operator': 'eq', 'value': 'secret'}, 'actions': [{'type': 'set_billing', 'method': 'invoice'}]}]}})
assert result.status_code == 422
assert not statements
def test_unauthenticated_administration_rejected():
app = FastAPI()
app.include_router(workflow_api.router)
client = TestClient(app)
assert client.post('/workflows/1/disable').status_code == 401
assert client.get('/workflows/catalog').status_code == 401
def test_simulator_requires_both_entity_reference_fields(api):
client, statements, _ = api
assert client.post('/api/v1/workflows/1/simulate', json={'entity_type': 'case'}).status_code == 422
assert not statements
def test_receipt_expires(api, monkeypatch):
_, _, version = api
monkeypatch.setattr(workflow_api.time, 'time', lambda: 100)
token = workflow_api._receipt(1, version, 4)
monkeypatch.setattr(workflow_api.time, 'time', lambda: 3701)
with pytest.raises(Exception) as exc:
workflow_api._verify_receipt(token, 1, version, 4)
assert exc.value.status_code == 409
def test_invalid_action_cannot_activate(api):
client, statements, version = api
version['definition']['branches'][0]['actions'] = [{'type': 'notify', 'message': 'Test'}]
receipt = workflow_api._receipt(1, version, 4)
response = client.post('/api/v1/workflows/1/activate', json={'version_id': 7, 'simulation_receipt': receipt})
assert response.status_code == 422
assert all(sql.startswith('SELECT') for sql, _ in statements)