- Core library with SVG and PNG generation - CLI tool with generate and batch commands - Cross-platform path handling for Windows compatibility - Comprehensive test suite with integration tests
308 lines
8.3 KiB
Go
308 lines
8.3 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/spf13/viper"
|
|
)
|
|
|
|
// TestRootCommand tests the basic structure and flags of the root command
|
|
func TestRootCommand(t *testing.T) {
|
|
// Reset viper for clean test state
|
|
viper.Reset()
|
|
|
|
tests := []struct {
|
|
name string
|
|
args []string
|
|
wantErr bool
|
|
validate func(t *testing.T, cmd *cobra.Command)
|
|
}{
|
|
{
|
|
name: "help flag",
|
|
args: []string{"--help"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
// Help should be available
|
|
if !cmd.HasAvailableFlags() {
|
|
t.Error("Expected command to have available flags")
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "size flag",
|
|
args: []string{"--size", "128"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
if viper.GetInt("size") != 128 {
|
|
t.Errorf("Expected size=128, got %d", viper.GetInt("size"))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "format flag svg",
|
|
args: []string{"--format", "svg"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
if viper.GetString("format") != "svg" {
|
|
t.Errorf("Expected format=svg, got %s", viper.GetString("format"))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "format flag png",
|
|
args: []string{"--format", "png"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
if viper.GetString("format") != "png" {
|
|
t.Errorf("Expected format=png, got %s", viper.GetString("format"))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "invalid format",
|
|
args: []string{"--format", "invalid"},
|
|
wantErr: true,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
// Should not reach here on error
|
|
},
|
|
},
|
|
{
|
|
name: "padding flag",
|
|
args: []string{"--padding", "0.15"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
if viper.GetFloat64("padding") != 0.15 {
|
|
t.Errorf("Expected padding=0.15, got %f", viper.GetFloat64("padding"))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "color-saturation flag",
|
|
args: []string{"--color-saturation", "0.8"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
if viper.GetFloat64("color-saturation") != 0.8 {
|
|
t.Errorf("Expected color-saturation=0.8, got %f", viper.GetFloat64("color-saturation"))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "bg-color flag",
|
|
args: []string{"--bg-color", "#ffffff"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
if viper.GetString("bg-color") != "#ffffff" {
|
|
t.Errorf("Expected bg-color=#ffffff, got %s", viper.GetString("bg-color"))
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "hue-restrictions flag",
|
|
args: []string{"--hue-restrictions", "0,120,240"},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, cmd *cobra.Command) {
|
|
hues := viper.GetStringSlice("hue-restrictions")
|
|
expected := []string{"0", "120", "240"}
|
|
if len(hues) != len(expected) {
|
|
t.Errorf("Expected %d hue restrictions, got %d", len(expected), len(hues))
|
|
}
|
|
for i, hue := range expected {
|
|
if i >= len(hues) || hues[i] != hue {
|
|
t.Errorf("Expected hue[%d]=%s, got %s", i, hue, hues[i])
|
|
}
|
|
}
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Reset viper for each test
|
|
viper.Reset()
|
|
|
|
// Create a fresh root command for each test
|
|
cmd := &cobra.Command{
|
|
Use: "jdenticon",
|
|
Short: "Generate identicons from any input string",
|
|
}
|
|
|
|
// Re-initialize flags
|
|
initTestFlags(cmd)
|
|
|
|
// Set args and execute
|
|
cmd.SetArgs(tt.args)
|
|
err := cmd.Execute()
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("Execute() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
if !tt.wantErr && tt.validate != nil {
|
|
tt.validate(t, cmd)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// initTestFlags initializes flags for testing (similar to root init())
|
|
func initTestFlags(cmd *cobra.Command) {
|
|
// Basic flags shared across commands
|
|
var formatFlag FormatFlag = FormatSVG // Default to SVG
|
|
cmd.PersistentFlags().IntP("size", "s", 200, "Size of the identicon in pixels")
|
|
cmd.PersistentFlags().VarP(&formatFlag, "format", "f", `Output format ("png" or "svg")`)
|
|
cmd.PersistentFlags().Float64P("padding", "p", 0.08, "Padding as percentage of icon size (0.0-0.5)")
|
|
|
|
// Color configuration flags
|
|
cmd.PersistentFlags().Float64("color-saturation", 0.5, "Saturation for colored shapes (0.0-1.0)")
|
|
cmd.PersistentFlags().Float64("grayscale-saturation", 0.0, "Saturation for grayscale shapes (0.0-1.0)")
|
|
cmd.PersistentFlags().String("bg-color", "", "Background color (hex format, e.g., #ffffff)")
|
|
|
|
// Advanced configuration flags
|
|
cmd.PersistentFlags().StringSlice("hue-restrictions", nil, "Restrict hues to specific degrees (0-360), e.g., --hue-restrictions=0,120,240")
|
|
cmd.PersistentFlags().String("color-lightness", "0.4,0.8", "Color lightness range as min,max (0.0-1.0)")
|
|
cmd.PersistentFlags().String("grayscale-lightness", "0.3,0.9", "Grayscale lightness range as min,max (0.0-1.0)")
|
|
cmd.PersistentFlags().Int("png-supersampling", 8, "PNG supersampling factor (1-16)")
|
|
|
|
// Bind flags to viper
|
|
viper.BindPFlag("size", cmd.PersistentFlags().Lookup("size"))
|
|
viper.BindPFlag("format", cmd.PersistentFlags().Lookup("format"))
|
|
viper.BindPFlag("padding", cmd.PersistentFlags().Lookup("padding"))
|
|
viper.BindPFlag("color-saturation", cmd.PersistentFlags().Lookup("color-saturation"))
|
|
viper.BindPFlag("grayscale-saturation", cmd.PersistentFlags().Lookup("grayscale-saturation"))
|
|
viper.BindPFlag("bg-color", cmd.PersistentFlags().Lookup("bg-color"))
|
|
viper.BindPFlag("hue-restrictions", cmd.PersistentFlags().Lookup("hue-restrictions"))
|
|
viper.BindPFlag("color-lightness", cmd.PersistentFlags().Lookup("color-lightness"))
|
|
viper.BindPFlag("grayscale-lightness", cmd.PersistentFlags().Lookup("grayscale-lightness"))
|
|
viper.BindPFlag("png-supersampling", cmd.PersistentFlags().Lookup("png-supersampling"))
|
|
}
|
|
|
|
// TestPopulateConfigFromFlags tests the configuration building logic
|
|
func TestPopulateConfigFromFlags(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
setup func()
|
|
wantErr bool
|
|
validate func(t *testing.T, config interface{}, size int)
|
|
}{
|
|
{
|
|
name: "default config",
|
|
setup: func() {
|
|
viper.Reset()
|
|
viper.Set("size", 200)
|
|
viper.Set("format", "svg")
|
|
viper.Set("png-supersampling", 8)
|
|
},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config interface{}, size int) {
|
|
if size != 200 {
|
|
t.Errorf("Expected size=200, got %d", size)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "custom config",
|
|
setup: func() {
|
|
viper.Reset()
|
|
viper.Set("size", 128)
|
|
viper.Set("padding", 0.12)
|
|
viper.Set("color-saturation", 0.7)
|
|
viper.Set("bg-color", "#000000")
|
|
viper.Set("png-supersampling", 8)
|
|
},
|
|
wantErr: false,
|
|
validate: func(t *testing.T, config interface{}, size int) {
|
|
if size != 128 {
|
|
t.Errorf("Expected size=128, got %d", size)
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: "invalid size",
|
|
setup: func() {
|
|
viper.Reset()
|
|
viper.Set("size", -1)
|
|
},
|
|
wantErr: true,
|
|
validate: nil,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
tt.setup()
|
|
|
|
config, size, err := populateConfigFromFlags()
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("populateConfigFromFlags() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
if !tt.wantErr && tt.validate != nil {
|
|
tt.validate(t, config, size)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestGetFormatFromViper tests format flag validation
|
|
func TestGetFormatFromViper(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
formatValue string
|
|
wantFormat FormatFlag
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "svg format",
|
|
formatValue: "svg",
|
|
wantFormat: FormatSVG,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "png format",
|
|
formatValue: "png",
|
|
wantFormat: FormatPNG,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid format",
|
|
formatValue: "invalid",
|
|
wantFormat: FormatSVG,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
viper.Reset()
|
|
viper.Set("format", tt.formatValue)
|
|
|
|
format, err := getFormatFromViper()
|
|
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("getFormatFromViper() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
if format != tt.wantFormat {
|
|
t.Errorf("getFormatFromViper() = %v, want %v", format, tt.wantFormat)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestMain sets up and tears down for tests
|
|
func TestMain(m *testing.M) {
|
|
// Setup
|
|
code := m.Run()
|
|
|
|
// Teardown
|
|
viper.Reset()
|
|
|
|
os.Exit(code)
|
|
}
|