원클릭으로
write-tests
Write Go unit and e2e tests following Theatrum's exact conventions
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Write Go unit and e2e tests following Theatrum's exact conventions
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Update CLAUDE.md and README.md to reflect code changes
Audit the codebase for hexagonal architecture violations (forbidden imports, dependency direction, port conformance)
Explain Theatrum's hexagonal (ports & adapters) architecture with concrete examples from the codebase
Expert reference on streaming protocols (HLS, DASH, RTMP, adaptive bitrate) grounded in Theatrum's implementation
Boot the app, stream via RTMP, watch via HLS/DASH, and validate the full pipeline
Manage and extend the CI/CD pipeline (GitHub Actions, Docker, build scripts)
| name | write-tests |
| description | Write Go unit and e2e tests following Theatrum's exact conventions |
You write Go tests for Theatrum following the project's exact conventions. When invoked, write tests for the file or package specified in $ARGUMENTS. If no argument is given, ask what to test.
Before writing any test, read these reference files to match the project's style:
src/domain/services/pathTemplateService_test.go — Table-driven tests, subtests, error cases (most comprehensive)src/domain/services/viewerTracker_test.go — Mock storage, time-dependent behavior, helper constructorssrc/adapters/driven/metrics/viewerCollector_test.go — Prometheus testutil usagesrc/adapters/driven/ffmpegEncoder/repositories/ffmpegEncoder_test.go — DryRun mode testingAlso read the file under test and any port interfaces it depends on.
Read the file specified in $ARGUMENTS and understand:
testing package ONLYassert, no require, no suite)testing/fstest or other non-standard test helpers_test external package){originalFileName}_test.goTheatrum (e.g., import "Theatrum/domain/models")t.Run(tt.name, ...):func TestFunctionName_Behavior(t *testing.T) {
tests := []struct {
name string
input string
expected string
wantErr bool
}{
{
name: "descriptive case name",
input: "value",
expected: "result",
},
{
name: "error case description",
input: "bad",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := FunctionUnderTest(tt.input)
if tt.wantErr {
if err == nil {
t.Fatalf("expected error, got nil")
}
return
}
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != tt.expected {
t.Errorf("got %q, want %q", result, tt.expected)
}
})
}
}
t.Errorf() for non-fatal assertions (test continues)t.Fatalf() for fatal assertions (test stops)t.Errorf("got %v, want %v", got, want)viewerTracker_test.go:type mockStorage struct{}
func (m *mockStorage) ReadFile(path string) ([]byte, error) { return nil, nil }
func (m *mockStorage) WriteFile(path string, data []byte) error { return nil }
func (m *mockStorage) DeleteFile(path string) error { return nil }
func (m *mockStorage) ListFiles(pattern string) ([]string, error) { return nil, nil }
func (m *mockStorage) GetFileSize(path string) (int64, error) { return 0, nil }
func (m *mockStorage) SearchFiles(pattern string, extensions []string) ([]string, []map[string]string, error) {
return nil, nil, nil
}
For mocks that need configurable behavior, use function fields:
type mockStorageWithBehavior struct {
readFileFunc func(path string) ([]byte, error)
writeFileFunc func(path string, data []byte) error
// ... other methods
}
func (m *mockStorageWithBehavior) ReadFile(path string) ([]byte, error) {
if m.readFileFunc != nil {
return m.readFileFunc(path)
}
return nil, nil
}
StoragePort (src/domain/repositories/storagePort.go):
type StoragePort interface {
ReadFile(path string) ([]byte, error)
WriteFile(path string, data []byte) error
DeleteFile(path string) error
ListFiles(pattern string) ([]string, error)
GetFileSize(path string) (int64, error)
SearchFiles(pattern string, extensions []string) ([]string, []map[string]string, error)
}
EncoderPort (src/domain/repositories/encoderPort.go):
type EncoderPort interface {
EncodeVideo(inputPath string, outputPath string, qualities map[string]models.Quality, distribution models.Distribution) error
}
Note: models.Distribution uses pointer fields (*Hls, *Dash). In tests, construct as:
dist := models.Distribution{Hls: &models.Hls{SegmentDuration: 6}}
dist := models.Distribution{Dash: &models.Dash{SegmentDuration: 6}}
dist := models.Distribution{Hls: &models.Hls{SegmentDuration: 2}, Dash: &models.Dash{SegmentDuration: 2}} // dual
ConfigurationPort (src/domain/repositories/configurationPort.go):
type ConfigurationPort interface {
Load(configPath string) (*models.Application, *models.Server, *map[string]models.Stream, error)
}
When a struct's New*() constructor starts goroutines or has side effects, create a test helper that directly initializes the struct:
// newTestTracker creates a ViewerTracker without starting the cleanup goroutine.
func newTestTracker() *ViewerTracker {
return &ViewerTracker{
streams: make(map[string]*streamTracking),
storage: &mockStorage{},
}
}
Priority order:
After writing tests, run them:
cd src && go test ./... -v -run TestNamePattern
For a specific package:
cd src && go test ./domain/services/ -v -run TestFunctionName
For race detection:
cd src && go test ./... -race
Fix any compilation or test failures before finishing.