mirror of
https://github.com/Skyvern-AI/skyvern.git
synced 2026-05-01 21:20:19 +00:00
fix(SKY-8799): preserve ISO datetime strings in workflow YAML loading (#5421)
This commit is contained in:
parent
793fb7ea01
commit
6bab0aeaee
4 changed files with 117 additions and 6 deletions
77
tests/unit/test_yaml_loader_no_dates.py
Normal file
77
tests/unit/test_yaml_loader_no_dates.py
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
"""Regression tests for SKY-8799.
|
||||
|
||||
PyYAML's default ``SafeLoader`` resolves ISO 8601 strings into Python
|
||||
``datetime`` objects. When users embed such strings inside the
|
||||
``default_value`` of a JSON workflow parameter, those values become
|
||||
``datetime`` instances on the way in and then crash ``json.dumps`` on
|
||||
the way back out, producing::
|
||||
|
||||
TypeError: Object of type datetime is not JSON serializable
|
||||
|
||||
``skyvern.utils.yaml_loader.safe_load_no_dates`` removes the timestamp
|
||||
implicit resolver so the values stay as plain strings.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
import yaml
|
||||
|
||||
from skyvern.utils.yaml_loader import safe_load_no_dates
|
||||
|
||||
ISO_BLOB = """
|
||||
parameters:
|
||||
- parameter_type: workflow
|
||||
key: payload
|
||||
workflow_parameter_type: json
|
||||
default_value:
|
||||
id: "12345"
|
||||
metadata:
|
||||
# Unquoted ISO 8601 strings are what trip the default SafeLoader.
|
||||
created_at: 2023-10-27T10:00:00Z
|
||||
updated_at: 2023-10-28T14:30:00Z
|
||||
tags: ["primary", "test-data"]
|
||||
"""
|
||||
|
||||
|
||||
def test_default_safe_load_does_parse_datetimes() -> None:
|
||||
# Sanity check: documents the behavior we are working around.
|
||||
parsed = yaml.safe_load(ISO_BLOB)
|
||||
default_value = parsed["parameters"][0]["default_value"]
|
||||
# The default loader turns the unquoted-looking timestamps into datetimes,
|
||||
# which is exactly what breaks downstream JSON serialization.
|
||||
assert not isinstance(default_value["metadata"]["created_at"], str)
|
||||
|
||||
|
||||
def test_safe_load_no_dates_keeps_iso_strings_as_strings() -> None:
|
||||
parsed = safe_load_no_dates(ISO_BLOB)
|
||||
metadata = parsed["parameters"][0]["default_value"]["metadata"]
|
||||
|
||||
assert metadata["created_at"] == "2023-10-27T10:00:00Z"
|
||||
assert metadata["updated_at"] == "2023-10-28T14:30:00Z"
|
||||
assert isinstance(metadata["created_at"], str)
|
||||
assert isinstance(metadata["updated_at"], str)
|
||||
|
||||
|
||||
def test_safe_load_no_dates_round_trips_through_json() -> None:
|
||||
parsed = safe_load_no_dates(ISO_BLOB)
|
||||
# The whole point: the parsed structure must be JSON-serializable
|
||||
# without a custom encoder.
|
||||
serialized = json.dumps(parsed)
|
||||
assert "2023-10-27T10:00:00Z" in serialized
|
||||
|
||||
|
||||
def test_safe_load_no_dates_preserves_other_implicit_types() -> None:
|
||||
parsed = safe_load_no_dates(
|
||||
"""
|
||||
an_int: 42
|
||||
a_float: 3.14
|
||||
a_bool: true
|
||||
a_null: null
|
||||
a_list: [1, 2, 3]
|
||||
"""
|
||||
)
|
||||
assert parsed["an_int"] == 42
|
||||
assert parsed["a_float"] == 3.14
|
||||
assert parsed["a_bool"] is True
|
||||
assert parsed["a_null"] is None
|
||||
assert parsed["a_list"] == [1, 2, 3]
|
||||
Loading…
Add table
Add a link
Reference in a new issue