Files
Aether/backend/pkg/config/config_test.go
T
HoneyandClaude Opus 5 35ebf609b7 fix(config): Datenbank-Passwort mit Sonderzeichen bricht den Start
Die Verbindungszeichenkette wurde mit fmt.Sprintf zusammengeklebt. Ein
Passwort mit % und @ ergab eine kaputte URL:

  parse "postgres://aether:8UoNj%CoWJ7ze@qy@localhost:5432/aether":
  invalid URL escape "%Co"

Der Server kam nicht über die Migrationen hinaus. Die key=value-Form hätte
dasselbe Problem mit einem Leerzeichen gehabt, nur stiller.

config.DSN() liefert jetzt eine postgres://-URL, gebaut mit net/url und
net.JoinHostPort (klammert IPv6). pgxpool und golang-migrate nehmen sie
beide unverändert – dsnToURL, splitDSN, indexByte und orDefault in pkg/db
sind ersatzlos entfallen. Test deckt %, @, :, /, Leerzeichen und IPv6 ab.

Dazu: DB_HOST und DB_PORT fehlten in der schlanken .env.example, weshalb
localhost statt postgres gegriffen hat. APP_ENV ergänzt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-24 11:20:01 +02:00

65 lines
1.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package config
import (
"net/url"
"testing"
)
func parseDSN(s string) (*url.URL, error) { return url.Parse(s) }
// Ein Passwort darf jedes Zeichen enthalten. Die Fälle hier sind nicht
// ausgedacht: `8UoNj%CoWJ7ze@qy` stammt aus einer echten Einrichtung und
// legte den Server lahm – „invalid URL escape %Co", noch vor den Migrationen.
func TestDSNEscapesPassword(t *testing.T) {
cases := []struct {
name string
pw string
}{
{"prozent und at", "8UoNj%CoWJ7ze@qy"},
{"doppelpunkt", "gehei:mnis"},
{"schraegstrich", "a/b?c#d"},
{"leerzeichen", "mit leer zeichen"},
{"nur sonderzeichen", "%%@@::////"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
c := &Config{
DBHost: "postgres", DBPort: "5432", DBName: "aether",
DBUser: "aether", DBPassword: tc.pw,
}
got := c.DSN()
// Die Probe ist nicht der erzeugte Text, sondern ob er sich
// wieder auseinandernehmen lässt – genau das tun pgx und
// golang-migrate mit ihm.
u, err := parseDSN(got)
if err != nil {
t.Fatalf("DSN nicht parsebar: %v (%s)", err, got)
}
if pw, _ := u.User.Password(); pw != tc.pw {
t.Errorf("Passwort kam als %q zurück, erwartet %q", pw, tc.pw)
}
if u.User.Username() != "aether" {
t.Errorf("Nutzer %q", u.User.Username())
}
if u.Host != "postgres:5432" {
t.Errorf("Host %q", u.Host)
}
if u.Path != "/aether" {
t.Errorf("Datenbank %q", u.Path)
}
})
}
}
// IPv6 muss geklammert werden, sonst frisst der Doppelpunkt den Port.
func TestDSNIPv6(t *testing.T) {
c := &Config{DBHost: "::1", DBPort: "5432", DBName: "aether", DBUser: "u"}
u, err := parseDSN(c.DSN())
if err != nil {
t.Fatalf("nicht parsebar: %v", err)
}
if u.Hostname() != "::1" || u.Port() != "5432" {
t.Errorf("Host %q Port %q", u.Hostname(), u.Port())
}
}