@@ -15,31 +15,103 @@ func TestGrammar(t *testing.T) {
1515 }
1616}
1717
18- func TestGrammarWords (t * testing.T ) {
18+ func TestGrammarRules (t * testing.T ) {
1919 t .Parallel ()
20+
2021 tt := []struct {
2122 rule string
2223 input string
2324 want bool
2425 }{
25- {
26- rule : "word" ,
27- input : "with-hyphen" ,
28- want : true ,
29- },
30- {
31- rule : "cswords" ,
32- input : "std,open" ,
33- want : true ,
34- },
26+ // ── word ────────────────────────────────────────────────────────────
27+ {rule : "word" , input : "hello" , want : true },
28+ {rule : "word" , input : "with-hyphen" , want : true },
29+ {rule : "word" , input : "word123" , want : true },
30+ {rule : "word" , input : "_underscore" , want : true },
31+ {rule : "word" , input : "" , want : false },
32+
33+ // ── cswords ─────────────────────────────────────────────────────────
34+ {rule : "cswords" , input : "single" , want : true },
35+ {rule : "cswords" , input : "std,open" , want : true },
36+ {rule : "cswords" , input : "a,b-c,d" , want : true },
37+ {rule : "cswords" , input : ",leading,comma" , want : false },
38+ {rule : "cswords" , input : "trailing,comma," , want : false },
39+ {rule : "cswords" , input : "a,,b" , want : false }, // double comma
40+ {rule : "cswords" , input : "-starts,with,hyphen" , want : false },
41+ {rule : "cswords" , input : "with space" , want : false }, // spaces not allowed
42+
43+ // ── comment ─────────────────────────────────────────────────────────
44+ {rule : "comment" , input : "# this is a comment" , want : true },
45+ {rule : "comment" , input : "#" , want : true },
46+ {rule : "comment" , input : "# " , want : true },
47+ {rule : "comment" , input : "not a comment" , want : false },
48+ {rule : "comment" , input : "" , want : false },
49+ {rule : "comment" , input : " # indented" , want : false }, // ^ anchors at start
50+
51+ // ── scoping ─────────────────────────────────────────────────────────
52+ {rule : "scoping" , input : "---" , want : true },
53+ {rule : "scoping" , input : "----" , want : true },
54+ {rule : "scoping" , input : "----------" , want : true },
55+ {rule : "scoping" , input : "--" , want : false }, // minimum three dashes
56+ {rule : "scoping" , input : "- -" , want : false }, // no spaces
57+ {rule : "scoping" , input : "--- x" , want : false }, // nothing after dashes
58+
59+ // ── fqdn ────────────────────────────────────────────────────────────
60+ {rule : "fqdn" , input : "www.example.com" , want : true },
61+ {rule : "fqdn" , input : "www.example.com." , want : true }, // trailing dot allowed
62+ {rule : "fqdn" , input : "*.foo.bar" , want : true }, // wildcard prefix
63+ {rule : "fqdn" , input : "example.com" , want : true },
64+ {rule : "fqdn" , input : "a.b.c.d.e.tld" , want : true },
65+ {rule : "fqdn" , input : "localhost" , want : false }, // bare hostname, no dot
66+ {rule : "fqdn" , input : "192.168.1.1" , want : false }, // TLD must end with letter
67+ {rule : "fqdn" , input : "" , want : false },
68+ {rule : "fqdn" , input : "foo bar.com" , want : false }, // spaces not allowed in label
69+
70+ // ── ident (no trailing $, anchored only at start) ───────────────────
71+ {rule : "ident" , input : "VLAN" , want : true },
72+ {rule : "ident" , input : "VRF" , want : true },
73+ {rule : "ident" , input : "STATUS_OK" , want : true },
74+ {rule : "ident" , input : "VLAN10" , want : true },
75+ {rule : "ident" , input : "A9" , want : true },
76+ {rule : "ident" , input : "A" , want : false }, // [A-Z0-9_]+ needs ≥1 more char
77+ {rule : "ident" , input : "lowercase" , want : false },
78+ {rule : "ident" , input : "Mixed" , want : false },
79+ {rule : "ident" , input : "1NVALID" , want : false }, // must start with A-Z
80+
81+ // ── ip (no trailing $, anchored only at start) ───────────────────────
82+ {rule : "ip" , input : "192.168.1.1" , want : true },
83+ {rule : "ip" , input : "192.168.1.1/24" , want : true }, // prefix is fine, no trailing $
84+ {rule : "ip" , input : "10.0.0.1" , want : true },
85+ {rule : "ip" , input : "2001:db8::1" , want : true },
86+ {rule : "ip" , input : "::1" , want : true },
87+ {rule : "ip" , input : "not-an-ip" , want : false },
88+ {rule : "ip" , input : "" , want : false },
89+
90+ // ── ipv4 (no trailing $) ─────────────────────────────────────────────
91+ {rule : "ipv4" , input : "192.168.1.1" , want : true },
92+ {rule : "ipv4" , input : "0.0.0.0" , want : true },
93+ {rule : "ipv4" , input : "255.255.255.255" , want : true },
94+ {rule : "ipv4" , input : "192.168.1.1/24" , want : true }, // CIDR — no trailing $ intended
95+ {rule : "ipv4" , input : "2001:db8::1" , want : false }, // IPv6 must not match ipv4
96+ {rule : "ipv4" , input : "1.2.3" , want : false }, // only three octets
97+ {rule : "ipv4" , input : "not-an-ip" , want : false },
98+
99+ // ── ipv6 (no trailing $) ─────────────────────────────────────────────
100+ {rule : "ipv6" , input : "2001:db8::1 foo" , want : true },
101+ {rule : "ipv6" , input : "::1" , want : true },
102+ {rule : "ipv6" , input : "fe80::1" , want : true },
103+ {rule : "ipv6" , input : "2001:db8::1/64" , want : true }, // prefix — no trailing $ intended
104+ {rule : "ipv6" , input : "192.168.1.1" , want : false }, // pure dotted-decimal has no colon
105+ {rule : "ipv6" , input : "not-an-ip" , want : false },
35106 }
36107
37108 for _ , tc := range tt {
38- t .Run (tc .input , func (t * testing.T ) {
109+ tc := tc
110+ t .Run (tc .rule + "/" + tc .input , func (t * testing.T ) {
39111 t .Parallel ()
40112 got := g .MustRx (tc .rule ).MatchString (tc .input )
41113 if got != tc .want {
42- t .Fatalf ("rule: %s, input: %s, got %v, want %v" , tc .rule , tc .input , got , tc .want )
114+ t .Fatalf ("rule=%q input=%q: got %v, want %v" , tc .rule , tc .input , got , tc .want )
43115 }
44116 })
45117 }
0 commit comments