Files
member-console/internal/config/spec_test.go
T
cgalo5758 88db730fcc Add dual licensing and SPDX headers
Introduce a commercial license option alongside AGPL-3.0-only, require a
CLA for contributors, and document the terms in COMMERCIAL.md and
NOTICE. Add a script to stamp SPDX headers on Go files and apply it
across the tree.
2026-09-06 02:29:42 -05:00

137 lines
5.0 KiB
Go

// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
package config
import (
"strings"
"testing"
"time"
)
// TestTypeOfInference covers every case design D1 names: an explicit Type
// wins; a non-empty Enum implies enum; otherwise the Default's Go type
// decides (bool, int, []string, time.Duration); a nil Default, or any other
// Go type (a bare string), implies string.
func TestTypeOfInference(t *testing.T) {
cases := []struct {
name string
key ConfigKey
want Type
}{
{"explicit type wins over a nil default", ConfigKey{Type: TypeURL}, TypeURL},
{"explicit type wins over a matching default", ConfigKey{Type: TypeString, Default: "blue"}, TypeString},
{"non-empty enum implies enum", ConfigKey{Default: "https", Enum: []string{"http", "https"}}, TypeEnum},
{"bool default implies bool", ConfigKey{Default: false}, TypeBool},
{"int default implies int", ConfigKey{Default: 7}, TypeInt},
{"[]string default implies list", ConfigKey{Default: []string(nil)}, TypeList},
{"time.Duration default implies duration", ConfigKey{Default: time.Hour}, TypeDuration},
{"string default implies string", ConfigKey{Default: "blue"}, TypeString},
{"nil default implies string", ConfigKey{}, TypeString},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := tc.key.TypeOf(); got != tc.want {
t.Errorf("TypeOf() = %q, want %q", got, tc.want)
}
})
}
}
// TestCheckSpecs covers every declaration-shape refusal design D1/D6 names,
// and that a well-formed spec (mirroring every real declaration shape:
// string, url, duration, positive duration, int, bool, list, enum) passes
// clean.
func TestCheckSpecs(t *testing.T) {
t.Run("a well-formed spec passes", func(t *testing.T) {
specs := []ConfigKey{
{Name: "it-color", Default: "blue"},
{Name: "it-url", Type: TypeURL},
{Name: "it-url-with-default", Type: TypeURL, Default: "https://it.example.com"},
{Name: "it-timeout", Default: time.Hour},
{Name: "it-sync-interval", Default: time.Hour, Positive: true},
{Name: "it-limit", Default: 7},
{Name: "it-count", Default: 3, Positive: true},
{Name: "it-enabled", Default: false},
{Name: "it-domains", Default: []string(nil)},
{Name: "it-scheme", Default: "https", Enum: []string{"http", "https"}},
}
if err := CheckSpecs(specs); err != nil {
t.Fatalf("expected a well-formed spec to pass, got: %v", err)
}
})
t.Run("a type that contradicts its default is refused naming the key and both types", func(t *testing.T) {
err := CheckSpecs([]ConfigKey{{Name: "it-flag", Type: TypeDuration, Default: true}})
if err == nil {
t.Fatal("expected an error, got nil")
}
for _, want := range []string{"it-flag", "duration", "bool"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("error %q missing %q", err.Error(), want)
}
}
})
t.Run("TypeEnum with no Enum members is refused", func(t *testing.T) {
err := CheckSpecs([]ConfigKey{{Name: "it-mode", Type: TypeEnum}})
if err == nil || !strings.Contains(err.Error(), "it-mode") {
t.Fatalf("want an error naming it-mode, got: %v", err)
}
})
t.Run("an enum with a non-string default is refused", func(t *testing.T) {
err := CheckSpecs([]ConfigKey{{Name: "it-mode", Default: true, Enum: []string{"a", "b"}}})
if err == nil || !strings.Contains(err.Error(), "it-mode") {
t.Fatalf("want an error naming it-mode, got: %v", err)
}
})
t.Run("Positive on a bool key is refused", func(t *testing.T) {
err := CheckSpecs([]ConfigKey{{Name: "it-enabled", Default: false, Positive: true}})
if err == nil || !strings.Contains(err.Error(), "it-enabled") {
t.Fatalf("want an error naming it-enabled, got: %v", err)
}
})
t.Run("Positive on a string key is refused", func(t *testing.T) {
err := CheckSpecs([]ConfigKey{{Name: "it-color", Default: "blue", Positive: true}})
if err == nil || !strings.Contains(err.Error(), "it-color") {
t.Fatalf("want an error naming it-color, got: %v", err)
}
})
t.Run("Positive on an int or duration key is fine", func(t *testing.T) {
specs := []ConfigKey{
{Name: "it-limit", Default: 7, Positive: true},
{Name: "it-timeout", Default: time.Hour, Positive: true},
}
if err := CheckSpecs(specs); err != nil {
t.Fatalf("expected no error, got: %v", err)
}
})
t.Run("a URL key with no default contradicts nothing", func(t *testing.T) {
if err := CheckSpecs([]ConfigKey{{Name: "it-url", Type: TypeURL}}); err != nil {
t.Fatalf("expected no error, got: %v", err)
}
})
t.Run("every problem aggregates into one error", func(t *testing.T) {
specs := []ConfigKey{
{Name: "it-flag", Type: TypeDuration, Default: true},
{Name: "it-mode", Type: TypeEnum},
{Name: "it-enabled", Default: false, Positive: true},
}
err := CheckSpecs(specs)
if err == nil {
t.Fatal("expected an error, got nil")
}
for _, want := range []string{"it-flag", "it-mode", "it-enabled"} {
if !strings.Contains(err.Error(), want) {
t.Errorf("aggregated error missing %q: %v", want, err)
}
}
})
}