Some checks failed
CI / Test (Go 1.24.x, ubuntu-latest) (push) Successful in 1m53s
CI / Code Quality (push) Failing after 26s
CI / Security Scan (push) Failing after 11s
CI / Test Coverage (push) Successful in 1m13s
CI / Benchmarks (push) Failing after 10m22s
CI / Build CLI (push) Failing after 8s
Benchmarks / Run Benchmarks (push) Failing after 10m13s
Release / Test (push) Successful in 55s
Release / Build (amd64, darwin, ) (push) Failing after 12s
Release / Build (amd64, linux, ) (push) Failing after 6s
Release / Build (amd64, windows, .exe) (push) Failing after 12s
Release / Build (arm64, darwin, ) (push) Failing after 12s
Release / Build (arm64, linux, ) (push) Failing after 12s
Release / Release (push) Has been skipped
CI / Test (Go 1.24.x, macos-latest) (push) Has been cancelled
CI / Test (Go 1.24.x, windows-latest) (push) Has been cancelled
Move hosting from GitHub to private Gitea instance.
469 lines
14 KiB
Go
469 lines
14 KiB
Go
package jdenticon
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dockr.co/kev/go-jdenticon/internal/constants"
|
|
)
|
|
|
|
// TestDoSProtection_InputLength tests protection against large input strings.
|
|
func TestDoSProtection_InputLength(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
inputLength int
|
|
config Config
|
|
expectError bool
|
|
errorType string
|
|
}{
|
|
{
|
|
name: "normal input with default config",
|
|
inputLength: 100,
|
|
config: DefaultConfig(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "maximum allowed input with default config",
|
|
inputLength: constants.DefaultMaxInputLength,
|
|
config: DefaultConfig(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "oversized input with default config",
|
|
inputLength: constants.DefaultMaxInputLength + 1,
|
|
config: DefaultConfig(),
|
|
expectError: true,
|
|
errorType: "*jdenticon.ErrValueTooLarge",
|
|
},
|
|
{
|
|
name: "oversized input with custom smaller limit",
|
|
inputLength: 1000,
|
|
config: func() Config {
|
|
c := DefaultConfig()
|
|
c.MaxInputLength = 500
|
|
return c
|
|
}(),
|
|
expectError: true,
|
|
errorType: "*jdenticon.ErrValueTooLarge",
|
|
},
|
|
{
|
|
name: "oversized input with disabled limit",
|
|
inputLength: constants.DefaultMaxInputLength + 1000,
|
|
config: func() Config {
|
|
c := DefaultConfig()
|
|
c.MaxInputLength = -1 // Disabled
|
|
return c
|
|
}(),
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Create input string of specified length
|
|
input := strings.Repeat("a", tt.inputLength)
|
|
|
|
// Test ToSVGWithConfig
|
|
_, err := ToSVGWithConfig(context.Background(), input, 100, tt.config)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("ToSVGWithConfig: expected error but got none")
|
|
return
|
|
}
|
|
|
|
// Check error type if specified
|
|
if tt.errorType != "" {
|
|
var valueErr *ErrValueTooLarge
|
|
if !errors.As(err, &valueErr) {
|
|
t.Errorf("ToSVGWithConfig: expected error type %s, got %T", tt.errorType, err)
|
|
} else if valueErr.ParameterName != "InputLength" {
|
|
t.Errorf("ToSVGWithConfig: expected InputLength error, got %s", valueErr.ParameterName)
|
|
}
|
|
}
|
|
} else if err != nil {
|
|
t.Errorf("ToSVGWithConfig: unexpected error: %v", err)
|
|
}
|
|
|
|
// Test ToPNGWithConfig
|
|
_, err = ToPNGWithConfig(context.Background(), input, 100, tt.config)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("ToPNGWithConfig: expected error but got none")
|
|
}
|
|
} else if err != nil {
|
|
t.Errorf("ToPNGWithConfig: unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestDoSProtection_IconSize tests protection against large icon sizes.
|
|
func TestDoSProtection_IconSize(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
size int
|
|
config Config
|
|
expectError bool
|
|
errorType string
|
|
}{
|
|
{
|
|
name: "normal size with default config",
|
|
size: 256,
|
|
config: DefaultConfig(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "maximum allowed size with default config",
|
|
size: constants.DefaultMaxIconSize,
|
|
config: DefaultConfig(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "oversized icon with default config",
|
|
size: constants.DefaultMaxIconSize + 1,
|
|
config: DefaultConfig(),
|
|
expectError: true,
|
|
errorType: "*jdenticon.ErrValueTooLarge",
|
|
},
|
|
{
|
|
name: "oversized icon with custom smaller limit",
|
|
size: 2000,
|
|
config: func() Config {
|
|
c := DefaultConfig()
|
|
c.MaxIconSize = 1000
|
|
return c
|
|
}(),
|
|
expectError: true,
|
|
errorType: "*jdenticon.ErrValueTooLarge",
|
|
},
|
|
{
|
|
name: "oversized icon with disabled limit",
|
|
size: constants.DefaultMaxIconSize + 1000,
|
|
config: func() Config {
|
|
c := DefaultConfig()
|
|
c.MaxIconSize = -1 // Disabled
|
|
return c
|
|
}(),
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "zero size",
|
|
size: 0,
|
|
config: DefaultConfig(),
|
|
expectError: true,
|
|
errorType: "jdenticon.ErrInvalidSize",
|
|
},
|
|
{
|
|
name: "negative size",
|
|
size: -100,
|
|
config: DefaultConfig(),
|
|
expectError: true,
|
|
errorType: "jdenticon.ErrInvalidSize",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
input := "test"
|
|
|
|
// Test ToSVGWithConfig
|
|
_, err := ToSVGWithConfig(context.Background(), input, tt.size, tt.config)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("ToSVGWithConfig: expected error but got none")
|
|
return
|
|
}
|
|
|
|
// Check error type if specified
|
|
if tt.errorType != "" {
|
|
switch tt.errorType {
|
|
case "*jdenticon.ErrValueTooLarge":
|
|
var valueErr *ErrValueTooLarge
|
|
if !errors.As(err, &valueErr) {
|
|
t.Errorf("ToSVGWithConfig: expected error type %s, got %T", tt.errorType, err)
|
|
} else if valueErr.ParameterName != "IconSize" {
|
|
t.Errorf("ToSVGWithConfig: expected IconSize error, got %s", valueErr.ParameterName)
|
|
}
|
|
case "jdenticon.ErrInvalidSize":
|
|
var sizeErr ErrInvalidSize
|
|
if !errors.As(err, &sizeErr) {
|
|
t.Errorf("ToSVGWithConfig: expected error type %s, got %T", tt.errorType, err)
|
|
}
|
|
}
|
|
}
|
|
} else if err != nil {
|
|
t.Errorf("ToSVGWithConfig: unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestDoSProtection_PNGEffectiveSize tests protection against PNG supersampling creating oversized effective images.
|
|
func TestDoSProtection_PNGEffectiveSize(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
size int
|
|
supersampling int
|
|
config Config
|
|
expectError bool
|
|
errorType string
|
|
}{
|
|
{
|
|
name: "normal PNG with default supersampling",
|
|
size: 512,
|
|
supersampling: 8, // 512 * 8 = 4096 (exactly at limit)
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "oversized effective PNG size",
|
|
size: 1024,
|
|
supersampling: 8, // 1024 * 8 = 8192 (exceeds 4096 limit)
|
|
expectError: true,
|
|
errorType: "*jdenticon.ErrEffectiveSizeTooLarge",
|
|
},
|
|
{
|
|
name: "large PNG with low supersampling (within limit)",
|
|
size: 2048,
|
|
supersampling: 2, // 2048 * 2 = 4096 (exactly at limit)
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "maximum PNG with 1x supersampling",
|
|
size: 4096,
|
|
supersampling: 1, // 4096 * 1 = 4096 (exactly at limit)
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "PNG with disabled size limit",
|
|
size: 2000,
|
|
supersampling: 10, // Would exceed default limit but should be allowed
|
|
config: func() Config {
|
|
c := DefaultConfig()
|
|
c.MaxIconSize = -1 // Disabled
|
|
return c
|
|
}(),
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
config := tt.config
|
|
if config.MaxIconSize == 0 && config.MaxInputLength == 0 {
|
|
// Use default config if not specified
|
|
config = DefaultConfig()
|
|
}
|
|
config.PNGSupersampling = tt.supersampling
|
|
|
|
input := "test"
|
|
|
|
_, err := ToPNGWithConfig(context.Background(), input, tt.size, config)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("ToPNGWithConfig: expected error but got none")
|
|
return
|
|
}
|
|
|
|
// Check error type if specified
|
|
if tt.errorType == "*jdenticon.ErrEffectiveSizeTooLarge" {
|
|
var effectiveErr *ErrEffectiveSizeTooLarge
|
|
if !errors.As(err, &effectiveErr) {
|
|
t.Errorf("ToPNGWithConfig: expected error type %s, got %T", tt.errorType, err)
|
|
} else {
|
|
expectedEffective := tt.size * tt.supersampling
|
|
if effectiveErr.Actual != expectedEffective {
|
|
t.Errorf("ToPNGWithConfig: expected effective size %d, got %d",
|
|
expectedEffective, effectiveErr.Actual)
|
|
}
|
|
if effectiveErr.Size != tt.size {
|
|
t.Errorf("ToPNGWithConfig: expected size %d, got %d",
|
|
tt.size, effectiveErr.Size)
|
|
}
|
|
if effectiveErr.Supersampling != tt.supersampling {
|
|
t.Errorf("ToPNGWithConfig: expected supersampling %d, got %d",
|
|
tt.supersampling, effectiveErr.Supersampling)
|
|
}
|
|
}
|
|
}
|
|
} else if err != nil {
|
|
t.Errorf("ToPNGWithConfig: unexpected error: %v", err)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestDoSProtection_DynamicSupersampling tests the dynamic supersampling feature in ToPNG.
|
|
func TestDoSProtection_DynamicSupersampling(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
size int
|
|
expectError bool
|
|
expectedMaxSS int // Expected maximum supersampling that should be used
|
|
}{
|
|
{
|
|
name: "small size uses full supersampling",
|
|
size: 256,
|
|
expectError: false,
|
|
expectedMaxSS: 8, // 256 * 8 = 2048 < 4096, so full supersampling
|
|
},
|
|
{
|
|
name: "medium size uses reduced supersampling",
|
|
size: 1024,
|
|
expectError: false,
|
|
expectedMaxSS: 4, // 1024 * 4 = 4096, reduced from default 8
|
|
},
|
|
{
|
|
name: "large size uses minimal supersampling",
|
|
size: 4096,
|
|
expectError: false,
|
|
expectedMaxSS: 1, // 4096 * 1 = 4096, minimal supersampling
|
|
},
|
|
{
|
|
name: "oversized even with minimal supersampling",
|
|
size: 4097,
|
|
expectError: true, // Even 4097 * 1 = 4097 > 4096
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
input := "test"
|
|
|
|
data, err := ToPNG(context.Background(), input, tt.size)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("ToPNG: expected error but got none")
|
|
}
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Errorf("ToPNG: unexpected error: %v", err)
|
|
return
|
|
}
|
|
|
|
if len(data) == 0 {
|
|
t.Errorf("ToPNG: expected PNG data but got empty result")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestDoSProtection_EmptyInput tests protection against empty input strings.
|
|
func TestDoSProtection_EmptyInput(t *testing.T) {
|
|
config := DefaultConfig()
|
|
|
|
_, err := ToSVGWithConfig(context.Background(), "", 100, config)
|
|
if err == nil {
|
|
t.Errorf("ToSVGWithConfig: expected error for empty input but got none")
|
|
return
|
|
}
|
|
|
|
var inputErr *ErrInvalidInput
|
|
if !errors.As(err, &inputErr) {
|
|
t.Errorf("ToSVGWithConfig: expected ErrInvalidInput, got %T", err)
|
|
} else if inputErr.Field != "input" {
|
|
t.Errorf("ToSVGWithConfig: expected input field error, got %s", inputErr.Field)
|
|
}
|
|
}
|
|
|
|
// TestDoSProtection_ConfigurableLimits tests that the configurable limits work as expected.
|
|
func TestDoSProtection_ConfigurableLimits(t *testing.T) {
|
|
// Test custom limits
|
|
customConfig := DefaultConfig()
|
|
customConfig.MaxIconSize = 1000 // Much smaller than default
|
|
customConfig.MaxInputLength = 10000 // Much smaller than default
|
|
customConfig.PNGSupersampling = 4
|
|
|
|
// This should work with custom config
|
|
_, err := ToSVGWithConfig(context.Background(), "test", 500, customConfig)
|
|
if err != nil {
|
|
t.Errorf("ToSVGWithConfig with custom config: unexpected error: %v", err)
|
|
}
|
|
|
|
// This should fail with custom config (size too large)
|
|
_, err = ToSVGWithConfig(context.Background(), "test", 1001, customConfig)
|
|
if err == nil {
|
|
t.Errorf("ToSVGWithConfig with custom config: expected error for oversized icon but got none")
|
|
}
|
|
|
|
// Test disabled limits
|
|
noLimitsConfig := DefaultConfig()
|
|
noLimitsConfig.MaxIconSize = -1 // Disabled
|
|
noLimitsConfig.MaxInputLength = -1 // Disabled
|
|
noLimitsConfig.PNGSupersampling = 1
|
|
|
|
// This should work even with very large size (disabled limits)
|
|
largeInput := strings.Repeat("x", constants.DefaultMaxInputLength+1000)
|
|
_, err = ToSVGWithConfig(context.Background(), largeInput, constants.DefaultMaxIconSize+1000, noLimitsConfig)
|
|
if err != nil {
|
|
t.Errorf("ToSVGWithConfig with disabled limits: unexpected error: %v", err)
|
|
}
|
|
}
|
|
|
|
// TestDoSProtection_GeneratorConsistency tests that Generator API respects configured limits.
|
|
func TestDoSProtection_GeneratorConsistency(t *testing.T) {
|
|
// Test generator with custom limits
|
|
customConfig := DefaultConfig()
|
|
customConfig.MaxIconSize = 1000
|
|
customConfig.MaxInputLength = 100
|
|
|
|
generator, err := NewGeneratorWithConfig(customConfig, 10)
|
|
if err != nil {
|
|
t.Fatalf("NewGeneratorWithConfig: unexpected error: %v", err)
|
|
}
|
|
|
|
// This should work within limits
|
|
_, err = generator.Generate(context.Background(), "test", 500)
|
|
if err != nil {
|
|
t.Errorf("Generator.Generate within limits: unexpected error: %v", err)
|
|
}
|
|
|
|
// This should fail due to size limit
|
|
_, err = generator.Generate(context.Background(), "test", 1001)
|
|
if err == nil {
|
|
t.Errorf("Generator.Generate with oversized icon: expected error but got none")
|
|
} else {
|
|
var valueErr *ErrValueTooLarge
|
|
if !errors.As(err, &valueErr) {
|
|
t.Errorf("Generator.Generate: expected ErrValueTooLarge, got %T", err)
|
|
} else if valueErr.ParameterName != "IconSize" {
|
|
t.Errorf("Generator.Generate: expected IconSize error, got %s", valueErr.ParameterName)
|
|
}
|
|
}
|
|
|
|
// This should fail due to input length limit
|
|
longInput := strings.Repeat("a", 101)
|
|
_, err = generator.Generate(context.Background(), longInput, 100)
|
|
if err == nil {
|
|
t.Errorf("Generator.Generate with long input: expected error but got none")
|
|
} else {
|
|
var valueErr *ErrValueTooLarge
|
|
if !errors.As(err, &valueErr) {
|
|
t.Errorf("Generator.Generate: expected ErrValueTooLarge, got %T", err)
|
|
} else if valueErr.ParameterName != "InputLength" {
|
|
t.Errorf("Generator.Generate: expected InputLength error, got %s", valueErr.ParameterName)
|
|
}
|
|
}
|
|
|
|
// Test generator with default limits
|
|
defaultGenerator, err := NewGenerator()
|
|
if err != nil {
|
|
t.Fatalf("NewGenerator: unexpected error: %v", err)
|
|
}
|
|
|
|
// Should work with normal inputs
|
|
_, err = defaultGenerator.Generate(context.Background(), "test", 256)
|
|
if err != nil {
|
|
t.Errorf("Default generator: unexpected error: %v", err)
|
|
}
|
|
|
|
// Should fail with oversized input
|
|
_, err = defaultGenerator.Generate(context.Background(), "test", constants.DefaultMaxIconSize+1)
|
|
if err == nil {
|
|
t.Errorf("Default generator with oversized icon: expected error but got none")
|
|
}
|
|
}
|