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()) } }