# Development ## Clone the repository ```bash git clone git@gitlab.com:ska-telescope/src/src-ms/ska-src-ms-system-testing-suite.git ``` ## Install dependencies ```bash 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 .env` - Edit `.env` as 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: ```python # 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. ```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: ```gherkin Scenario Outline: Replicate file between RSEs Given a data file exists at location for destination RSE with id and parent node 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 , , Node 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: ```python # Implement step definitions @given(parsers.cfparse("Given a data file exists at location for destination RSE with id and parent node ")) 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. ```python @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 ```python @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 ```python @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.cfparse` for parameterized steps - Handle 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