minitest-cli 0.41.0__tar.gz → 0.42.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/PKG-INFO +48 -1
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/README.md +47 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/pyproject.toml +1 -1
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_network_helpers.py +9 -4
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/apps.py +2 -0
- minitest_cli-0.42.0/src/minitest_cli/commands/backend_api.py +152 -0
- minitest_cli-0.42.0/src/minitest_cli/commands/backend_api_helpers.py +93 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run.py +34 -11
- minitest_cli-0.42.0/src/minitest_cli/commands/run_backend_override.py +30 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_commit.py +8 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_tagged.py +43 -5
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/batch.py +8 -0
- minitest_cli-0.42.0/tests/_backend_api_server.py +101 -0
- minitest_cli-0.42.0/tests/test_backend_api_commands.py +96 -0
- minitest_cli-0.42.0/tests/test_backend_api_openapi_auth.py +104 -0
- minitest_cli-0.42.0/tests/test_run_backend_override.py +57 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_run_commands.py +101 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/uv.lock +4 -4
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/.env.example +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/.github/workflows/ci.yml +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/.github/workflows/install-scripts.yml +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/.github/workflows/release.yml +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/.gitignore +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/.opencode/skill/release/SKILL.md +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/AGENTS.md +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/RELEASE.md +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/install.ps1 +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/install.sh +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/pyrightconfig.json +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/renovate.json +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/api/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/api/apps_manager_client.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/api/client.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/api/errors.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/assets/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/assets/callback.html +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/_abi_mismatch.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/_response_errors.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_knowledge.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_knowledge_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_skill.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_skill_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_skill_secret.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_skill_write.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/apps_dependencies.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/apps_get.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/apps_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/apps_table.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/auth.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/auth_api_key.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/batch.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/batch_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/batch_warnings.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build_bundle.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build_commit.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build_conversion.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build_fix_prompts.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build_get.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/build_signing.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/capabilities.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/commit_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/deprecated_alias.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_asks.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_conversation.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_create.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_feature.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_feature_apply.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_feature_conflicts.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_feature_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_feature_show.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_legacy.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_lifecycle.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_pr.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_render.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_update.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/draft_watch.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/env.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/env_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/flow_types.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/flow_types_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/flow_types_write.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/glossary.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/init.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/init_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/init_playbook.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/issues.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/issues_fix.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/issues_get.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/issues_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/issues_projection.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/issues_service.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/knowledge.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/knowledge_documents.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/knowledge_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/private_network.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/private_network_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/recording_timeline.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/residential_proxy.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_cancel.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_commit_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_display.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_feedback.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_list.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_logs.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_recording.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_recording_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/run_targets.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_body.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_create.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_display.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_history.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_options.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_scope.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_spec.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_update.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/schedules_where.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/screens.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/screens_detail.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/screens_format.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/screens_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/screens_tree.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret_create.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret_extract.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret_lint.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret_manage.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret_state.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/secret_value.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/skill.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/tags.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/tags_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_file.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_file_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_file_list.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_profile.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_profile_card.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_profile_default.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_profile_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_profile_list.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_profile_secret_refs.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/test_profile_update.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/upgrade.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_app_skills.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_bindings.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_camera.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_create.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_criteria.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_criterion_edits.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_delete.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_device_count.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_iap.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_modify.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_overrides.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_profiles.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_setup_commit.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/user_story_update.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/verdicts.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/verdicts_helpers.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/verdicts_projection.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/app_context.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/auth.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/config.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/credentials.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/oauth.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/tenants.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/core/token_exchange.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/main.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/app.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/app_env_vars.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/app_failure.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/app_screen.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/base.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/build.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/commit_build.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/draft_feature.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/flow_type.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/issues.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/lane_logs.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/recording.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/story_run.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/tag.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/targets.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/models/user_story.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/confirm.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/dependency_sim.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/mermaid.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/output.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/skill_refresh.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/update_check.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/utils/video_frames.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/__init__.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/_commit_transport.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/_network_settings_server.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/_secret_router.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/conftest.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/draft_backend.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_api_client_auth.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_app_context.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_app_knowledge_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_app_skill_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_apps_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_apps_dependencies.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_auth.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_auth_api_key.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_auth_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_batch_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_build_bundle.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_build_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_build_fix_prompts.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_build_get_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_capabilities_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_channel_header.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_code_quality.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_commit_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_config_defaults.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_deprecated_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_deprecated_flags.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_draft_ask_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_draft_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_draft_conversation_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_draft_feature_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_draft_pr_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_env.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_flow_types_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_init_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_issues.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_issues_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_issues_get_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_knowledge_v2_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_mermaid.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_native_device_types.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_network_errors.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_physical_devices.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_private_network_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_recording_timeline.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_residential_proxy_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_response_errors.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_run_cancel.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_run_frames_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_run_logs_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_schedules_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_screens_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_secret_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_secret_create.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_secret_lint_extract.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_skill_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_suite_change_provenance.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_tags_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_test_file_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_test_profile_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_test_profile_secret_refs.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_upgrade_command.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_user_story_bindings_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_user_story_commands.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_user_story_in_app_purchases.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_user_story_setup_commit.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_verdicts.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_verdicts_actionable.py +0 -0
- {minitest_cli-0.41.0 → minitest_cli-0.42.0}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: minitest-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.42.0
|
|
4
4
|
Summary: Minitest CLI – command-line interface for the Minitest testing platform
|
|
5
5
|
Project-URL: Homepage, https://minitap.ai/
|
|
6
6
|
Project-URL: Source, https://github.com/minitap-ai/minitest-cli
|
|
@@ -368,6 +368,53 @@ least one domain. Connectors cannot be created from the CLI: a workspace owner
|
|
|
368
368
|
or admin creates them in the webapp, under **Workspace settings → Private
|
|
369
369
|
network**.
|
|
370
370
|
|
|
371
|
+
## Backend API
|
|
372
|
+
|
|
373
|
+
Mini can read the OpenAPI document of a backend you own and call it during runs,
|
|
374
|
+
authenticated with app vault secrets (it is off by default). `set` reads the
|
|
375
|
+
current settings first and changes only what you pass.
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
minitest --app <app-id> apps backend-api show
|
|
379
|
+
minitest --app <app-id> apps backend-api set --enable \
|
|
380
|
+
--base-url https://api.staging.example.com \
|
|
381
|
+
--openapi-url https://api.staging.example.com/openapi.json \
|
|
382
|
+
--secret API_TOKEN [--remove-secret <name> ...] [--clear-secrets] \
|
|
383
|
+
[--skill <name-or-id> | --clear-skill]
|
|
384
|
+
minitest --app <app-id> apps backend-api set --disable
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
Secrets are named references to the app vault (`minitest secret set`); `show`
|
|
388
|
+
warns about names no longer in the vault. URLs must be public `http(s)`
|
|
389
|
+
addresses: private, loopback and cloud-metadata addresses are rejected.
|
|
390
|
+
|
|
391
|
+
When the OpenAPI document itself needs authentication, say how to download it.
|
|
392
|
+
The credential comes from the app vault, is used for that download only, and is
|
|
393
|
+
sent only to the saved OpenAPI URL's origin (a one-run OpenAPI URL on another
|
|
394
|
+
origin gets none):
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
minitest --app <app-id> apps backend-api set --openapi-auth bearer --openapi-auth-secret API_TOKEN
|
|
398
|
+
minitest --app <app-id> apps backend-api set --openapi-auth header \
|
|
399
|
+
--openapi-auth-header X-Api-Key --openapi-auth-secret API_TOKEN
|
|
400
|
+
minitest --app <app-id> apps backend-api set --openapi-auth basic \
|
|
401
|
+
--openapi-auth-username docs-reader --openapi-auth-secret DOCS_PASSWORD
|
|
402
|
+
minitest --app <app-id> apps backend-api set --openapi-auth query \
|
|
403
|
+
--openapi-auth-param api_key --openapi-auth-secret API_TOKEN
|
|
404
|
+
minitest --app <app-id> apps backend-api set --openapi-auth none
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
To point one run at another backend, pass `--backend-base-url` and/or
|
|
408
|
+
`--backend-openapi-url` to `run start`, `run all` or `run from-commit`. Only
|
|
409
|
+
that run uses them; the saved settings do not change, and the app's backend API
|
|
410
|
+
must be on.
|
|
411
|
+
|
|
412
|
+
```bash
|
|
413
|
+
minitest --app <app-id> run all --web \
|
|
414
|
+
--backend-base-url https://pr-42.api.example.com \
|
|
415
|
+
--backend-openapi-url https://pr-42.api.example.com/openapi.json
|
|
416
|
+
```
|
|
417
|
+
|
|
371
418
|
## Exit Codes
|
|
372
419
|
|
|
373
420
|
| Code | Meaning |
|
|
@@ -334,6 +334,53 @@ least one domain. Connectors cannot be created from the CLI: a workspace owner
|
|
|
334
334
|
or admin creates them in the webapp, under **Workspace settings → Private
|
|
335
335
|
network**.
|
|
336
336
|
|
|
337
|
+
## Backend API
|
|
338
|
+
|
|
339
|
+
Mini can read the OpenAPI document of a backend you own and call it during runs,
|
|
340
|
+
authenticated with app vault secrets (it is off by default). `set` reads the
|
|
341
|
+
current settings first and changes only what you pass.
|
|
342
|
+
|
|
343
|
+
```bash
|
|
344
|
+
minitest --app <app-id> apps backend-api show
|
|
345
|
+
minitest --app <app-id> apps backend-api set --enable \
|
|
346
|
+
--base-url https://api.staging.example.com \
|
|
347
|
+
--openapi-url https://api.staging.example.com/openapi.json \
|
|
348
|
+
--secret API_TOKEN [--remove-secret <name> ...] [--clear-secrets] \
|
|
349
|
+
[--skill <name-or-id> | --clear-skill]
|
|
350
|
+
minitest --app <app-id> apps backend-api set --disable
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
Secrets are named references to the app vault (`minitest secret set`); `show`
|
|
354
|
+
warns about names no longer in the vault. URLs must be public `http(s)`
|
|
355
|
+
addresses: private, loopback and cloud-metadata addresses are rejected.
|
|
356
|
+
|
|
357
|
+
When the OpenAPI document itself needs authentication, say how to download it.
|
|
358
|
+
The credential comes from the app vault, is used for that download only, and is
|
|
359
|
+
sent only to the saved OpenAPI URL's origin (a one-run OpenAPI URL on another
|
|
360
|
+
origin gets none):
|
|
361
|
+
|
|
362
|
+
```bash
|
|
363
|
+
minitest --app <app-id> apps backend-api set --openapi-auth bearer --openapi-auth-secret API_TOKEN
|
|
364
|
+
minitest --app <app-id> apps backend-api set --openapi-auth header \
|
|
365
|
+
--openapi-auth-header X-Api-Key --openapi-auth-secret API_TOKEN
|
|
366
|
+
minitest --app <app-id> apps backend-api set --openapi-auth basic \
|
|
367
|
+
--openapi-auth-username docs-reader --openapi-auth-secret DOCS_PASSWORD
|
|
368
|
+
minitest --app <app-id> apps backend-api set --openapi-auth query \
|
|
369
|
+
--openapi-auth-param api_key --openapi-auth-secret API_TOKEN
|
|
370
|
+
minitest --app <app-id> apps backend-api set --openapi-auth none
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
To point one run at another backend, pass `--backend-base-url` and/or
|
|
374
|
+
`--backend-openapi-url` to `run start`, `run all` or `run from-commit`. Only
|
|
375
|
+
that run uses them; the saved settings do not change, and the app's backend API
|
|
376
|
+
must be on.
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
minitest --app <app-id> run all --web \
|
|
380
|
+
--backend-base-url https://pr-42.api.example.com \
|
|
381
|
+
--backend-openapi-url https://pr-42.api.example.com/openapi.json
|
|
382
|
+
```
|
|
383
|
+
|
|
337
384
|
## Exit Codes
|
|
338
385
|
|
|
339
386
|
| Code | Meaning |
|
{minitest_cli-0.41.0 → minitest_cli-0.42.0}/src/minitest_cli/commands/app_network_helpers.py
RENAMED
|
@@ -38,12 +38,12 @@ def fail(message: str, code: int = EXIT_GENERAL_ERROR) -> typer.Exit:
|
|
|
38
38
|
return typer.Exit(code=code)
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
def _raise_for_status(resp: httpx.Response) -> None:
|
|
41
|
+
def _raise_for_status(resp: httpx.Response, forbidden: str) -> None:
|
|
42
42
|
if resp.status_code < 400:
|
|
43
43
|
return
|
|
44
44
|
detail = extract_detail(resp)
|
|
45
45
|
if resp.status_code == 403:
|
|
46
|
-
raise fail(f"{
|
|
46
|
+
raise fail(f"{forbidden} ({detail})", EXIT_AUTH_ERROR)
|
|
47
47
|
if resp.status_code == 401:
|
|
48
48
|
raise fail(f"Authentication failed: {detail}", EXIT_AUTH_ERROR)
|
|
49
49
|
if resp.status_code == 404:
|
|
@@ -54,12 +54,17 @@ def _raise_for_status(resp: httpx.Response) -> None:
|
|
|
54
54
|
|
|
55
55
|
|
|
56
56
|
def call_api(
|
|
57
|
-
settings: Settings,
|
|
57
|
+
settings: Settings,
|
|
58
|
+
method: str,
|
|
59
|
+
path: str,
|
|
60
|
+
body: dict[str, Any] | None = None,
|
|
61
|
+
*,
|
|
62
|
+
forbidden: str = ADMIN_ONLY_MESSAGE,
|
|
58
63
|
) -> dict[str, Any]:
|
|
59
64
|
async def _run() -> dict[str, Any]:
|
|
60
65
|
async with ApiClient(settings) as client:
|
|
61
66
|
resp = await client.request(method, path, json=body)
|
|
62
|
-
_raise_for_status(resp)
|
|
67
|
+
_raise_for_status(resp, forbidden)
|
|
63
68
|
try:
|
|
64
69
|
return resp.json()
|
|
65
70
|
except ValueError as exc:
|
|
@@ -12,6 +12,7 @@ from minitest_cli.api.client import ApiClient
|
|
|
12
12
|
from minitest_cli.api.errors import format_network_error
|
|
13
13
|
from minitest_cli.commands import (
|
|
14
14
|
apps_dependencies,
|
|
15
|
+
backend_api,
|
|
15
16
|
apps_get,
|
|
16
17
|
private_network,
|
|
17
18
|
residential_proxy,
|
|
@@ -38,6 +39,7 @@ app.command(name="dependencies")(apps_dependencies.dependencies)
|
|
|
38
39
|
app.command(name="get")(apps_get.get_app)
|
|
39
40
|
app.add_typer(residential_proxy.app)
|
|
40
41
|
app.add_typer(private_network.app)
|
|
42
|
+
app.add_typer(backend_api.app)
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
@app.callback()
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""'apps backend-api': the backend Mini reads an OpenAPI document of and calls during runs."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated, Any
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from minitest_cli.commands.app_network_helpers import call_api, fail, is_json_mode, resolve_target
|
|
8
|
+
from minitest_cli.core.config import Settings
|
|
9
|
+
from minitest_cli.commands.app_skill_helpers import skills_path
|
|
10
|
+
from minitest_cli.commands.backend_api_helpers import (
|
|
11
|
+
merge_secret_names,
|
|
12
|
+
openapi_auth_body,
|
|
13
|
+
render_config,
|
|
14
|
+
)
|
|
15
|
+
from minitest_cli.utils.output import print_success
|
|
16
|
+
|
|
17
|
+
app = typer.Typer(
|
|
18
|
+
name="backend-api",
|
|
19
|
+
help=(
|
|
20
|
+
"Backend API of the app (--app): during runs Mini searches its OpenAPI document and "
|
|
21
|
+
"calls it, authenticated with app vault secrets. Off by default."
|
|
22
|
+
),
|
|
23
|
+
no_args_is_help=True,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _path(app_id: str) -> str:
|
|
28
|
+
return f"/api/v1/apps/{app_id}/backend-config"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _call(
|
|
32
|
+
settings: Settings, method: str, path: str, body: dict[str, Any] | None = None
|
|
33
|
+
) -> dict[str, Any]:
|
|
34
|
+
return call_api(settings, method, path, body, forbidden="You cannot access this app.")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@app.command(name="show")
|
|
38
|
+
def show() -> None:
|
|
39
|
+
"""Show the app's backend API settings."""
|
|
40
|
+
settings, app_id = resolve_target()
|
|
41
|
+
render_config(_call(settings, "GET", _path(app_id)))
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@app.command(name="set")
|
|
45
|
+
def set_config(
|
|
46
|
+
enable: Annotated[
|
|
47
|
+
bool | None,
|
|
48
|
+
typer.Option(
|
|
49
|
+
"--enable/--disable",
|
|
50
|
+
help="Switch it on or off. On needs a base URL and an OpenAPI URL.",
|
|
51
|
+
),
|
|
52
|
+
] = None,
|
|
53
|
+
base_url: Annotated[
|
|
54
|
+
str | None,
|
|
55
|
+
typer.Option("--base-url", help="Base URL Mini calls, e.g. https://api.staging.example."),
|
|
56
|
+
] = None,
|
|
57
|
+
openapi_url: Annotated[
|
|
58
|
+
str | None,
|
|
59
|
+
typer.Option("--openapi-url", help="URL of the OpenAPI (or Swagger) document."),
|
|
60
|
+
] = None,
|
|
61
|
+
secret: Annotated[
|
|
62
|
+
list[str] | None,
|
|
63
|
+
typer.Option(
|
|
64
|
+
"--secret",
|
|
65
|
+
help="App vault secret Mini authenticates calls with (repeatable), e.g. API_TOKEN.",
|
|
66
|
+
),
|
|
67
|
+
] = None,
|
|
68
|
+
remove_secret: Annotated[
|
|
69
|
+
list[str] | None,
|
|
70
|
+
typer.Option("--remove-secret", help="Authentication secret to drop (repeatable)."),
|
|
71
|
+
] = None,
|
|
72
|
+
clear_secrets: Annotated[
|
|
73
|
+
bool,
|
|
74
|
+
typer.Option("--clear-secrets", help="Drop every current secret before adding --secret."),
|
|
75
|
+
] = False,
|
|
76
|
+
skill: Annotated[
|
|
77
|
+
str | None,
|
|
78
|
+
typer.Option("--skill", help="App skill (name or id) explaining how to use the backend."),
|
|
79
|
+
] = None,
|
|
80
|
+
clear_skill: Annotated[
|
|
81
|
+
bool, typer.Option("--clear-skill", help="Stop pointing Mini at a skill.")
|
|
82
|
+
] = False,
|
|
83
|
+
openapi_auth: Annotated[
|
|
84
|
+
str | None,
|
|
85
|
+
typer.Option(
|
|
86
|
+
"--openapi-auth",
|
|
87
|
+
help=(
|
|
88
|
+
"How the OpenAPI document is downloaded when it needs authentication: none, "
|
|
89
|
+
"bearer, header, basic or query. Used for nothing else, and only sent to the "
|
|
90
|
+
"saved OpenAPI URL's origin."
|
|
91
|
+
),
|
|
92
|
+
),
|
|
93
|
+
] = None,
|
|
94
|
+
openapi_auth_secret: Annotated[
|
|
95
|
+
str | None,
|
|
96
|
+
typer.Option("--openapi-auth-secret", help="App vault secret holding the credential."),
|
|
97
|
+
] = None,
|
|
98
|
+
openapi_auth_header: Annotated[
|
|
99
|
+
str | None, typer.Option("--openapi-auth-header", help="Header name, for header.")
|
|
100
|
+
] = None,
|
|
101
|
+
openapi_auth_username: Annotated[
|
|
102
|
+
str | None, typer.Option("--openapi-auth-username", help="Username, for basic.")
|
|
103
|
+
] = None,
|
|
104
|
+
openapi_auth_param: Annotated[
|
|
105
|
+
str | None, typer.Option("--openapi-auth-param", help="Query parameter, for query.")
|
|
106
|
+
] = None,
|
|
107
|
+
) -> None:
|
|
108
|
+
"""Change the app's backend API settings; options not passed stay as they are."""
|
|
109
|
+
if skill and clear_skill:
|
|
110
|
+
raise fail("Pass either --skill or --clear-skill, not both.")
|
|
111
|
+
changes = [enable is not None, base_url, openapi_url, secret, remove_secret, clear_secrets]
|
|
112
|
+
auth_names = {
|
|
113
|
+
"--openapi-auth-header": openapi_auth_header,
|
|
114
|
+
"--openapi-auth-username": openapi_auth_username,
|
|
115
|
+
"--openapi-auth-param": openapi_auth_param,
|
|
116
|
+
}
|
|
117
|
+
auth_given = any([openapi_auth, openapi_auth_secret, *auth_names.values()])
|
|
118
|
+
if not any([*changes, skill, clear_skill, auth_given]):
|
|
119
|
+
raise fail(
|
|
120
|
+
"Nothing to change: pass --enable/--disable, --base-url, --openapi-url, "
|
|
121
|
+
"a secret option, a skill option or an --openapi-auth option."
|
|
122
|
+
)
|
|
123
|
+
settings, app_id = resolve_target()
|
|
124
|
+
current = _call(settings, "GET", _path(app_id))
|
|
125
|
+
body: dict[str, Any] = {
|
|
126
|
+
"enabled": current["enabled"] if enable is None else enable,
|
|
127
|
+
"baseUrl": current["baseUrl"] if base_url is None else base_url,
|
|
128
|
+
"openapiUrl": current["openapiUrl"] if openapi_url is None else openapi_url,
|
|
129
|
+
"authenticationSecretNames": merge_secret_names(
|
|
130
|
+
current["authenticationSecretNames"],
|
|
131
|
+
add=secret,
|
|
132
|
+
remove=remove_secret,
|
|
133
|
+
clear=clear_secrets,
|
|
134
|
+
),
|
|
135
|
+
"skillId": current["skill"]["id"] if current["skill"] else None,
|
|
136
|
+
}
|
|
137
|
+
if auth_given:
|
|
138
|
+
body["openapiAuth"] = openapi_auth_body(
|
|
139
|
+
current["openapiAuth"],
|
|
140
|
+
auth_type=openapi_auth,
|
|
141
|
+
secret=openapi_auth_secret,
|
|
142
|
+
names=auth_names,
|
|
143
|
+
)
|
|
144
|
+
if clear_skill:
|
|
145
|
+
body["skillId"] = None
|
|
146
|
+
elif skill:
|
|
147
|
+
body["skillId"] = _call(settings, "GET", f"{skills_path(app_id)}/{skill}")["id"]
|
|
148
|
+
|
|
149
|
+
updated = _call(settings, "PUT", _path(app_id), body)
|
|
150
|
+
if not is_json_mode():
|
|
151
|
+
print_success("Backend API updated.")
|
|
152
|
+
render_config(updated)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""Rendering and secret-list editing for 'apps backend-api'."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from minitest_cli.commands.app_network_helpers import fail, is_json_mode, on_off
|
|
6
|
+
from minitest_cli.utils.output import print_json, print_warning
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def merge_secret_names(
|
|
10
|
+
current: list[str], *, add: list[str] | None, remove: list[str] | None, clear: bool
|
|
11
|
+
) -> list[str]:
|
|
12
|
+
"""Apply ``--clear-secrets``, ``--remove-secret`` then ``--secret``."""
|
|
13
|
+
removed = {name.strip() for name in remove or []}
|
|
14
|
+
unknown = sorted(removed - set(current))
|
|
15
|
+
if unknown:
|
|
16
|
+
raise fail(f"Not an authentication secret of this app: {', '.join(unknown)}.")
|
|
17
|
+
base = [] if clear else [name for name in current if name not in removed]
|
|
18
|
+
return list(dict.fromkeys([*base, *(name.strip() for name in add or [])]))
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
OPENAPI_AUTH_TYPES = ("none", "bearer", "header", "basic", "query")
|
|
22
|
+
_AUTH_NAME_FIELD = {
|
|
23
|
+
"header": ("headerName", "--openapi-auth-header"),
|
|
24
|
+
"basic": ("username", "--openapi-auth-username"),
|
|
25
|
+
"query": ("paramName", "--openapi-auth-param"),
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def openapi_auth_body(
|
|
30
|
+
current: dict[str, Any],
|
|
31
|
+
*,
|
|
32
|
+
auth_type: str | None,
|
|
33
|
+
secret: str | None,
|
|
34
|
+
names: dict[str, str | None],
|
|
35
|
+
) -> dict[str, Any]:
|
|
36
|
+
"""Apply the --openapi-auth options to the saved setting; ``names`` is keyed by option."""
|
|
37
|
+
kind = auth_type or current["type"]
|
|
38
|
+
if kind not in OPENAPI_AUTH_TYPES:
|
|
39
|
+
raise fail(f"--openapi-auth must be one of: {', '.join(OPENAPI_AUTH_TYPES)}.")
|
|
40
|
+
field = _AUTH_NAME_FIELD.get(kind)
|
|
41
|
+
stray = [
|
|
42
|
+
option for option, value in names.items() if value and (not field or option != field[1])
|
|
43
|
+
]
|
|
44
|
+
if kind == "none" and secret:
|
|
45
|
+
stray.insert(0, "--openapi-auth-secret")
|
|
46
|
+
if stray:
|
|
47
|
+
raise fail(f"{', '.join(stray)} does not apply to --openapi-auth {kind}.")
|
|
48
|
+
if kind == "none":
|
|
49
|
+
return {"type": "none"}
|
|
50
|
+
same = current["type"] == kind
|
|
51
|
+
body = {"type": kind, "secretName": secret or (current.get("secretName") if same else None)}
|
|
52
|
+
if field:
|
|
53
|
+
body[field[0]] = names[field[1]] or (current.get(field[0]) if same else None)
|
|
54
|
+
required = [("--openapi-auth-secret", body["secretName"])] + (
|
|
55
|
+
[(field[1], body[field[0]])] if field else []
|
|
56
|
+
)
|
|
57
|
+
missing = [option for option, value in required if not value]
|
|
58
|
+
if missing:
|
|
59
|
+
raise fail(f"--openapi-auth {kind} needs {' and '.join(missing)}.")
|
|
60
|
+
return body
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _describe_openapi_auth(auth: dict[str, Any]) -> str:
|
|
64
|
+
if auth["type"] == "none":
|
|
65
|
+
return "none"
|
|
66
|
+
field = _AUTH_NAME_FIELD.get(auth["type"])
|
|
67
|
+
detail = f" {field[0]}={auth[field[0]]}" if field else ""
|
|
68
|
+
return f"{auth['type']}{detail} secret={auth['secretName']}"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def render_config(config: dict[str, Any]) -> None:
|
|
72
|
+
if is_json_mode():
|
|
73
|
+
print_json(config)
|
|
74
|
+
else:
|
|
75
|
+
skill = config["skill"]
|
|
76
|
+
print(f"Enabled: {on_off(config['enabled'])}") # noqa: T201
|
|
77
|
+
print(f"Base URL: {config['baseUrl'] or '-'}") # noqa: T201
|
|
78
|
+
print(f"OpenAPI URL: {config['openapiUrl'] or '-'}") # noqa: T201
|
|
79
|
+
secrets = ", ".join(config["authenticationSecretNames"]) or "-"
|
|
80
|
+
print(f"Authentication secrets: {secrets}") # noqa: T201
|
|
81
|
+
print(f"Skill: {skill['name'] + ' ' + skill['id'] if skill else '-'}") # noqa: T201
|
|
82
|
+
print(f"OpenAPI download: {_describe_openapi_auth(config['openapiAuth'])}") # noqa: T201
|
|
83
|
+
missing = config["missingSecretNames"]
|
|
84
|
+
if missing:
|
|
85
|
+
print_warning(
|
|
86
|
+
f"Not in the app vault anymore: {', '.join(missing)}. Create them with "
|
|
87
|
+
"'minitest secret set' or drop them with --remove-secret."
|
|
88
|
+
)
|
|
89
|
+
if config["openapiAuthSecretMissing"]:
|
|
90
|
+
print_warning(
|
|
91
|
+
f"The OpenAPI download secret {config['openapiAuth']['secretName']} is not in the "
|
|
92
|
+
"app vault anymore: runs will not start until it is created again or replaced."
|
|
93
|
+
)
|
|
@@ -18,6 +18,11 @@ from minitest_cli.commands.run_helpers import (
|
|
|
18
18
|
run_api_call,
|
|
19
19
|
TERMINAL_STATUSES,
|
|
20
20
|
)
|
|
21
|
+
from minitest_cli.commands.run_backend_override import (
|
|
22
|
+
BackendBaseUrlOpt,
|
|
23
|
+
BackendOpenapiUrlOpt,
|
|
24
|
+
backend_api_override,
|
|
25
|
+
)
|
|
21
26
|
from minitest_cli.commands.run_cancel import cancel
|
|
22
27
|
from minitest_cli.commands.run_commit import from_commit
|
|
23
28
|
from minitest_cli.commands.run_targets import (
|
|
@@ -34,7 +39,7 @@ from minitest_cli.commands.run_feedback import feedback
|
|
|
34
39
|
from minitest_cli.commands.run_list import list_runs
|
|
35
40
|
from minitest_cli.commands.run_logs import register_logs_command
|
|
36
41
|
from minitest_cli.commands.run_recording import register_recording_commands
|
|
37
|
-
from minitest_cli.commands.run_tagged import print_batch_started, start_tagged
|
|
42
|
+
from minitest_cli.commands.run_tagged import print_batch_started, start_selected, start_tagged
|
|
38
43
|
from minitest_cli.commands.verdicts import verdicts
|
|
39
44
|
from minitest_cli.models.batch import BatchResponse, CreateBatchRequest
|
|
40
45
|
from minitest_cli.models.story_run import (
|
|
@@ -54,8 +59,9 @@ app = typer.Typer(
|
|
|
54
59
|
|
|
55
60
|
@app.command()
|
|
56
61
|
def start(
|
|
57
|
-
|
|
58
|
-
str | None,
|
|
62
|
+
user_stories: Annotated[
|
|
63
|
+
list[str] | None,
|
|
64
|
+
typer.Argument(help="Scenario names or UUIDs to run (one or more). Excludes --tag."),
|
|
59
65
|
] = None,
|
|
60
66
|
tag: Annotated[
|
|
61
67
|
list[str] | None,
|
|
@@ -68,13 +74,18 @@ def start(
|
|
|
68
74
|
android_device_type: AndroidDeviceTypeOpt = None,
|
|
69
75
|
ios_physical: IosPhysicalOpt = False,
|
|
70
76
|
android_physical: AndroidPhysicalOpt = False,
|
|
77
|
+
backend_base_url: BackendBaseUrlOpt = None,
|
|
78
|
+
backend_openapi_url: BackendOpenapiUrlOpt = None,
|
|
71
79
|
watch: Annotated[
|
|
72
80
|
bool, typer.Option("--watch/--no-watch", help="Poll for results (default: watch).")
|
|
73
81
|
] = True,
|
|
74
82
|
) -> None:
|
|
75
|
-
"""Start a run for one
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
"""Start a run for one or more scenarios, or for every scenario carrying --tag.
|
|
84
|
+
|
|
85
|
+
Several scenarios, like --tag, start one run and print its summary without polling.
|
|
86
|
+
"""
|
|
87
|
+
if bool(user_stories) == bool(tag):
|
|
88
|
+
print_error("Pass either scenarios or --tag, not both or neither.")
|
|
78
89
|
raise typer.Exit(code=1)
|
|
79
90
|
settings, app_id, json_mode = resolve_app()
|
|
80
91
|
targets = build_targets(
|
|
@@ -86,15 +97,22 @@ def start(
|
|
|
86
97
|
ios_physical,
|
|
87
98
|
android_physical,
|
|
88
99
|
)
|
|
89
|
-
|
|
90
|
-
|
|
100
|
+
override = backend_api_override(backend_base_url, backend_openapi_url)
|
|
101
|
+
story_refs = list(dict.fromkeys(user_stories or []))
|
|
102
|
+
if tag or len(story_refs) > 1:
|
|
103
|
+
start_batch = start_tagged if tag else start_selected
|
|
104
|
+
print_batch_started(
|
|
105
|
+
start_batch(settings, app_id, tag or story_refs, targets, override), json_mode
|
|
106
|
+
)
|
|
91
107
|
return
|
|
92
|
-
story_ref =
|
|
108
|
+
story_ref = story_refs[0]
|
|
93
109
|
|
|
94
110
|
async def _start() -> StoryRunResponse:
|
|
95
111
|
async with ApiClient(settings) as client:
|
|
96
112
|
user_story_id = await resolve_user_story_id(client, app_id, story_ref)
|
|
97
|
-
body = CreateBatchRequest(
|
|
113
|
+
body = CreateBatchRequest(
|
|
114
|
+
user_story_ids=[user_story_id], targets=targets, backend_api_override=override
|
|
115
|
+
)
|
|
98
116
|
batch = await post_batch(client, app_id, body)
|
|
99
117
|
if not batch.story_runs:
|
|
100
118
|
print_error("Batch created but no scenario runs were returned.")
|
|
@@ -147,6 +165,8 @@ def run_all(
|
|
|
147
165
|
android_device_type: AndroidDeviceTypeOpt = None,
|
|
148
166
|
ios_physical: IosPhysicalOpt = False,
|
|
149
167
|
android_physical: AndroidPhysicalOpt = False,
|
|
168
|
+
backend_base_url: BackendBaseUrlOpt = None,
|
|
169
|
+
backend_openapi_url: BackendOpenapiUrlOpt = None,
|
|
150
170
|
) -> None:
|
|
151
171
|
"""Start a run covering every scenario of the app."""
|
|
152
172
|
settings, app_id, json_mode = resolve_app()
|
|
@@ -162,7 +182,10 @@ def run_all(
|
|
|
162
182
|
|
|
163
183
|
async def _run_all() -> BatchResponse:
|
|
164
184
|
async with ApiClient(settings) as client:
|
|
165
|
-
body = CreateBatchRequest(
|
|
185
|
+
body = CreateBatchRequest(
|
|
186
|
+
targets=targets,
|
|
187
|
+
backend_api_override=backend_api_override(backend_base_url, backend_openapi_url),
|
|
188
|
+
)
|
|
166
189
|
return await post_batch(client, app_id, body)
|
|
167
190
|
|
|
168
191
|
print_batch_started(run_api_call(_run_all()), json_mode)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""One-run backend API URL flags shared by the commands that start runs."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
from minitest_cli.models.batch import BackendApiRunOverride
|
|
8
|
+
|
|
9
|
+
BackendBaseUrlOpt = Annotated[
|
|
10
|
+
str | None,
|
|
11
|
+
typer.Option(
|
|
12
|
+
"--backend-base-url",
|
|
13
|
+
help="Backend API base URL for this run only (the app's backend API must be on).",
|
|
14
|
+
),
|
|
15
|
+
]
|
|
16
|
+
BackendOpenapiUrlOpt = Annotated[
|
|
17
|
+
str | None,
|
|
18
|
+
typer.Option(
|
|
19
|
+
"--backend-openapi-url",
|
|
20
|
+
help="OpenAPI document URL for this run only (the app's backend API must be on).",
|
|
21
|
+
),
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def backend_api_override(
|
|
26
|
+
base_url: str | None, openapi_url: str | None
|
|
27
|
+
) -> BackendApiRunOverride | None:
|
|
28
|
+
if base_url is None and openapi_url is None:
|
|
29
|
+
return None
|
|
30
|
+
return BackendApiRunOverride(base_url=base_url, openapi_url=openapi_url)
|
|
@@ -18,6 +18,11 @@ from minitest_cli.commands.run_commit_helpers import (
|
|
|
18
18
|
batch_commit_payload,
|
|
19
19
|
poll_batch,
|
|
20
20
|
)
|
|
21
|
+
from minitest_cli.commands.run_backend_override import (
|
|
22
|
+
BackendBaseUrlOpt,
|
|
23
|
+
BackendOpenapiUrlOpt,
|
|
24
|
+
backend_api_override,
|
|
25
|
+
)
|
|
21
26
|
from minitest_cli.commands.run_helpers import (
|
|
22
27
|
resolve_app,
|
|
23
28
|
resolve_user_story_id,
|
|
@@ -64,6 +69,8 @@ def from_commit(
|
|
|
64
69
|
legacy_user_story: LegacyUserStoryOpt = None,
|
|
65
70
|
watch: WatchOpt = True,
|
|
66
71
|
timeout: TimeoutOpt = DEFAULT_TIMEOUT_SECONDS,
|
|
72
|
+
backend_base_url: BackendBaseUrlOpt = None,
|
|
73
|
+
backend_openapi_url: BackendOpenapiUrlOpt = None,
|
|
67
74
|
) -> None:
|
|
68
75
|
"""Build a GitHub commit and run the test suite against it."""
|
|
69
76
|
settings, app_id, json_mode = resolve_app()
|
|
@@ -86,6 +93,7 @@ def from_commit(
|
|
|
86
93
|
commit_sha=sha,
|
|
87
94
|
platforms=platforms if targets is None else None,
|
|
88
95
|
targets=targets,
|
|
96
|
+
backend_api_override=backend_api_override(backend_base_url, backend_openapi_url),
|
|
89
97
|
)
|
|
90
98
|
batch = await post_batch(client, app_id, body)
|
|
91
99
|
if not watch:
|
|
@@ -1,14 +1,20 @@
|
|
|
1
|
-
"""Batch launch over
|
|
1
|
+
"""Batch launch over a scenario selection (several scenarios, or every scenario carrying a
|
|
2
|
+
tag), and the shared batch summary output."""
|
|
2
3
|
|
|
3
4
|
import typer
|
|
4
5
|
|
|
5
6
|
from minitest_cli.api.client import ApiClient
|
|
6
7
|
from minitest_cli.commands.batch_helpers import batch_summary_payload, post_batch
|
|
7
|
-
from minitest_cli.commands.run_helpers import
|
|
8
|
+
from minitest_cli.commands.run_helpers import (
|
|
9
|
+
RUN_TABLE_HEADERS,
|
|
10
|
+
format_run_row,
|
|
11
|
+
resolve_user_story_id,
|
|
12
|
+
run_api_call,
|
|
13
|
+
)
|
|
8
14
|
from minitest_cli.commands.tags_helpers import resolve_tag_ids
|
|
9
15
|
from minitest_cli.commands.user_story_helpers import EXIT_NOT_FOUND, fetch_all_user_stories
|
|
10
16
|
from minitest_cli.core.config import Settings
|
|
11
|
-
from minitest_cli.models.batch import BatchResponse, CreateBatchRequest
|
|
17
|
+
from minitest_cli.models.batch import BackendApiRunOverride, BatchResponse, CreateBatchRequest
|
|
12
18
|
from minitest_cli.models.targets import BatchTarget
|
|
13
19
|
from minitest_cli.utils.output import print_error, print_info, print_json, print_table
|
|
14
20
|
|
|
@@ -26,7 +32,11 @@ def print_batch_started(batch: BatchResponse, json_mode: bool) -> None:
|
|
|
26
32
|
|
|
27
33
|
|
|
28
34
|
def start_tagged(
|
|
29
|
-
settings: Settings,
|
|
35
|
+
settings: Settings,
|
|
36
|
+
app_id: str,
|
|
37
|
+
tags: list[str],
|
|
38
|
+
targets: list[BatchTarget],
|
|
39
|
+
backend_api_override: BackendApiRunOverride | None = None,
|
|
30
40
|
) -> BatchResponse:
|
|
31
41
|
async def _start() -> BatchResponse:
|
|
32
42
|
async with ApiClient(settings) as client:
|
|
@@ -37,7 +47,35 @@ def start_tagged(
|
|
|
37
47
|
print_error(f"No scenario carries any of these tags: {', '.join(tags)}.")
|
|
38
48
|
raise typer.Exit(code=EXIT_NOT_FOUND)
|
|
39
49
|
body = CreateBatchRequest(
|
|
40
|
-
user_story_ids=[str(s["id"]) for s in stories],
|
|
50
|
+
user_story_ids=[str(s["id"]) for s in stories],
|
|
51
|
+
targets=targets,
|
|
52
|
+
backend_api_override=backend_api_override,
|
|
53
|
+
)
|
|
54
|
+
return await post_batch(client, app_id, body)
|
|
55
|
+
|
|
56
|
+
return run_api_call(_start())
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def start_selected(
|
|
60
|
+
settings: Settings,
|
|
61
|
+
app_id: str,
|
|
62
|
+
story_refs: list[str],
|
|
63
|
+
targets: list[BatchTarget],
|
|
64
|
+
backend_api_override: BackendApiRunOverride | None = None,
|
|
65
|
+
) -> BatchResponse:
|
|
66
|
+
"""Start one batch covering the given scenarios (names or UUIDs).
|
|
67
|
+
|
|
68
|
+
Only the selected scenarios run: dependencies between them are kept, and a
|
|
69
|
+
scenario whose parent is not selected starts on its own.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
async def _start() -> BatchResponse:
|
|
73
|
+
async with ApiClient(settings) as client:
|
|
74
|
+
story_ids = [await resolve_user_story_id(client, app_id, ref) for ref in story_refs]
|
|
75
|
+
body = CreateBatchRequest(
|
|
76
|
+
user_story_ids=list(dict.fromkeys(story_ids)),
|
|
77
|
+
targets=targets,
|
|
78
|
+
backend_api_override=backend_api_override,
|
|
41
79
|
)
|
|
42
80
|
return await post_batch(client, app_id, body)
|
|
43
81
|
|