Merge pull request #871 from p12tic/unittest

Migrate tests to unittest
This commit is contained in:
Povilas Kanapickas 2024-03-08 11:39:20 +02:00 committed by GitHub
commit 9d1407ba90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 810 additions and 886 deletions

View File

@ -7,15 +7,12 @@ on:
jobs: jobs:
lint-ruff: lint-ruff:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11"]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: actions/checkout@v3 - name: Set up Python 3.10
name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4
with: with:
python-version: ${{ matrix.python-version }} python-version: "3.11"
- name: Analysing the code with ruff - name: Analysing the code with ruff
run: | run: |
pip install -r test-requirements.txt pip install -r test-requirements.txt

View File

@ -25,15 +25,9 @@ jobs:
python -m pip install --upgrade pip python -m pip install --upgrade pip
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi if [ -f test-requirements.txt ]; then pip install -r test-requirements.txt; fi
- name: Lint with flake8 - name: Test with unittest
run: | run: |
# stop the build if there are Python syntax errors or undefined names coverage run --source podman_compose -m unittest pytests/*.py
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics python -m unittest tests/*.py
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
coverage run --source podman_compose -m pytest ./pytests
python -m pytest ./tests
coverage combine coverage combine
coverage report coverage report

View File

@ -41,8 +41,8 @@ $ pre-commit run --all-files
``` ```
6. Run code coverage 6. Run code coverage
```shell ```shell
coverage run --source podman_compose -m pytest ./pytests coverage run --source podman_compose -m unittest pytests/*.py
python -m pytest ./tests python -m unittest tests/*.py
coverage combine coverage combine
coverage report coverage report
coverage html coverage html

View File

@ -103,11 +103,11 @@ There is also AWX 17.1.0
Inside `tests/` directory we have many useless docker-compose stacks Inside `tests/` directory we have many useless docker-compose stacks
that are meant to test as many cases as we can to make sure we are compatible that are meant to test as many cases as we can to make sure we are compatible
### Unit tests with pytest ### Unit tests with unittest
run a pytest with following command run a unittest with following command
```shell ```shell
python -m pytest pytests python -m unittest pytests/*.py
``` ```
# Contributing guide # Contributing guide

0
pytests/__init__.py Normal file
View File

View File

@ -2,127 +2,105 @@ import copy
import os import os
import argparse import argparse
import yaml import yaml
import unittest
from parameterized import parameterized
from podman_compose import normalize_service, PodmanCompose from podman_compose import normalize_service, PodmanCompose
test_cases_simple = [ class TestMergeBuild(unittest.TestCase):
({"test": "test"}, {"test": "test"}), @parameterized.expand([
({"build": "."}, {"build": {"context": "."}}), ({"test": "test"}, {"test": "test"}),
({"build": "./dir-1"}, {"build": {"context": "./dir-1"}}), ({"build": "."}, {"build": {"context": "."}}),
({"build": {"context": "./dir-1"}}, {"build": {"context": "./dir-1"}}), ({"build": "./dir-1"}, {"build": {"context": "./dir-1"}}),
( ({"build": {"context": "./dir-1"}}, {"build": {"context": "./dir-1"}}),
{"build": {"dockerfile": "dockerfile-1"}}, (
{"build": {"dockerfile": "dockerfile-1"}}, {"build": {"dockerfile": "dockerfile-1"}},
), {"build": {"dockerfile": "dockerfile-1"}},
( ),
{"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}}, (
{"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}}, {"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}},
), {"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}},
] ),
])
def test_simple(self, input, expected):
self.assertEqual(normalize_service(input), expected)
@parameterized.expand([
({"test": "test"}, {"test": "test"}),
({"build": "."}, {"build": {"context": "./sub_dir/."}}),
({"build": "./dir-1"}, {"build": {"context": "./sub_dir/dir-1"}}),
({"build": {"context": "./dir-1"}}, {"build": {"context": "./sub_dir/dir-1"}}),
(
{"build": {"dockerfile": "dockerfile-1"}},
{"build": {"context": "./sub_dir", "dockerfile": "dockerfile-1"}},
),
(
{"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}},
{"build": {"context": "./sub_dir/dir-1", "dockerfile": "dockerfile-1"}},
),
])
def test_normalize_service_with_sub_dir(self, input, expected):
self.assertEqual(normalize_service(input, sub_dir="./sub_dir"), expected)
def test_normalize_service_simple(): @parameterized.expand([
for test_case, expected in copy.deepcopy(test_cases_simple): ({}, {}, {}),
test_original = copy.deepcopy(test_case) ({}, {"test": "test"}, {"test": "test"}),
test_case = normalize_service(test_case) ({"test": "test"}, {}, {"test": "test"}),
test_result = expected == test_case ({"test": "test-1"}, {"test": "test-2"}, {"test": "test-2"}),
if not test_result: ({}, {"build": "."}, {"build": {"context": "."}}),
print("test: ", test_original) ({"build": "."}, {}, {"build": {"context": "."}}),
print("expected: ", expected) ({"build": "./dir-1"}, {"build": "./dir-2"}, {"build": {"context": "./dir-2"}}),
print("actual: ", test_case) ({}, {"build": {"context": "./dir-1"}}, {"build": {"context": "./dir-1"}}),
assert test_result ({"build": {"context": "./dir-1"}}, {}, {"build": {"context": "./dir-1"}}),
(
{"build": {"context": "./dir-1"}},
test_cases_sub_dir = [ {"build": {"context": "./dir-2"}},
({"test": "test"}, {"test": "test"}), {"build": {"context": "./dir-2"}},
({"build": "."}, {"build": {"context": "./sub_dir/."}}), ),
({"build": "./dir-1"}, {"build": {"context": "./sub_dir/dir-1"}}), (
({"build": {"context": "./dir-1"}}, {"build": {"context": "./sub_dir/dir-1"}}), {},
( {"build": {"dockerfile": "dockerfile-1"}},
{"build": {"dockerfile": "dockerfile-1"}}, {"build": {"dockerfile": "dockerfile-1"}},
{"build": {"context": "./sub_dir", "dockerfile": "dockerfile-1"}}, ),
), (
( {"build": {"dockerfile": "dockerfile-1"}},
{"build": {"context": "./dir-1", "dockerfile": "dockerfile-1"}}, {},
{"build": {"context": "./sub_dir/dir-1", "dockerfile": "dockerfile-1"}}, {"build": {"dockerfile": "dockerfile-1"}},
), ),
] (
{"build": {"dockerfile": "./dockerfile-1"}},
{"build": {"dockerfile": "./dockerfile-2"}},
def test_normalize_service_with_sub_dir(): {"build": {"dockerfile": "./dockerfile-2"}},
for test_case, expected in copy.deepcopy(test_cases_sub_dir): ),
test_original = copy.deepcopy(test_case) (
test_case = normalize_service(test_case, sub_dir="./sub_dir") {"build": {"dockerfile": "./dockerfile-1"}},
test_result = expected == test_case {"build": {"context": "./dir-2"}},
if not test_result: {"build": {"dockerfile": "./dockerfile-1", "context": "./dir-2"}},
print("test: ", test_original) ),
print("expected: ", expected) (
print("actual: ", test_case) {"build": {"dockerfile": "./dockerfile-1", "context": "./dir-1"}},
assert test_result {"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
{"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
),
test_cases_merges = [ (
({}, {}, {}), {"build": {"dockerfile": "./dockerfile-1"}},
({}, {"test": "test"}, {"test": "test"}), {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
({"test": "test"}, {}, {"test": "test"}), {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
({"test": "test-1"}, {"test": "test-2"}, {"test": "test-2"}), ),
({}, {"build": "."}, {"build": {"context": "."}}), (
({"build": "."}, {}, {"build": {"context": "."}}), {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
({"build": "./dir-1"}, {"build": "./dir-2"}, {"build": {"context": "./dir-2"}}), {"build": {"dockerfile": "./dockerfile-1"}},
({}, {"build": {"context": "./dir-1"}}, {"build": {"context": "./dir-1"}}), {"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1"]}},
({"build": {"context": "./dir-1"}}, {}, {"build": {"context": "./dir-1"}}), ),
( (
{"build": {"context": "./dir-1"}}, {"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
{"build": {"context": "./dir-2"}}, {"build": {"dockerfile": "./dockerfile-1", "args": ["ENV2=2"]}},
{"build": {"context": "./dir-2"}}, {"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1", "ENV2=2"]}},
), ),
( ])
{}, def test_parse_compose_file_when_multiple_composes(self, input, override, expected):
{"build": {"dockerfile": "dockerfile-1"}}, compose_test_1 = {"services": {"test-service": input}}
{"build": {"dockerfile": "dockerfile-1"}}, compose_test_2 = {"services": {"test-service": override}}
),
(
{"build": {"dockerfile": "dockerfile-1"}},
{},
{"build": {"dockerfile": "dockerfile-1"}},
),
(
{"build": {"dockerfile": "./dockerfile-1"}},
{"build": {"dockerfile": "./dockerfile-2"}},
{"build": {"dockerfile": "./dockerfile-2"}},
),
(
{"build": {"dockerfile": "./dockerfile-1"}},
{"build": {"context": "./dir-2"}},
{"build": {"dockerfile": "./dockerfile-1", "context": "./dir-2"}},
),
(
{"build": {"dockerfile": "./dockerfile-1", "context": "./dir-1"}},
{"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
{"build": {"dockerfile": "./dockerfile-2", "context": "./dir-2"}},
),
(
{"build": {"dockerfile": "./dockerfile-1"}},
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
),
(
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
{"build": {"dockerfile": "./dockerfile-1"}},
{"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1"]}},
),
(
{"build": {"dockerfile": "./dockerfile-2", "args": ["ENV1=1"]}},
{"build": {"dockerfile": "./dockerfile-1", "args": ["ENV2=2"]}},
{"build": {"dockerfile": "./dockerfile-1", "args": ["ENV1=1", "ENV2=2"]}},
),
]
def test__parse_compose_file_when_multiple_composes() -> None:
for test_input, test_override, expected_result in copy.deepcopy(test_cases_merges):
compose_test_1 = {"services": {"test-service": test_input}}
compose_test_2 = {"services": {"test-service": test_override}}
dump_yaml(compose_test_1, "test-compose-1.yaml") dump_yaml(compose_test_1, "test-compose-1.yaml")
dump_yaml(compose_test_2, "test-compose-2.yaml") dump_yaml(compose_test_2, "test-compose-2.yaml")
@ -135,15 +113,7 @@ def test__parse_compose_file_when_multiple_composes() -> None:
if podman_compose.services: if podman_compose.services:
podman_compose.services["test-service"].pop("_deps") podman_compose.services["test-service"].pop("_deps")
actual_compose = podman_compose.services["test-service"] actual_compose = podman_compose.services["test-service"]
if actual_compose != expected_result: self.assertEqual(actual_compose, expected)
print("compose: ", test_input)
print("override: ", test_override)
print("expected: ", expected_result)
print("actual: ", actual_compose)
compose_expected = expected_result
assert compose_expected == actual_compose
def set_args(podman_compose: PodmanCompose, file_names: list[str]) -> None: def set_args(podman_compose: PodmanCompose, file_names: list[str]) -> None:

View File

@ -2,76 +2,57 @@ import copy
import os import os
import argparse import argparse
import yaml import yaml
import unittest
from parameterized import parameterized
from podman_compose import normalize_service, PodmanCompose from podman_compose import normalize_service, PodmanCompose
test_keys = ["command", "entrypoint"] test_keys = ["command", "entrypoint"]
test_cases_normalise_pre_merge = [
({"$$$": []}, {"$$$": []}),
({"$$$": ["sh"]}, {"$$$": ["sh"]}),
({"$$$": ["sh", "-c", "date"]}, {"$$$": ["sh", "-c", "date"]}),
({"$$$": "sh"}, {"$$$": ["sh"]}),
({"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
(
{"$$$": "bash -c 'sleep infinity'"},
{"$$$": ["bash", "-c", "sleep infinity"]},
),
]
test_cases_merges = [ class TestMergeBuild(unittest.TestCase):
({}, {"$$$": []}, {"$$$": []}), @parameterized.expand([
({"$$$": []}, {}, {"$$$": []}), ({"$$$": []}, {"$$$": []}),
({"$$$": []}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}), ({"$$$": ["sh"]}, {"$$$": ["sh"]}),
({"$$$": "sh-2"}, {"$$$": []}, {"$$$": []}), ({"$$$": ["sh", "-c", "date"]}, {"$$$": ["sh", "-c", "date"]}),
({}, {"$$$": "sh"}, {"$$$": ["sh"]}), ({"$$$": "sh"}, {"$$$": ["sh"]}),
({"$$$": "sh"}, {}, {"$$$": ["sh"]}), ({"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
({"$$$": "sh-1"}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}), (
({"$$$": ["sh-1"]}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}), {"$$$": "bash -c 'sleep infinity'"},
({"$$$": "sh-1"}, {"$$$": ["sh-2"]}, {"$$$": ["sh-2"]}), {"$$$": ["bash", "-c", "sleep infinity"]},
({"$$$": "sh-1"}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}), ),
({"$$$": ["sh-1"]}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}), ])
({"$$$": ["sh-1", "sh-2"]}, {"$$$": ["sh-3", "sh-4"]}, {"$$$": ["sh-3", "sh-4"]}), def test_normalize_service(self, input_template, expected_template):
({}, {"$$$": ["sh-3", "sh 4"]}, {"$$$": ["sh-3", "sh 4"]}),
({"$$$": "sleep infinity"}, {"$$$": "sh"}, {"$$$": ["sh"]}),
({"$$$": "sh"}, {"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
(
{},
{"$$$": "bash -c 'sleep infinity'"},
{"$$$": ["bash", "-c", "sleep infinity"]},
),
]
def template_to_expression(base, override, expected, key):
base_copy = copy.deepcopy(base)
override_copy = copy.deepcopy(override)
expected_copy = copy.deepcopy(expected)
expected_copy[key] = expected_copy.pop("$$$")
if "$$$" in base:
base_copy[key] = base_copy.pop("$$$")
if "$$$" in override:
override_copy[key] = override_copy.pop("$$$")
return base_copy, override_copy, expected_copy
def test_normalize_service():
for test_input_template, expected_template in test_cases_normalise_pre_merge:
for key in test_keys: for key in test_keys:
test_input, _, expected = template_to_expression( test_input, _, expected = template_to_expression(
test_input_template, {}, expected_template, key input_template, {}, expected_template, key
) )
test_input = normalize_service(test_input) self.assertEqual(normalize_service(test_input), expected)
test_result = expected == test_input
if not test_result:
print("base_template: ", test_input_template)
print("expected: ", expected)
print("actual: ", test_input)
assert test_result
@parameterized.expand([
def test__parse_compose_file_when_multiple_composes() -> None: ({}, {"$$$": []}, {"$$$": []}),
for base_template, override_template, expected_template in copy.deepcopy(test_cases_merges): ({"$$$": []}, {}, {"$$$": []}),
({"$$$": []}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
({"$$$": "sh-2"}, {"$$$": []}, {"$$$": []}),
({}, {"$$$": "sh"}, {"$$$": ["sh"]}),
({"$$$": "sh"}, {}, {"$$$": ["sh"]}),
({"$$$": "sh-1"}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
({"$$$": ["sh-1"]}, {"$$$": "sh-2"}, {"$$$": ["sh-2"]}),
({"$$$": "sh-1"}, {"$$$": ["sh-2"]}, {"$$$": ["sh-2"]}),
({"$$$": "sh-1"}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}),
({"$$$": ["sh-1"]}, {"$$$": ["sh-2", "sh-3"]}, {"$$$": ["sh-2", "sh-3"]}),
({"$$$": ["sh-1", "sh-2"]}, {"$$$": ["sh-3", "sh-4"]}, {"$$$": ["sh-3", "sh-4"]}),
({}, {"$$$": ["sh-3", "sh 4"]}, {"$$$": ["sh-3", "sh 4"]}),
({"$$$": "sleep infinity"}, {"$$$": "sh"}, {"$$$": ["sh"]}),
({"$$$": "sh"}, {"$$$": "sleep infinity"}, {"$$$": ["sleep", "infinity"]}),
(
{},
{"$$$": "bash -c 'sleep infinity'"},
{"$$$": ["bash", "-c", "sleep infinity"]},
),
])
def test_parse_compose_file_when_multiple_composes(
self, base_template, override_template, expected_template
):
for key in test_keys: for key in test_keys:
base, override, expected = template_to_expression( base, override, expected = template_to_expression(
base_template, override_template, expected_template, key base_template, override_template, expected_template, key
@ -90,12 +71,20 @@ def test__parse_compose_file_when_multiple_composes() -> None:
if podman_compose.services: if podman_compose.services:
podman_compose.services["test-service"].pop("_deps") podman_compose.services["test-service"].pop("_deps")
actual = podman_compose.services["test-service"] actual = podman_compose.services["test-service"]
if actual != expected: self.assertEqual(actual, expected)
print("compose: ", base)
print("override: ", override)
print("result: ", expected)
assert expected == actual
def template_to_expression(base, override, expected, key):
base_copy = copy.deepcopy(base)
override_copy = copy.deepcopy(override)
expected_copy = copy.deepcopy(expected)
expected_copy[key] = expected_copy.pop("$$$")
if "$$$" in base:
base_copy[key] = base_copy.pop("$$$")
if "$$$" in override:
override_copy[key] = override_copy.pop("$$$")
return base_copy, override_copy, expected_copy
def set_args(podman_compose: PodmanCompose, file_names: list[str]) -> None: def set_args(podman_compose: PodmanCompose, file_names: list[str]) -> None:

View File

@ -4,126 +4,119 @@ import argparse
import copy import copy
import os import os
import yaml import yaml
import unittest
from parameterized import parameterized
from podman_compose import ( from podman_compose import (
normalize_service,
normalize,
normalize_final, normalize_final,
normalize_service_final, normalize_service_final,
PodmanCompose, PodmanCompose,
) )
cwd = os.path.abspath(".") cwd = os.path.abspath(".")
test_cases_simple_normalization = [
({"image": "test-image"}, {"image": "test-image"}),
(
{"build": "."},
{
"build": {"context": cwd, "dockerfile": "Dockerfile"},
},
),
(
{"build": "../relative"},
{
"build": {
"context": os.path.normpath(os.path.join(cwd, "../relative")),
"dockerfile": "Dockerfile",
},
},
),
(
{"build": "./relative"},
{
"build": {
"context": os.path.normpath(os.path.join(cwd, "./relative")),
"dockerfile": "Dockerfile",
},
},
),
(
{"build": "/workspace/absolute"},
{
"build": {
"context": "/workspace/absolute",
"dockerfile": "Dockerfile",
},
},
),
(
{
"build": {
"dockerfile": "Dockerfile",
},
},
{
"build": {
"context": cwd,
"dockerfile": "Dockerfile",
},
},
),
(
{
"build": {
"context": ".",
},
},
{
"build": {
"context": cwd,
"dockerfile": "Dockerfile",
},
},
),
(
{
"build": {"context": "../", "dockerfile": "test-dockerfile"},
},
{
"build": {
"context": os.path.normpath(os.path.join(cwd, "../")),
"dockerfile": "test-dockerfile",
},
},
),
(
{
"build": {"context": ".", "dockerfile": "./dev/test-dockerfile"},
},
{
"build": {
"context": cwd,
"dockerfile": "./dev/test-dockerfile",
},
},
),
]
# class TestNormalizeFullBuild(unittest.TestCase):
# [service.build] is normalised after merges cases_simple_normalization = [
# ({"image": "test-image"}, {"image": "test-image"}),
def test_normalize_service_final_returns_absolute_path_in_context() -> None: (
project_dir = cwd {"build": "."},
for test_input, expected_service in copy.deepcopy(test_cases_simple_normalization): {
actual_service = normalize_service_final(test_input, project_dir) "build": {"context": cwd, "dockerfile": "Dockerfile"},
assert expected_service == actual_service },
),
(
{"build": "../relative"},
{
"build": {
"context": os.path.normpath(os.path.join(cwd, "../relative")),
"dockerfile": "Dockerfile",
},
},
),
(
{"build": "./relative"},
{
"build": {
"context": os.path.normpath(os.path.join(cwd, "./relative")),
"dockerfile": "Dockerfile",
},
},
),
(
{"build": "/workspace/absolute"},
{
"build": {
"context": "/workspace/absolute",
"dockerfile": "Dockerfile",
},
},
),
(
{
"build": {
"dockerfile": "Dockerfile",
},
},
{
"build": {
"context": cwd,
"dockerfile": "Dockerfile",
},
},
),
(
{
"build": {
"context": ".",
},
},
{
"build": {
"context": cwd,
"dockerfile": "Dockerfile",
},
},
),
(
{
"build": {"context": "../", "dockerfile": "test-dockerfile"},
},
{
"build": {
"context": os.path.normpath(os.path.join(cwd, "../")),
"dockerfile": "test-dockerfile",
},
},
),
(
{
"build": {"context": ".", "dockerfile": "./dev/test-dockerfile"},
},
{
"build": {
"context": cwd,
"dockerfile": "./dev/test-dockerfile",
},
},
),
]
@parameterized.expand(cases_simple_normalization)
def test_normalize_service_final_returns_absolute_path_in_context(self, input, expected):
# Tests that [service.build] is normalized after merges
project_dir = cwd
self.assertEqual(normalize_service_final(input, project_dir), expected)
def test_normalize_returns_absolute_path_in_context() -> None: @parameterized.expand(cases_simple_normalization)
project_dir = cwd def test_normalize_returns_absolute_path_in_context(self, input, expected):
for test_input, expected_result in copy.deepcopy(test_cases_simple_normalization): project_dir = cwd
compose_test = {"services": {"test-service": test_input}} compose_test = {"services": {"test-service": input}}
compose_expected = {"services": {"test-service": expected_result}} compose_expected = {"services": {"test-service": expected}}
actual_compose = normalize_final(compose_test, project_dir) self.assertEqual(normalize_final(compose_test, project_dir), compose_expected)
assert compose_expected == actual_compose
@parameterized.expand(cases_simple_normalization)
# def test_parse_compose_file_when_single_compose(self, input, expected):
# running full parse over single compose files compose_test = {"services": {"test-service": input}}
#
def test__parse_compose_file_when_single_compose() -> None:
for test_input, expected_result in copy.deepcopy(test_cases_simple_normalization):
compose_test = {"services": {"test-service": test_input}}
dump_yaml(compose_test, "test-compose.yaml") dump_yaml(compose_test, "test-compose.yaml")
podman_compose = PodmanCompose() podman_compose = PodmanCompose()
@ -135,117 +128,106 @@ def test__parse_compose_file_when_single_compose() -> None:
if podman_compose.services: if podman_compose.services:
podman_compose.services["test-service"].pop("_deps") podman_compose.services["test-service"].pop("_deps")
actual_compose = podman_compose.services["test-service"] actual_compose = podman_compose.services["test-service"]
if actual_compose != expected_result: self.assertEqual(actual_compose, expected)
print("compose: ", test_input)
print("result: ", expected_result)
assert expected_result == actual_compose @parameterized.expand([
(
{},
test_cases_with_merges = [ {"build": "."},
( {"build": {"context": cwd, "dockerfile": "Dockerfile"}},
{}, ),
{"build": "."}, (
{"build": {"context": cwd, "dockerfile": "Dockerfile"}}, {"build": "."},
), {},
( {"build": {"context": cwd, "dockerfile": "Dockerfile"}},
{"build": "."}, ),
{}, (
{"build": {"context": cwd, "dockerfile": "Dockerfile"}}, {"build": "/workspace/absolute"},
), {"build": "./relative"},
( {
{"build": "/workspace/absolute"}, "build": {
{"build": "./relative"}, "context": os.path.normpath(os.path.join(cwd, "./relative")),
{ "dockerfile": "Dockerfile",
"build": { }
"context": os.path.normpath(os.path.join(cwd, "./relative")), },
"dockerfile": "Dockerfile", ),
} (
}, {"build": "./relative"},
), {"build": "/workspace/absolute"},
( {"build": {"context": "/workspace/absolute", "dockerfile": "Dockerfile"}},
{"build": "./relative"}, ),
{"build": "/workspace/absolute"}, (
{"build": {"context": "/workspace/absolute", "dockerfile": "Dockerfile"}}, {"build": "./relative"},
), {"build": "/workspace/absolute"},
( {"build": {"context": "/workspace/absolute", "dockerfile": "Dockerfile"}},
{"build": "./relative"}, ),
{"build": "/workspace/absolute"}, (
{"build": {"context": "/workspace/absolute", "dockerfile": "Dockerfile"}}, {"build": {"dockerfile": "test-dockerfile"}},
), {},
( {"build": {"context": cwd, "dockerfile": "test-dockerfile"}},
{"build": {"dockerfile": "test-dockerfile"}}, ),
{}, (
{"build": {"context": cwd, "dockerfile": "test-dockerfile"}}, {},
), {"build": {"dockerfile": "test-dockerfile"}},
( {"build": {"context": cwd, "dockerfile": "test-dockerfile"}},
{}, ),
{"build": {"dockerfile": "test-dockerfile"}}, (
{"build": {"context": cwd, "dockerfile": "test-dockerfile"}}, {},
), {"build": {"dockerfile": "test-dockerfile"}},
( {"build": {"context": cwd, "dockerfile": "test-dockerfile"}},
{}, ),
{"build": {"dockerfile": "test-dockerfile"}}, (
{"build": {"context": cwd, "dockerfile": "test-dockerfile"}}, {"build": {"dockerfile": "test-dockerfile-1"}},
), {"build": {"dockerfile": "test-dockerfile-2"}},
( {"build": {"context": cwd, "dockerfile": "test-dockerfile-2"}},
{"build": {"dockerfile": "test-dockerfile-1"}}, ),
{"build": {"dockerfile": "test-dockerfile-2"}}, (
{"build": {"context": cwd, "dockerfile": "test-dockerfile-2"}}, {"build": "/workspace/absolute"},
), {"build": {"dockerfile": "test-dockerfile"}},
( {"build": {"context": "/workspace/absolute", "dockerfile": "test-dockerfile"}},
{"build": "/workspace/absolute"}, ),
{"build": {"dockerfile": "test-dockerfile"}}, (
{"build": {"context": "/workspace/absolute", "dockerfile": "test-dockerfile"}}, {"build": {"dockerfile": "test-dockerfile"}},
), {"build": "/workspace/absolute"},
( {"build": {"context": "/workspace/absolute", "dockerfile": "test-dockerfile"}},
{"build": {"dockerfile": "test-dockerfile"}}, ),
{"build": "/workspace/absolute"}, (
{"build": {"context": "/workspace/absolute", "dockerfile": "test-dockerfile"}}, {"build": {"dockerfile": "./test-dockerfile-1"}},
), {"build": {"dockerfile": "./test-dockerfile-2", "args": ["ENV1=1"]}},
( {
{"build": {"dockerfile": "./test-dockerfile-1"}}, "build": {
{"build": {"dockerfile": "./test-dockerfile-2", "args": ["ENV1=1"]}}, "context": cwd,
{ "dockerfile": "./test-dockerfile-2",
"build": { "args": ["ENV1=1"],
"context": cwd, }
"dockerfile": "./test-dockerfile-2", },
"args": ["ENV1=1"], ),
} (
}, {"build": {"dockerfile": "./test-dockerfile-1", "args": ["ENV1=1"]}},
), {"build": {"dockerfile": "./test-dockerfile-2"}},
( {
{"build": {"dockerfile": "./test-dockerfile-1", "args": ["ENV1=1"]}}, "build": {
{"build": {"dockerfile": "./test-dockerfile-2"}}, "context": cwd,
{ "dockerfile": "./test-dockerfile-2",
"build": { "args": ["ENV1=1"],
"context": cwd, }
"dockerfile": "./test-dockerfile-2", },
"args": ["ENV1=1"], ),
} (
}, {"build": {"dockerfile": "./test-dockerfile-1", "args": ["ENV1=1"]}},
), {"build": {"dockerfile": "./test-dockerfile-2", "args": ["ENV2=2"]}},
( {
{"build": {"dockerfile": "./test-dockerfile-1", "args": ["ENV1=1"]}}, "build": {
{"build": {"dockerfile": "./test-dockerfile-2", "args": ["ENV2=2"]}}, "context": cwd,
{ "dockerfile": "./test-dockerfile-2",
"build": { "args": ["ENV1=1", "ENV2=2"],
"context": cwd, }
"dockerfile": "./test-dockerfile-2", },
"args": ["ENV1=1", "ENV2=2"], ),
} ])
}, def test_parse_when_multiple_composes(self, input, override, expected):
), compose_test_1 = {"services": {"test-service": input}}
] compose_test_2 = {"services": {"test-service": override}}
#
# running full parse over merged
#
def test__parse_compose_file_when_multiple_composes() -> None:
for test_input, test_override, expected_result in copy.deepcopy(test_cases_with_merges):
compose_test_1 = {"services": {"test-service": test_input}}
compose_test_2 = {"services": {"test-service": test_override}}
dump_yaml(compose_test_1, "test-compose-1.yaml") dump_yaml(compose_test_1, "test-compose-1.yaml")
dump_yaml(compose_test_2, "test-compose-2.yaml") dump_yaml(compose_test_2, "test-compose-2.yaml")
@ -262,13 +244,7 @@ def test__parse_compose_file_when_multiple_composes() -> None:
if podman_compose.services: if podman_compose.services:
podman_compose.services["test-service"].pop("_deps") podman_compose.services["test-service"].pop("_deps")
actual_compose = podman_compose.services["test-service"] actual_compose = podman_compose.services["test-service"]
if actual_compose != expected_result: self.assertEqual(actual_compose, expected)
print("compose: ", test_input)
print("override: ", test_override)
print("result: ", expected_result)
compose_expected = expected_result
assert compose_expected == actual_compose
def set_args(podman_compose: PodmanCompose, file_names: list[str], no_normalize: bool) -> None: def set_args(podman_compose: PodmanCompose, file_names: list[str], no_normalize: bool) -> None:

View File

@ -1,21 +1,19 @@
# pylint: disable=redefined-outer-name # pylint: disable=redefined-outer-name
import pytest import unittest
from podman_compose import parse_short_mount from podman_compose import parse_short_mount
@pytest.fixture class ParseShortMountTests(unittest.TestCase):
def multi_propagation_mount_str(): def test_multi_propagation(self):
return "/foo/bar:/baz:U,Z" self.assertEqual(
parse_short_mount("/foo/bar:/baz:U,Z", "/"),
{
def test_parse_short_mount_multi_propagation(multi_propagation_mount_str): "type": "bind",
expected = { "source": "/foo/bar",
"type": "bind", "target": "/baz",
"source": "/foo/bar", "bind": {
"target": "/baz", "propagation": "U,Z",
"bind": { },
"propagation": "U,Z", },
}, )
}
assert parse_short_mount(multi_propagation_mount_str, "/") == expected

View File

@ -37,12 +37,10 @@ setup(
"pyyaml", "pyyaml",
"python-dotenv", "python-dotenv",
], ],
extras_require={"devel": ["ruff", "pre-commit", "coverage"]}, extras_require={"devel": ["ruff", "pre-commit", "coverage", "parameterize"]},
# test_suite='tests', # test_suite='tests',
# tests_require=[ # tests_require=[
# 'coverage', # 'coverage',
# 'pytest-cov',
# 'pytest',
# 'tox', # 'tox',
# ] # ]
) )

View File

@ -1,4 +1,6 @@
-e .
coverage==7.4.3 coverage==7.4.3
parameterized==0.9.0
pytest==8.0.2 pytest==8.0.2
tox==4.13.0 tox==4.13.0
ruff==0.3.1 ruff==0.3.1

0
tests/__init__.py Normal file
View File

View File

@ -1,27 +0,0 @@
"""conftest.py
Defines global pytest fixtures available to all tests.
"""
# pylint: disable=redefined-outer-name
from pathlib import Path
import os
import pytest
@pytest.fixture
def base_path():
"""Returns the base path for the project"""
return Path(__file__).parent.parent
@pytest.fixture
def test_path(base_path):
"""Returns the path to the tests directory"""
return os.path.join(base_path, "tests")
@pytest.fixture
def podman_compose_path(base_path):
"""Returns the path to the podman compose script"""
return os.path.join(base_path, "podman_compose.py")

View File

@ -1,8 +1,10 @@
from pathlib import Path from pathlib import Path
import subprocess import subprocess
import os
import unittest
def capture(command): def run_subprocess(command):
proc = subprocess.Popen( proc = subprocess.Popen(
command, command,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -12,75 +14,89 @@ def capture(command):
return out, err, proc.returncode return out, err, proc.returncode
def test_podman_compose_extends_w_file_subdir(): def base_path():
""" """Returns the base path for the project"""
Test that podman-compose can execute podman-compose -f <file> up with extended File which return Path(__file__).parent.parent
includes a build context
:return:
"""
main_path = Path(__file__).parent.parent
command_up = [
"coverage",
"run",
str(main_path.joinpath("podman_compose.py")),
"-f",
str(main_path.joinpath("tests", "extends_w_file_subdir", "docker-compose.yml")),
"up",
"-d",
]
command_check_container = [
"coverage",
"run",
str(main_path.joinpath("podman_compose.py")),
"-f",
str(main_path.joinpath("tests", "extends_w_file_subdir", "docker-compose.yml")),
"ps",
"--format",
'{{.Image}}',
]
command_down = [
"podman",
"rmi",
"--force",
"localhost/subdir_test:me",
"docker.io/library/busybox",
]
out, _, returncode = capture(command_up)
assert 0 == returncode
# check container was created and exists
out, err, returncode = capture(command_check_container)
assert 0 == returncode
assert b'localhost/subdir_test:me\n' == out
out, _, returncode = capture(command_down)
# cleanup test image(tags)
assert 0 == returncode
print('ok')
# check container did not exists anymore
out, _, returncode = capture(command_check_container)
assert 0 == returncode
assert b'' == out
def test_podman_compose_extends_w_empty_service(): def test_path():
""" """Returns the path to the tests directory"""
Test that podman-compose can execute podman-compose -f <file> up with extended File which return os.path.join(base_path(), "tests")
includes an empty service. (e.g. if the file is used as placeholder for more complex configurations.)
:return:
"""
main_path = Path(__file__).parent.parent
command_up = [
"python3",
str(main_path.joinpath("podman_compose.py")),
"-f",
str(main_path.joinpath("tests", "extends_w_empty_service", "docker-compose.yml")),
"up",
"-d",
]
_, _, returncode = capture(command_up) def podman_compose_path():
assert 0 == returncode """Returns the path to the podman compose script"""
return os.path.join(base_path(), "podman_compose.py")
class TestPodmanCompose(unittest.TestCase):
def test_extends_w_file_subdir(self):
"""
Test that podman-compose can execute podman-compose -f <file> up with extended File which
includes a build context
:return:
"""
main_path = Path(__file__).parent.parent
command_up = [
"coverage",
"run",
str(main_path.joinpath("podman_compose.py")),
"-f",
str(main_path.joinpath("tests", "extends_w_file_subdir", "docker-compose.yml")),
"up",
"-d",
]
command_check_container = [
"coverage",
"run",
str(main_path.joinpath("podman_compose.py")),
"-f",
str(main_path.joinpath("tests", "extends_w_file_subdir", "docker-compose.yml")),
"ps",
"--format",
'{{.Image}}',
]
command_down = [
"podman",
"rmi",
"--force",
"localhost/subdir_test:me",
"docker.io/library/busybox",
]
out, _, returncode = run_subprocess(command_up)
self.assertEqual(returncode, 0)
# check container was created and exists
out, err, returncode = run_subprocess(command_check_container)
self.assertEqual(returncode, 0)
self.assertEqual(out, b'localhost/subdir_test:me\n')
out, _, returncode = run_subprocess(command_down)
# cleanup test image(tags)
self.assertEqual(returncode, 0)
# check container did not exists anymore
out, _, returncode = run_subprocess(command_check_container)
self.assertEqual(returncode, 0)
self.assertEqual(out, b'')
def test_extends_w_empty_service(self):
"""
Test that podman-compose can execute podman-compose -f <file> up with extended File which
includes an empty service. (e.g. if the file is used as placeholder for more complex configurations.)
:return:
"""
main_path = Path(__file__).parent.parent
command_up = [
"python3",
str(main_path.joinpath("podman_compose.py")),
"-f",
str(main_path.joinpath("tests", "extends_w_empty_service", "docker-compose.yml")),
"up",
"-d",
]
_, _, returncode = run_subprocess(command_up)
self.assertEqual(returncode, 0)

View File

@ -6,72 +6,75 @@ Tests the podman-compose config command which is used to return defined compose
# pylint: disable=redefined-outer-name # pylint: disable=redefined-outer-name
import os import os
from test_podman_compose import capture from .test_podman_compose import podman_compose_path
import pytest from .test_podman_compose import run_subprocess
from .test_podman_compose import test_path
import unittest
from parameterized import parameterized
@pytest.fixture def profile_compose_file():
def profile_compose_file(test_path):
""" "Returns the path to the `profile` compose file used for this test module""" """ "Returns the path to the `profile` compose file used for this test module"""
return os.path.join(test_path, "profile", "docker-compose.yml") return os.path.join(test_path(), "profile", "docker-compose.yml")
def test_config_no_profiles(podman_compose_path, profile_compose_file): class TestComposeConfig(unittest.TestCase):
""" def test_config_no_profiles(self):
Tests podman-compose config command without profile enablement. """
Tests podman-compose config command without profile enablement.
"""
config_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
profile_compose_file(),
"config",
]
:param podman_compose_path: The fixture used to specify the path to the podman compose file. out, _, return_code = run_subprocess(config_cmd)
:param profile_compose_file: The fixtued used to specify the path to the "profile" compose used in the test. self.assertEqual(return_code, 0)
"""
config_cmd = ["coverage", "run", podman_compose_path, "-f", profile_compose_file, "config"]
out, _, return_code = capture(config_cmd) string_output = out.decode("utf-8")
assert return_code == 0 self.assertIn("default-service", string_output)
self.assertNotIn("service-1", string_output)
self.assertNotIn("service-2", string_output)
string_output = out.decode("utf-8") @parameterized.expand(
assert "default-service" in string_output [
assert "service-1" not in string_output (
assert "service-2" not in string_output ["--profile", "profile-1", "config"],
{"default-service": True, "service-1": True, "service-2": False},
),
(
["--profile", "profile-2", "config"],
{"default-service": True, "service-1": False, "service-2": True},
),
(
["--profile", "profile-1", "--profile", "profile-2", "config"],
{"default-service": True, "service-1": True, "service-2": True},
),
],
)
def test_config_profiles(self, profiles, expected_services):
"""
Tests podman-compose
:param profiles: The enabled profiles for the parameterized test.
:param expected_services: Dictionary used to model the expected "enabled" services in the profile.
Key = service name, Value = True if the service is enabled, otherwise False.
"""
config_cmd = ["coverage", "run", podman_compose_path(), "-f", profile_compose_file()]
config_cmd.extend(profiles)
out, _, return_code = run_subprocess(config_cmd)
self.assertEqual(return_code, 0)
@pytest.mark.parametrize( actual_output = out.decode("utf-8")
"profiles, expected_services",
[
(
["--profile", "profile-1", "config"],
{"default-service": True, "service-1": True, "service-2": False},
),
(
["--profile", "profile-2", "config"],
{"default-service": True, "service-1": False, "service-2": True},
),
(
["--profile", "profile-1", "--profile", "profile-2", "config"],
{"default-service": True, "service-1": True, "service-2": True},
),
],
)
def test_config_profiles(podman_compose_path, profile_compose_file, profiles, expected_services):
"""
Tests podman-compose
:param podman_compose_path: The fixture used to specify the path to the podman compose file.
:param profile_compose_file: The fixtued used to specify the path to the "profile" compose used in the test.
:param profiles: The enabled profiles for the parameterized test.
:param expected_services: Dictionary used to model the expected "enabled" services in the profile.
Key = service name, Value = True if the service is enabled, otherwise False.
"""
config_cmd = ["coverage", "run", podman_compose_path, "-f", profile_compose_file]
config_cmd.extend(profiles)
out, _, return_code = capture(config_cmd) self.assertEqual(len(expected_services), 3)
assert return_code == 0
actual_output = out.decode("utf-8") actual_services = {}
for service, _ in expected_services.items():
actual_services[service] = service in actual_output
assert len(expected_services) == 3 self.assertEqual(expected_services, actual_services)
actual_services = {}
for service, _ in expected_services.items():
actual_services[service] = service in actual_output
assert expected_services == actual_services

View File

@ -1,8 +1,9 @@
from pathlib import Path from pathlib import Path
import subprocess import subprocess
import unittest
def capture(command): def run_subprocess(command):
proc = subprocess.Popen( proc = subprocess.Popen(
command, command,
stdout=subprocess.PIPE, stdout=subprocess.PIPE,
@ -12,61 +13,62 @@ def capture(command):
return out, err, proc.returncode return out, err, proc.returncode
def test_podman_compose_include(): class TestPodmanComposeInclude(unittest.TestCase):
""" def test_podman_compose_include(self):
Test that podman-compose can execute podman-compose -f <file> up with include """
:return: Test that podman-compose can execute podman-compose -f <file> up with include
""" :return:
main_path = Path(__file__).parent.parent """
main_path = Path(__file__).parent.parent
command_up = [ command_up = [
"coverage", "coverage",
"run", "run",
str(main_path.joinpath("podman_compose.py")), str(main_path.joinpath("podman_compose.py")),
"-f", "-f",
str(main_path.joinpath("tests", "include", "docker-compose.yaml")), str(main_path.joinpath("tests", "include", "docker-compose.yaml")),
"up", "up",
"-d", "-d",
] ]
command_check_container = [ command_check_container = [
"podman", "podman",
"ps", "ps",
"-a", "-a",
"--filter", "--filter",
"label=io.podman.compose.project=include", "label=io.podman.compose.project=include",
"--format", "--format",
'"{{.Image}}"', '"{{.Image}}"',
] ]
command_container_id = [ command_container_id = [
"podman", "podman",
"ps", "ps",
"-a", "-a",
"--filter", "--filter",
"label=io.podman.compose.project=include", "label=io.podman.compose.project=include",
"--format", "--format",
'"{{.ID}}"', '"{{.ID}}"',
] ]
command_down = ["podman", "rm", "--force", "CONTAINER_ID"] command_down = ["podman", "rm", "--force", "CONTAINER_ID"]
out, _, returncode = capture(command_up) out, _, returncode = run_subprocess(command_up)
assert 0 == returncode self.assertEqual(returncode, 0)
out, _, returncode = capture(command_check_container) out, _, returncode = run_subprocess(command_check_container)
assert 0 == returncode self.assertEqual(returncode, 0)
assert out == b'"docker.io/library/busybox:latest"\n' self.assertEqual(out, b'"docker.io/library/busybox:latest"\n')
# Get container ID to remove it # Get container ID to remove it
out, _, returncode = capture(command_container_id) out, _, returncode = run_subprocess(command_container_id)
assert 0 == returncode self.assertEqual(returncode, 0)
assert out != b"" self.assertNotEqual(out, b"")
container_id = out.decode().strip().replace('"', "") container_id = out.decode().strip().replace('"', "")
command_down[3] = container_id command_down[3] = container_id
out, _, returncode = capture(command_down) out, _, returncode = run_subprocess(command_down)
# cleanup test image(tags) # cleanup test image(tags)
assert 0 == returncode self.assertEqual(returncode, 0)
assert out != b"" self.assertNotEqual(out, b"")
# check container did not exists anymore # check container did not exists anymore
out, _, returncode = capture(command_check_container) out, _, returncode = run_subprocess(command_check_container)
assert 0 == returncode self.assertEqual(returncode, 0)
assert out == b"" self.assertEqual(out, b"")

View File

@ -7,182 +7,192 @@ Tests the podman compose up and down commands used to create and remove services
# pylint: disable=redefined-outer-name # pylint: disable=redefined-outer-name
import os import os
import time import time
import unittest
from test_podman_compose import capture from .test_podman_compose import run_subprocess
from .test_podman_compose import podman_compose_path
from .test_podman_compose import test_path
def test_exit_from(podman_compose_path, test_path): class TestPodmanCompose(unittest.TestCase):
up_cmd = [ def test_exit_from(self):
"coverage", up_cmd = [
"run", "coverage",
podman_compose_path, "run",
"-f", podman_compose_path(),
os.path.join(test_path, "exit-from", "docker-compose.yaml"), "-f",
"up", os.path.join(test_path(), "exit-from", "docker-compose.yaml"),
] "up",
]
out, _, return_code = capture(up_cmd + ["--exit-code-from", "sh1"]) out, _, return_code = run_subprocess(up_cmd + ["--exit-code-from", "sh1"])
assert return_code == 1 self.assertEqual(return_code, 1)
out, _, return_code = capture(up_cmd + ["--exit-code-from", "sh2"]) out, _, return_code = run_subprocess(up_cmd + ["--exit-code-from", "sh2"])
assert return_code == 2 self.assertEqual(return_code, 2)
def test_run(self):
"""
This will test depends_on as well
"""
run_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "deps", "docker-compose.yaml"),
"run",
"--rm",
"sleep",
"/bin/sh",
"-c",
"wget -q -O - http://web:8000/hosts",
]
def test_run(podman_compose_path, test_path): out, _, return_code = run_subprocess(run_cmd)
""" self.assertIn(b'127.0.0.1\tlocalhost', out)
This will test depends_on as well
"""
run_cmd = [
"coverage",
"run",
podman_compose_path,
"-f",
os.path.join(test_path, "deps", "docker-compose.yaml"),
"run",
"--rm",
"sleep",
"/bin/sh",
"-c",
"wget -q -O - http://web:8000/hosts",
]
out, _, return_code = capture(run_cmd) # Run it again to make sure we can run it twice. I saw an issue where a second run, with the container left up,
assert b'127.0.0.1\tlocalhost' in out # would fail
run_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "deps", "docker-compose.yaml"),
"run",
"--rm",
"sleep",
"/bin/sh",
"-c",
"wget -q -O - http://web:8000/hosts",
]
# Run it again to make sure we can run it twice. I saw an issue where a second run, with the container left up, out, _, return_code = run_subprocess(run_cmd)
# would fail assert b'127.0.0.1\tlocalhost' in out
run_cmd = [ self.assertEqual(return_code, 0)
"coverage",
"run",
podman_compose_path,
"-f",
os.path.join(test_path, "deps", "docker-compose.yaml"),
"run",
"--rm",
"sleep",
"/bin/sh",
"-c",
"wget -q -O - http://web:8000/hosts",
]
out, _, return_code = capture(run_cmd) # This leaves a container running. Not sure it's intended, but it matches docker-compose
assert b'127.0.0.1\tlocalhost' in out down_cmd = [
assert return_code == 0 "coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "deps", "docker-compose.yaml"),
"down",
]
# This leaves a container running. Not sure it's intended, but it matches docker-compose out, _, return_code = run_subprocess(run_cmd)
down_cmd = [ self.assertEqual(return_code, 0)
"coverage",
"run",
podman_compose_path,
"-f",
os.path.join(test_path, "deps", "docker-compose.yaml"),
"down",
]
out, _, return_code = capture(run_cmd) def test_up_with_ports(self):
assert return_code == 0 up_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "ports", "docker-compose.yml"),
"up",
"-d",
"--force-recreate",
]
down_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "ports", "docker-compose.yml"),
"down",
"--volumes",
]
def test_up_with_ports(podman_compose_path, test_path): try:
up_cmd = [ out, _, return_code = run_subprocess(up_cmd)
"coverage", self.assertEqual(return_code, 0)
"run",
podman_compose_path,
"-f",
os.path.join(test_path, "ports", "docker-compose.yml"),
"up",
"-d",
"--force-recreate",
]
down_cmd = [ finally:
"coverage", out, _, return_code = run_subprocess(down_cmd)
"run", self.assertEqual(return_code, 0)
podman_compose_path,
"-f",
os.path.join(test_path, "ports", "docker-compose.yml"),
"down",
"--volumes",
]
try: def test_down_with_vols(self):
out, _, return_code = capture(up_cmd) up_cmd = [
assert return_code == 0 "coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "vol", "docker-compose.yaml"),
"up",
"-d",
]
finally: down_cmd = [
out, _, return_code = capture(down_cmd) "coverage",
assert return_code == 0 "run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "vol", "docker-compose.yaml"),
"down",
"--volumes",
]
try:
out, _, return_code = run_subprocess(["podman", "volume", "create", "my-app-data"])
self.assertEqual(return_code, 0)
out, _, return_code = run_subprocess([
"podman",
"volume",
"create",
"actual-name-of-volume",
])
self.assertEqual(return_code, 0)
def test_down_with_vols(podman_compose_path, test_path): out, _, return_code = run_subprocess(up_cmd)
up_cmd = [ self.assertEqual(return_code, 0)
"coverage",
"run",
podman_compose_path,
"-f",
os.path.join(test_path, "vol", "docker-compose.yaml"),
"up",
"-d",
]
down_cmd = [ run_subprocess(["podman", "inspect", "volume", ""])
"coverage",
"run",
podman_compose_path,
"-f",
os.path.join(test_path, "vol", "docker-compose.yaml"),
"down",
"--volumes",
]
try: finally:
out, _, return_code = capture(["podman", "volume", "create", "my-app-data"]) out, _, return_code = run_subprocess(down_cmd)
assert return_code == 0 run_subprocess(["podman", "volume", "rm", "my-app-data"])
out, _, return_code = capture(["podman", "volume", "create", "actual-name-of-volume"]) run_subprocess(["podman", "volume", "rm", "actual-name-of-volume"])
assert return_code == 0 self.assertEqual(return_code, 0)
out, _, return_code = capture(up_cmd) def test_down_with_orphans(self):
assert return_code == 0 container_id, _, return_code = run_subprocess([
"podman",
"run",
"--rm",
"-d",
"busybox",
"/bin/busybox",
"httpd",
"-f",
"-h",
"/etc/",
"-p",
"8000",
])
capture(["podman", "inspect", "volume", ""]) down_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
os.path.join(test_path(), "ports", "docker-compose.yml"),
"down",
"--volumes",
"--remove-orphans",
]
finally: out, _, return_code = run_subprocess(down_cmd)
out, _, return_code = capture(down_cmd) self.assertEqual(return_code, 0)
capture(["podman", "volume", "rm", "my-app-data"])
capture(["podman", "volume", "rm", "actual-name-of-volume"])
assert return_code == 0
_, _, exists = run_subprocess([
"podman",
"container",
"exists",
container_id.decode("utf-8"),
])
def test_down_with_orphans(podman_compose_path, test_path): self.assertEqual(exists, 1)
container_id, _, return_code = capture([
"podman",
"run",
"--rm",
"-d",
"busybox",
"/bin/busybox",
"httpd",
"-f",
"-h",
"/etc/",
"-p",
"8000",
])
down_cmd = [
"coverage",
"run",
podman_compose_path,
"-f",
os.path.join(test_path, "ports", "docker-compose.yml"),
"down",
"--volumes",
"--remove-orphans",
]
out, _, return_code = capture(down_cmd)
assert return_code == 0
_, _, exists = capture(["podman", "container", "exists", container_id.decode("utf-8")])
assert exists == 1

View File

@ -6,87 +6,83 @@ Tests the podman compose up and down commands used to create and remove services
# pylint: disable=redefined-outer-name # pylint: disable=redefined-outer-name
import os import os
from test_podman_compose import capture from .test_podman_compose import run_subprocess
import pytest from .test_podman_compose import podman_compose_path
from .test_podman_compose import test_path
from parameterized import parameterized
import unittest
@pytest.fixture def profile_compose_file():
def profile_compose_file(test_path):
""" "Returns the path to the `profile` compose file used for this test module""" """ "Returns the path to the `profile` compose file used for this test module"""
return os.path.join(test_path, "profile", "docker-compose.yml") return os.path.join(test_path(), "profile", "docker-compose.yml")
@pytest.fixture(autouse=True) class TestUpDown(unittest.TestCase):
def teardown(podman_compose_path, profile_compose_file): def tearDown(self):
""" """
Ensures that the services within the "profile compose file" are removed between each test case. Ensures that the services within the "profile compose file" are removed between each test case.
"""
# run the test case
:param podman_compose_path: The path to the podman compose script. down_cmd = [
:param profile_compose_file: The path to the compose file used for this test module. "coverage",
""" "run",
# run the test case podman_compose_path(),
yield "--profile",
"profile-1",
"--profile",
"profile-2",
"-f",
profile_compose_file(),
"down",
]
run_subprocess(down_cmd)
down_cmd = [ @parameterized.expand(
"coverage", [
"run", (
podman_compose_path, ["--profile", "profile-1", "up", "-d"],
"--profile", {"default-service": True, "service-1": True, "service-2": False},
"profile-1", ),
"--profile", (
"profile-2", ["--profile", "profile-2", "up", "-d"],
"-f", {"default-service": True, "service-1": False, "service-2": True},
profile_compose_file, ),
"down", (
] ["--profile", "profile-1", "--profile", "profile-2", "up", "-d"],
capture(down_cmd) {"default-service": True, "service-1": True, "service-2": True},
),
],
)
def test_up(self, profiles, expected_services):
up_cmd = [
"coverage",
"run",
podman_compose_path(),
"-f",
profile_compose_file(),
]
up_cmd.extend(profiles)
out, _, return_code = run_subprocess(up_cmd)
self.assertEqual(return_code, 0)
@pytest.mark.parametrize( check_cmd = [
"profiles, expected_services", "podman",
[ "container",
( "ps",
["--profile", "profile-1", "up", "-d"], "--format",
{"default-service": True, "service-1": True, "service-2": False}, '"{{.Names}}"',
), ]
( out, _, return_code = run_subprocess(check_cmd)
["--profile", "profile-2", "up", "-d"], self.assertEqual(return_code, 0)
{"default-service": True, "service-1": False, "service-2": True},
),
(
["--profile", "profile-1", "--profile", "profile-2", "up", "-d"],
{"default-service": True, "service-1": True, "service-2": True},
),
],
)
def test_up(podman_compose_path, profile_compose_file, profiles, expected_services):
up_cmd = [
"coverage",
"run",
podman_compose_path,
"-f",
profile_compose_file,
]
up_cmd.extend(profiles)
out, _, return_code = capture(up_cmd) self.assertEqual(len(expected_services), 3)
assert return_code == 0 actual_output = out.decode("utf-8")
check_cmd = [ actual_services = {}
"podman", for service, _ in expected_services.items():
"container", actual_services[service] = service in actual_output
"ps",
"--format",
'"{{.Names}}"',
]
out, _, return_code = capture(check_cmd)
assert return_code == 0
assert len(expected_services) == 3 self.assertEqual(expected_services, actual_services)
actual_output = out.decode("utf-8")
actual_services = {}
for service, _ in expected_services.items():
actual_services[service] = service in actual_output
assert expected_services == actual_services