From d28bd1e4ecc1ea8f12c35456cc4c9ea10a285b6f Mon Sep 17 00:00:00 2001 From: Fang-Pen Lin Date: Mon, 1 Dec 2025 19:06:31 -0800 Subject: [PATCH] Add test for retry logic --- .../bdd/features/pki/acme/challenge.feature | 22 ++++++++++++++++ backend/bdd/features/steps/pki_acme.py | 26 +++++++++++++++++-- 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/backend/bdd/features/pki/acme/challenge.feature b/backend/bdd/features/pki/acme/challenge.feature index 21c63329f9..1d16f6baa5 100644 --- a/backend/bdd/features/pki/acme/challenge.feature +++ b/backend/bdd/features/pki/acme/challenge.feature @@ -22,6 +22,28 @@ Feature: Challenge And I parse the full-chain certificate from order finalized_order as cert And the value cert with jq ".subject.common_name" should be equal to "localhost" + Scenario: Validate challenge with retry + Given I have an ACME cert profile as "acme_profile" + When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory" + Then I register a new ACME account with email fangpen@infisical.com and EAB key id "{acme_profile.eab_kid}" with secret "{acme_profile.eab_secret}" as acme_account + When I create certificate signing request as csr + Then I add names to certificate signing request csr + """ + { + "COMMON_NAME": "localhost" + } + """ + And I create a RSA private key pair as cert_key + And I sign the certificate signing request csr with private key cert_key and output it as csr_pem in PEM format + And I submit the certificate signing request PEM csr_pem certificate order to the ACME server as order + And I select challenge with type http-01 for domain localhost from order in order as challenge + And I wait 45 seconds before serve challenge response for challenge at localhost + And I tell ACME server that challenge is ready to be verified + And I poll and finalize the ACME order order as finalized_order + And the value finalized_order.body with jq ".status" should be equal to "valid" + And I parse the full-chain certificate from order finalized_order as cert + And the value cert with jq ".subject.common_name" should be equal to "localhost" + Scenario: Validate challenges for multiple domains Given I have an ACME cert profile as "acme_profile" When I have an ACME client connecting to "{BASE_URL}/api/v1/cert-manager/acme/profiles/{acme_profile.id}/directory" diff --git a/backend/bdd/features/steps/pki_acme.py b/backend/bdd/features/steps/pki_acme.py index 53ad2d15bf..2dd61c123e 100644 --- a/backend/bdd/features/steps/pki_acme.py +++ b/backend/bdd/features/steps/pki_acme.py @@ -2,6 +2,8 @@ import json import logging import re import urllib.parse +import time +import threading import acme.client import jq @@ -800,6 +802,7 @@ def select_challenge( def serve_challenges( context: Context, challenges: list[messages.ChallengeBody], + wait_time: int | None = None, ): if hasattr(context, "web_server"): context.web_server.shutdown_and_server_close() @@ -816,7 +819,19 @@ def serve_challenges( ) # TODO: make port configurable servers = standalone.HTTP01DualNetworkedServers(("0.0.0.0", 8087), resources) - servers.serve_forever() + if wait_time is None: + servers.serve_forever() + else: + + def wait_and_start(): + logger.info("Waiting %s seconds before we start serving.", wait_time) + time.sleep(wait_time) + logger.info("Start server now") + servers.serve_forever() + + thread = threading.Thread(target=wait_and_start) + thread.daemon = True + thread.start() context.web_server = servers @@ -882,7 +897,6 @@ def step_impl( domain=domain.value, order_var_path=order_var_path, ) - print("@" * 20, domain, challenge.chall.path) logger.info( "Found challenge for domain %s with type %s, challenge=%s", domain.value, @@ -905,6 +919,14 @@ def step_impl( notify_challenge_ready(context=context, challenge=challenge) +@then( + "I wait {wait_time} seconds before serve challenge response for {var_path} at {hostname}" +) +def step_impl(context: Context, wait_time: str, var_path: str, hostname: str): + challenge = eval_var(context, var_path, as_json=False) + serve_challenges(context=context, challenges=[challenge], wait_time=int(wait_time)) + + @then("I serve challenge response for {var_path} at {hostname}") def step_impl(context: Context, var_path: str, hostname: str): challenge = eval_var(context, var_path, as_json=False)