Writing Skills
writing-skills
效率与协作技能,来自 obra/superpowers,查看原文获取完整用途和操作说明。
quality-smoke-testing【部署与运维】Use when the user asks about smoke testing, build verification testing (BVT), deployment health checks, canary testing, sanity testing, or CI/CD pipeline health checks. Do NOT use for: full regression testing (quality-regression-testing), acceptance testing (quality-acceptance-testing), or end-to-end testing (quality-e2e-testing).
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Validate that the most critical functionality of an application works after a deployment, build, or environment change. Smoke tests provide fast, reliable feedback that the system is stable enough for further testing or production release. This skill covers smoke suite design, CI/CD gate integration, rollback automation, and environment-specific strategies.
User mentions smoke test, BVT, build verification, deployment health check, canary test, sanity check, or asks "is the build stable?"
Structured report with:
Is this a critical business path?
├── YES → Can the system function without it?
│ ├── NO → Include as critical smoke test
│ └── YES → Include as non-critical smoke test
└── NO → Move to regression suite
Can it run in under 30 seconds?
├── YES → Keep in smoke suite
└── NO → Can it be split/optimized?
├── YES → Optimize and keep
└── NO → Move to regression suite
Is it 100% deterministic?
├── YES → Keep in smoke suite
└── NO → Fix flakiness or remove
Deployment target?
├── Staging → Full smoke suite (write operations OK)
│ ├── Threshold: 100% critical, 95% non-critical
│ └── Rollback: Automatic
├── Canary → Core smoke (read-only + limited write)
│ ├── Threshold: 100% all tests
│ └── Rollback: Canary drain
└── Production → Read-only smoke suite
├── Threshold: 100% all tests
└── Rollback: Automatic full rollback
| Aspect | Smoke Testing | Regression Testing | Health Checks |
|---|---|---|---|
| Scope | Critical paths | Complete suite | Single component |
| Duration | < 5 minutes | Minutes to hours | < 1 second |
| Frequency | Per deployment | Per change + scheduled | Continuous |
| Tests | 10-25 | 100-10000+ | 1-3 per service |
| Action on fail | Rollback | Block merge | Alert |
| Determinism | 100% required | < 1% flakiness OK | OK to be intermittent |
| Data | Defaults/seed | Complex setup | None |
// e2e/smoke/critical-paths.spec.ts
import { test, expect } from "@playwright/test";
test.describe("Smoke: Infrastructure", () => {
test("homepage returns 200", async ({ page }) => {
const response = await page.goto("/");
expect(response?.status()).toBe(200);
});
test("health endpoint is healthy", async ({ request }) => {
const response = await request.get("/api/health");
expect(response.ok()).toBeTruthy();
const body = await response.json();
expect(body.status).toBe("healthy");
});
});
test.describe("Smoke: Authentication", () => {
test("login page loads with form elements", async ({ page }) => {
await page.goto("/login");
await expect(page.getByTestId("email-input")).toBeVisible();
await expect(page.getByTestId("password-input")).toBeVisible();
await expect(page.getByTestId("submit-button")).toBeVisible();
});
test("login with valid credentials succeeds", async ({ page }) => {
await page.goto("/login");
await page.getByTestId("email-input").fill("smoke@example.com");
await page.getByTestId("password-input").fill(process.env.SMOKE_PASSWORD!);
await page.getByTestId("submit-button").click();
await expect(page).toHaveURL(/dashboard/);
});
});
test.describe("Smoke: Core Business Flow", () => {
test("product listing page loads", async ({ page }) => {
await page.goto("/products");
await expect(page.getByTestId("product-grid")).toBeVisible();
await expect(page.getByTestId("product-card")).toHaveCount({ gte: 1 });
});
test("search returns results", async ({ page }) => {
await page.goto("/products");
await page.getByTestId("search-input").fill("wireless");
await page.getByTestId("search-submit").click();
await expect(page.getByTestId("search-results")).toBeVisible();
});
});
# smoke/test_api_health.py
import requests
import sys
SMOKE_TESTS = [
{"name": "homepage", "url": "/", "expected_status": 200},
{"name": "health endpoint", "url": "/api/health", "expected_status": 200},
{"name": "login page", "url": "/login", "expected_status": 200},
{"name": "products API", "url": "/api/products", "expected_status": 200},
]
def test_all():
base_url = sys.argv[1] if len(sys.argv) > 1 else "http://localhost:3000"
failures = []
for test in SMOKE_TESTS:
try:
resp = requests.get(f"{base_url}{test['url']}", timeout=10)
if resp.status_code != test["expected_status"]:
failures.append(f"FAIL: {test['name']} - expected {test['expected_status']}, got {resp.status_code}")
else:
print(f"PASS: {test['name']}")
except Exception as e:
failures.append(f"FAIL: {test['name']} - {e}")
if failures:
print("\n".join(failures))
sys.exit(1)
print(f"\nAll {len(SMOKE_TESTS)} smoke tests passed")
if __name__ == "__main__":
test_all()
name: Deploy with Smoke Tests
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
outputs:
status: ${{ steps.smoke.outputs.status }}
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: ./deploy.sh staging
- name: Smoke tests
id: smoke
run: |
npx playwright test --grep @smoke --reporter=json > smoke-results.json
if grep -q '"status":"failed"' smoke-results.json; then
echo "status=failed" >> $GITHUB_OUTPUT
else
echo "status=passed" >> $GITHUB_OUTPUT
fi
- name: Rollback on smoke failure
if: steps.smoke.outputs.status == 'failed'
run: ./rollback.sh staging
promote:
needs: deploy
if: needs.deploy.outputs.status == 'passed'
runs-on: ubuntu-latest
steps:
- name: Promote to production
run: ./promote.sh staging production
# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
spec:
source:
repoURL: https://github.com/example/app
syncPolicy:
automated:
selfHeal: true
allowEmpty: false
retry:
limit: 3
health:
- test: "smoke-test"
commands:
- "curl -f http://app:3000/api/health"
- "curl -f http://app:3000/api/products | jq '. | length > 0'"
Smoke suites that exceed 25 tests become slow and lose their purpose as a fast health check. Prune aggressively. Smoke tests are gates, not comprehensive validators. Move detailed tests to regression.
Smoke tests must be 100% deterministic. Zero tolerance for flakiness. If a smoke test fails intermittently, fix it immediately or remove it from the smoke suite. Flaky smoke tests erode trust in the entire deployment pipeline.
Smoke tests requiring complex data setup (API seeding, database migrations, multiple test accounts) are fragile and slow. Smoke tests should work with minimal or default data. Use existing seed data.
Production smoke tests that create test orders, register test users, or modify data cause data pollution and may trigger real downstream effects. Production smoke tests must be read-only. Use GET requests only.
Smoke test failures that only notify humans instead of triggering automatic rollback introduce delay in incident response. Critical smoke test failures must trigger automatic rollback. Test the rollback automation quarterly.
Starting functional smoke tests before verifying infrastructure is healthy wastes time debugging false failures. Infrastructure checks (health endpoints, database connectivity, cache connectivity) must run first. If infrastructure is down, skip functional tests and fail fast.
| Level | Characteristics | Practices |
|---|---|---|
| 1: Initial | Manual smoke | Manual checks after deployment, inconsistent execution, no automation |
| 2: Defined | Automated basic smoke | Scripted health checks, CI/CD integration, manual rollback on failure |
| 3: Managed | Comprehensive smoke suite | Infrastructure + critical path smoke, automatic rollback, 100% deterministic, reviewed every sprint |
| 4: Measured | Smoke as deployment gate | Automatic rollback on critical failure, post-deployment monitoring, layered smoke (staging vs production), execution time trend tracked |
| 5: Optimized | Predictive smoke | Canary analysis combines with smoke results, progressive delivery based on smoke + metrics, self-healing smoke suite automatically updates baseline expectations |
canary_test:
deployment:
strategy: "10% canary → 50% → 100% rollout"
interval: "10 minutes between stages"
health_metrics:
- "Error rate: < 1% (compare to baseline)"
- "P95 latency: < 120% of baseline"
- "CPU utilization: < 80%"
smoke_tests:
pre_rollout:
- "Health endpoint returns 200"
- "Authentication works"
- "Core read API returns data"
- "Core write API succeeds"
during_rollout:
- "Monitor same metrics in real-time"
- "Auto-abort canary if any metric exceeds threshold"
smoke_metrics:
reliability:
pass_rate_last_30_days: 99.7%
consecutive_passes: 187
last_failure: "2026-05-12 (DB connection pool exhausted)"
performance:
avg_execution_time: "2m 43s"
p95_execution_time: "4m 12s"
max_execution_time: "5m 01s"
coverage:
infrastructure_checks: 4
auth_checks: 3
core_business_checks: 8
total_tests: 15
rollback:
auto_rollbacks_triggered: 2
avg_rollback_time: "45s"
rollback_success_rate: 100%
After smoke testing, hand off to:
quality-regression-testing — if smoke passes, run targeted regression on affected areasquality-acceptance-testing — if smoke passes and UAT is scheduledquality-e2e-testing — for deeper end-to-end verification of critical journeysquality-load-testing — if smoke reveals performance concerns` interface EventObserver { onEvent(event: T): Promise; }
class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `
config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1
| Metric | Threshold | Severity | Action |
|---|---|---|---|
| Error rate | > 1% over 5min | Critical | Page on-call |
| p99 latency | > 2s over 5min | Warning | Investigate |
| Throughput drop | > 50% over 1min | Critical | Check upstream |
| Queue depth | > 1000 over 1min | Warning | Scale consumers |
| Disk usage | > 85% | Warning | Clean or expand |
| Memory usage | > 90% heap | Critical | Restart or scale |
| Anti-Pattern | Symptom | Root Cause | Solution |
|---|---|---|---|
| Premature optimization | Complex code for no measured benefit | Guessing instead of profiling | Measure first, optimize based on data |
| Copy-paste reuse | Duplicate code across codebase | Lack of abstraction | Extract shared logic into libraries |
| Gold-plating | Features with no current requirement | Over-engineering | YAGNI — build what's needed now |
| Magical thinking | Assumptions without validation | Skipping error handling | Handle all failure modes explicitly |
Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).
| Decision Point | Option A | Option B | Decision Criteria |
|---|---|---|---|
| Environment | Deployed staging (post-deploy) | CI preview (pre-deploy) | Deployment complexity, rollback speed |
| Selection criteria | Critical user journeys (business-focused) | All available endpoints (tech-focused) | Test execution time, coverage needs |
| Trigger | Automated post-deploy (always) | Manual (on demand) | Release frequency, risk tolerance |
| Pass/fail action | Block deployment (mandatory) | Alert only (advisory) | Service criticality, deployment speed |