orama/pkg/config/decode_test.go
2026-02-13 16:18:22 +02:00

210 lines
5.4 KiB
Go

package config
import (
"strings"
"testing"
)
func TestDecodeStrictValidYAML(t *testing.T) {
yamlInput := `
node:
id: "test-node"
listen_addresses:
- "/ip4/0.0.0.0/tcp/4001"
data_dir: "./data"
max_connections: 100
logging:
level: "debug"
format: "json"
`
var cfg Config
err := DecodeStrict(strings.NewReader(yamlInput), &cfg)
if err != nil {
t.Fatalf("expected no error for valid YAML, got: %v", err)
}
if cfg.Node.ID != "test-node" {
t.Errorf("expected node ID 'test-node', got %q", cfg.Node.ID)
}
if len(cfg.Node.ListenAddresses) != 1 || cfg.Node.ListenAddresses[0] != "/ip4/0.0.0.0/tcp/4001" {
t.Errorf("unexpected listen addresses: %v", cfg.Node.ListenAddresses)
}
if cfg.Node.DataDir != "./data" {
t.Errorf("expected data_dir './data', got %q", cfg.Node.DataDir)
}
if cfg.Node.MaxConnections != 100 {
t.Errorf("expected max_connections 100, got %d", cfg.Node.MaxConnections)
}
if cfg.Logging.Level != "debug" {
t.Errorf("expected logging level 'debug', got %q", cfg.Logging.Level)
}
if cfg.Logging.Format != "json" {
t.Errorf("expected logging format 'json', got %q", cfg.Logging.Format)
}
}
func TestDecodeStrictUnknownFieldsError(t *testing.T) {
yamlInput := `
node:
id: "test-node"
data_dir: "./data"
unknown_field: "should cause error"
`
var cfg Config
err := DecodeStrict(strings.NewReader(yamlInput), &cfg)
if err == nil {
t.Fatal("expected error for unknown field, got nil")
}
if !strings.Contains(err.Error(), "invalid config") {
t.Errorf("expected error to contain 'invalid config', got: %v", err)
}
}
func TestDecodeStrictTopLevelUnknownField(t *testing.T) {
yamlInput := `
node:
id: "test-node"
bogus_section:
key: "value"
`
var cfg Config
err := DecodeStrict(strings.NewReader(yamlInput), &cfg)
if err == nil {
t.Fatal("expected error for unknown top-level field, got nil")
}
}
func TestDecodeStrictEmptyReader(t *testing.T) {
var cfg Config
err := DecodeStrict(strings.NewReader(""), &cfg)
// An empty document produces an EOF error from the YAML decoder
if err == nil {
t.Fatal("expected error for empty reader, got nil")
}
}
func TestDecodeStrictMalformedYAML(t *testing.T) {
tests := []struct {
name string
input string
}{
{
name: "invalid indentation",
input: "node:\n id: \"test\"\n bad_indent: true",
},
{
name: "tab characters",
input: "node:\n\tid: \"test\"",
},
{
name: "unclosed quote",
input: "node:\n id: \"unclosed",
},
{
name: "colon in unquoted value",
input: "node:\n id: bad: value: here",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var cfg Config
err := DecodeStrict(strings.NewReader(tt.input), &cfg)
if err == nil {
t.Error("expected error for malformed YAML, got nil")
}
})
}
}
func TestDecodeStrictPartialConfig(t *testing.T) {
// Only set some fields; others should remain at zero values
yamlInput := `
logging:
level: "warn"
format: "console"
`
var cfg Config
err := DecodeStrict(strings.NewReader(yamlInput), &cfg)
if err != nil {
t.Fatalf("expected no error for partial config, got: %v", err)
}
if cfg.Logging.Level != "warn" {
t.Errorf("expected logging level 'warn', got %q", cfg.Logging.Level)
}
if cfg.Logging.Format != "console" {
t.Errorf("expected logging format 'console', got %q", cfg.Logging.Format)
}
// Unset fields should be zero values
if cfg.Node.ID != "" {
t.Errorf("expected empty node ID, got %q", cfg.Node.ID)
}
if cfg.Node.MaxConnections != 0 {
t.Errorf("expected zero max_connections, got %d", cfg.Node.MaxConnections)
}
}
func TestDecodeStrictDatabaseConfig(t *testing.T) {
yamlInput := `
database:
data_dir: "./db"
replication_factor: 5
shard_count: 32
max_database_size: 2147483648
rqlite_port: 6001
rqlite_raft_port: 8001
rqlite_join_address: "10.0.0.1:6001"
min_cluster_size: 3
`
var cfg Config
err := DecodeStrict(strings.NewReader(yamlInput), &cfg)
if err != nil {
t.Fatalf("expected no error, got: %v", err)
}
if cfg.Database.DataDir != "./db" {
t.Errorf("expected data_dir './db', got %q", cfg.Database.DataDir)
}
if cfg.Database.ReplicationFactor != 5 {
t.Errorf("expected replication_factor 5, got %d", cfg.Database.ReplicationFactor)
}
if cfg.Database.ShardCount != 32 {
t.Errorf("expected shard_count 32, got %d", cfg.Database.ShardCount)
}
if cfg.Database.MaxDatabaseSize != 2147483648 {
t.Errorf("expected max_database_size 2147483648, got %d", cfg.Database.MaxDatabaseSize)
}
if cfg.Database.RQLitePort != 6001 {
t.Errorf("expected rqlite_port 6001, got %d", cfg.Database.RQLitePort)
}
if cfg.Database.RQLiteRaftPort != 8001 {
t.Errorf("expected rqlite_raft_port 8001, got %d", cfg.Database.RQLiteRaftPort)
}
if cfg.Database.RQLiteJoinAddress != "10.0.0.1:6001" {
t.Errorf("expected rqlite_join_address '10.0.0.1:6001', got %q", cfg.Database.RQLiteJoinAddress)
}
if cfg.Database.MinClusterSize != 3 {
t.Errorf("expected min_cluster_size 3, got %d", cfg.Database.MinClusterSize)
}
}
func TestDecodeStrictNonStructTarget(t *testing.T) {
// DecodeStrict should also work with simpler types
yamlInput := `
key1: value1
key2: value2
`
var result map[string]string
err := DecodeStrict(strings.NewReader(yamlInput), &result)
if err != nil {
t.Fatalf("expected no error decoding to map, got: %v", err)
}
if result["key1"] != "value1" {
t.Errorf("expected key1='value1', got %q", result["key1"])
}
if result["key2"] != "value2" {
t.Errorf("expected key2='value2', got %q", result["key2"])
}
}