Skip to content

Commit

Permalink
PSCE-263: tests: adds initial end to end test for rules transform (#65)
Browse files Browse the repository at this point in the history
* tests: adds initial end to end tests for rules transform

Adds a mock api to provide static responses for git client
Adds container images build and removal fixture
Adds initial command test case and response

Signed-off-by: Jennifer Power <[email protected]>

* chore: incorporates PR feedback

Signed-off-by: Jennifer Power <[email protected]>

---------

Signed-off-by: Jennifer Power <[email protected]>
  • Loading branch information
jpower432 committed Oct 23, 2023
1 parent 1c6e387 commit 83a0e59
Show file tree
Hide file tree
Showing 11 changed files with 453 additions and 148 deletions.
9 changes: 7 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,19 @@ For workflow diagrams, see the [diagrams](./docs/diagrams/) under the `docs` fol

### Format and Styling

```
```bash
make format
make lint
```

### Running tests
```
```bash
# Run all tests
make test
make test-slow

# Run specific tests
make test-e2e
```

### Run with poetry
Expand Down
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
PYMODULE := trestlebot
E2E := e2e
TESTS := tests

all: develop lint test
Expand Down Expand Up @@ -30,6 +31,14 @@ test:
@poetry run pytest --cov --cov-config=pyproject.toml --cov-report=xml
.PHONY: test

test-slow:
@poetry run pytest --slow --cov --cov-config=pyproject.toml --cov-report=xml
.PHONY: test-slow

test-e2e:
@poetry run pytest $(TESTS)/$(E2E) --slow --cov --cov-config=pyproject.toml --cov-report=xml
.PHONY: test-e2e

test-code-cov:
@poetry run pytest --cov=trestlebot --exitfirst --cov-config=pyproject.toml --cov-report=xml --cov-fail-under=80
.PHONY: test-code-cov
Expand Down
271 changes: 137 additions & 134 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
requires = ['poetry-core>=1.2.0', 'wheel',]
build-backend = 'poetry.core.masonry.api'


[tool.poetry]
name = 'trestlebot'
version = '0.1.0'
Expand All @@ -10,11 +11,13 @@ description = "trestle-bot assists users in leveraging Compliance-Trestle in aut
authors = ["Jennifer Power <[email protected]>",]

include = ['LICENSE']
exclude = ['tests/', 'docs/']
license = 'Apache-2.0'
readme = 'README.md'

repository = 'https://github.com/RedHatProductSecurity/trestle-bot'


[tool.poetry.scripts]
trestlebot-autosync = "trestlebot.entrypoints.autosync:main"
trestlebot-rules-transform = "trestlebot.entrypoints.rule_transform:main"
Expand All @@ -40,6 +43,7 @@ pre-commit = "^3.4.0"
[tool.poetry.group.tests.dependencies]
pytest = "^7.3.2"
pytest-cov = "^4.1.0"
pytest-skip-slow = "^0.0.5"

[tool.coverage.run]
branch = true
Expand Down
3 changes: 3 additions & 0 deletions tests/e2e/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
FROM docker.io/wiremock/wiremock:3.2.0-2

COPY mappings/ /home/wiremock/mappings/
47 changes: 47 additions & 0 deletions tests/e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# End-to-End Testing

End-to-end tests are used to verify the CLI functionality of trestle-bot from a user's perspective, running in a containerized environment.

## Prerequisites

Before running the end-to-end tests, ensure you have the following prerequisites installed:

- [Podman](https://podman.io/docs/installation) - Container management tool
- [Python 3](https://www.python.org/downloads/) - Required for test automation
- [Poetry](https://python-poetry.org/docs/#installation) - Dependency management

## Resources

- **`mappings`**: This directory contains JSON mappings used with WireMock to mock the Git server endpoints.
- **`play-kube.yml`**: This file includes Kubernetes resources for deploying the mock API server in a pod.
- **`Dockerfile`**: The Dockerfile used to build the mock server container image.

## Running the Tests

To run the end-to-end tests, follow these steps:

1. Clone the project repository:

```bash
git clone https://github.com/RedHatProductSecurity/trestle-bot.git
cd trestle-bot
```

2. Install the project dependencies:

```bash
poetry install --without dev --no-root
```

3. Run the tests:

```bash
make test-e2e
```

> **Note:** This should always be run from the root of the project directory.

## Additional Notes
- The WireMock tool is used to mock Git server endpoints for testing.
- Podman is used for container and pod management and to build the container image for the mock API server.
- In the future, we plan to provide an option to use pre-built trestle-bot container images from a registry instead of building them locally.
48 changes: 48 additions & 0 deletions tests/e2e/mappings/mapping.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"mappings": [
{
"request": {
"method": "GET",
"url": "/test.git/HEAD"
},
"response": {
"status": 200,
"body": "ref: refs/heads/test\n",
"headers": {
"Content-Type": "application/x-git-advertisement"
}
}
},
{
"request": {
"method": "GET",
"urlPattern": "/test.git/info/refs.*",
"queryParameters": {
"service": {
"equalTo": "git-receive-pack"
}
}
},
"response": {
"status": 200,
"body": "3e84c924d2574c95e8a7e8d7a76530b95d16f784\trefs/heads/test\n",
"headers": {
"Content-Type": "application/x-git-advertisement"
}
}
},
{
"request": {
"method": "POST",
"url": "/test.git/git-receive-pack"
},
"response": {
"status": 200,
"body": "0000ACK refs/heads/test\n0000",
"headers": {
"Content-Type": "application/x-git-receive-pack-result"
}
}
}
]
}
12 changes: 12 additions & 0 deletions tests/e2e/play-kube.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
apiVersion: v1
kind: Pod
metadata:
name: trestlebot-e2e-pod
labels:
app: trestlebot-e2e
spec:
containers:
- name: mock-server-container
image: localhost/mock-server:latest
ports:
- containerPort: 8080
172 changes: 172 additions & 0 deletions tests/e2e/test_e2e.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#!/usr/bin/python

# Copyright 2023 Red Hat, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""E2E tests for the rules transform command."""

import argparse
import pathlib
import subprocess
from typing import Dict, List, Tuple

import pytest
from git.repo import Repo
from trestle.core.commands.init import InitCmd

from tests.conftest import YieldFixture
from tests.testutils import args_dict_to_list, setup_rules_view
from trestlebot.const import RULES_VIEW_DIR


image_name = "localhost/trestlebot:latest"
mock_server_image_name = "localhost/mock-server:latest"
pod_name = "trestlebot-e2e-pod"
e2e_context = "tests/e2e"
container_file = "Dockerfile"


# Define test cases and expected outcomes
test_cases: List[Tuple[Dict[str, str], int]] = [
(
{
"branch": "test",
"rules-view-path": RULES_VIEW_DIR,
"committer-name": "test",
"committer-email": "[email protected]",
"file-patterns": ".",
},
0,
),
(
{
"branch": "test",
"rules-view-path": RULES_VIEW_DIR,
"file-patterns": ".",
},
2,
),
]


def build_image_command(data_path: str, command_args: Dict[str, str]) -> List[str]:
"""Build a command to be run in the shell."""
return [
"podman",
"run",
"--pod",
pod_name,
"--entrypoint",
"trestlebot-rules-transform",
"--rm",
"-v",
f"{data_path}:/trestle",
"-w",
"/trestle",
image_name,
*args_dict_to_list(command_args),
]


@pytest.fixture(scope="module")
def podman_setup() -> YieldFixture[int]:
"""Build the trestlebot container image and run the mock server in a pod."""
# Build the container image
subprocess.run(
[
"podman",
"build",
"-f",
container_file,
"-t",
image_name,
],
check=True,
)

# Build mock server container image
subprocess.run(
[
"podman",
"build",
"-f",
f"{e2e_context}/{container_file}",
"-t",
mock_server_image_name,
e2e_context,
],
check=True,
)

# Create a pod
response = subprocess.run(
["podman", "play", "kube", f"{e2e_context}/play-kube.yml"], check=True
)
yield response.returncode

# Clean up the container image, pod and mock server
try:
subprocess.run(
["podman", "play", "kube", "--down", f"{e2e_context}/play-kube.yml"],
check=True,
)
subprocess.run(["podman", "rmi", image_name], check=True)
subprocess.run(["podman", "rmi", mock_server_image_name], check=True)
except subprocess.CalledProcessError as e:
raise RuntimeError(f"Failed to clean up podman resources: {e}")


# Run each test case
@pytest.mark.slow
@pytest.mark.parametrize("command_args, response", test_cases)
def test_rules_transform_e2e(
tmp_repo: Tuple[str, Repo],
podman_setup: int,
command_args: Dict[str, str],
response: int,
) -> None:
"""Test the trestlebot rules transform command."""
# Check that the container image was built successfully
# and the mock server is running
assert podman_setup == 0

tmp_repo_str, repo = tmp_repo

tmp_repo_path = pathlib.Path(tmp_repo_str)

# Create a trestle workspace in the temporary git repository
args = argparse.Namespace(
verbose=0,
trestle_root=tmp_repo_path,
full=True,
local=False,
govdocs=False,
)
init = InitCmd()
init._run(args)

# Setup the rules directory
setup_rules_view(tmp_repo_path, "test-comp")

remote_url = "http://localhost:8080/test.git"
repo.create_remote("origin", url=remote_url)

# Build the command to be run in the shell
command = build_image_command(tmp_repo_str, command_args)

# Run the command
run_response = subprocess.run(command, cwd=tmp_repo_path)

# Get subprocess response
assert run_response.returncode == response
12 changes: 11 additions & 1 deletion tests/testutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
import json
import pathlib
import shutil
from typing import List, Optional
from typing import Dict, List, Optional

from git.repo import Repo
from trestle.common.model_utils import ModelUtils
Expand Down Expand Up @@ -49,6 +49,16 @@ def clean(repo_path: str, repo: Optional[Repo]) -> None:
shutil.rmtree(repo_path)


def args_dict_to_list(args_dict: Dict[str, str]) -> List[str]:
"""Transform dictionary of args to a list of args."""
args = []
for k, v in args_dict.items():
args.append(f"--{k}")
if v is not None:
args.append(v)
return args


def load_from_json(
tmp_trestle_dir: pathlib.Path,
file_prefix: str,
Expand Down
Loading

0 comments on commit 83a0e59

Please sign in to comment.