# Go Blackbox

> Detect white box Go tests and propose conversion to black box tests (package foo_test). Internal skill used by golang-pro agent to enforce black box testing conventions.

- **Type:** Skill
- **Install:** `agentstack add skill-sgaunet-claude-plugins-go-blackbox`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [sgaunet](https://agentstack.voostack.com/s/sgaunet)
- **Installs:** 0
- **Category:** [Cloud & Infrastructure](https://agentstack.voostack.com/c/cloud-infrastructure)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [sgaunet](https://github.com/sgaunet)
- **Source:** https://github.com/sgaunet/claude-plugins/tree/main/plugins/go-specialist/skills/go-blackbox

## Install

```sh
agentstack add skill-sgaunet-claude-plugins-go-blackbox
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Go Black Box Test Enforcement

Detect Go test files using white box testing (same package name) and propose conversion to black box testing (`package foo_test`). Creates `export_test.go` bridge files when internal symbols need external test access.

## When to Use

- Creating new test files (enforce black box from the start)
- Reviewing existing test files in a Go project
- User asks to audit or improve test quality
- The golang-pro agent encounters `*_test.go` files during implementation
- The golang-pro agent encounters goroutine-spawning code while writing or reviewing tests

## Prerequisites

1. **go.mod exists**: Abort if missing.
2. **Go source files exist**: At least one `.go` file and one `*_test.go` file to analyze.

## Workflow: Detect White Box Tests

### Step 1: Scan Test Files

Use Glob to find all test files:

```
**/*_test.go
```

Exclude `vendor/` and `export_test.go` files from analysis.

### Step 2: Classify Each Test File

For each `*_test.go` file, read the `package` declaration (first non-comment, non-blank line starting with `package`).

**Classification rules:**

| Package declaration | Source package | Classification |
|---|---|---|
| `package foo_test` | `foo` | Black box (correct) |
| `package foo` | `foo` | White box (needs conversion) |
| `package foo_test` | N/A (no source) | Black box (standalone) |

To determine the source package: look for non-test `.go` files in the same directory and read their `package` declaration.

### Step 3: Check for export_test.go

For each directory containing test files, check whether an `export_test.go` already exists.

### Step 4: Report Findings

Present a summary table:

| File | Package | Classification | export_test.go |
|---|---|---|---|
| `calc_test.go` | `calculator` | White box | No |
| `math_test.go` | `calculator_test` | Black box | N/A |
| `internal_test.go` | `parser` | White box | Yes |

## Workflow: Convert White Box to Black Box

For each white box test file identified:

### Step 1: Analyze Unexported Symbol Usage

Read the test file and identify references to unexported symbols (lowercase-initial identifiers) from the package under test:

- Unexported functions/methods called directly
- Unexported types used in variable declarations or assertions
- Unexported struct fields accessed directly
- Unexported constants/variables referenced

### Step 2: Determine Conversion Strategy

**Case A — No unexported symbols used:**
- Change `package foo` to `package foo_test`
- Add import for the package under test
- Prefix exported symbol references with the package name (e.g., `Add(1, 2)` → `calculator.Add(1, 2)`)

**Case B — Unexported symbols used:**
- Change `package foo` to `package foo_test` in the test file
- Add import for the package under test
- Create or update `export_test.go` in the same directory with `package foo` (no `_test` suffix) that exports needed internals:

```go
package foo

// Exported for testing in foo_test package.
var (
    TestableInternalFunc = internalFunc
    TestableInternalVar  = internalVar
)
```

- Update test file to use the exported aliases via the package import

**Case C — Heavy internal coupling (>10 unexported references):**
- Flag for manual review rather than automatic conversion
- Suggest refactoring the API to export what tests need, or keeping this specific file as white box with a justifying comment

### Step 3: Validate

After conversion, run:

```bash
go test ./...
go vet ./...
```

If tests fail, diagnose: missing imports, renamed references, or unexported symbols not yet bridged via `export_test.go`.

## Workflow: Add Goroutine Leak Detection

After conversion (or during new test creation), detect test functions that exercise goroutine-spawning code and add `go.uber.org/goleak` verification.

### Step 1: Identify Goroutine-Spawning Source Code

For each package under test, scan the **non-test** `.go` source files for goroutine launches:

```
Grep pattern: `go\s+func\s*\(|go\s+[a-zA-Z]`
```

This matches:
- `go func() { ... }()` — anonymous goroutine
- `go myFunction(...)` — named function goroutine
- `go obj.Method(...)` — method goroutine

Exclude `vendor/` and generated files. Record which **exported functions/methods** directly or transitively spawn goroutines.

### Step 2: Map Tests to Goroutine-Spawning Functions

For each test file in the package, identify test functions that call (directly or via helpers) the goroutine-spawning functions found in Step 1.

**Classification:**

| Test Function | Calls Goroutine-Spawning Code | Action |
|---|---|---|
| `TestWorkerPool` | Yes (`StartWorkers` uses `go`) | Add `goleak.VerifyNone(t)` |
| `TestParseConfig` | No | Skip — no goroutine risk |
| `TestServerStart` | Yes (`Serve` uses `go`) | Add `goleak.VerifyNone(t)` |

### Step 3: Add goleak to Test Functions

For each test function classified as needing leak detection:

1. **Add the defer call** as the first statement in the test function body:

```go
func TestWorkerPool(t *testing.T) {
    defer goleak.VerifyNone(t)
    // ... rest of test
}
```

2. **Add the import** to the test file if not already present:

```go
import (
    "testing"

    "go.uber.org/goleak"
)
```

3. **Install the module** if not already in `go.mod`:

```bash
go get go.uber.org/goleak@latest
go mod tidy
```

### Step 4: Handle goleak Options

Some goroutines are expected and should not trigger false positives. Common cases:

| Scenario | Option |
|---|---|
| Known background goroutine from a dependency | `goleak.IgnoreTopFunction("package.function")` |
| Goroutine matching a pattern | `goleak.IgnoreAnyFunction("package.glob*")` |

If `goleak.VerifyNone(t)` fails due to known background goroutines (e.g., from `net/http` or logging libraries), add options:

```go
defer goleak.VerifyNone(t,
    goleak.IgnoreTopFunction("net/http.(*Server).Serve"),
)
```

Do not suppress unknown goroutines — investigate them first.

### Step 5: Validate

Run the tests with the race detector to confirm no leaks and no regressions:

```bash
go test -race -count=1 ./...
```

If `goleak.VerifyNone` reports leaked goroutines:
1. Check if the tested code properly shuts down goroutines (context cancellation, channel close, `sync.WaitGroup`)
2. If the goroutine is from a third-party dependency and cannot be controlled, add a targeted `goleak.IgnoreTopFunction` option
3. Never blanket-ignore all goroutines — each ignore must be specific and documented

## export_test.go Conventions

1. **File name**: Always `export_test.go` (Go convention)
2. **Package**: Same as the source package (NOT `_test` suffix) — this is what makes it a bridge
3. **Build constraint**: None needed — Go includes `export_test.go` only during `go test`
4. **Naming**: Prefix exported aliases with `Testable` or use the capitalized form of the original name
5. **Comment**: Add `// Exported for testing in _test package.` header comment
6. **Scope**: Export only what tests actually need — do not blanket-export all internals

## Error Handling

| Condition | Action |
|-----------|--------|
| No `go.mod` found | Abort: "Not a Go module." |
| No test files found | Report: "No test files found. Nothing to analyze." |
| All tests already black box | Report: "All test files use black box naming. No conversion needed." |
| Test compilation fails after conversion | Diagnose missing imports or unexported references. Attempt fix (max 2 retries). |
| `go vet` fails after conversion | Display errors and suggest manual review. |
| `go get go.uber.org/goleak` fails | Display error. Check network connectivity and module proxy settings. |
| `goleak.VerifyNone` reports leaked goroutines | Investigate source. Fix goroutine lifecycle first; use `IgnoreTopFunction` only for third-party goroutines that cannot be controlled. |
| No goroutine-spawning code found in package | Skip goleak workflow: "No goroutine usage detected. Goroutine leak detection not needed." |

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [sgaunet](https://github.com/sgaunet)
- **Source:** [sgaunet/claude-plugins](https://github.com/sgaunet/claude-plugins)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-sgaunet-claude-plugins-go-blackbox
- Seller: https://agentstack.voostack.com/s/sgaunet
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
