plugins/github-copilot-modernization/skills/create-test-baseline/SKILL.md
Create a test baseline for the project to be modernized. The baseline will be used for later verification of modernization tasks.
npx skillsauth add microsoft/github-copilot-modernization create-test-baselineInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Produce a precise, executable-quality specification of the application's externally observable behavior that must be preserved across migration. The output is test-cases/test-cases.md plus the test-cases/testdata/ fixtures it references — no test code is generated in this phase. The verify-test-baseline skill consumes this spec to generate *PostMigrationIT tests against the new implementation.
test-cases/testdata/ as files. The spec references them by path (relative to test-cases/). No inline byte literals, no hand-built JSON strings in the spec.test-cases/test-cases.md and test-cases/testdata/ are FROZEN after this skill completes — never modified, renamed, moved, or deleted by subsequent phases.*BaselineIT test code is produced. The migration uses a replace strategy: any code that exercises the old implementation would either import old SDKs (which get deleted) or substitute internal services for the real entry point (which defeats the purpose). All executable tests are produced in the verify phase against the new implementation.test-cases.md under that module's test source root. A module's test-cases.md MUST contain only test cases whose entry points belong to that module. Never place test cases for one module inside another module's test-cases.md (e.g. API-gateway module test cases must not appear in a backend-services module's spec).test-cases/ for that module. A module that only serves as a thin HTTP frontend delegating to another module's backend services — without touching any migrated resource itself — does not need test cases.verify-test-baseline skill handles the actual mock implementation.Frozen baseline artifacts live under a dedicated test-cases/ subdirectory of the project's test source root — the standard directory the build tool already uses for tests (e.g. src/test/ for Maven/Gradle Java modules, tests/ for Python / Node / Go projects, <module>/test/ for multi-module repos). In a multi-module repo, place artifacts under each module's own test source root. Do NOT invent a new top-level folder.
The per-run summary report lives under the modernization work folder, not the test source root.
<test-source-root>/
└── test-cases/ # FROZEN — entire folder is the baseline spec bundle
├── test-cases.md # FROZEN — the full behavioral spec
├── infra-decision-table.md # FROZEN — mock/real/testcontainer decision per external dependency (Step 2)
└── testdata/ # FROZEN — fixtures referenced by test-cases.md
├── inputs/ # raw input files (images, JSON payloads, CSV, etc.)
├── expectations/ # golden outputs at the application boundary
└── ... # other data as needed (configuration, seed data, etc.)
${modernization-work-folder}/${taskid}/
└── baseline-summary.md # NEW — per-run summary (Step 6)
All paths inside test-cases.md (e.g. testdata/inputs/sample.jpg) are interpreted relative to <test-source-root>/test-cases/, the folder that contains test-cases.md.
Scan the production source for everything that constitutes an external boundary or orchestration entry point. Record each one with file path, symbol, and owning module so it can be cross-checked in Step 2.
What qualifies as an entry point (principle, not a closed list):
An entry point is any code location invoked by something outside the application's own call graph — the network, the OS, the runtime scheduler, a message broker, an external SDK consumer, etc. If the application does not call it itself, it is an entry point.
Typical examples (use as hints, not as an exhaustive checklist — apply the principle above to whatever the codebase actually uses):
main methods, background workers)Every orchestration entry point in the migration scope MUST be covered by at least one end-to-end test case that triggers it with realistic input and verifies the final observable outcome. Test cases that exercise only helpers called within an entry point do NOT count toward this requirement.
Multi-module scoping: Group entry points by owning module. For each module, determine whether it directly accesses any resource being migrated (databases, message brokers, cloud storage, caches, etc. that are in the migration scope). Modules whose code never directly interacts with a migration-relevant resource are out of scope — do not produce test cases for them. Record the per-module decision (in-scope / out-of-scope with reason) so it is auditable in the baseline summary.
Scan the project's test source roots for existing IT / integration / E2E tests. Map each existing test to the entry points inventoried in Step 1 and note which coverage categories (happy-path, boundary, special-input, failure) it covers.
Also extract the project's testing conventions: naming patterns, test framework and assertion style, fixture/test-data organization, helper utilities, and infrastructure setup (Testcontainers, embedded servers, mocks, etc.). These conventions are binding — the verify-test-baseline skill MUST follow them when generating test code.
Include the existing test inventory and extracted conventions in the baseline summary (Step 7).
test-cases.mdFor each in-scope module (as determined in Step 1), produce <module-test-source-root>/test-cases/test-cases.md using test-cases-template.md. Each module gets its own independent spec file containing only test cases for entry points that belong to that module.
Existing test alignment rules:
covered-by-existing in the spec with the test's fully qualified name. Do NOT duplicate it.Module isolation rules:
test-cases.md MUST NOT contain test cases for entry points defined in other modules.Preconditions field (e.g. "Service B returns X"). The infra decision table marks it as mock; the verify-test-baseline skill handles the actual mock implementation.For each entry point, cover the four coverage buckets:
Use 2–5 representative records per entity (table row, queue message, container object, etc.).
Every test case MUST have all required fields populated — see "Required field checklist" below. Missing or vague fields make the spec unverifiable and must be filled in before freezing.
For every payload referenced in test-cases.md, create a file under <test-source-root>/test-cases/testdata/ and reference it from the spec as testdata/... (i.e. relative to <test-source-root>/test-cases/).
Requirements:
inputs/, expectations/, configuration/, seed-data/.testdata/ rather than inventing a parallel layout.test-cases.md. Every Input and Expected Output block either references a file or contains a small structured value (status code, exit code, scalar string) that does not warrant a file.sample.jpg, upload-request.json, error-not-found.json.With the full set of test cases and their referenced dependencies now visible, decide which external dependencies the post-migration tests will exercise as real resources, testcontainer resources, or mock at the SDK / HTTP boundary. This decision is recorded once here and is reused as-is by verify-test-baseline — verification does not re-decide.
Inputs:
infra/ directory (*.md, *.yml, *.yaml) if it exists. If the user has scheduled an infrastructure-provisioning task before baseline setup, run it first so that infra/ reflects the resources that will actually be available at verification time.Mandatory user confirmation before drafting rows:
real, mock, testcontainer, or mixed (per dependency). Reuse the answer from the planning questionnaire when available; if missing and an interactive question tool is available, ask explicitly before generating the table.Rules:
mock: mark every dependency mock regardless of infra/ presence.real: dependency present in infra/ (provisioned endpoint + credentials) → real.real and a dependency is not present in infra/, mark it mock at the SDK / HTTP boundary and record that it is not provisioned.testcontainer: dependency with a supported emulator/containerized dependency strategy → testcontainer.testcontainer and a dependency has no viable emulator/container strategy, mark it mock and record no-testcontainer-emulator as the reason.mixed: the mode is per dependency — ask the user for the decision per row, then apply the real, testcontainer, or mock rules above for each dependency individually.infra/ does not exist at all and mode is not testcontainer, mark every external dependency as mock and record infra-missing as the reason.infra/ presence.Confirm with the user before saving. Draft the full table in chat first, then — if an interactive user-question tool is available in the current environment (e.g. ask_user, vscode_askQuestions, or any equivalent surfaced by the host) — use it to present the draft and ask the user to confirm or correct each row's Decision and Auth Method. Apply any corrections, then write the file. If no such tool is available, skip the prompt and proceed to save (do not block the workflow).
Output: save to <test-source-root>/test-cases/infra-decision-table.md using infra-decision-table-template.md. One row per dependency, all columns required. The template defines the canonical column set, allowed values, decision rules, and banned phrasings.
This file is part of the frozen baseline bundle (see Step 7) and is consumed as-is by verify-test-baseline. Do not proceed to Step 6 until the table is saved.
Before declaring the baseline frozen, validate test-cases.md against this checklist. Each test case MUST satisfy every item, or it is not ready to freeze.
| # | Field | Validation rule |
|---|---|---|
| 1 | ID | Unique, format TC-<MODULE>-<NNN> (e.g. TC-WEB-001, TC-WKR-002). |
| 2 | Category | One of: happy-path, boundary, special-input, failure. |
| 3 | Entry Point Type | A short, consistent label describing the invocation mechanism (e.g. HTTP, CLI, Scheduled, Message-queue listener). The same mechanism must use the same label across all cases. |
| 4 | Entry Point | Exact identifier from production code: HTTP method+path, fully qualified method, CLI command, queue/topic name, service interface + method signature. No vague references. |
| 5 | Trigger | Concrete, technology-agnostic description of how the entry point is invoked: payload reference, headers, argv, message body. Sufficient for a code generator to construct the call. |
| 6 | Preconditions | All required application / resource state before the trigger, with references to testdata/ for any seed data. Use none if truly none. |
| 7 | Expected Response | The synchronous return at the entry point: status code + body file reference for HTTP, exit code + stdout/stderr file references for CLI, return value for methods. Use none (fire-and-forget) for async listeners with no synchronous response. |
| 8 | Resource Verification | At least one bullet, OR explicit none with justification. Each bullet names a specific resource (object key, row PK, queue name + message shape) and the observable state. Stated in resource-neutral terms so the same check applies pre- and post-migration. |
| 9 | Negative Verification | For failure / skip / no-op cases, list what MUST NOT happen (e.g. "no new row in image_metadata"). Mandatory whenever Category is failure or behavior is "skip". |
| 10 | Data References | All file paths under testdata/ cited in the case actually exist. |
Banned phrasings (auto-reject):
Entry-point coverage check: every orchestration entry point cataloged in Step 1 appears as the Entry Point of at least one happy-path test case AND at least one failure test case. An entry point counts as covered if it is covered by an existing test (referenced as covered-by-existing in Step 3) OR by a new test case in test-cases.md. Both sources count toward the coverage requirement.
Any validation failure → fix the spec. Do not freeze with defects.
<test-source-root>/test-cases/ folder FROZEN. Subsequent phases must not modify anything inside it; any required change forces a re-freeze cycle (unfreeze → amend → re-validate → re-freeze).${modernization-work-folder}/${taskid}/baseline-summary.md summarizing: per-module scoping decisions, entry-point inventory, existing test inventory and extracted conventions (from Step 2), test case counts (existing-covered vs. new) by category per module, testdata/ file list, the Step 5 infra decision table, and confirmation that the Step 6 checklist passed.testing
Generate and run post-migration tests from the frozen baseline specification.
devops
How team members request infrastructure connection info and handle secrets in team mode
tools
Convert an arbitrary CSV report (e.g. a Black Duck export or a custom migration-issue inventory) into a schema-valid assessment `report.json` the modernization pipeline can consume — so it appears in the assessment UI and migration solutions resolve automatically. This skill is LLM-driven: you read and interpret the CSV yourself and author `report.json` by hand against the schema. A small helper script only does deterministic lookups (migration solutions, the ruleId for a solution) and validates the finished report. There is NO "convert everything" script and NO assumed column layout. Triggers: "convert csv to assessment report", "import csv report", "turn this spreadsheet into a report.json", "Black Duck csv to report", "build report.json from csv", "migrate a third-party assessment export". NOT for: AppCAT-style analysis from source (use `assessment`), generating a modernization plan (use `create-modernization-plan`), or editing a report.json the pipeline already produced.
development
Run multi-layer integration tests for modernized Java applications. Supports 4 layers - Layer 1 (TestContainers), Layer 2 (Smoke Tests), Layer 3 (Azure Integration), Layer 4 (Behavioral Comparison). Java projects only - skip if source code is not Java. Triggers: "generate integration tests", "add integration tests", "create Layer 1 tests", "create Layer 2 tests" NOT for: unit tests, performance tests, load tests.