Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
- name: Version replacement based on tag ↔️
if: github.ref_type == 'tag'
run: |
TAG_VERSION=${GITHUB_REF#refs/tags/}
TAG_VERSION=${GITHUB_REF##*/}
echo "Tag version: $TAG_VERSION"
uv version $TAG_VERSION

Expand Down
43 changes: 31 additions & 12 deletions src/keboola/vcr/scaffolder.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
from runpy import run_path
from typing import Any

from .recorder import VCRRecorder
from .validator import save_output_snapshot

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -68,6 +71,7 @@ def scaffold_from_json(
freeze_time_at: str | None = None,
secrets_file: Path | None = None,
chain_state: bool = False,
regenerate: bool = False,
) -> list[Path]:
"""
Create test folders from definitions file.
Expand All @@ -83,6 +87,9 @@ def scaffold_from_json(
whose keys are deep-merged into each config.
chain_state: Forward out/state.json from each test as
in/state.json for the next test (for ERP token refresh).
regenerate: Delete existing cassettes before recording so fresh
interactions are captured from the live API. When False,
tests that already have a cassette are skipped.

Returns:
List of created test folder paths
Expand Down Expand Up @@ -138,6 +145,7 @@ def scaffold_from_json(
freeze_time_at=freeze_time_at,
secrets_override=secrets_override,
input_state=chained_state,
regenerate=regenerate,
)
created_paths.append(test_path)

Expand All @@ -158,6 +166,7 @@ def scaffold_from_dict(
record: bool = True,
freeze_time_at: str | None = None,
secrets_override: dict[str, Any] | None = None,
regenerate: bool = False,
) -> Path:
"""
Create a single test folder from a definition dict.
Expand All @@ -169,6 +178,8 @@ def scaffold_from_dict(
record: Whether to run component and record cassette
freeze_time_at: ISO timestamp for time freezing
secrets_override: Optional dict of secrets to deep-merge at recording time
regenerate: Delete existing cassette before recording; when False,
existing cassettes are skipped.

Returns:
Path to created test folder
Expand All @@ -180,6 +191,7 @@ def scaffold_from_dict(
record=record,
freeze_time_at=freeze_time_at,
secrets_override=secrets_override,
regenerate=regenerate,
)

# ------------------------------------------------------------------
Expand All @@ -195,6 +207,7 @@ def _scaffold_single_test(
freeze_time_at: str | None,
secrets_override: dict[str, Any] | None = None,
input_state: dict[str, Any] | None = None,
regenerate: bool = False,
) -> Path:
"""Create folder structure for a single test."""
# Validate definition
Expand Down Expand Up @@ -240,15 +253,20 @@ def _scaffold_single_test(

# Record cassette if requested
if record and component_script:
self._record_test(
test_dir=test_dir,
source_data_dir=source_data_dir,
expected_out_dir=expected_out_dir,
component_script=component_script,
config=config,
freeze_time_at=freeze_time_at,
secrets_override=secrets_override,
)
cassette_path = source_data_dir / "cassettes" / VCRRecorder.DEFAULT_CASSETTE_FILE
if not regenerate and cassette_path.exists():
logger.info(f"Skipping {test_name} — cassette exists (use --regenerate to force)")
else:
self._record_test(
test_dir=test_dir,
source_data_dir=source_data_dir,
expected_out_dir=expected_out_dir,
component_script=component_script,
config=config,
freeze_time_at=freeze_time_at,
secrets_override=secrets_override,
regenerate=regenerate,
)

return test_dir

Expand All @@ -261,6 +279,7 @@ def _record_test(
config: dict[str, Any],
freeze_time_at: str | None,
secrets_override: dict[str, Any] | None = None,
regenerate: bool = False,
) -> None:
"""Run component and record cassette.

Expand All @@ -269,9 +288,6 @@ def _record_test(
recording finishes the on-disk ``config.json`` is restored to the
original dummy values so that real credentials are never committed.
"""
from .recorder import VCRRecorder
from .validator import save_output_snapshot

# Build the config that will actually be used during recording.
# If an external secrets override is provided, deep-merge it in.
if secrets_override:
Expand All @@ -292,6 +308,9 @@ def _record_test(
secrets_override=secrets_override,
)

if regenerate:
recorder.clear_cassette()

# Run component with recording
def run_component():
os.environ["KBC_DATADIR"] = str(source_data_dir)
Expand Down