Repository navigation
feat(deploy): post-deploy smoke tests from project manifest (#753) - #1075
Merged
Nanle-code merged 2 commits intoSep 28, 2026
Merged
Nanle-code merged 2 commits into
Nanle-code merged 2 commits into
Conversation
…e-code#753) Projects can declare [[smoke_tests]] in starforge-project.toml. After `deploy --execute` is confirmed (contract ID returned), each test runs against the new contract via `stellar contract invoke` or a shell command, with exit/output assertions and a per-test timeout. - Declarations are validated at manifest load, before deploying - Smoke failures exit with new code 9 (SMOKE_TEST_FAILURE), classified by error type and distinct from deploy failures - --skip-smoke opts out; dry runs and failed deploys never run them - Sample project in examples/smoke-tests, schema in docs/SMOKE_TESTS.md
|
@solaawojobi00-bit Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
solaawojobi00-bit
restored the
fix/issue-753-post-deploy-smoke-tests
branch
September 28, 2026 10:55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Post-deploy smoke tests from project manifests
Problem
starforge deploy --executestops as soon as the Stellar CLI reports success. Nothing checks that the deployed contract actually works. Broken initialisation or configuration is found later, usually by a user. A project also has no way to declare the checks it expects after a deploy.SMOKE FAILline per failing test, exit 9timeout_secs, counted as a smoke failure--execute,--json--skip-smokesmoke_testsSolution
Schema.
starforge-project.tomlgains an optional[[smoke_tests]]array. Each entry has:nameinvoke(a contract function plus args, run asstellar contract invoke --id <new contract>) orcommand(a shell command, run from the manifest's directory)expect_exit(default 0),expect_output(trimmed exact match) andexpect_containstimeout_secs(1–3600, default 60)Unknown keys are rejected, the same as the rest of the manifest.
Validation before deploy. The manifest is loaded and validated before any deploy work starts, so a bad declaration can't surface after a contract is live.
Runs only after a confirmed deploy. Tests run only once the Stellar CLI exits successfully and a contract ID is parsed from its output. If no contract ID can be parsed, the tests are skipped with a warning. Every test runs even after one fails, so one report shows everything broken.
Separate classification. A smoke failure returns a typed
SmokeTestFailureerror, which maps to new exit code 9SMOKE_TEST_FAILURE. It is matched by type before the message heuristics, so its text (which mentions a contract and a network) is never misread as an execution or network error. The deploy record staysSuccess, and both the error and the recovery hints say the contract is live.Environment.
commandchecks receiveSTARFORGE_CONTRACT_ID,STARFORGE_NETWORKandSTARFORGE_SOURCE.invokearguments are passed as an argv vector with no shell, so manifest values can't inject commands.Changes
src/utils/smoke_tests.rs(new)SmokeTestandInvokeCheck, both withdeny_unknown_fields.validate(): names are non-empty and unique, exactly one ofcommand/invokeis set, neither is empty, and the timeout is in range.run_all()andrun_with_timeout(). The timeout is enforced by polling, and pipes are drained on background threads so chatty output can't block. This follows the existing pattern inmutate.rs.SmokeReportproduces theSMOKE PASS/SMOKE FAILlines.SmokeTestFailureis the typed error.src/utils/project_config.rsparse_project_lockfile_strnow also runssmoke_tests::validate, so every load path enforces the rules. The field is not part ofto_overlay(), so it never affects the merged config.src/utils/exit_codes.rsNew
ExitCode::SmokeTestFailure = 9with a name and description.determine_exit_codechecks the error chain forSmokeTestFailurebefore the message heuristics:src/commands/deploy.rs--skip-smokeflag.load_smoke_tests()runs straight after the WASM-exists check, before optimisation, policy checks or signing.run_post_deploy_smoke_tests()runs afterupdate_status(Success)and only when a contract ID was parsed.src/main.rsAdds recovery hints for smoke failures in the
deploybranch. They say the contract is deployed, point to the manifest entry, and mention--skip-smoke. The next condition was reformatted to satisfy rustfmt.Docs
docs/SMOKE_TESTS.md(new): the schema table, when tests run, validation rules, output format and the exit-code table.docs/COMMAND_REFERENCE.mdanddocs/CONFIGURATION.md: short sections that point to it.docs/SUMMARY.md: adds the new page to the book.examples/smoke-tests/(new)A sample project that targets the
simple-countertemplate:get_count == 0,increment == 1and a contract-ID shell checkfailing/starforge-project.toml, which deliberately expects42and demonstrates exit 9Tests
tests/cli_exit_codes.rs: code 9 value, name and classification, including when wrapped in context, while a real deploy failure staysExecution.Regression Tests
smoke_tests_parse_from_manifest,valid_declarations_pass_validation,unknown_fields_are_rejected_by_serdeinvalid_smoke_tests_fail_manifest_validation,missing_check_is_rejected,both_command_and_invoke_is_rejected,duplicate_and_empty_names_are_rejected,out_of_range_timeout_is_rejectedsample_project_manifests_are_valid(parses both example manifests)test_classify_smoke_test_failure_distinct_from_deploy_failure,failure_error_is_typed_and_says_deploy_succeeded,test_exit_code_numeric_valuespassing_command_passes,wrong_exit_code_fails_with_reason,expected_nonzero_exit_can_pass,output_mismatch_failstimeout_is_a_smoke_failurecontract_id_is_exported_to_commands,invoke_args_target_the_deployed_contractall_tests_run_after_a_failuredocs/SMOKE_TESTS.mdTesting
smoke_testsmodule tests, plus the manifest-level parse tests, run in isolation with the module'sserde/toml/anyhowdependencies (Windows 10):cargo clippy --all-targets -- -D warningson the module: no warnings.rustfmt --check: clean on all touched hunks.scripts/docs-snippets.py --lint-only: no findings in the new or edited docs.Notes for Reviewers
CONFIG_ERROR. Code 9 is checked by type before any message heuristics.Successon a smoke failure. The contract is on-chain, so marking the deploy failed (or triggering the auto-rollback path) would misreport what happened. Only the command's exit code changes.deploynow loadsstarforge-project.toml. It uses the existing upward discovery, and only when--skip-smokeis not set. A broken manifest therefore now stops a deploy, which matches howload_effective()treats it elsewhere.invokechecks use the same--sourcevalue the deploy command uses (wallet.public_key), for consistency.Closes #753