1616package gomatrixserverlib
1717
1818import (
19+ "bytes"
1920 "encoding/json"
2021 "reflect"
2122 "testing"
@@ -78,9 +79,10 @@ func TestSortJSON(t *testing.T) {
7879}
7980
8081func testCompactJSON (t * testing.T , input , want string ) {
81- got := string (CompactJSON ([]byte (input ), nil ))
82+ bytes := CompactJSON ([]byte (input ), nil )
83+ got := string (bytes )
8284 if got != want {
83- t .Errorf ("CompactJSON(%q):\n want: %q\n got: %q" , input , want , got )
85+ t .Errorf ("CompactJSON(%q):\n want: %q\n got: %q\n bytes: % X " , input , want , got , bytes )
8486 }
8587}
8688
@@ -106,12 +108,192 @@ func TestCompactJSON(t *testing.T) {
106108 testCompactJSON (t , `["\u0061\u005C\u0042\u0022"]` , `["a\\B\""]` )
107109 testCompactJSON (t , `["\u0120"]` , "[\" \u0120 \" ]" )
108110 testCompactJSON (t , `["\u0FFF"]` , "[\" \u0FFF \" ]" )
111+ testCompactJSON (t , `["\u0FFf"]` , "[\" \u0FFF \" ]" )
109112 testCompactJSON (t , `["\u1820"]` , "[\" \u1820 \" ]" )
110113 testCompactJSON (t , `["\uFFFF"]` , "[\" \uFFFF \" ]" )
111114 testCompactJSON (t , `["\uD842\uDC20"]` , "[\" \U00020820 \" ]" )
112115 testCompactJSON (t , `["\uDBFF\uDFFF"]` , "[\" \U0010FFFF \" ]" )
113116
117+ // Unpaired UTF-16 surrogate pair
118+ testCompactJSON (t , `["\uDEAD"]` , "[\" \" ]" )
119+
120+ testCompactJSON (t , `["\\"]` , "[\" \\ \\ \" ]" )
121+ testCompactJSON (t , `"` , "\" " )
122+ testCompactJSON (t , `["\b"]` , "[\" \\ b\" ]" )
123+ testCompactJSON (t , `["\f"]` , "[\" \\ f\" ]" )
124+ testCompactJSON (t , `["\n"]` , "[\" \\ n\" ]" )
125+ testCompactJSON (t , `["\r"]` , "[\" \\ r\" ]" )
126+ testCompactJSON (t , `["\t"]` , "[\" \\ t\" ]" )
127+
128+ testCompactJSON (t , `"\u000a"` , "\" \\ n\" " )
129+ testCompactJSON (t , `"\u000A"` , "\" \\ n\" " )
130+ testCompactJSON (t , `"\u0022"` , "\" \\ \" \" " )
131+ testCompactJSON (t , `"\u005c"` , "\" \\ \\ \" " )
132+
114133 testCompactJSON (t , `["\"\\\/"]` , `["\"\\/"]` )
134+ testCompactJSON (t , `["\/"]` , `["/"]` )
135+ }
136+
137+ func TestVerifyCanonical (t * testing.T ) {
138+ tests := []struct {
139+ name string
140+ input []byte
141+ valid bool
142+ }{
143+ //{
144+ // name: "escaped unicode",
145+ // input: []byte(`{"\u00F1":0}`),
146+ // valid: false,
147+ //},
148+ //{
149+ // name: "long form unicode",
150+ // input: []byte(`{"\u0009":0}`),
151+ // valid: false,
152+ //},
153+ //{
154+ // name: "escaped unicode surrogate pair",
155+ // input: []byte(`{"\ud83d\udc08":0}`),
156+ // valid: false,
157+ //},
158+ {
159+ name : "negative zero" ,
160+ input : []byte (`{"a":-0}` ),
161+ valid : false ,
162+ },
163+ {
164+ name : "number out of bounds upper" ,
165+ input : []byte (`{"a":9007199254740992}` ),
166+ valid : false ,
167+ },
168+ {
169+ name : "number out of bounds lower" ,
170+ input : []byte (`{"a":-9007199254740992}` ),
171+ valid : false ,
172+ },
173+ {
174+ name : "exponential notation number" ,
175+ input : []byte (`{"a":1e5}` ),
176+ valid : false ,
177+ },
178+ {
179+ name : "fractional number" ,
180+ input : []byte (`{"a":1.5}` ),
181+ valid : false ,
182+ },
183+ //{
184+ // name: "unsorted keys",
185+ // input: []byte(`{"b":0,"a":1}`),
186+ // valid: false,
187+ //},
188+ //{
189+ // name: "unsorted keys in array",
190+ // input: []byte(`{"a":[{"b":0,"a":1},{"b":0,"a":1}]}`),
191+ // valid: false,
192+ //},
193+ //{
194+ // name: "unnecessary whitespace",
195+ // input: []byte(`{"a": 0}`),
196+ // valid: false,
197+ //},
198+ //{
199+ // name: "unpaired UTF-16 surrogate",
200+ // input: []byte(`{"a":"\uDEAD"}`),
201+ // valid: false,
202+ //},
203+ //{
204+ // name: "failure combo",
205+ // input: []byte(`{ "\u00F1": -0, "2": 0, "1": 9007199254740991, "3":1e5, "4": [{"2": 0, "1": 0},{"2": 0, "1": 0}] }`),
206+ // valid: false,
207+ //},
208+ {
209+ name : "canonical JSON" ,
210+ input : []byte (`{"1":9007199254740991,"2":0,"3":-9007199254740991,"4":[{"1":0,"2":0},{"1":0,"2":0}],"ñ":0}` ),
211+ valid : true ,
212+ },
213+ //{
214+ // name: "duplicate keys",
215+ // input: []byte(`{"a":0,"a":1}`),
216+ // valid: false,
217+ //},
218+ //{
219+ // name: "nested duplicate keys",
220+ // input: []byte(`{"a":[{"a":0,"a":1}]}`),
221+ // valid: false,
222+ //},
223+ }
224+ for _ , tt := range tests {
225+ t .Run (tt .name , func (t * testing.T ) {
226+ _ , err := EnforcedCanonicalJSON (tt .input , RoomVersionV11 )
227+
228+ if ! tt .valid && err == nil {
229+ t .Fatalf ("JSON passes canonical check when it shouldn't. \n Original: %s (% X)" , tt .input , tt .input )
230+ }
231+ if tt .valid && err != nil {
232+ t .Fatalf ("JSON doesn't pass canonical check when it should. \n Original: %s (% X)" , tt .input , tt .input )
233+ }
234+ })
235+ }
236+ }
237+
238+ func TestCanonicalConversion (t * testing.T ) {
239+ tests := []struct {
240+ name string
241+ input []byte
242+ canonical []byte
243+ }{
244+ {
245+ name : "escaped unicode" ,
246+ input : []byte (`{"\u00F1":0}` ),
247+ canonical : []byte (`{"ñ":0}` ),
248+ },
249+ {
250+ name : "escaped unicode surrogate pair" ,
251+ input : []byte (`{"\ud83d\udc08":0}` ),
252+ canonical : []byte (`{"🐈":0}` ),
253+ },
254+ {
255+ name : "negative zero" ,
256+ input : []byte (`{"a":-0}` ),
257+ canonical : []byte (`{"a":0}` ),
258+ },
259+ //{
260+ // name: "exponential notation number",
261+ // input: []byte(`{"a":1e5}`),
262+ // canonical: []byte(`{"a":100000}`),
263+ //},
264+ {
265+ name : "unsorted keys" ,
266+ input : []byte (`{"b":0,"a":1}` ),
267+ canonical : []byte (`{"a":1,"b":0}` ),
268+ },
269+ {
270+ name : "unsorted keys in array" ,
271+ input : []byte (`{"a":[{"b":0,"a":1},{"b":0,"a":1}]}` ),
272+ canonical : []byte (`{"a":[{"a":1,"b":0},{"a":1,"b":0}]}` ),
273+ },
274+ {
275+ name : "unnecessary whitespace" ,
276+ input : []byte (`{"a": 0}` ),
277+ canonical : []byte (`{"a":0}` ),
278+ },
279+ {
280+ name : "conversion combo" ,
281+ input : []byte (`{ "\u00F1": -0, "2": 0, "1": 9007199254740991, "4": [{"2": 0, "1": 0},{"2": 0, "1": 0}] }` ),
282+ canonical : []byte (`{"1":9007199254740991,"2":0,"4":[{"1":0,"2":0},{"1":0,"2":0}],"ñ":0}` ),
283+ },
284+ }
285+ for _ , tt := range tests {
286+ t .Run (tt .name , func (t * testing.T ) {
287+ gmslCanonical , err := CanonicalJSON (tt .input )
288+ if err != nil {
289+ t .Fatalf ("Failed parsing json: %s" , err .Error ())
290+ }
291+
292+ if ! bytes .Equal (tt .canonical , gmslCanonical ) {
293+ t .Fatalf ("GMSL canonical JSON is not canonical. \n Original: %s (% X) \n GMSL Canonical: %s (% X) \n Expected Form: %s (% X)" , tt .input , tt .input , gmslCanonical , gmslCanonical , tt .canonical , tt .canonical )
294+ }
295+ })
296+ }
115297}
116298
117299func TestCompactUnicodeEscapeWithUTF16Surrogate (t * testing.T ) {
0 commit comments