/tdd
Test-Driven Development workflow for ALL Prowler components (UI, SDK, API). Trigger: ALWAYS when implementing features, fixing bugs, or refactoring - regardless of component. This is a MANDATORY workflow, not optional.
$ npx -y skills add prowler-cloud/prowler --skill tdd --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/tdd
Context preview
The summary Claude sees to decide when to auto-load this skill.
Test-Driven Development workflow for ALL Prowler components (UI, SDK, API). Trigger: ALWAYS when implementing features, fixing bugs, or refactoring - regardless of component. This is a MANDATORY workflow, not optional.
SKILL.md
tdd.SKILL.mdname: tdd
description: >
Test-Driven Development workflow for ALL Prowler components (UI, SDK, API).
Trigger: ALWAYS when implementing features, fixing bugs, or refactoring - regardless of component.
This is a MANDATORY workflow, not optional.
license: Apache-2.0
metadata:
author: prowler-cloud
version: "2.0"
scope: [root, ui, api, prowler]
auto_invoke:
- "Implementing feature"
- "Fixing bug"
- "Refactoring code"
- "Working on task"
- "Modifying component"
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, TaskTDD Cycle (MANDATORY)
+-----------------------------------------+
| RED -> GREEN -> REFACTOR |
| ^ | |
| +------------------------+ |
+-----------------------------------------+
The question is NOT "should I write tests?" but "what tests do I need?"
---
The Three Laws of TDD
1. **No production code** until you have a failing test 2. **No more test** than necessary to fail 3. **No more code** than necessary to pass
---
Detect Your Stack
Before starting, identify which component you're working on:
| Working in | Stack | Runner | Test pattern | Details | |------------|-------|--------|-------------|---------| | `ui/` | TypeScript / React | Vitest + RTL | `*.test.{ts,tsx}` (co-located) | See `vitest` skill | | `prowler/` | Python | pytest + moto | `*_test.py` (suffix) in `tests/` | See `prowler-test-sdk` skill | | `api/` | Python / Django | pytest + django | `test_*.py` (prefix) in `api/src/backend/**/tests/` | See `prowler-test-api` skill |
---
Phase 0: Assessment (ALWAYS FIRST)
Before writing ANY code:
UI (`ui/`)
# 1. Find existing tests
fd "*.test.tsx" ui/components/feature/
# 2. Check coverage
pnpm test:coverage -- components/feature/
# 3. Read existing tests
SDK (`prowler/`)
# 1. Find existing tests
fd "*_test.py" tests/providers/aws/services/ec2/
# 2. Run specific test
uv run pytest tests/providers/aws/services/ec2/ec2_ami_public/ -v
# 3. Read existing tests
API (`api/`)
# 1. Find existing tests
fd "test_*.py" api/src/backend/api/tests/
# 2. Run specific test
uv run pytest api/src/backend/api/tests/test_models.py -v
# 3. Read existing tests
Decision Tree (All Stacks)
+------------------------------------------+
| Does test file exist for this code? |
+----------+-----------------------+-------+
| NO | YES
v v
+------------------+ +------------------+
| CREATE test file | | Check coverage |
| -> Phase 1: RED | | for your change |
+------------------+ +--------+---------+
|
+--------+--------+
| Missing cases? |
+---+---------+---+
| YES | NO
v v
+-----------+ +-----------+
| ADD tests | | Proceed |
| Phase 1 | | Phase 2 |
+-----------+ +-----------+---
Phase 1: RED - Write Failing Tests
For NEW Functionality
UI (Vitest)
describe("PriceCalculator", () => {
it("should return 0 for quantities below threshold", () => {
// Given
const quantity = 3;
// When
const result = calculateDiscount(quantity);
// Then
expect(result).toBe(0);
});
});SDK (pytest)
class Test_ec2_ami_public:
@mock_aws
def test_no_public_amis(self):
# Given - No AMIs exist
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch("prowler...ec2_service", new=EC2(aws_provider)):
from prowler...ec2_ami_public import ec2_ami_public
# When
check = ec2_ami_public()
result = check.execute()
# Then
assert len(result) == 0API (pytest-django)
@pytest.mark.django_db
class TestResourceModel:
def test_create_resource_with_tags(self, aws_provider):
# Given
provider = aws_provider
tenant_id = provider.tenant_id
# When
resource = Resource.objects.create(
tenant_id=tenant_id, provider=provider,
uid="arn:aws:ec2:us-east-1:123456789:instance/i-1234",
name="test", region="us-east-1", service="ec2", type="instance",
)
# Then
assert resource.uid == "arn:aws:ec2:us-east-1:123456789:instance/i-1234"**Run -> MUST fail:** Test references code that doesn't exist yet.
For BUG FIXES
Write a test that **reproduces the bug** first:
**UI:** `expect(() => render(<DatePicker value={null} />)).not.toThrow();`
**SDK:** `assert result[0].status == "FAIL" # Currently returns PASS incorrectly`
**API:** `assert response.status_code == 403 # Currently returns 200`
Run -> Should FAIL (reproducing the bug).
For REFACTORING
Capture ALL current behavior BEFORE refactoring:
# Any stack: run ALL existing tests, they should PASS
# This is your safety net - if any fail after refactoring, you broke something
Run -> All should PASS (baseline).
---
Phase 2: GREEN - Minimum Code
Write the MINIMUM code to make the test pass. Hardcoding is valid for the first test.
**UI:**
// Test expects calculateDiscount(100, 10) === 10
function calculateDiscount() {
return 10; // FAKE IT - hardcoded is valid for first test
}**Python (SDK/API):**
# Test expects check.execute() returns 0 results
def execute(self):
return [] # FAKE IT - hardcoded is valid for first test**This passes. But we're not done...**
---
Phase 3: Triangulation (CRITICAL)
**One test allows faking. Multiple tests FORCE real logic.**
Add tests with different inputs that break the hardcoded value:
| Scenario | Required? | |---------
Read more
name: tdd
description: >
Test-Driven Development workflow for ALL Prowler components (UI, SDK, API).
Trigger: ALWAYS when implementing features, fixing bugs, or refactoring - regardless of component.
This is a MANDATORY workflow, not optional.
license: Apache-2.0
metadata:
author: prowler-cloud
version: "2.0"
scope: [root, ui, api, prowler]
auto_invoke:
- "Implementing feature"
- "Fixing bug"
- "Refactoring code"
- "Working on task"
- "Modifying component"
allowed-tools: Read, Edit, Write, Glob, Grep, Bash, TaskTDD Cycle (MANDATORY)
+-----------------------------------------+ | RED -> GREEN -> REFACTOR | | ^ | | | +------------------------+ | +-----------------------------------------+
The question is NOT "should I write tests?" but "what tests do I need?"
---
The Three Laws of TDD
1. **No production code** until you have a failing test 2. **No more test** than necessary to fail 3. **No more code** than necessary to pass
---
Detect Your Stack
Before starting, identify which component you're working on:
| Working in | Stack | Runner | Test pattern | Details | |------------|-------|--------|-------------|---------| | `ui/` | TypeScript / React | Vitest + RTL | `*.test.{ts,tsx}` (co-located) | See `vitest` skill | | `prowler/` | Python | pytest + moto | `*_test.py` (suffix) in `tests/` | See `prowler-test-sdk` skill | | `api/` | Python / Django | pytest + django | `test_*.py` (prefix) in `api/src/backend/**/tests/` | See `prowler-test-api` skill |
---
Phase 0: Assessment (ALWAYS FIRST)
Before writing ANY code:
UI (`ui/`)
# 1. Find existing tests fd "*.test.tsx" ui/components/feature/ # 2. Check coverage pnpm test:coverage -- components/feature/ # 3. Read existing tests
SDK (`prowler/`)
# 1. Find existing tests fd "*_test.py" tests/providers/aws/services/ec2/ # 2. Run specific test uv run pytest tests/providers/aws/services/ec2/ec2_ami_public/ -v # 3. Read existing tests
API (`api/`)
# 1. Find existing tests fd "test_*.py" api/src/backend/api/tests/ # 2. Run specific test uv run pytest api/src/backend/api/tests/test_models.py -v # 3. Read existing tests
Decision Tree (All Stacks)
+------------------------------------------+
| Does test file exist for this code? |
+----------+-----------------------+-------+
| NO | YES
v v
+------------------+ +------------------+
| CREATE test file | | Check coverage |
| -> Phase 1: RED | | for your change |
+------------------+ +--------+---------+
|
+--------+--------+
| Missing cases? |
+---+---------+---+
| YES | NO
v v
+-----------+ +-----------+
| ADD tests | | Proceed |
| Phase 1 | | Phase 2 |
+-----------+ +-----------+---
Phase 1: RED - Write Failing Tests
For NEW Functionality
UI (Vitest)
describe("PriceCalculator", () => {
it("should return 0 for quantities below threshold", () => {
// Given
const quantity = 3;
// When
const result = calculateDiscount(quantity);
// Then
expect(result).toBe(0);
});
});SDK (pytest)
class Test_ec2_ami_public:
@mock_aws
def test_no_public_amis(self):
# Given - No AMIs exist
aws_provider = set_mocked_aws_provider([AWS_REGION_US_EAST_1])
with mock.patch("prowler...ec2_service", new=EC2(aws_provider)):
from prowler...ec2_ami_public import ec2_ami_public
# When
check = ec2_ami_public()
result = check.execute()
# Then
assert len(result) == 0API (pytest-django)
@pytest.mark.django_db
class TestResourceModel:
def test_create_resource_with_tags(self, aws_provider):
# Given
provider = aws_provider
tenant_id = provider.tenant_id
# When
resource = Resource.objects.create(
tenant_id=tenant_id, provider=provider,
uid="arn:aws:ec2:us-east-1:123456789:instance/i-1234",
name="test", region="us-east-1", service="ec2", type="instance",
)
# Then
assert resource.uid == "arn:aws:ec2:us-east-1:123456789:instance/i-1234"**Run -> MUST fail:** Test references code that doesn't exist yet.
For BUG FIXES
Write a test that **reproduces the bug** first:
**UI:** `expect(() => render(<DatePicker value={null} />)).not.toThrow();`
**SDK:** `assert result[0].status == "FAIL" # Currently returns PASS incorrectly`
**API:** `assert response.status_code == 403 # Currently returns 200`
Run -> Should FAIL (reproducing the bug).
For REFACTORING
Capture ALL current behavior BEFORE refactoring:
# Any stack: run ALL existing tests, they should PASS # This is your safety net - if any fail after refactoring, you broke something
Run -> All should PASS (baseline).
---
Phase 2: GREEN - Minimum Code
Write the MINIMUM code to make the test pass. Hardcoding is valid for the first test.
**UI:**
// Test expects calculateDiscount(100, 10) === 10
function calculateDiscount() {
return 10; // FAKE IT - hardcoded is valid for first test
}**Python (SDK/API):**
# Test expects check.execute() returns 0 results
def execute(self):
return [] # FAKE IT - hardcoded is valid for first test**This passes. But we're not done...**
---
Phase 3: Triangulation (CRITICAL)
**One test allows faking. Multiple tests FORCE real logic.**
Add tests with different inputs that break the hardcoded value:
| Scenario | Required? | |---------
Prowler is the world’s most widely used Open-Source Cloud Security Platform that automates security and compliance across any cloud environment.
Repo: prowler-cloud/prowler
Other skills on prowler.
- /framework-compliance-triage
Make a cloud account compliant with a security or industry framework using Prowler Cloud.
Open skill - /ai-sdk-5
Vercel AI SDK 5 patterns. Trigger: When building AI features with AI SDK v5 (chat, streaming, tools/function calling, UIMessage parts), including migration from v4.
Open skill - /django-drf
Django REST Framework patterns. Trigger: When implementing generic DRF APIs (ViewSets, serializers, routers, permissions, filtersets). For Prowler API specifics (RLS/RBAC/Providers), also use prowler-api.
Open skill - /django-migration-psql
Reviews Django migration files for PostgreSQL best practices specific to Prowler. Trigger: When creating migrations, running makemigrations/pgmakemigrations, reviewing migration PRs, adding indexes or constraints to database tables, modifying existing migration files, or writing
Open skill - /gh-aw
Create and maintain GitHub Agentic Workflows (gh-aw) for Prowler. Trigger: When creating agentic workflows, modifying gh-aw frontmatter, configuring safe-outputs, setting up MCP servers in workflows, importing Copilot Custom Agents, or debugging gh-aw compilation.
Open skill - /jsonapi
Strict JSON:API v1.1 specification compliance. Trigger: When creating or modifying API endpoints, reviewing API responses, or validating JSON:API compliance.
Open skill

