Development
Clone the repository
git clone git@gitlab.com:ska-telescope/src/src-ms/ska-src-ms-system-testing-suite.git
Install dependencies
poetry install
Run the application
The application can be run locally using Docker Compose, which will set up the necessary environment for testing.
Configure environment:
cp .env.template .envEdit
.envas needed
Run tests:
docker-compose up --build
Adding new tests
This repository uses pytest-bdd to describe end-to-end flows in Gherkin feature files and implement them with Python step definitions. Below is a concise guide to add a new scenario like the Data Replication Flow example you provided.
Folder Structure
Place feature files under: tests/feature/
You can group by domain, e.g., tests/feature/replication/
Name feature files with .feature suffix, e.g., data_replication_flow.feature
Implement step definitions in Python files under tests/, typically named test__steps.py
Ensure your step file calls scenarios(…) to auto-load feature files
Example step file discovery snippet:
# tests/test_replication_flow_steps.py
import glob
import os
from pytest_bdd import scenarios
feature_dir = os.path.join(os.path.dirname(__file__), "feature")
feature_files = glob.glob(os.path.join(feature_dir, "*.feature"))
for feature_file in feature_files:
scenarios(feature_file)
Writing Feature Files
Create a .feature file describing your business flow in Gherkin.
# tests/feature/data_replication_flow.feature
Feature: Data Replication Flow
Scenario: Replicate file to another RSE and verify its status
Given a data file exists at a known RSE location
When user locate the file using the namespace and file name
Then the system should return the current storage location of the file
When user replicate the file to another RSE with a specified lifetime
Then the replication job should be accepted and return a job ID
When user poll the status of the replication job using the job ID
Then the system should confirm the job is in progress or completed
Tips:
Use clear, imperative language in Given/When/Then.
Prefer deterministic steps; avoid environment-specific assumptions unless they are provided via configuration or Examples tables.
For multiple inputs, consider Scenario Outline with Examples:
Scenario Outline: Replicate file between RSEs
Given a data file <file_name> exists at <SOURCE_RSE> location for destination RSE <RSE_NAME> with id <RSE_ID> and parent node <PARENT_NODE_NAME>
When user locate the file using the namespace and file name
Then the system should return the current storage location of the file
When user replicate the file to <RSE_NAME> , <RSE_ID> , Node <PARENT_NODE_NAME> with a specified lifetime
Then the replication job should be accepted and return a job ID
When user poll the status of the replication job using the job ID
Then the system should confirm the job is in progress or completed
Examples:
| file_name | SOURCE_RSE | RSE_NAME | RSE_ID | PARENT_NODE_NAME |
| sample1 | SRC_A | DST_A | 00000000-0000-0000-0000-000000000001 | node-01 |
Note:
You can generate specific feature files from feature template by creating a script that populates the Examples table with the data you need. Automate the data preparation first—have the script produce all required example rows—then use that output to programmatically write the corresponding .feature files, ensuring each file includes the correctly filled Examples section.
Implementing Step Definitions
Create or extend a step file that binds Gherkin sentences to Python functions.
Use pytest-bdd decorators: @given, @when, @then
Parse dynamic values with parsers.cfparse and named placeholders matching your scenario
Share state across steps with a context fixture (e.g., a SimpleNamespace or namedtuple)
Example step definitions:
# Implement step definitions
@given(parsers.cfparse("Given a data file <file_name> exists at <SOURCE_RSE> location for destination RSE <RSE_NAME> with id <RSE_ID> and parent node <PARENT_NODE_NAME>"))
def file_exists(api_request_context_factory, iam, context, request, file_name, SOURCE_RSE, RSE_NAME, RSE_ID, PARENT_NODE_NAME):
# Implementation similar to existing step definition
pass
# Add other required step definitions...
Notes:
The placeholders in parsers.cfparse must match those in your .feature steps.
Use your project’s existing fixtures/utilities for API clients, configuration, and assertions.
Creating the context fixture
The context fixture allows you to share state between steps in your BDD scenarios. It creates a namespace for storing and accessing data across multiple test steps.
@pytest.fixture
def context():
"""Create test context"""
return namedtuple("TestContext", ["api_ctx", "file_name", "job_id", "responses"])
Using the context fixture
Setting values in the context
@given("a data file exists at a known RSE location")
def file_exists(api_request_context_factory, iam, context):
# Create API context with authentication
exchange_token = iam.exchange_token(fetch_user_token, "data-management-api").json()
context.api_ctx = api_request_context_factory(service_endpoint, exchange_token["access_token"])
# Set file information
context.file_name = "test_file.dat"
Accessing values in the context
@when("user locate the file using the namespace and file name")
def locate_file(context):
"""Locate file using API"""
# Access previously stored values
api_ctx = context.api_ctx
file_name = context.file_name
# Store new response in context
context.located_response = api_ctx.get(f"data/locate/{namespace}/{file_name}")
Notes:
Define all expected attributes in the namedtuple
Use descriptive attribute names
Set values as early as possible in the test flow
Use context only for data that needs to be shared between steps
Reusing the fixture
Common fixtures (API clients, configuration, IAM helpers, custom assertions) should be provided by tests/conftest.py or shared utility modules under tests/resources/.
Prefer project-provided assertion helpers for consistent reporting.
Store transient state in the context fixture to pass data between steps.
Scenario Outlines and data-driven tests
Use Scenario Outline with an Examples table when the same flow must be validated across multiple inputs.
Keep the number of examples reasonable to keep runs fast.
If inputs are environment-specific, drive them from configuration or CI variables instead of hardcoding.
Best Practices
Keep feature files focused on business scenarios
Use
parsers.cfparsefor parameterized stepsHandle API responses consistently with helper functions
Include appropriate assertions in “Then” steps
Use descriptive scenario and step names
Add proper error handling and timeouts for polling operations