feat(06-01): add Formatter interface, Registry, and TTY color detection
- pkg/output/formatter.go: Formatter interface, Options, Registry with Register/Get/Names, ErrUnknownFormat sentinel - pkg/output/colors.go: IsTTY + ColorsEnabled honoring NO_COLOR - Promote github.com/mattn/go-isatty to direct dependency - Unit tests cover registry round-trip, unknown lookup, sorted Names, non-TTY buffer, NO_COLOR override Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
72
pkg/output/formatter_test.go
Normal file
72
pkg/output/formatter_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/salvacybersec/keyhunter/pkg/engine"
|
||||
)
|
||||
|
||||
// fakeFormatter is a minimal Formatter used to verify the registry round-trip.
|
||||
type fakeFormatter struct{ tag string }
|
||||
|
||||
func (f fakeFormatter) Format(findings []engine.Finding, w io.Writer, opts Options) error {
|
||||
_, err := w.Write([]byte(f.tag))
|
||||
return err
|
||||
}
|
||||
|
||||
func TestFormatter_RegisterAndGet(t *testing.T) {
|
||||
Register("__test_fake__", fakeFormatter{tag: "ok"})
|
||||
t.Cleanup(func() { delete(registry, "__test_fake__") })
|
||||
|
||||
f, err := Get("__test_fake__")
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, f)
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, f.Format(nil, &buf, Options{}))
|
||||
assert.Equal(t, "ok", buf.String())
|
||||
}
|
||||
|
||||
func TestFormatter_GetUnknown(t *testing.T) {
|
||||
_, err := Get("__definitely_not_registered__")
|
||||
require.Error(t, err)
|
||||
assert.True(t, errors.Is(err, ErrUnknownFormat), "error should wrap ErrUnknownFormat")
|
||||
}
|
||||
|
||||
func TestFormatter_NamesSorted(t *testing.T) {
|
||||
Register("__z_fake__", fakeFormatter{})
|
||||
Register("__a_fake__", fakeFormatter{})
|
||||
t.Cleanup(func() {
|
||||
delete(registry, "__z_fake__")
|
||||
delete(registry, "__a_fake__")
|
||||
})
|
||||
|
||||
names := Names()
|
||||
// The full registry may contain other entries (e.g. "table"); just assert
|
||||
// that our two names appear in sorted order relative to each other.
|
||||
var aIdx, zIdx = -1, -1
|
||||
for i, n := range names {
|
||||
switch n {
|
||||
case "__a_fake__":
|
||||
aIdx = i
|
||||
case "__z_fake__":
|
||||
zIdx = i
|
||||
}
|
||||
}
|
||||
require.GreaterOrEqual(t, aIdx, 0)
|
||||
require.GreaterOrEqual(t, zIdx, 0)
|
||||
assert.Less(t, aIdx, zIdx, "Names() must return entries in ascending order")
|
||||
}
|
||||
|
||||
func TestOptions_Defaults(t *testing.T) {
|
||||
var opts Options
|
||||
assert.False(t, opts.Unmask)
|
||||
assert.Equal(t, "", opts.ToolName)
|
||||
assert.Equal(t, "", opts.ToolVersion)
|
||||
}
|
||||
Reference in New Issue
Block a user