1
0
Fork 0
mirror of https://github.com/miniflux/v2.git synced 2025-09-30 19:22:11 +00:00
miniflux-v2/internal/config/parser_test.go
Frédéric Guillot 5e607be86a
refactor(config): rewrite config parser
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
2025-09-14 10:51:04 -07:00

435 lines
12 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 (
"net/url"
"os"
"reflect"
"testing"
"time"
)
func TestParseStringValue(t *testing.T) {
// Test with non-empty value
result := parseStringValue("test", "fallback")
if result != "test" {
t.Errorf("Expected 'test', got '%s'", result)
}
// Test with empty value
result = parseStringValue("", "fallback")
if result != "fallback" {
t.Errorf("Expected 'fallback', got '%s'", result)
}
// Test with empty value and empty fallback
result = parseStringValue("", "")
if result != "" {
t.Errorf("Expected empty string, got '%s'", result)
}
}
func TestParseBoolValue(t *testing.T) {
// Test with empty value - should return fallback
result, err := parseBoolValue("", true)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if result != true {
t.Errorf("Expected true, got %v", result)
}
// Test true values
trueValues := []string{"1", "yes", "true", "on", "YES", "TRUE", "ON"}
for _, value := range trueValues {
result, err := parseBoolValue(value, false)
if err != nil {
t.Errorf("Unexpected error for value '%s': %v", value, err)
}
if result != true {
t.Errorf("Expected true for '%s', got %v", value, result)
}
}
// Test false values
falseValues := []string{"0", "no", "false", "off", "NO", "FALSE", "OFF"}
for _, value := range falseValues {
result, err := parseBoolValue(value, true)
if err != nil {
t.Errorf("Unexpected error for value '%s': %v", value, err)
}
if result != false {
t.Errorf("Expected false for '%s', got %v", value, result)
}
}
// Test invalid value - should return error
_, err = parseBoolValue("invalid", false)
if err == nil {
t.Error("Expected error for invalid boolean value")
}
}
func TestParseIntValue(t *testing.T) {
// Test with empty value - should return fallback
result := parseIntValue("", 42)
if result != 42 {
t.Errorf("Expected 42, got %d", result)
}
// Test with valid integer
result = parseIntValue("123", 42)
if result != 123 {
t.Errorf("Expected 123, got %d", result)
}
// Test with invalid integer - should return fallback
result = parseIntValue("invalid", 42)
if result != 42 {
t.Errorf("Expected 42, got %d", result)
}
// Test with zero
result = parseIntValue("0", 42)
if result != 0 {
t.Errorf("Expected 0, got %d", result)
}
}
func TestParsedInt64Value(t *testing.T) {
// Test with empty value - should return fallback
result := ParsedInt64Value("", 42)
if result != 42 {
t.Errorf("Expected 42, got %d", result)
}
// Test with valid int64
result = ParsedInt64Value("9223372036854775807", 42)
if result != 9223372036854775807 {
t.Errorf("Expected 9223372036854775807, got %d", result)
}
// Test with invalid int64 - should return fallback
result = ParsedInt64Value("invalid", 42)
if result != 42 {
t.Errorf("Expected 42, got %d", result)
}
}
func TestParseStringListValue(t *testing.T) {
// Test with empty value - should return fallback
fallback := []string{"a", "b"}
result := parseStringListValue("", fallback)
if !reflect.DeepEqual(result, fallback) {
t.Errorf("Expected %v, got %v", fallback, result)
}
// Test with single value
result = parseStringListValue("item1", nil)
expected := []string{"item1"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
// Test with multiple values
result = parseStringListValue("item1,item2,item3", nil)
expected = []string{"item1", "item2", "item3"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
// Test with duplicates - should remove duplicates
result = parseStringListValue("item1,item2,item1", nil)
expected = []string{"item1", "item2"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
// Test with spaces
result = parseStringListValue(" item1 , item2 , item3 ", nil)
expected = []string{"item1", "item2", "item3"}
if !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %v, got %v", expected, result)
}
}
func TestParseDurationValue(t *testing.T) {
// Test with empty value - should return fallback
fallback := 5 * time.Second
result := parseDurationValue("", time.Second, fallback)
if result != fallback {
t.Errorf("Expected %v, got %v", fallback, result)
}
// Test with valid duration
result = parseDurationValue("30", time.Second, fallback)
expected := 30 * time.Second
if result != expected {
t.Errorf("Expected %v, got %v", expected, result)
}
// Test with minutes
result = parseDurationValue("5", time.Minute, fallback)
expected = 5 * time.Minute
if result != expected {
t.Errorf("Expected %v, got %v", expected, result)
}
// Test with invalid value - should return fallback
result = parseDurationValue("invalid", time.Second, fallback)
if result != fallback {
t.Errorf("Expected %v, got %v", fallback, result)
}
}
func TestParseURLValue(t *testing.T) {
// Test with empty value - should return fallback
fallbackURL, _ := url.Parse("https://fallback.com")
result, err := parseURLValue("", fallbackURL)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if result != fallbackURL {
t.Errorf("Expected %v, got %v", fallbackURL, result)
}
// Test with valid URL
result, err = parseURLValue("https://example.com", nil)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
if result.String() != "https://example.com" {
t.Errorf("Expected https://example.com, got %s", result.String())
}
// Test with invalid URL - should return fallback and error
result, err = parseURLValue("://invalid", fallbackURL)
if err == nil {
t.Error("Expected error for invalid URL")
}
if result != fallbackURL {
t.Errorf("Expected fallback URL, got %v", result)
}
}
func TestConfigFileParsing(t *testing.T) {
fileContent := `
# This is a comment
LOG_FILE=miniflux.log
LOG_DATE_TIME=1
LOG_FORMAT=json
LISTEN_ADDR=:8080,:8443
`
// Write a temporary config file and parse it
tmpFile, err := os.CreateTemp("", "miniflux-*.txt")
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
filename := tmpFile.Name()
if _, err := tmpFile.WriteString(fileContent); err != nil {
t.Fatalf("Failed to write to temporary file: %v", err)
}
configParser := NewConfigParser()
configOptions, err := configParser.ParseFile(filename)
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.LogFile() != "miniflux.log" {
t.Fatalf("Unexpected log file, got %q", configOptions.LogFile())
}
if configOptions.LogDateTime() != true {
t.Fatalf("Unexpected log datetime, got %v", configOptions.LogDateTime())
}
if configOptions.LogFormat() != "json" {
t.Fatalf("Unexpected log format, got %q", configOptions.LogFormat())
}
if configOptions.LogLevel() != "info" {
t.Fatalf("Unexpected log level, got %q", configOptions.LogLevel())
}
if len(configOptions.ListenAddr()) != 2 || configOptions.ListenAddr()[0] != ":8080" || configOptions.ListenAddr()[1] != ":8443" {
t.Fatalf("Unexpected listen addresses, got %v", configOptions.ListenAddr())
}
}
func TestConfigFileParsingWithIncorrectKeyValuePair(t *testing.T) {
fileContent := `
LOG_FILE=miniflux.log
INVALID_LINE
`
// Write a temporary config file and parse it
tmpFile, err := os.CreateTemp("", "miniflux-*.txt")
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
filename := tmpFile.Name()
if _, err := tmpFile.WriteString(fileContent); err != nil {
t.Fatalf("Failed to write to temporary file: %v", err)
}
configParser := NewConfigParser()
_, err = configParser.ParseFile(filename)
if err != nil {
t.Fatal("Invalid lines should be ignored, but got error:", err)
}
}
func TestParseAdminPasswordFileOption(t *testing.T) {
tmpFile, err := os.CreateTemp("", "password-*.txt")
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
password := "supersecret"
if _, err := tmpFile.WriteString(password); err != nil {
t.Fatalf("Failed to write to temporary file: %v", err)
}
os.Clearenv()
os.Setenv("ADMIN_PASSWORD_FILE", tmpFile.Name())
configParser := NewConfigParser()
configOptions, err := configParser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.AdminPassword() != password {
t.Fatalf("Unexpected admin password, got %q", configOptions.AdminPassword())
}
}
func TestParseAdminPasswordFileOptionWithEmptyFile(t *testing.T) {
tmpFile, err := os.CreateTemp("", "empty-password-*.txt")
if err != nil {
t.Fatalf("Failed to create temporary file: %v", err)
}
defer os.Remove(tmpFile.Name())
defer tmpFile.Close()
os.Clearenv()
os.Setenv("ADMIN_PASSWORD_FILE", tmpFile.Name())
configParser := NewConfigParser()
_, err = configParser.ParseEnvironmentVariables()
if err == nil {
t.Fatal("Expected error due to empty password file, but got none")
}
}
func TestParseLogFileOptionDefaultValue(t *testing.T) {
os.Clearenv()
configParser := NewConfigParser()
configOptions, err := configParser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.LogFile() != "stderr" {
t.Fatalf("Unexpected default log file, got %q", configOptions.LogFile())
}
}
func TestParseLogFileOptionWithCustomFilename(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_FILE", "miniflux.log")
configParser := NewConfigParser()
configOptions, err := configParser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.LogFile() != "miniflux.log" {
t.Fatalf("Unexpected log file, got %q", configOptions.LogFile())
}
}
func TestParseLogFileOptionWithEmptyValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_FILE", "")
configParser := NewConfigParser()
configOptions, err := configParser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.LogFile() != "stderr" {
t.Fatalf("Unexpected log file, got %q", configOptions.LogFile())
}
}
func TestParseLogDateTimeOptionDefaultValue(t *testing.T) {
os.Clearenv()
configParser := NewConfigParser()
configOptions, err := configParser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.LogDateTime() != false {
t.Fatalf("Unexpected default log datetime, got %v", configOptions.LogDateTime())
}
}
func TestParseLogDateTimeOptionWithCustomValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_DATE_TIME", "true")
configParser := NewConfigParser()
configOptions, err := configParser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.LogDateTime() != true {
t.Fatalf("Unexpected log datetime, got %v", configOptions.LogDateTime())
}
}
func TestParseLogDateTimeOptionWithEmptyValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_DATE_TIME", "")
configParser := NewConfigParser()
configOptions, err := configParser.ParseEnvironmentVariables()
if err != nil {
t.Fatalf("Unexpected parsing error: %v", err)
}
if configOptions.LogDateTime() != false {
t.Fatalf("Unexpected log datetime, got %v", configOptions.LogDateTime())
}
}
func TestParseLogDateTimeOptionWithIncorrectValue(t *testing.T) {
os.Clearenv()
os.Setenv("LOG_DATE_TIME", "invalid")
configParser := NewConfigParser()
if _, err := configParser.ParseEnvironmentVariables(); err == nil {
t.Fatal("Expected parsing error, got nil")
}
}