diff --git a/diracx-db/src/diracx/db/sql/utils/base.py b/diracx-db/src/diracx/db/sql/utils/base.py index 5f04ba9c..dfe6baa8 100644 --- a/diracx-db/src/diracx/db/sql/utils/base.py +++ b/diracx-db/src/diracx/db/sql/utils/base.py @@ -232,7 +232,7 @@ def find_time_resolution(value): if isinstance(value, datetime): return None, value if match := re.fullmatch( - r"\d{4}(-\d{2}(-\d{2}(([ T])\d{2}(:\d{2}(:\d{2}(\.\d{6}Z?)?)?)?)?)?)?", value + r"\d{4}(-\d{2}(-\d{2}(([ T])\d{2}(:\d{2}(:\d{2}(\.\d{1,6}Z?)?)?)?)?)?)?", value ): if match.group(6): precision, pattern = "SECOND", r"\1-\2-\3 \4:\5:\6" @@ -249,7 +249,7 @@ def find_time_resolution(value): return ( precision, re.sub( - r"^(\d{4})-?(\d{2})?-?(\d{2})?[ T]?(\d{2})?:?(\d{2})?:?(\d{2})?\.?(\d{6})?Z?$", + r"^(\d{4})-?(\d{2})?-?(\d{2})?[ T]?(\d{2})?:?(\d{2})?:?(\d{2})?\.?(\d{1,6})?Z?$", pattern, value, ), diff --git a/diracx-db/src/diracx/db/sql/utils/functions.py b/diracx-db/src/diracx/db/sql/utils/functions.py index 17e1d155..b327c1ca 100644 --- a/diracx-db/src/diracx/db/sql/utils/functions.py +++ b/diracx-db/src/diracx/db/sql/utils/functions.py @@ -47,7 +47,8 @@ class date_trunc(expression.FunctionElement): # noqa: N801 """ type = DateTime() - inherit_cache = True + # Cache does not work as intended with time resolution values, so we disable it + inherit_cache = False def __init__(self, *args, time_resolution, **kwargs) -> None: super().__init__(*args, **kwargs) diff --git a/diracx-routers/pyproject.toml b/diracx-routers/pyproject.toml index c70f08c4..d99a51cc 100644 --- a/diracx-routers/pyproject.toml +++ b/diracx-routers/pyproject.toml @@ -35,7 +35,7 @@ dependencies = [ dynamic = ["version"] [project.optional-dependencies] -testing = ["diracx-testing", "moto[server]", "pytest-httpx"] +testing = ["diracx-testing", "moto[server]", "pytest-httpx", "freezegun",] types = [ "boto3-stubs", "types-aiobotocore[essential]", diff --git a/diracx-routers/tests/test_job_manager.py b/diracx-routers/tests/test_job_manager.py index 0084c138..0ae0d0b1 100644 --- a/diracx-routers/tests/test_job_manager.py +++ b/diracx-routers/tests/test_job_manager.py @@ -5,6 +5,7 @@ import pytest from fastapi.testclient import TestClient +from freezegun import freeze_time from diracx.core.models import JobStatus @@ -255,6 +256,244 @@ def test_insert_malformed_jdl(normal_user_client): assert r.status_code == 400, r.json() +@freeze_time("2024-01-01T00:00:00.123456Z") +def test_insert_and_search_by_datetime(normal_user_client): + """Test inserting a job and then searching for it. + + Focus on the SubmissionTime parameter. + """ + # job_definitions = [TEST_JDL%(normal_user_client.dirac_token_payload)] + job_definitions = [TEST_JDL] + r = normal_user_client.post("/api/jobs/jdl", json=job_definitions) + listed_jobs = r.json() + assert r.status_code == 200, listed_jobs + assert len(listed_jobs) == len(job_definitions) + + # 1.1 Search for all jobs submitted in 2024 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 1.2 Search for all jobs submitted before 2024 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "lt", + "value": "2024", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 0 + + # 2.1 Search for all jobs submitted after 2024-01 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "gt", + "value": "2024-01", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 0 + + # 2.2 Search for all jobs submitted before 2024-02 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "lt", + "value": "2024-02", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 3 Search for all jobs submitted during 2024-01-01 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024-01-01", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 4.1 Search for all jobs submitted during 2024-01-01 00 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024-01-01 00", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 4.2 Search for all jobs submitted during 2024-01-01T00 (with the 'T' separator) + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024-01-01T00", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 4.3 Search for all jobs not submitted during 2024-01-01 01 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "neq", + "value": "2024-01-01 01", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 5.1 Search for all jobs submitted after 2024-01-01 00:00:00 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "gt", + "value": "2024-01-01 00:00:00", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 0 + + # 5.2 Search for all jobs not submitted on 2024-01-01 00:00:00 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "neq", + "value": "2024-01-01 00:00:00", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 0 + + # 5.3 Search for all jobs submitted on 2024-01-01 00:00:00 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024-01-01 00:00:00", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 6.1 Search for all jobs submitted on 2024-01-01 00:00:00.123456 + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024-01-01 00:00:00.123456", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 6.2 Search for all jobs submitted on 2024-01-01 00:00:00.123456Z + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024-01-01 00:00:00.123456Z", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + # 6.3 Search for all jobs submitted on 2024-01-01 00:00:00.123Z + r = normal_user_client.post( + "/api/jobs/search", + json={ + "search": [ + { + "parameter": "SubmissionTime", + "operator": "eq", + "value": "2024-01-01 00:00:00.123Z", + } + ] + }, + ) + assert r.status_code == 200, r.json() + assert len(r.json()) == 1 + + def test_search_distinct(normal_user_client): """Test that the distinct parameter works as expected.""" job_definitions = [TEST_JDL, TEST_JDL, TEST_JDL]