|
| 1 | +--- |
| 2 | +title: SCS Conformance Test Implementation Guide |
| 3 | +type: |
| 4 | +status: Draft |
| 5 | +track: Global |
| 6 | +--- |
| 7 | + |
| 8 | +SovereignCloudStack (SCS) uses [conformance tests][gh-scripts] to certify |
| 9 | +compliance of a given cloud offering with respect to a given [certificate |
| 10 | +scope][cert-scopes] such as *SCS Compatible IaaS v4*. |
| 11 | +Our aim is that these tests are reliable, consistent and comprehensible for |
| 12 | +the users. |
| 13 | +This document is a guideline for conformance test authors and summarizes the |
| 14 | +current best practices to achieve this goal. |
| 15 | + |
| 16 | +## 1. Mapping of RFC2119 Keywords |
| 17 | + |
| 18 | +Test authors working on new and existing conformance tests for an SCS standard |
| 19 | +must implement the keywords such as MUST and SHOULD according to the [SCS |
| 20 | +RFC2119 Keyword Test Guide][scs-rfc2119-guide]. |
| 21 | + |
| 22 | +## 2. Unit and Regression Tests |
| 23 | + |
| 24 | +Test authors are *strongly* encouraged to include unit and regression tests for |
| 25 | +the conformance test's logic. |
| 26 | +Conformance tests will inevitably contain some non-trivial algorithms, be it for |
| 27 | +parsing flavor names or evaluating CVE vulnerability reports. |
| 28 | +Testing them automatically and regularly against valid and invalid inputs helps |
| 29 | +test authors to find programming mistakes early on. |
| 30 | +As a bonus, a well written unit test also makes it easier for reviewers to cross |
| 31 | +check a given pull request with new, enhanced or refactored conformance tests. |
| 32 | + |
| 33 | +In general, unit and regression tests for the conformance tests are located in |
| 34 | +the same location as the conformance tests, that is, the `Tests/` directory of |
| 35 | +the [SCS standards repository][scs-standards]. |
| 36 | +Setup and development of the unit tests is described in the [SCS conformance |
| 37 | +tests README][tests-readme]. |
| 38 | + |
| 39 | +### Naming Conventions |
| 40 | + |
| 41 | +Any module that ends in `_test` will be picked up as a unit test module by |
| 42 | +pytest in our CI pipeline (in other words, all files matched by the pattern |
| 43 | +`Tests/**/*_test.py`). |
| 44 | +Pytest will execute any function prefixed with `test_` as a test function, or, |
| 45 | +alternatively, tests based on the `unittest` module from the Python standard |
| 46 | +library. |
| 47 | + |
| 48 | +Follow these naming rules: |
| 49 | + |
| 50 | +* The name of a unit test module is constructed from the module to be unit |
| 51 | + tested and the `_test` suffix. For example, unit tests for `flavor_names.py` |
| 52 | + should reside in `flavor_names_test.py`. |
| 53 | +* The name of a Python module in general should be a valid [Python |
| 54 | + identifier][python-identifiers], to allow imports via the `import` statement. |
| 55 | + For example, use `flavor_names.py` instead of `flavor-names.py`. |
| 56 | + |
| 57 | +### Write Testable Conformance Tests |
| 58 | + |
| 59 | +Software design fills complete book shelves and this section can only cover some |
| 60 | +basic best practices. |
| 61 | +As a general rule of thumb, the earlier you begin writing unit tests, the better |
| 62 | +– it will force you to write modules that are easy to test. |
| 63 | + |
| 64 | +Divide conformance tests scripts into smaller, loosely coupled units, i.e., |
| 65 | +functions that serve one purpose each. |
| 66 | +Use the [*dependency injection*][wiki-di] technique, i.e., pass externally |
| 67 | +retrieved data as function arguments instead of hardcoding the calls to the |
| 68 | +retrieval functions inside a function. |
| 69 | +Avoid logging calls deep down in the call hierarchy and use explicit return |
| 70 | +values or raise exceptions, instead. |
| 71 | +Here is an abstract example of a conformance test which does exactly that: |
| 72 | + |
| 73 | +```python |
| 74 | +# property_compliance.py |
| 75 | + |
| 76 | +def retrieve(location): |
| 77 | + """Retrieve raw data via network.""" |
| 78 | + ... |
| 79 | + |
| 80 | +def parse(data): |
| 81 | + """Parse raw data and return a dict.""" |
| 82 | + ... |
| 83 | + |
| 84 | +def handle(...): |
| 85 | + """Evaluate parsed data.""" |
| 86 | + ... |
| 87 | + |
| 88 | +def main(args): |
| 89 | + ... |
| 90 | + data = retrieve(location) |
| 91 | + parsed = parse(data) |
| 92 | + result = handle(parsed) |
| 93 | + ... |
| 94 | + if result.some_prop != expected_value: |
| 95 | + logging.warn("some_prop is not as expected: %s (vs. %s)", result.some_prop, expected_value) |
| 96 | + ... |
| 97 | + return result.success |
| 98 | + |
| 99 | +if __name__ == "__main__": |
| 100 | + # using sys.exit(…) only here makes it possible to unit test main(…) |
| 101 | + sys.exit(main(sys.argv)) |
| 102 | +``` |
| 103 | + |
| 104 | +Adhering to this style makes it easier to test an algorithm in isolation, |
| 105 | +without actually making a call to some external service. |
| 106 | + |
| 107 | +### Pytest Test Example |
| 108 | + |
| 109 | +Assuming we want to unit test some members of the module `property_compliance` |
| 110 | +from the previous section, we would create a file `property_compliance_test.py` |
| 111 | +with the following content as a starting point: |
| 112 | + |
| 113 | +```python |
| 114 | +"""Unit tests for property_compliance. |
| 115 | +
|
| 116 | +(c) Your Name <[email protected]>, 4/2024 |
| 117 | +SPDX-License-Identifier: CC-BY-SA-4.0 |
| 118 | +""" |
| 119 | + |
| 120 | +import pytest |
| 121 | + |
| 122 | +from property_compliance import parse |
| 123 | + |
| 124 | + |
| 125 | +def test_success(): |
| 126 | + assert parse("some valid input") == "expected output" |
| 127 | + |
| 128 | + |
| 129 | +def test_failure(): |
| 130 | + with pytest.raises(ValueError): |
| 131 | + parse("invalid input") |
| 132 | +``` |
| 133 | + |
| 134 | +## 3. Conformance Tests Shouldn't Require Admin Privileges |
| 135 | + |
| 136 | +Conformance tests are expected to be executable without admin privileges (see §2 of |
| 137 | +[Regulations for achieving SCS-compatible certification][scs-0004-v1]). |
| 138 | +In particular, this means: |
| 139 | + |
| 140 | +* The cloud credentials (e.g., kubeconfig and OpenStack `clouds.yaml`) passed to |
| 141 | + the scripts are non-admin credentials. |
| 142 | +* Conformance tests scripts should not require root privileges, except for the |
| 143 | + installation of operating system prerequisites (e.g., a Python interpreter). |
| 144 | + |
| 145 | +[scs-standards]: https://github.com/SovereignCloudStack/standards/ |
| 146 | +[tests-readme]: https://github.com/SovereignCloudStack/standards/blob/main/Tests/README.md |
| 147 | +[python-identifiers]: https://docs.python.org/3/reference/lexical_analysis.html#identifiers |
| 148 | +[gh-scripts]: https://github.com/SovereignCloudStack/standards/tree/main/Tests |
| 149 | +[cert-scopes]: https://docs.scs.community/standards/certification/scopes-versions |
| 150 | +[scs-rfc2119-guide]: https://docs.scs.community/contributor-docs/operations/tests/rfc2119-keyword-test-guide |
| 151 | +[scs-0004-v1]: https://github.com/SovereignCloudStack/standards/blob/main/Standards/scs-0004-v1-achieving-certification.md |
| 152 | +[wiki-di]: https://en.wikipedia.org/wiki/Dependency_injection |
0 commit comments