From 07adc6ac665c7e31b411885fdacae71f33c80079 Mon Sep 17 00:00:00 2001 From: abikouo Date: Tue, 9 Sep 2025 13:00:49 +0200 Subject: [PATCH 01/11] wire pattern creation tasks to dispatcher --- core/tasks/common.py | 1 + core/tasks/demo.py | 2 +- core/{task_runner.py => tasks/pattern.py} | 16 ++++++++------ .../{test_tasks.py => test_pattern_tasks.py} | 20 +++++++++--------- core/views.py | 21 +++++++++++++++++++ 5 files changed, 43 insertions(+), 17 deletions(-) create mode 100644 core/tasks/common.py rename core/{task_runner.py => tasks/pattern.py} (89%) rename core/tests/{test_tasks.py => test_pattern_tasks.py} (93%) diff --git a/core/tasks/common.py b/core/tasks/common.py new file mode 100644 index 00000000..57fe3801 --- /dev/null +++ b/core/tasks/common.py @@ -0,0 +1 @@ +DISPATCHERD_DEFAULT_CHANNEL = "pattern-service-tasks" diff --git a/core/tasks/demo.py b/core/tasks/demo.py index 338ba0f8..e5fd943c 100644 --- a/core/tasks/demo.py +++ b/core/tasks/demo.py @@ -1,7 +1,7 @@ from dispatcherd.publish import submit_task from dispatcherd.publish import task -DISPATCHERD_DEFAULT_CHANNEL = "pattern-service-tasks" +from core.tasks.common import DISPATCHERD_DEFAULT_CHANNEL @task(queue=DISPATCHERD_DEFAULT_CHANNEL, decorate=False) diff --git a/core/task_runner.py b/core/tasks/pattern.py similarity index 89% rename from core/task_runner.py rename to core/tasks/pattern.py index 57a164ce..8ff77493 100644 --- a/core/task_runner.py +++ b/core/tasks/pattern.py @@ -3,6 +3,12 @@ import os from contextlib import closing +from dispatcherd.publish import task + +from core.models import Pattern +from core.models import PatternInstance +from core.models import Task +from core.tasks.common import DISPATCHERD_DEFAULT_CHANNEL from core.utils.controller import assign_execute_roles from core.utils.controller import build_collection_uri from core.utils.controller import create_execution_environment @@ -13,14 +19,11 @@ from core.utils.controller import get_http_session from core.utils.controller import save_instance_state -from .models import Pattern -from .models import PatternInstance -from .models import Task - logger = logging.getLogger(__name__) -def run_pattern_task(pattern_id: int, task_id: int) -> None: +@task(queue=DISPATCHERD_DEFAULT_CHANNEL, decorate=False) +def pattern_create(pattern_id: int, task_id: int) -> None: """ Orchestrates downloading a collection and saving a pattern definition. @@ -66,7 +69,8 @@ def run_pattern_task(pattern_id: int, task_id: int) -> None: task.mark_failed({"error": error_message}) -def run_pattern_instance_task(instance_id: int, task_id: int) -> None: +@task(queue=DISPATCHERD_DEFAULT_CHANNEL, decorate=False) +def pattern_instance_create(instance_id: int, task_id: int) -> None: task = Task.objects.get(id=task_id) try: instance = PatternInstance.objects.select_related("pattern").get(id=instance_id) diff --git a/core/tests/test_tasks.py b/core/tests/test_pattern_tasks.py similarity index 93% rename from core/tests/test_tasks.py rename to core/tests/test_pattern_tasks.py index 1158d166..8a7a1676 100644 --- a/core/tests/test_tasks.py +++ b/core/tests/test_pattern_tasks.py @@ -14,8 +14,8 @@ from core.models import Pattern from core.models import PatternInstance from core.models import Task -from core.task_runner import run_pattern_instance_task -from core.task_runner import run_pattern_task +from core.tasks.pattern import pattern_create +from core.tasks.pattern import pattern_instance_create class SharedDataMixin: @@ -83,11 +83,11 @@ def tearDown(self): class PatternTaskTest(SharedDataMixin, TestCase): @patch("core.models.Task.set_status", autospec=True, wraps=Task.set_status) @patch("core.task_runner.download_collection") - def test_run_pattern_task_success(self, mock_download, mock_update_status): + def test_pattern_create_success(self, mock_download, mock_update_status): temp_dir_path = self.create_temp_collection_dir() mock_download.return_value.__enter__.return_value = temp_dir_path - run_pattern_task(self.pattern.id, self.task.id) + pattern_create(self.pattern.id, self.task.id) expected_calls = [ (self.task, "Running", {"info": "Processing pattern"}), @@ -116,7 +116,7 @@ def test_run_pattern_task_success(self, mock_download, mock_update_status): @patch("core.models.Task.set_status", autospec=True) @patch("core.task_runner.download_collection", side_effect=FileNotFoundError) - def test_run_pattern_task_file_not_found(self, mock_download, mock_update_status): + def test_pattern_create_file_not_found(self, mock_download, mock_update_status): pattern = Pattern.objects.create( collection_name="demo.collection", collection_version="1.0.0", @@ -124,7 +124,7 @@ def test_run_pattern_task_file_not_found(self, mock_download, mock_update_status ) task = Task.objects.create(status="Initiated", details={}) - run_pattern_task(pattern.id, task.id) + pattern_create(pattern.id, task.id) mock_update_status.assert_called_with( task, "Failed", {"error": "Pattern definition not found."} @@ -133,8 +133,8 @@ def test_run_pattern_task_file_not_found(self, mock_download, mock_update_status @patch( "core.task_runner.download_collection", side_effect=Exception("Download failed") ) - def test_run_pattern_task_handles_download_failure(self, mock_download): - run_pattern_task(self.pattern.id, self.task.id) + def test_pattern_create_handles_download_failure(self, mock_download): + pattern_create(self.pattern.id, self.task.id) self.task.refresh_from_db() self.assertEqual(self.task.status, "Failed") self.assertIn("Download failed", self.task.details.get("error", "")) @@ -168,7 +168,7 @@ def test_run_pattern_instance_success( mock_create_labels.side_effect = [[]] mock_create_jts.side_effect = [[]] - run_pattern_instance_task( + pattern_instance_create( instance_id=self.pattern_instance.id, task_id=self.task.id, ) @@ -215,7 +215,7 @@ def test_failure_path( mock_create_project.side_effect = RuntimeError("error") # No exception should propagate because the task function swallows it - run_pattern_instance_task( + pattern_instance_create( instance_id=self.pattern_instance.id, task_id=self.task.id, ) diff --git a/core/views.py b/core/views.py index ddfca669..68258c13 100644 --- a/core/views.py +++ b/core/views.py @@ -1,6 +1,7 @@ import uuid from ansible_base.lib.utils.views.ansible_base import AnsibleBaseView +from dispatcherd.publish import submit_task from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema_view from rest_framework import status @@ -21,7 +22,10 @@ from core.serializers import PatternInstanceSerializer from core.serializers import PatternSerializer from core.serializers import TaskSerializer +from core.tasks.common import DISPATCHERD_DEFAULT_CHANNEL from core.tasks.demo import sumbit_hello_world +from core.tasks.pattern import pattern_create +from core.tasks.pattern import pattern_instance_create class CoreViewSet(AnsibleBaseView): @@ -61,6 +65,15 @@ def create(self, request: Request, *args: tuple, **kwargs: dict) -> Response: status="Initiated", details={"model": "Pattern", "id": pattern.id} ) + submit_task( + pattern_create, + queue=DISPATCHERD_DEFAULT_CHANNEL, + args=( + pattern.id, + task.id, + ), + ) + headers = self.get_success_headers(serializer.data) return Response( { @@ -129,6 +142,14 @@ def create(self, request: Request, *args: tuple, **kwargs: dict) -> Response: status="Initiated", details={"model": "PatternInstance", "id": instance.id} ) + submit_task( + pattern_instance_create, + queue=DISPATCHERD_DEFAULT_CHANNEL, + args=( + instance.id, + task.id, + ), + ) headers = self.get_success_headers(serializer.data) return Response( { From ed200c90a0477dc96dee2c60094fcf43ba61010f Mon Sep 17 00:00:00 2001 From: abikouo Date: Tue, 9 Sep 2025 13:17:53 +0200 Subject: [PATCH 02/11] Fix some tests --- core/tests/test_pattern_tasks.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/core/tests/test_pattern_tasks.py b/core/tests/test_pattern_tasks.py index 8a7a1676..0ff18aab 100644 --- a/core/tests/test_pattern_tasks.py +++ b/core/tests/test_pattern_tasks.py @@ -82,7 +82,7 @@ def tearDown(self): class PatternTaskTest(SharedDataMixin, TestCase): @patch("core.models.Task.set_status", autospec=True, wraps=Task.set_status) - @patch("core.task_runner.download_collection") + @patch("core.tasks.pattern.download_collection") def test_pattern_create_success(self, mock_download, mock_update_status): temp_dir_path = self.create_temp_collection_dir() mock_download.return_value.__enter__.return_value = temp_dir_path @@ -115,7 +115,7 @@ def test_pattern_create_success(self, mock_download, mock_update_status): self.assertEqual(self.pattern.pattern_definition, {"mock_key": "mock_value"}) @patch("core.models.Task.set_status", autospec=True) - @patch("core.task_runner.download_collection", side_effect=FileNotFoundError) + @patch("core.tasks.pattern.download_collection", side_effect=FileNotFoundError) def test_pattern_create_file_not_found(self, mock_download, mock_update_status): pattern = Pattern.objects.create( collection_name="demo.collection", @@ -131,7 +131,7 @@ def test_pattern_create_file_not_found(self, mock_download, mock_update_status): ) @patch( - "core.task_runner.download_collection", side_effect=Exception("Download failed") + "core.tasks.pattern.download_collection", side_effect=Exception("Download failed") ) def test_pattern_create_handles_download_failure(self, mock_download): pattern_create(self.pattern.id, self.task.id) @@ -141,13 +141,13 @@ def test_pattern_create_handles_download_failure(self, mock_download): class PatternInstanceTaskTest(SharedDataMixin, TestCase): - @patch("core.task_runner.get_http_session") - @patch("core.task_runner.assign_execute_roles") - @patch("core.task_runner.save_instance_state") - @patch("core.task_runner.create_job_templates") - @patch("core.task_runner.create_labels") - @patch("core.task_runner.create_execution_environment") - @patch("core.task_runner.create_project") + @patch("core.tasks.pattern.get_http_session") + @patch("core.tasks.pattern.assign_execute_roles") + @patch("core.tasks.pattern.save_instance_state") + @patch("core.tasks.pattern.create_job_templates") + @patch("core.tasks.pattern.create_labels") + @patch("core.tasks.pattern.create_execution_environment") + @patch("core.tasks.pattern.create_project") @patch("core.models.Task.set_status", autospec=True) def test_run_pattern_instance_success( self, @@ -198,12 +198,12 @@ def test_run_pattern_instance_success( mock_save_instance.assert_called_once() mock_assign_roles.assert_called_once() - @patch("core.task_runner.assign_execute_roles") - @patch("core.task_runner.save_instance_state") - @patch("core.task_runner.create_job_templates") - @patch("core.task_runner.create_labels") - @patch("core.task_runner.create_execution_environment") - @patch("core.task_runner.create_project") + @patch("core.tasks.pattern.assign_execute_roles") + @patch("core.tasks.pattern.save_instance_state") + @patch("core.tasks.pattern.create_job_templates") + @patch("core.tasks.pattern.create_labels") + @patch("core.tasks.pattern.create_execution_environment") + @patch("core.tasks.pattern.create_project") @patch("core.models.Task.set_status", autospec=True) def test_failure_path( self, From e8469988b3864885857da008d19d94577c6f8f89 Mon Sep 17 00:00:00 2001 From: abikouo Date: Tue, 9 Sep 2025 13:58:49 +0200 Subject: [PATCH 03/11] view tests --- core/tests/test_pattern_tasks.py | 3 ++- core/tests/test_views.py | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/core/tests/test_pattern_tasks.py b/core/tests/test_pattern_tasks.py index 0ff18aab..c4f92c1f 100644 --- a/core/tests/test_pattern_tasks.py +++ b/core/tests/test_pattern_tasks.py @@ -131,7 +131,8 @@ def test_pattern_create_file_not_found(self, mock_download, mock_update_status): ) @patch( - "core.tasks.pattern.download_collection", side_effect=Exception("Download failed") + "core.tasks.pattern.download_collection", + side_effect=Exception("Download failed"), ) def test_pattern_create_handles_download_failure(self, mock_download): pattern_create(self.pattern.id, self.task.id) diff --git a/core/tests/test_views.py b/core/tests/test_views.py index f7e109d0..e6d333f0 100644 --- a/core/tests/test_views.py +++ b/core/tests/test_views.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + from django.urls import reverse from rest_framework import status from rest_framework.test import APITestCase @@ -60,7 +62,8 @@ def test_pattern_detail_view(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["collection_name"], "mynamespace.mycollection") - def test_pattern_create_view(self): + @patch("core.viewas.submit_task") + def test_pattern_create_view(self, mock_submit_task): url = reverse("pattern-list") data = { "collection_name": "new.namespace.collection", @@ -80,6 +83,9 @@ def test_pattern_create_view(self): task_id = response.data.get("task_id") self.assertIsInstance(task_id, int) + # assert dispatcher task created + mock_submit_task.assert_called_once() + # Task exists task = Task.objects.get(id=task_id) self.assertEqual(task.status, "Initiated") @@ -149,7 +155,8 @@ def test_pattern_instance_detail_view(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["organization_id"], 1) - def test_pattern_instance_create_view(self): + @patch("core.viewas.submit_task") + def test_pattern_instance_create_view(self, mock_submit_task): url = reverse("pattern_instance-list") data = { "organization_id": 2, @@ -170,6 +177,9 @@ def test_pattern_instance_create_view(self): self.assertEqual(instance.organization_id, 2) self.assertEqual(instance.credentials["user"], "tester") + # assert dispatcher task created + mock_submit_task.assert_called_once() + # Task id returned directly task_id = response.data.get("task_id") self.assertIsInstance(task_id, int) From 4f646eb8ed6f0c79191f81702342b6b876e4d77e Mon Sep 17 00:00:00 2001 From: abikouo Date: Tue, 9 Sep 2025 14:00:37 +0200 Subject: [PATCH 04/11] Fix typo --- core/tests/test_views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/test_views.py b/core/tests/test_views.py index e6d333f0..afb85178 100644 --- a/core/tests/test_views.py +++ b/core/tests/test_views.py @@ -62,7 +62,7 @@ def test_pattern_detail_view(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["collection_name"], "mynamespace.mycollection") - @patch("core.viewas.submit_task") + @patch("core.views.submit_task") def test_pattern_create_view(self, mock_submit_task): url = reverse("pattern-list") data = { @@ -155,7 +155,7 @@ def test_pattern_instance_detail_view(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["organization_id"], 1) - @patch("core.viewas.submit_task") + @patch("core.views.submit_task") def test_pattern_instance_create_view(self, mock_submit_task): url = reverse("pattern_instance-list") data = { From c27d6a843d516c1127ab416b1c3a4e967f1fbdb0 Mon Sep 17 00:00:00 2001 From: abikouo Date: Tue, 9 Sep 2025 16:57:45 +0200 Subject: [PATCH 05/11] mock locally --- core/tests/test_views.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/tests/test_views.py b/core/tests/test_views.py index afb85178..9834e7ca 100644 --- a/core/tests/test_views.py +++ b/core/tests/test_views.py @@ -62,8 +62,7 @@ def test_pattern_detail_view(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["collection_name"], "mynamespace.mycollection") - @patch("core.views.submit_task") - def test_pattern_create_view(self, mock_submit_task): + def test_pattern_create_view(self): url = reverse("pattern-list") data = { "collection_name": "new.namespace.collection", @@ -72,7 +71,9 @@ def test_pattern_create_view(self, mock_submit_task): "pattern_name": "new_pattern", } - response = self.client.post(url, data, format="json") + with patch("core.views.submit_task") as mock_submit_task: + response = self.client.post(url, data, format="json") + mock_submit_task.assert_called_once() self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) # Pattern created @@ -83,9 +84,6 @@ def test_pattern_create_view(self, mock_submit_task): task_id = response.data.get("task_id") self.assertIsInstance(task_id, int) - # assert dispatcher task created - mock_submit_task.assert_called_once() - # Task exists task = Task.objects.get(id=task_id) self.assertEqual(task.status, "Initiated") From 257575c61d49a97315f394de584660c048a8d740 Mon Sep 17 00:00:00 2001 From: abikouo Date: Wed, 10 Sep 2025 12:26:19 +0200 Subject: [PATCH 06/11] test views --- core/tests/test_api_v1.py | 8 ++++++-- core/tests/test_views.py | 11 ++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/tests/test_api_v1.py b/core/tests/test_api_v1.py index b1a646f2..da5c92be 100644 --- a/core/tests/test_api_v1.py +++ b/core/tests/test_api_v1.py @@ -1,3 +1,5 @@ +from unittest.mock import patch + import pytest from freezegun import freeze_time from rest_framework import status @@ -39,7 +41,8 @@ def test_list_controller_labels_success(client, controller_label): assert response.json() == [api_examples.controller_label_get_response.value] -def test_create_pattern_success(client, db): +@patch("core.views.submit_task", return_value=False) +def test_create_pattern_success(mock_submit_task, client, db): url = "/api/pattern-service/v1/patterns/" data = api_examples.pattern_post_request.value response = client.post(url, data, format="json") @@ -61,7 +64,8 @@ def test_list_patterns_success(client, pattern): assert response.json() == [api_examples.pattern_get_response.value] -def test_create_pattern_instance_success(client, pattern): +@patch("core.views.submit_task", return_value=False) +def test_create_pattern_instance_success(mock_submit_task, client, pattern): url = "/api/pattern-service/v1/pattern_instances/" data = api_examples.pattern_instance_post_request.value data["pattern"] = pattern.pk diff --git a/core/tests/test_views.py b/core/tests/test_views.py index 9834e7ca..acf1e376 100644 --- a/core/tests/test_views.py +++ b/core/tests/test_views.py @@ -62,7 +62,8 @@ def test_pattern_detail_view(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["collection_name"], "mynamespace.mycollection") - def test_pattern_create_view(self): + @patch("core.views.submit_task", return_value=False) + def test_pattern_create_view(self, mock_submit_task): url = reverse("pattern-list") data = { "collection_name": "new.namespace.collection", @@ -71,11 +72,11 @@ def test_pattern_create_view(self): "pattern_name": "new_pattern", } - with patch("core.views.submit_task") as mock_submit_task: - response = self.client.post(url, data, format="json") - mock_submit_task.assert_called_once() + response = self.client.post(url, data, format="json") self.assertEqual(response.status_code, status.HTTP_202_ACCEPTED) + mock_submit_task.assert_called_once() + # Pattern created pattern = Pattern.objects.get(pattern_name="new_pattern") self.assertIsNotNone(pattern) @@ -153,7 +154,7 @@ def test_pattern_instance_detail_view(self): self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data["organization_id"], 1) - @patch("core.views.submit_task") + @patch("core.views.submit_task", return_value=False) def test_pattern_instance_create_view(self, mock_submit_task): url = reverse("pattern_instance-list") data = { From b83a518292fc61d61d6f5182a50c340f272d74a5 Mon Sep 17 00:00:00 2001 From: abikouo Date: Thu, 11 Sep 2025 17:00:22 +0200 Subject: [PATCH 07/11] integration tests --- core/tests/integration/test_pattern.py | 35 +++++++++++++ pattern_service/settings/testing_defaults.py | 27 +++++++++- tools/podman/compose-test.yaml | 54 +++++++++++++++++++- tools/podman/hub-api-config-test.json | 14 +++++ 4 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 core/tests/integration/test_pattern.py create mode 100644 tools/podman/hub-api-config-test.json diff --git a/core/tests/integration/test_pattern.py b/core/tests/integration/test_pattern.py new file mode 100644 index 00000000..1b8d1137 --- /dev/null +++ b/core/tests/integration/test_pattern.py @@ -0,0 +1,35 @@ +import pytest +from rest_framework import status +from rest_framework.test import APIClient + + +@pytest.mark.django_db +def test_create_pattern(): + client = APIClient() + data = { + "collection_name": "ansible.test", + "collection_version": "1.2.3", + "pattern_name": "new_pattern", + } + response = client.post("/api/pattern-service/v1/patterns/", data, format="json") + + assert response.status_code == status.HTTP_202_ACCEPTED + assert "task_id" in response.data + assert "message" in response.data + assert response.data["message"] == "Pattern creation initiated. Check task status for progress." + + +@pytest.mark.django_db +def test_create_pattern_instance(): + client = APIClient() + data = { + "collection_name": "ansible.test", + "collection_version": "1.2.3", + "pattern_name": "new_pattern", + } + response = client.post("/api/pattern-service/v1/patterns/", data, format="json") + + assert response.status_code == status.HTTP_202_ACCEPTED + assert "task_id" in response.data + assert "message" in response.data + assert response.data["message"] == "Pattern creation initiated. Check task status for progress." diff --git a/pattern_service/settings/testing_defaults.py b/pattern_service/settings/testing_defaults.py index 33ce0e58..34b3d01e 100644 --- a/pattern_service/settings/testing_defaults.py +++ b/pattern_service/settings/testing_defaults.py @@ -5,7 +5,7 @@ SECRET_KEY = "insecure" DB_NAME = "test_pattern_db" -DB_USER = "postgres" +DB_USER = "pattern" DB_PASSWORD = "insecure" DATABASES = { @@ -22,6 +22,31 @@ }, } +DEBUG = True + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "simple": { + "format": "{levelname} {name} {lineno} {message}", + "style": "{", + }, + }, + "handlers": { + "console": { + "level": DEBUG, + "class": "logging.StreamHandler", + "formatter": "simple", + }, + }, + "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, + "loggers": { + "dispatcherd": {"handlers": ["console"], "level": "INFO"}, + }, +} + + # Base URL of your AAP service AAP_URL = "http://localhost:44926" # or your default URL diff --git a/tools/podman/compose-test.yaml b/tools/podman/compose-test.yaml index 7761312f..fe420f81 100644 --- a/tools/podman/compose-test.yaml +++ b/tools/podman/compose-test.yaml @@ -2,6 +2,58 @@ services: postgres: image: quay.io/sclorg/postgresql-15-c9s:latest environment: - POSTGRESQL_ADMIN_PASSWORD: insecure + POSTGRESQL_USER: pattern + POSTGRESQL_PASSWORD: insecure + POSTGRESQL_DATABASE: test_pattern_db ports: - '5432:5432' + # volumes: + # - 'postgres_data:/var/lib/pgsql/data' + healthcheck: + test: ["CMD", "pg_isready", "-U", "pattern", "-d", "pattern_db"] + interval: 5s + timeout: 5s + retries: 3 + start_period: 5s + # ports: + # - '5432:5432' + # healthcheck: + # test: [ 'CMD', 'pg_isready', '-U', 'postgres' ] + # interval: 5s + # timeout: 5s + # retries: 3 + # start_period: 5s + worker: + image: localhost/pattern-service-worker + environment: + PATTERN_SERVICE_DISPATCHER_NODE_ID: test-worker + PATTERN_SERVICE_MODE: testing + PATTERN_SERVICE_DB_HOST: postgres + PATTERN_SERVICE_DB_PORT: 5432 + # PATTERN_SERVICE_DB_NAME: pattern_db + PATTERN_SERVICE_DB_USER: pattern + # PATTERN_SERVICE_DB_PASSWORD: pattern123 + build: + context: ../../ + dockerfile: tools/podman/Containerfile.dev + command: + - /bin/bash + - -c + - >- + python3.11 /app/manage.py makemigrations + && python3.11 /app/manage.py migrate + && python3.11 /app/manage.py worker + depends_on: + postgres: + condition: service_healthy + restart: always + hub_api: + image: docker.io/mockserver/mockserver:latest + ports: + - 44926:1080 + environment: + MOCKSERVER_WATCH_INITIALIZATION_JSON: "true" + MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties + MOCKSERVER_INITIALIZATION_JSON_PATH: /config/initializerJson.json + volumes: + - ./hub-api-config-test.json:/config/initializerJson.json:Z diff --git a/tools/podman/hub-api-config-test.json b/tools/podman/hub-api-config-test.json new file mode 100644 index 00000000..4a007a1c --- /dev/null +++ b/tools/podman/hub-api-config-test.json @@ -0,0 +1,14 @@ +[ + { + "httpRequest" : { + "method" : "GET", + "path" : "/api/galaxy/v3/plugin/ansible/content/published/collections/artifacts/ansible.test-1.2.3.tar.gz" + }, + "httpResponse" : { + "body" : { + "content": "collection content" + }, + "statusCode": 200 + } + } +] From 98bbefbb6f5e529a81cdb02095d011edc305ddb8 Mon Sep 17 00:00:00 2001 From: abikouo Date: Fri, 12 Sep 2025 17:01:31 +0200 Subject: [PATCH 08/11] minor updates --- core/tests/integration/test_pattern.py | 10 ++++++++-- pattern_service/settings/dispatcher.py | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/core/tests/integration/test_pattern.py b/core/tests/integration/test_pattern.py index 1b8d1137..fcfcd597 100644 --- a/core/tests/integration/test_pattern.py +++ b/core/tests/integration/test_pattern.py @@ -16,7 +16,10 @@ def test_create_pattern(): assert response.status_code == status.HTTP_202_ACCEPTED assert "task_id" in response.data assert "message" in response.data - assert response.data["message"] == "Pattern creation initiated. Check task status for progress." + assert ( + response.data["message"] + == "Pattern creation initiated. Check task status for progress." + ) @pytest.mark.django_db @@ -32,4 +35,7 @@ def test_create_pattern_instance(): assert response.status_code == status.HTTP_202_ACCEPTED assert "task_id" in response.data assert "message" in response.data - assert response.data["message"] == "Pattern creation initiated. Check task status for progress." + assert ( + response.data["message"] + == "Pattern creation initiated. Check task status for progress." + ) diff --git a/pattern_service/settings/dispatcher.py b/pattern_service/settings/dispatcher.py index f5d331fb..77d63fb1 100644 --- a/pattern_service/settings/dispatcher.py +++ b/pattern_service/settings/dispatcher.py @@ -32,6 +32,22 @@ def override_dispatcher_settings(loaded_settings: Dynaconf) -> None: db_sslkey = loaded_settings.get("DB_SSLKEY", default="") db_sslrootcert = loaded_settings.get("DB_SSLROOTCERT", default="") + if loaded_settings.get("MODE", "development") == "development": + databases["default"] = { + "ENGINE": "django.db.backends.postgresql", + "HOST": db_host, + "PORT": db_port, + "USER": db_user, + "PASSWORD": db_user_pass, + "NAME": db_name, + "OPTIONS": { + "sslmode": db_sslmode, + "sslcert": db_sslcert, + "sslkey": db_sslkey, + "sslrootcert": db_sslrootcert, + }, + } + databases["dispatcher"] = { "ENGINE": "django.db.backends.postgresql", "HOST": db_host, From 77b96f0273b8f8ad39c10ebb13a4e135955091bb Mon Sep 17 00:00:00 2001 From: Bikouo Aubin <79859644+abikouo@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:42:01 +0200 Subject: [PATCH 09/11] Update tools/podman/hub-api-config-test.json Co-authored-by: Bianca Henderson --- tools/podman/hub-api-config-test.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/podman/hub-api-config-test.json b/tools/podman/hub-api-config-test.json index 4a007a1c..66676f29 100644 --- a/tools/podman/hub-api-config-test.json +++ b/tools/podman/hub-api-config-test.json @@ -1,11 +1,11 @@ [ { - "httpRequest" : { - "method" : "GET", - "path" : "/api/galaxy/v3/plugin/ansible/content/published/collections/artifacts/ansible.test-1.2.3.tar.gz" + "httpRequest": { + "method": "GET", + "path": "/api/galaxy/v3/plugin/ansible/content/published/collections/artifacts/ansible.test-1.2.3.tar.gz" }, - "httpResponse" : { - "body" : { + "httpResponse": { + "body": { "content": "collection content" }, "statusCode": 200 From 66c289cd6d0486f6bd8cd7e03f8d7acb76e1bfb0 Mon Sep 17 00:00:00 2001 From: abikouo Date: Tue, 16 Sep 2025 17:42:39 +0200 Subject: [PATCH 10/11] update preload_modules --- pattern_service/settings/defaults.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pattern_service/settings/defaults.py b/pattern_service/settings/defaults.py index 1c6cfaeb..0d6d3e39 100644 --- a/pattern_service/settings/defaults.py +++ b/pattern_service/settings/defaults.py @@ -115,9 +115,7 @@ "version": 2, "service": { "main_kwargs": {"node_id": "pattern-service-a"}, - "process_manager_kwargs": { - "preload_modules": ["pattern_service.core.tasks.hazmat"] - }, + "process_manager_kwargs": {"preload_modules": ["core.tasks.hazmat"]}, }, "brokers": { "pg_notify": { From c46f3f7a7c8c81e47a35793ce32e47f221c8a66b Mon Sep 17 00:00:00 2001 From: abikouo Date: Wed, 17 Sep 2025 16:01:07 +0200 Subject: [PATCH 11/11] add server for integration tests --- core/tests/conftest.py | 5 + .../collections/cloud-testing-4.3.2.tar.gz | Bin 0 -> 5129 bytes core/tests/integration/test_pattern.py | 23 +--- core/tests/test_api_v1.py | 105 ++++++++++++++++-- pattern_service/settings/dispatcher.py | 29 +++-- pattern_service/settings/testing_defaults.py | 4 +- tools/podman/Containerfile.hub | 15 +++ tools/podman/compose-test.yaml | 42 +++---- tools/podman/hub-api-config-test.json | 14 --- 9 files changed, 147 insertions(+), 90 deletions(-) create mode 100644 core/tests/integration/collections/cloud-testing-4.3.2.tar.gz create mode 100644 tools/podman/Containerfile.hub delete mode 100644 tools/podman/hub-api-config-test.json diff --git a/core/tests/conftest.py b/core/tests/conftest.py index 110f6825..b4916eb7 100644 --- a/core/tests/conftest.py +++ b/core/tests/conftest.py @@ -13,6 +13,7 @@ def client(): @pytest.fixture() def automation(db, pattern_instance) -> models.Automation: + models.Automation.objects.all().delete() automation = models.Automation.objects.create( automation_type=api_examples.automation_get_response.value["automation_type"], automation_id=api_examples.automation_get_response.value["automation_id"], @@ -24,6 +25,7 @@ def automation(db, pattern_instance) -> models.Automation: @pytest.fixture() def controller_label(db) -> models.ControllerLabel: + models.ControllerLabel.objects.all().delete() controller_label = models.ControllerLabel.objects.create( label_id=api_examples.controller_label_get_response.value["label_id"] ) @@ -32,6 +34,7 @@ def controller_label(db) -> models.ControllerLabel: @pytest.fixture() def pattern(db) -> models.Pattern: + models.Pattern.objects.all().delete() pattern = models.Pattern.objects.create( collection_name=api_examples.pattern_post_request.value["collection_name"], collection_version=api_examples.pattern_post_request.value[ @@ -44,6 +47,7 @@ def pattern(db) -> models.Pattern: @pytest.fixture() def pattern_instance(db, pattern) -> models.PatternInstance: + models.PatternInstance.objects.all().delete() pattern_instance = models.PatternInstance.objects.create( credentials=api_examples.pattern_instance_post_request.value["credentials"], executors=api_examples.pattern_instance_post_request.value["executors"], @@ -55,6 +59,7 @@ def pattern_instance(db, pattern) -> models.PatternInstance: @pytest.fixture() def task(db) -> models.Task: + models.Task.objects.all().delete() task = models.Task.objects.create( status=api_examples.task_get_response.value["status"], details=api_examples.task_get_response.value["details"], diff --git a/core/tests/integration/collections/cloud-testing-4.3.2.tar.gz b/core/tests/integration/collections/cloud-testing-4.3.2.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..655cc3c612a50e943cc4ac578f80673f68469ae3 GIT binary patch literal 5129 zcmV+k6!z;MiwFp2a>{4||6^=#b!07cWpi|CZf7ktE;BANE_7jX0PQ_%bK5qy`K(`o zRd&Wn#-#YB=sVeoo4Boa(=?Mf+qu)*(SRT*v8G5aLD^Q*{P#TvASFt%9VfAs+yB#=_icu^12SY~o^#e@WiiQ$v>YA~h$X{<&bg>4;}9`qdAUf2`&~Rn zHC!}VF;$UgycPfe<2}3kId3s&=v2Z~*%B^TomEX+{o$PcQ5NLzm)E2&b1n!i z5>n-K8JFd$AZb~X77bHD%QMCc!PfxWh)J5|vi9f4Pmf-_J{q>QRlG=e!7`3GynWZA zT9qPe%6f?fu5O*N?rSlUeQMHkF)1t2LND23&Wnb2tx?gla?UF{-%k@KiNhddG>U@&UK1;Aktpjq zZ2+F#wPPLe?w$@e3G0EqyE;A~|NYPjH|4(-`hlb6|E}ae@^B}k-%2^%vAvRzJ$6sB zx{cao_hOOf8}Tb%U#IlhRr2e0WvndHZ2ASNTnWrPD;je-&$n?{NxAF9+$D03g#nL3 z$FqI-5OK23i5p`TAu>Hu5 zW7iu)3Jk92Mv(&}Yo%%6ayO2cH~iGr00r`CEp3EYXN7tp%+;;^BtHX zqzNB;QNo>w2eHTeXdH$-Wk94}?EBbw#(^R~xji|Jd2ST^+)(zCE!bY&s5NF5z|<`^@K2#O>5hU@ZMlZi@h;wOQU~Q@EAH zF7<$0tTFSz6p2A%C!RZYpx0g7=V=-{sl{AB9LJ#@TaN8B126KDz)PZN>_H>P{9^<_ zHZ97U-xlJ2=rac<2R4hq7PNS3TRsm`%T1#+O>7WY7BKpF%z_|+G;f^RksW%zM{S>3 z$xV5JUoss`kT_Og+sunS3v521LW^C7(9?HAm*? z$xC|iBc}|T6XmX4LGj#0$ z1noetj{NmQHv_)QI*_ShzIZkN!*jd-V|h-9>p!j=+#|l`|GOW5E&sLr*Yf|Hm;c)< zpgUauw_pOdDgSN9^DVvpzl&fP#-OT8oX;E(ct{pYry%JQIrO5M`HzYJ~pSBTvjAvRxa`cVKxv&YM%2mS~LW6tMWO;&FNpK zS@Rnff5pWjZ?MK@#-ZGNju_7AlAO_cNy>tVMJ)J#7960|i=xE>5ST&6oYx5Cgo;xE z0I1ADWJA^RZPH7i=^JaY1Fiox|7-oP!)5&6Z5`h6{O>yb?*;bu z{4ekVt^aqW|K&nxA`VExQwoZ5FsK(fm%1gcDN|1b^yP&i&w~lj%OXp-T7D!1)GyBu zl%{XXobu))OLZro3>*(IJd#XMP-&0?)CqPeFQ(0`9}SjCGh6m!GCY^1j!vn%RlNtq zk#8#5PpODpLl+6&J*_0PplT@hXqNx_mUri`#{jc@^rv492 zJMiIWto8r5p#R%_=)0`rQ`UcdSFitS`LD-c%YPkqA^+D$s=fmGZ`+=2Y5A|^zaD=r z|8>}{{NFf|erx=H-TI#&hFkLAgBgHc|Jy~t!;lB$X*cErRu*hg*Sug$Ttl1I<)S(u ze}DUywEl%1p^$g)j9zrjQ;2~Qj24XNMphVAT~2E*M7xPNP^)s)BF`r^uEDA8L_+Z7 zWV=|1+gD}93!bcAYrYjB59RmX7sYmrSQcznmqqpmmvM0oIL-QX;8x-~dxPtT#v_Fc zo%ZtWJ=z-#elKGg)vPdH2wXkvw#*COV5hVWm=oGMTz-JFo=6wg`-BK^PR z#C7NT--Zxu>wnMJ`+qys|Hr+YUWzeRgbSvj9<1N4zyn*q30nP=#3q8cC% z{H!~mUr0vc86^V-iY($q(JTyg-bp6$;;btu%0y6Fg6LgumF$|e|3y>V!4+6kkT|t>|-DRC!%h@RuA?uqk z+8nC^?5@XXb3Qq9S2@mM@D5gQz!MaApb;tPWOLoSYux47@XM&L&rjwIj4e~l=w`u` zH*97kyuw=W759mpp=W_-Vg zdKZpNno^ehaECQzL-TgTo$W?H%LLxtvT9OGwX5R532M{63!KeLbvI2%UypB2%fo8{ zNkV=6kyL#JNNn!o8`Ae0gqZO>KOiv`vyBPKHS%Cvl=MlXlan&f2Ra93;7a(tCuGnY zbfg|TY&-I4Zw7)KbG&DwDT`H>YT3-nqL;%pZD-iBGpHjzU3S&r@lqqDNKYxv1#hG0 zw5hX;H3@ct)<8DErloZ`|0j&ZgX`Le{rs?No1(j6&kU0DTve0Vt6()Umx`WOc*a$A z-Ue^trA9{EsaI2RHm}OMAxJ~}q!hgrWI3sIAC$s`amYY*ngHy<-VetoKfd{H^7EUM zmv2t?9`2JN9qz+Wu`Eq#|NsaOFA)z2WHn>*y@60@}vxYxN_cr*x-*fUgoqZ68S%Bp)12#rK!>D>44j({g>CROF?kfmJ=5LZV;aW(x#w7L}oLKRNTRZiUQYtc1?h^|XcV%OmR) zbZt8nTWB9|rW&lQIeE~5-4cTU_u(d?{KI$;IhPo7Qe{d3 zOdun3`bQ6}VYJ8S45+s28$%MsP3Y_;w7Bb}o((|ym!*+3tASZW*&f30a;*v`C1fhf zE#2Pr>Xhd6VtH9P!>FM8J)?e)GDq(ng&!dqW;Mr?hVllCJz`X3g!ikBpt^$BqyD97 z+wJ^rk>!bWZ+O488&ZCI;6BL!i?Oyz3u>~i>3SQi%iq#-e+V{pXK#vd6=!dY6A(}h z$E)>5(60pABngVu98Xqe0Q+_V&$8s^R%fpvN}{%%u@a_RSvRFuS3NTD zgk_UczHDJ}Xd;w8eDV7DyXQxfr$4`ff5(%59sTcyoH*y$Zc@{86o5FpWU9v~Szizc z10*>>(&nmYGs}cDa-`!&=cb}eUSy17uOTyfhM?pW7HNzgW(e{&xdg-~Ya2 z{r_Bz`z}s^irM=oL03jYOMjnr(y}Qpj2<0#t3Lfpjy`CTTy9r<+%ii(ea;G(9X#0Ox>wh@x zc-(xVjPXeY_I!46Kvpy{>T-zDIyQ<{bJh@O43zeh5Pk}(IWf4#0mUmUcpiAC6Zn>%b z#>}L>VtzEpT4#t>q$qT%B`?kf=H{}ZOvoxsxsOhujFmNdv?9$_%ZiRv;(b3F?*pBY zOaKj__YCg1CyFxVnmu`$Rfq(1I6Fl*oX({Jj~doxZc6w+mgm6o^*&}`6nLjhrDF(< zOC_xK(L-lO&kzM<6#VX{p+0w$u}zn^vfg-?@Y|9FItP_P7##>A78O$Ne%o02eVUbT zTpz0Z22E0;OAu~f>|-_PH+O&S{YCH(PcK3T^vgmM`<)<1_vj_@$Ik!YzDa^&ftyp4 z^!y8-0m6y#^V6~bGXX>tZkI^Gt7Ik@D0@-N0;|?7kqwx9i8*Zy0VI(ua^8zGt1OY^ z2gQFv_Zg6uSi2JHARLhI6iFN@4`S)NGB721oZ)}?1FE)_(Lc3^?)`JbNu_2++K6q$kRM}hC^`QJAXdLO~Q)A`>` z?|%+##s8M4?|;_oe|r3PE&s=kZ;pc~^2WB7|KFDUuPEL&a0iOuqvXHm$o)Ux*79G= ze?9)Ymj99EnW62Bqd@Qfe5>+b(0P^f(XI0TP33P-P`|>Yub?q zYCy05eB<&THz!B8#s4>!|DKQczdM1J|62a*@&7{d-vRmWYx%F`e-|mYDnEz+uRH$@ z*8Z0K4+2-8|Jq3yD1WS>nDIHC^n9>v(=x5r<7-=Omlvb0Pt9xq&K>GE!fxe7V2OV_KK?fal r&_M?sbkIQu9dyt^2OV_KK?fal&_M?sbkISEZz%j1L*Cq?0LTCUR_rEr literal 0 HcmV?d00001 diff --git a/core/tests/integration/test_pattern.py b/core/tests/integration/test_pattern.py index fcfcd597..cd72396e 100644 --- a/core/tests/integration/test_pattern.py +++ b/core/tests/integration/test_pattern.py @@ -7,27 +7,8 @@ def test_create_pattern(): client = APIClient() data = { - "collection_name": "ansible.test", - "collection_version": "1.2.3", - "pattern_name": "new_pattern", - } - response = client.post("/api/pattern-service/v1/patterns/", data, format="json") - - assert response.status_code == status.HTTP_202_ACCEPTED - assert "task_id" in response.data - assert "message" in response.data - assert ( - response.data["message"] - == "Pattern creation initiated. Check task status for progress." - ) - - -@pytest.mark.django_db -def test_create_pattern_instance(): - client = APIClient() - data = { - "collection_name": "ansible.test", - "collection_version": "1.2.3", + "collection_name": "cloud.testing", + "collection_version": "4.3.2", "pattern_name": "new_pattern", } response = client.post("/api/pattern-service/v1/patterns/", data, format="json") diff --git a/core/tests/test_api_v1.py b/core/tests/test_api_v1.py index da5c92be..051c7fa8 100644 --- a/core/tests/test_api_v1.py +++ b/core/tests/test_api_v1.py @@ -17,14 +17,50 @@ def test_retrieve_automation_success(client, automation): url = f"/api/pattern-service/v1/automations/{automation.pk}/" response = client.get(url) assert response.status_code == status.HTTP_200_OK - assert response.json() == api_examples.automation_get_response.value + expected = api_examples.automation_get_response.value + pattern_instance_id = response.json().get("pattern_instance") + expected.update( + { + "url": f"/api/pattern-service/v1/automations/{automation.pk}/", + "id": automation.pk, + "pattern_instance": pattern_instance_id, + "related": { + "pattern_instance": ( + f"/api/pattern-service/v1/pattern_instances/{pattern_instance_id}/" + ), + }, + "summary_fields": {"pattern_instance": {"id": 1}}, + } + ) + assert response.json() == expected def test_list_automations_success(client, automation): url = "/api/pattern-service/v1/automations/" response = client.get(url) assert response.status_code == status.HTTP_200_OK - assert response.json() == [api_examples.automation_get_response.value] + expected = api_examples.automation_get_response.value + expected.update( + { + "url": f"/api/pattern-service/v1/automations/{automation.pk}/", + "id": automation.pk, + } + ) + pattern_instance_id = response.json()[0].get("pattern_instance") + expected.update( + { + "url": f"/api/pattern-service/v1/automations/{automation.pk}/", + "id": automation.pk, + "pattern_instance": pattern_instance_id, + "related": { + "pattern_instance": ( + f"/api/pattern-service/v1/pattern_instances/{pattern_instance_id}/" + ), + }, + "summary_fields": {"pattern_instance": {"id": pattern_instance_id}}, + } + ) + assert response.json() == [expected] def test_retrieve_controller_label_success(client, controller_label): @@ -38,7 +74,14 @@ def test_list_controller_labels_success(client, controller_label): url = "/api/pattern-service/v1/controller_labels/" response = client.get(url) assert response.status_code == status.HTTP_200_OK - assert response.json() == [api_examples.controller_label_get_response.value] + expected = api_examples.controller_label_get_response.value + expected.update( + { + "url": f"/api/pattern-service/v1/controller_labels/{controller_label.pk}/", + "id": controller_label.pk, + } + ) + assert response.json() == [expected] @patch("core.views.submit_task", return_value=False) @@ -47,13 +90,20 @@ def test_create_pattern_success(mock_submit_task, client, db): data = api_examples.pattern_post_request.value response = client.post(url, data, format="json") assert response.status_code == status.HTTP_202_ACCEPTED - assert response.json() == api_examples.pattern_post_response.value + assert response.json().get( + "message" + ) == api_examples.pattern_post_response.value.get("message") + assert isinstance(response.json().get("task_id"), int) def test_retrieve_pattern_success(client, pattern): url = f"/api/pattern-service/v1/patterns/{pattern.pk}/" response = client.get(url) assert response.status_code == status.HTTP_200_OK + expected = api_examples.pattern_get_response.value + expected.update( + {"url": f"/api/pattern-service/v1/patterns/{pattern.pk}/", "id": pattern.pk} + ) assert response.json() == api_examples.pattern_get_response.value @@ -61,7 +111,11 @@ def test_list_patterns_success(client, pattern): url = "/api/pattern-service/v1/patterns/" response = client.get(url) assert response.status_code == status.HTTP_200_OK - assert response.json() == [api_examples.pattern_get_response.value] + expected = api_examples.pattern_get_response.value + expected.update( + {"url": f"/api/pattern-service/v1/patterns/{pattern.pk}/", "id": pattern.pk} + ) + assert response.json() == [expected] @patch("core.views.submit_task", return_value=False) @@ -71,7 +125,10 @@ def test_create_pattern_instance_success(mock_submit_task, client, pattern): data["pattern"] = pattern.pk response = client.post(url, data, format="json") assert response.status_code == status.HTTP_202_ACCEPTED - assert response.json() == api_examples.pattern_instance_post_response.value + assert response.json().get( + "message" + ) == api_examples.pattern_instance_post_response.value.get("message") + assert isinstance(response.json().get("task_id"), int) def test_retrieve_pattern_instance_success(client, controller_label, pattern_instance): @@ -79,7 +136,20 @@ def test_retrieve_pattern_instance_success(client, controller_label, pattern_ins url = f"/api/pattern-service/v1/pattern_instances/{pattern_instance.pk}/" response = client.get(url) assert response.status_code == status.HTTP_200_OK - assert response.json() == api_examples.pattern_instance_get_response.value + pattern_id = response.json().get("pattern") + assert isinstance(pattern_id, int) + expected = api_examples.pattern_instance_get_response.value + expected.update( + { + "url": f"/api/pattern-service/v1/pattern_instances/{pattern_instance.pk}/", + "id": pattern_instance.pk, + "controller_labels": [controller_label.id], + "summary_fields": {"pattern": {"id": pattern_id}}, + "related": {"pattern": f"/api/pattern-service/v1/patterns/{pattern_id}/"}, + "pattern": pattern_id, + } + ) + assert response.json() == expected def test_list_pattern_instances_success(client, controller_label, pattern_instance): @@ -87,13 +157,28 @@ def test_list_pattern_instances_success(client, controller_label, pattern_instan url = "/api/pattern-service/v1/pattern_instances/" response = client.get(url) assert response.status_code == status.HTTP_200_OK - assert response.json() == [api_examples.pattern_instance_get_response.value] + pattern_id = response.json()[0].get("pattern") + assert isinstance(pattern_id, int) + expected = api_examples.pattern_instance_get_response.value + expected.update( + { + "url": f"/api/pattern-service/v1/pattern_instances/{pattern_instance.pk}/", + "id": pattern_instance.pk, + "controller_labels": [controller_label.id], + "summary_fields": {"pattern": {"id": pattern_id}}, + "related": {"pattern": f"/api/pattern-service/v1/patterns/{pattern_id}/"}, + "pattern": pattern_id, + } + ) + assert response.json() == [expected] def test_retrieve_task_success(client, task): url = f"/api/pattern-service/v1/tasks/{task.pk}/" response = client.get(url) assert response.status_code == status.HTTP_200_OK + expected = api_examples.task_get_response.value + expected.update({"url": url, "id": task.pk}) assert response.json() == api_examples.task_get_response.value @@ -101,4 +186,6 @@ def test_list_tasks_success(client, task): url = "/api/pattern-service/v1/tasks/" response = client.get(url) assert response.status_code == status.HTTP_200_OK - assert response.json() == [api_examples.task_get_response.value] + expected = api_examples.task_get_response.value + expected.update({"url": f"/api/pattern-service/v1/tasks/{task.pk}/", "id": task.pk}) + assert response.json() == [expected] diff --git a/pattern_service/settings/dispatcher.py b/pattern_service/settings/dispatcher.py index 77d63fb1..c8cc4721 100644 --- a/pattern_service/settings/dispatcher.py +++ b/pattern_service/settings/dispatcher.py @@ -32,21 +32,20 @@ def override_dispatcher_settings(loaded_settings: Dynaconf) -> None: db_sslkey = loaded_settings.get("DB_SSLKEY", default="") db_sslrootcert = loaded_settings.get("DB_SSLROOTCERT", default="") - if loaded_settings.get("MODE", "development") == "development": - databases["default"] = { - "ENGINE": "django.db.backends.postgresql", - "HOST": db_host, - "PORT": db_port, - "USER": db_user, - "PASSWORD": db_user_pass, - "NAME": db_name, - "OPTIONS": { - "sslmode": db_sslmode, - "sslcert": db_sslcert, - "sslkey": db_sslkey, - "sslrootcert": db_sslrootcert, - }, - } + databases["default"] = { + "ENGINE": "django.db.backends.postgresql", + "HOST": db_host, + "PORT": db_port, + "USER": db_user, + "PASSWORD": db_user_pass, + "NAME": db_name, + "OPTIONS": { + "sslmode": db_sslmode, + "sslcert": db_sslcert, + "sslkey": db_sslkey, + "sslrootcert": db_sslrootcert, + }, + } databases["dispatcher"] = { "ENGINE": "django.db.backends.postgresql", diff --git a/pattern_service/settings/testing_defaults.py b/pattern_service/settings/testing_defaults.py index 34b3d01e..3d31200f 100644 --- a/pattern_service/settings/testing_defaults.py +++ b/pattern_service/settings/testing_defaults.py @@ -4,8 +4,8 @@ BASE_DIR = Path(__file__).resolve().parent.parent SECRET_KEY = "insecure" -DB_NAME = "test_pattern_db" -DB_USER = "pattern" +DB_NAME = "postgres" +DB_USER = "postgres" DB_PASSWORD = "insecure" DATABASES = { diff --git a/tools/podman/Containerfile.hub b/tools/podman/Containerfile.hub new file mode 100644 index 00000000..ba9cdb25 --- /dev/null +++ b/tools/podman/Containerfile.hub @@ -0,0 +1,15 @@ +FROM busybox:1.35 + +# Create a non-root user to own the files and run our server +RUN adduser -D static +USER static +WORKDIR /home/static + +# Copy the static website +RUN mkdir -p api/galaxy/v3/plugin/ansible/content/published/collections/artifacts + +# Use the .dockerignore file to control what ends up inside the image! +COPY core/tests/integration/collections/*.tar.gz api/galaxy/v3/plugin/ansible/content/published/collections/artifacts + +# Run BusyBox httpd +CMD ["busybox", "httpd", "-f", "-v", "-p", "3000"] diff --git a/tools/podman/compose-test.yaml b/tools/podman/compose-test.yaml index fe420f81..992e3353 100644 --- a/tools/podman/compose-test.yaml +++ b/tools/podman/compose-test.yaml @@ -2,27 +2,17 @@ services: postgres: image: quay.io/sclorg/postgresql-15-c9s:latest environment: - POSTGRESQL_USER: pattern - POSTGRESQL_PASSWORD: insecure - POSTGRESQL_DATABASE: test_pattern_db + POSTGRESQL_ADMIN_PASSWORD: insecure ports: - '5432:5432' # volumes: # - 'postgres_data:/var/lib/pgsql/data' healthcheck: - test: ["CMD", "pg_isready", "-U", "pattern", "-d", "pattern_db"] + test: ["CMD", "pg_isready", "-U", "postgres"] interval: 5s timeout: 5s retries: 3 start_period: 5s - # ports: - # - '5432:5432' - # healthcheck: - # test: [ 'CMD', 'pg_isready', '-U', 'postgres' ] - # interval: 5s - # timeout: 5s - # retries: 3 - # start_period: 5s worker: image: localhost/pattern-service-worker environment: @@ -30,30 +20,24 @@ services: PATTERN_SERVICE_MODE: testing PATTERN_SERVICE_DB_HOST: postgres PATTERN_SERVICE_DB_PORT: 5432 - # PATTERN_SERVICE_DB_NAME: pattern_db - PATTERN_SERVICE_DB_USER: pattern - # PATTERN_SERVICE_DB_PASSWORD: pattern123 + PATTERN_SERVICE_DB_NAME: postgres + PATTERN_SERVICE_DB_USER: postgres + PATTERN_SERVICE_DB_PASSWORD: insecure + PATTERN_SERVICE_AAP_URL: "http://localhost:3000" build: context: ../../ dockerfile: tools/podman/Containerfile.dev command: - /bin/bash - -c - - >- - python3.11 /app/manage.py makemigrations - && python3.11 /app/manage.py migrate - && python3.11 /app/manage.py worker + - python3.11 /app/manage.py worker depends_on: postgres: condition: service_healthy restart: always - hub_api: - image: docker.io/mockserver/mockserver:latest - ports: - - 44926:1080 - environment: - MOCKSERVER_WATCH_INITIALIZATION_JSON: "true" - MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties - MOCKSERVER_INITIALIZATION_JSON_PATH: /config/initializerJson.json - volumes: - - ./hub-api-config-test.json:/config/initializerJson.json:Z + hub: + image: localhost/pattern-service-integration-hub + build: + context: ../../ + dockerfile: tools/podman/Containerfile.hub + restart: always diff --git a/tools/podman/hub-api-config-test.json b/tools/podman/hub-api-config-test.json deleted file mode 100644 index 66676f29..00000000 --- a/tools/podman/hub-api-config-test.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "httpRequest": { - "method": "GET", - "path": "/api/galaxy/v3/plugin/ansible/content/published/collections/artifacts/ansible.test-1.2.3.tar.gz" - }, - "httpResponse": { - "body": { - "content": "collection content" - }, - "statusCode": 200 - } - } -]