用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/Snowflake-Labs/coco-skills --skill dvp-test-setup-generator命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
正在显示 SKILL.md
| name | dvp-test-setup-generator |
| description | Generate pytest files for source and migrated workloads based on entrypoints.json. |
Generate a pytest project under dvp/03-tests/.
dvp/03-tests/source/) to produce baseline CSV outputs.dvp/03-tests/migrated/ (Snowpark API) ordvp/03-tests/migrated_scos/ (SCOS / Snowpark Connect)For detailed rationale and examples, see:
skills/spark-migration/snowpark-api/dvp/docs/data-validator/dvp-test-setup-generator.mddvp/01-source/, dvp/03-tests/, dvp/04-results/).dvp-code-adapter.dvp/02-migrated/ or dvp/02-migrated_scos/.| Input | Required | Location |
|---|---|---|
| Entrypoints inventory | Yes | dvp/04-results/entrypoints.json |
| IO schema | Yes | dvp/04-results/data_io_schema.json |
| Synthetic data | Yes | dvp/04-results/synthetic_data/*.csv |
| Output | Format | Location |
|---|---|---|
| pytest project | files | dvp/03-tests/ |
| generated tests (source) | Python | dvp/03-tests/source/**/test_*.py |
| generated tests (migrated) | Python | dvp/03-tests/migrated/**/test_*.py (if selected) |
| generated tests (scos) | Python | dvp/03-tests/migrated_scos/**/test_*.py (if selected) |
dvp/03-tests/data/expected_output/is a runtime artifact created when running the source suite; it is not generated by this skill.
Every time you begin a step, sub-step, or significant action, prefix the message with a timestamp in the format [YYYY-MM-DD HH:MM:SS]. Obtain the current time by running date '+%Y-%m-%d %H:%M:%S' in bash.
Example:
[2026-03-24 14:05:32] Starting Step 1: Locate required files...
[2026-03-24 14:05:45] Created test_job_customer_stats.py
[2026-03-24 14:05:46] Step 1 complete.
Ensure the workload directory has a git repository on the sma/migration-process branch. This is idempotent — if the orchestrator already initialized git, this is a no-op.
result = sma_api.git_ensure_ready("<workload_path>")
Copy scaffolding from dvp-test-setup-generator/templates/ into <workload_path>/dvp/03-tests/:
IMPORTANT:
.gitignoreis a dotfile — shell globs like*do NOT match dotfiles by default. You MUST copy it explicitly:cp templates/.gitignore <workload_path>/.gitignore
<workload_path>/dvp/03-tests/:
conftest.py, config.py, requirements.txtsource/conftest.pyDVP-TESTING.md to the workload root (<workload_path>/DVP-TESTING.md), NOT inside dvp/03-tests/..gitignore to the workload root (<workload_path>/.gitignore), NOT inside dvp/03-tests/:
dvp/02-migrated/ exists: copy migrated/conftest.py and generate pytest.ini with testpaths = source migrateddvp/02-migrated_scos/ exists: copy migrated_scos/conftest.py and generate pytest.ini with testpaths = source migrated_scos.vscode/settings.json at the workload root (<workload_path>/.vscode/settings.json), NOT inside dvp/03-tests/:
mkdir -p <workload_path>/.vscode
{
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": [
"dvp/03-tests",
"-v",
Load dvp/04-results/entrypoints.json and generate tests for entries where status == "detected".
For each entrypoint, determine the invocation target:
adapted_source exists and contains :: → parse it: the last :: segment is the callable function name, preceding segments (if any) are scope (class/object). Example: workload.py:163::main → call main; App.scala:5::MyApp::main → call MyApp.mainadapted_source exists but has no :: → the entrypoint is the whole file (execute as script)adapted_source does not exist → the code-adapter has not run; stop and reportThe adapted_source field uses the hybrid format <path>:<lineno>(::segment)*. See entrypoints-source-spec.md.
Preserve subfolders: if an entrypoint lives under etl/daily_metrics.py, generate tests under:
dvp/03-tests/source/etl/test_daily_metrics.pydvp/03-tests/<selected-suite>/etl/test_daily_metrics.pyFor each entrypoint:
adapted_source to extract the file path and function name.data_io_schema.json and filter entries whose source field starts with the same filename as the entrypoint (e.g., for entrypoint job_customer_stats with adapted_source: "job_customer_stats.py:9::main", filter entries where source starts with "job_customer_stats.py").INPUT_FILES (role=input, type=file), INPUT_TABLES (role=input, type=table), OUTPUT_FILES (role=output, type=file), OUTPUT_TABLES (role=output, type=table).BaseSourceWorkloadTest.Required test method name: The execution test in every generated test_*.py file must be named test_validate_pipeline_runs. Do NOT invent alternative names like test_run_without_error, test_workload_completes, etc. This ensures consistent tracking across regenerations.
Required fixture parameter: test_validate_pipeline_runs must use self and workload_session as parameters (it's a class method). Do NOT invent fixture names like source_spark, spark, session, etc. The workload_session fixture is defined in each suite's conftest.py and returns the correct session type automatically:
SparkSessionSessionWhy class-based: The base classes (BaseSourceWorkloadTest, BaseMigratedWorkloadTest, BaseScosWorkloadTest) inherit from BaseIOConfig, which provides three output validation tests automatically via inheritance:
test_all_outputs_have_data — checks every output has at least one rowtest_all_outputs_match_baseline_row_count — compares row counts against baselinetest_all_outputs_match_baseline — full data comparison (schema + values)These tests iterate over OUTPUT_FILES + OUTPUT_TABLES. If both lists are empty, the tests run but have nothing to iterate over (no failures). The run_workload fixture in the base class also handles input setup and cleanup automatically.
Class naming: Convert the entrypoint name to PascalCase and prefix with Test. Example: job_customer_stats → TestJobCustomerStats.
Source test template (use this exact pattern):
"""Source test for <name> entrypoint."""
import importlib
import importlib.util
import sys
from pathlib import Path
# Load suite conftest to access BaseSourceWorkloadTest (importlib mode safe)
_conftest_path = Path(__file__).resolve().parent / "conftest.py"
_spec = importlib.util.spec_from_file_location("_source_conftest", _conftest_path)
_mod = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_mod)
BaseSourceWorkloadTest = _mod.BaseSourceWorkloadTest
class Test<ClassName>(BaseSourceWorkloadTest):
"""Test suite for the <name> source workload."""
INPUT_FILES = <input_files_list>
INPUT_TABLES = <input_tables_list>
OUTPUT_FILES = <output_files_list>
OUTPUT_TABLES = <output_tables_list>
def _call_main(self, session):
"""Import and execute the source workload with the test session."""
import os
from pathlib import Path
# --- env var isolation: always recompute OUTPUT_DATA_PATH ---
input_path = os.environ.get("INPUT_DATA_PATH", ".")
output_dir = str(Path(input_path).parent / "output")
os.environ["OUTPUT_DATA_PATH"] = output_dir
os.makedirs(output_dir, exist_ok=True)
# --- module cache cleanup: prevent stale config/utils from prior test ---
for mod_name in list(sys.modules.keys()):
if mod_name.startswith(("config", "utils", "pipelines")):
del sys.modules[mod_name]
source_dir = Path(__file__).resolve().parent.parent.parent /
source_file = source_dir /
spec = importlib.util.spec_from_file_location(, source_file)
mod = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = mod
sys.path.insert(, (source_dir))
:
spec.loader.exec_module(mod)
fn = (mod, , )
fn :
result = fn(session)
result result
:
sys.path.pop()
():
Migrated test template (use this exact pattern):
"""Migrated test for <name> entrypoint (Snowpark API)."""
import importlib
import importlib.util
import sys
from pathlib import Path
# Load suite conftest to access BaseMigratedWorkloadTest (importlib mode safe)
_conftest_path = Path(__file__).resolve().parent / "conftest.py"
_spec = importlib.util.spec_from_file_location("_migrated_conftest", _conftest_path)
_mod = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_mod)
BaseMigratedWorkloadTest = _mod.BaseMigratedWorkloadTest
class Test<ClassName>(BaseMigratedWorkloadTest):
"""Test suite for the <name> migrated workload."""
INPUT_FILES = <input_files_list>
INPUT_TABLES = <input_tables_list>
OUTPUT_FILES = <output_files_list>
OUTPUT_TABLES = <output_tables_list>
def _call_main(self, session):
"""Import and execute the migrated workload with the test session."""
import os
from pathlib import Path
# --- module cache cleanup: prevent stale config/utils from prior test ---
for mod_name in list(sys.modules.keys()):
if mod_name.startswith(("config", "utils", "pipelines")):
del sys.modules[mod_name]
migrated_dir = Path(__file__).resolve().parent.parent.parent / "02-migrated"
migrated_file = migrated_dir / "<filename>"
spec = importlib.util.spec_from_file_location("<module_name>", migrated_file)
mod = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = mod
sys.path.insert(0, str(migrated_dir))
try:
spec.loader.exec_module(mod)
fn = (mod, , )
fn :
result = fn(session)
result result
:
sys.path.pop()
():
SCOS test template — same as migrated but:
BaseScosWorkloadTest instead of BaseMigratedWorkloadTest (name the spec "_scos_conftest")"02-migrated" to "02-migrated_scos"I/O attribute rules:
INPUT_FILES, INPUT_TABLES, OUTPUT_FILES, OUTPUT_TABLES is a Python list of dicts, copied from matching data_io_schema.json entries.data_io_schema.json is JSON — you MUST convert JSON null to Python None when writing dict literals in the test file. Writing null produces NameError: name 'null' is not defined.name field in OUTPUT_FILES entries must match the actual filesystem subdirectory name the workload writes to (e.g., churn_scores, daily_summary, fraud_flags), NOT the config variable name (e.g., OUTPUT_CHURN_PATH). Read 01-source/config/settings.py to find the actual path suffixes used by each output. The _try_read_output() and persist_baseline() functions in conftest resolve outputs via output/{name}.name, columns, key_columns, format.[].INPUT_FILES = [{"name": "orders", "type": "file", "format": "memory", "role": "input", "path": None, "columns": [...]}]After generating the test files, run the registration script to populate sma_storage.sqlite3 for dashboard tracking.
python3 "<skills_path>/spark-migration/dvp/dvp-test-setup-generator/scripts/register_tests.py" \
--workload-path "<workload_path>"
The script automatically:
dvp/04-results/entrypoints.json for detected entrypointsdvp/03-tests/source/ and dvp/03-tests/migrated/ (or migrated_scos/) for test_*.py filessma_api.register_tests() to insert them into the entrypoint_tests tableThis enables the Test Tracker module in the SMA Dashboard.
After generation, remind the user that dvp/03-tests/config.py auto-reads credentials from ~/.snowflake/connections.toml. If the user has a valid TOML config, no manual editing is needed. Environment variables (SNOWFLAKE_CONNECTION_NAME, SNOWFLAKE_TEST_DATABASE, etc.) override TOML values.
After test project is generated, commit the changes:
result = sma_api.git_commit("<workload_path>", """DVP Test Setup: Generated test project with N test files
Test suites: source + migrated (or migrated_scos)
Entrypoints covered: N
Output: dvp/03-tests/""")
Verify branches:
result = sma_api.git_verify_branches("<workload_path>")
entrypoints.json or data_io_schema.json stop and report which file is missing.MANDATORY: After completing all steps (whether running standalone or invoked from the orchestrator), ALWAYS present this summary table:
Test Setup Complete
┌──────────────────┬──────────┬──────────────────────────────────────────────────────────┐
│ Step │ Status │ Details │
├──────────────────┼──────────┼──────────────────────────────────────────────────────────┤
│ Test Setup │ Done │ Generated test project with N test files │
│ DB Registration │ Done │ Registered N tests in sma_storage.sqlite3 │
└──────────────────┴──────────┴──────────────────────────────────────────────────────────┘
Output location: <output>/
Git branches:
• main — original code (unmodified)
• sma/migration-process — test setup changes applied
Next steps (always show verbatim after the table):
Next steps:
1. Review DVP-TESTING.md for detailed testing instructions
2. cd <workload_path>/dvp/03-tests && pip install -r requirements.txt
3. Verify ~/.snowflake/connections.toml has your Snowflake connection
4. pytest dvp/03-tests/source/ -v (generate PySpark baselines)
5. pytest dvp/03-tests/migrated/ -v (validate Snowpark conversion)
6. Open the SMA Dashboard to view results
Replace
migrated/withmigrated_scos/when the SCOS suite was generated.
Rules:
N with actual count of test files generated<workload_path> with the actual workload pathDone, Skipped, or FailedFailed with which file is missingsma_api.git_verify_branches() to confirm both branches exist