package wgconf import ( "os" "path/filepath" "reflect" "testing" ) func TestParseConfig(t *testing.T) { content := `[Interface] PrivateKey = ABC123XYZ Address = 10.0.0.1/24 DNS = 1.1.1.1 [Peer] PublicKey = DEF456UVW Endpoint = 1.2.3.4:51820 AllowedIPs = 0.0.0.0/0 [Peer] PublicKey = GHI789TSR Endpoint = 5.6.7.8:51820 AllowedIPs = 192.168.1.0/24, 192.168.2.0/24` tmpFile := filepath.Join(t.TempDir(), "test.conf") if err := os.WriteFile(tmpFile, []byte(content), 0644); err != nil { t.Fatal(err) } cfg, err := Parse(tmpFile) if err != nil { t.Fatalf("Parse failed: %v", err) } if cfg.PrivateKey != "ABC123XYZ" { t.Errorf("expected PrivateKey ABC123XYZ, got %s", cfg.PrivateKey) } if cfg.Address != "10.0.0.1/24" { t.Errorf("expected Address 10.0.0.1/24, got %s", cfg.Address) } if cfg.DNS != "1.1.1.1" { t.Errorf("expected DNS 1.1.1.1, got %s", cfg.DNS) } if len(cfg.Peers) != 2 { t.Fatalf("expected 2 peers, got %d", len(cfg.Peers)) } p1 := cfg.Peers[0] if p1.PublicKey != "DEF456UVW" || p1.Endpoint != "1.2.3.4:51820" || len(p1.AllowedIPs) != 1 || p1.AllowedIPs[0] != "0.0.0.0/0" { t.Errorf("Peer 1 mismatch: %+v", p1) } p2 := cfg.Peers[1] if p2.PublicKey != "GHI789TSR" || p2.Endpoint != "5.6.7.8:51820" || len(p2.AllowedIPs) != 2 || p2.AllowedIPs[0] != "192.168.1.0/24" { t.Errorf("Peer 2 mismatch: %+v", p2) } } func TestParseInvalidConfig(t *testing.T) { content := `[Interface] InvalidLineWithoutEquals` tmpFile := filepath.Join(t.TempDir(), "invalid.conf") if err := os.WriteFile(tmpFile, []byte(content), 0644); err != nil { t.Fatal(err) } _, err := Parse(tmpFile) if err == nil { t.Error("expected error for invalid line format, got nil") } } func TestParseConfigInTheWildEdgeCases(t *testing.T) { tests := []struct { name string content string want *Config wantErr bool }{ { name: "Case insensitivity for sections and keys", content: ` [interface] privatekey = my-private-key address = 10.0.1.2/24 dns = 8.8.8.8 [peer] publickey = peer-public-key endpoint = 5.5.5.5:51820 allowedips = 10.0.1.0/24, fd00::1/64 `, want: &Config{ PrivateKey: "my-private-key", Address: "10.0.1.2/24", DNS: "8.8.8.8", Peers: []Peer{ { PublicKey: "peer-public-key", Endpoint: "5.5.5.5:51820", AllowedIPs: []string{"10.0.1.0/24", "fd00::1/64"}, }, }, }, }, { name: "Inline and block comments", content: ` # This is a whole-line comment ; This is another whole-line comment starting with semicolon [Interface] PrivateKey = key-with-hash-inside#123 # Comment at end of line Address = 10.0.0.1/24 ; inline semicolon comment DNS = 1.1.1.1 # DNS fallback [Peer] PublicKey = peerkey ; comment here # This is a comment between fields Endpoint = 1.1.1.1:1111 AllowedIPs = 10.0.0.0/24, 10.0.1.0/24 # comment at the end of allowed ips `, want: &Config{ PrivateKey: "key-with-hash-inside#123", Address: "10.0.0.1/24", DNS: "1.1.1.1", Peers: []Peer{ { PublicKey: "peerkey", Endpoint: "1.1.1.1:1111", AllowedIPs: []string{"10.0.0.0/24", "10.0.1.0/24"}, }, }, }, }, { name: "Crazy whitespaces and tabs", content: ` [Interface] PrivateKey = key123 Address = 10.0.0.2/24 DNS = 9.9.9.9 [Peer] PublicKey = key456 Endpoint = 2.2.2.2:2222 AllowedIPs = 192.168.1.1/32 , 192.168.1.2/32 `, want: &Config{ PrivateKey: "key123", Address: "10.0.0.2/24", DNS: "9.9.9.9", Peers: []Peer{ { PublicKey: "key456", Endpoint: "2.2.2.2:2222", AllowedIPs: []string{"192.168.1.1/32", "192.168.1.2/32"}, }, }, }, }, { name: "Ignore unrecognized keys under Interface and Peer", content: ` [Interface] PrivateKey = pk Address = 10.0.0.1/24 ListenPort = 51820 FwMark = 1234 MTU = 1420 PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE [Peer] PublicKey = pubk PresharedKey = preshared_key_abc Endpoint = 3.3.3.3:3333 AllowedIPs = 0.0.0.0/0 PersistentKeepalive = 25 `, want: &Config{ PrivateKey: "pk", Address: "10.0.0.1/24", DNS: "", Peers: []Peer{ { PublicKey: "pubk", Endpoint: "3.3.3.3:3333", AllowedIPs: []string{"0.0.0.0/0"}, }, }, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tmpFile := filepath.Join(t.TempDir(), "test_wild.conf") if err := os.WriteFile(tmpFile, []byte(tt.content), 0644); err != nil { t.Fatal(err) } cfg, err := Parse(tmpFile) if (err != nil) != tt.wantErr { t.Fatalf("Parse() error = %v, wantErr %v", err, tt.wantErr) } if err == nil { if !reflect.DeepEqual(cfg, tt.want) { t.Errorf("Parse() got = %+v, want = %+v", cfg, tt.want) } } }) } }