mirror of
https://github.com/miniflux/v2.git
synced 2025-09-30 19:22:11 +00:00
This PR refactors the configuration parser, replacing the old parser implementation with a new, more structured approach that includes validation and improved organization. Key changes: - Complete rewrite of the configuration parser using a map-based structure with built-in validation - Addition of comprehensive validator functions for configuration values - Renamed numerous configuration getter methods for better consistency
372 lines
8.8 KiB
Go
372 lines
8.8 KiB
Go
// SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package config // import "miniflux.app/v2/internal/config"
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestValidateChoices(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
rawValue string
|
|
choices []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "valid choice",
|
|
rawValue: "option1",
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid choice from middle",
|
|
rawValue: "option2",
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid choice from end",
|
|
rawValue: "option3",
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "invalid choice",
|
|
rawValue: "invalid",
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "empty value with non-empty choices",
|
|
rawValue: "",
|
|
choices: []string{"option1", "option2"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "case sensitive - different case",
|
|
rawValue: "OPTION1",
|
|
choices: []string{"option1", "option2"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "single choice valid",
|
|
rawValue: "only",
|
|
choices: []string{"only"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "empty choices list",
|
|
rawValue: "anything",
|
|
choices: []string{},
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateChoices(tt.rawValue, tt.choices)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("expected error but got none")
|
|
} else {
|
|
// Verify error message format
|
|
expectedPrefix := "value must be one of:"
|
|
if !strings.Contains(err.Error(), expectedPrefix) {
|
|
t.Errorf("error message should contain '%s', got: %s", expectedPrefix, err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("expected no error but got: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateListChoices(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
inputValues []string
|
|
choices []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "all valid choices",
|
|
inputValues: []string{"option1", "option2"},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "single valid choice",
|
|
inputValues: []string{"option1"},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "empty input list",
|
|
inputValues: []string{},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "all choices from available list",
|
|
inputValues: []string{"option1", "option2", "option3"},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "duplicate valid choices",
|
|
inputValues: []string{"option1", "option1", "option2"},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "one invalid choice",
|
|
inputValues: []string{"option1", "invalid"},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "all invalid choices",
|
|
inputValues: []string{"invalid1", "invalid2"},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "case sensitive - different case",
|
|
inputValues: []string{"OPTION1"},
|
|
choices: []string{"option1", "option2"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "empty string in input",
|
|
inputValues: []string{""},
|
|
choices: []string{"option1", "option2"},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "empty choices list with non-empty input",
|
|
inputValues: []string{"anything"},
|
|
choices: []string{},
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "mixed valid and invalid choices",
|
|
inputValues: []string{"option1", "invalid", "option2"},
|
|
choices: []string{"option1", "option2", "option3"},
|
|
expectError: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateListChoices(tt.inputValues, tt.choices)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("expected error but got none")
|
|
} else {
|
|
// Verify error message format
|
|
expectedPrefix := "value must be one of:"
|
|
if !strings.Contains(err.Error(), expectedPrefix) {
|
|
t.Errorf("error message should contain '%s', got: %s", expectedPrefix, err.Error())
|
|
}
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("expected no error but got: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateGreaterThan(t *testing.T) {
|
|
if err := validateGreaterThan("10", 5); err != nil {
|
|
t.Errorf("expected no error, got: %v", err)
|
|
}
|
|
|
|
if err := validateGreaterThan("5", 5); err == nil {
|
|
t.Errorf("expected error, got none")
|
|
}
|
|
|
|
if err := validateGreaterThan("abc", 5); err == nil {
|
|
t.Errorf("expected error for non-integer input, got none")
|
|
}
|
|
|
|
if err := validateGreaterThan("-1", 0); err == nil {
|
|
t.Errorf("expected error for value below minimum, got none")
|
|
}
|
|
}
|
|
|
|
func TestValidateGreaterOrEqualThan(t *testing.T) {
|
|
if err := validateGreaterOrEqualThan("10", 5); err != nil {
|
|
t.Errorf("expected no error, got: %v", err)
|
|
}
|
|
|
|
if err := validateGreaterOrEqualThan("5", 5); err != nil {
|
|
t.Errorf("expected no error for equal value, got: %v", err)
|
|
}
|
|
|
|
if err := validateGreaterOrEqualThan("abc", 5); err == nil {
|
|
t.Errorf("expected error for non-integer input, got none")
|
|
}
|
|
|
|
if err := validateGreaterOrEqualThan("-1", 0); err == nil {
|
|
t.Errorf("expected error for value below minimum, got none")
|
|
}
|
|
}
|
|
|
|
func TestValidateRange(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
rawValue string
|
|
min int
|
|
max int
|
|
expectError bool
|
|
errorMsg string
|
|
}{
|
|
{
|
|
name: "valid integer within range",
|
|
rawValue: "5",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid integer at minimum",
|
|
rawValue: "1",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid integer at maximum",
|
|
rawValue: "10",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid zero in range",
|
|
rawValue: "0",
|
|
min: -5,
|
|
max: 5,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid negative in range",
|
|
rawValue: "-3",
|
|
min: -5,
|
|
max: 5,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "integer below minimum",
|
|
rawValue: "0",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be between 1 and 10",
|
|
},
|
|
{
|
|
name: "integer above maximum",
|
|
rawValue: "11",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be between 1 and 10",
|
|
},
|
|
{
|
|
name: "integer far below minimum",
|
|
rawValue: "-100",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be between 1 and 10",
|
|
},
|
|
{
|
|
name: "integer far above maximum",
|
|
rawValue: "100",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be between 1 and 10",
|
|
},
|
|
{
|
|
name: "non-integer string",
|
|
rawValue: "abc",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be an integer",
|
|
},
|
|
{
|
|
name: "empty string",
|
|
rawValue: "",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be an integer",
|
|
},
|
|
{
|
|
name: "float string",
|
|
rawValue: "5.5",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be an integer",
|
|
},
|
|
{
|
|
name: "string with spaces",
|
|
rawValue: " 5 ",
|
|
min: 1,
|
|
max: 10,
|
|
expectError: true,
|
|
errorMsg: "value must be an integer",
|
|
},
|
|
{
|
|
name: "single value range",
|
|
rawValue: "5",
|
|
min: 5,
|
|
max: 5,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "single value range - below",
|
|
rawValue: "4",
|
|
min: 5,
|
|
max: 5,
|
|
expectError: true,
|
|
errorMsg: "value must be between 5 and 5",
|
|
},
|
|
{
|
|
name: "single value range - above",
|
|
rawValue: "6",
|
|
min: 5,
|
|
max: 5,
|
|
expectError: true,
|
|
errorMsg: "value must be between 5 and 5",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateRange(tt.rawValue, tt.min, tt.max)
|
|
if tt.expectError {
|
|
if err == nil {
|
|
t.Errorf("expected error but got none")
|
|
} else if tt.errorMsg != "" && err.Error() != tt.errorMsg {
|
|
t.Errorf("expected error message '%s', got '%s'", tt.errorMsg, err.Error())
|
|
}
|
|
} else {
|
|
if err != nil {
|
|
t.Errorf("expected no error but got: %v", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|