5
5
package webdav
6
6
7
7
import (
8
+ "bytes"
8
9
"encoding/xml"
10
+ "io"
9
11
"net/http"
10
12
"net/http/httptest"
11
13
"reflect"
@@ -345,13 +347,6 @@ func TestReadPropfind(t *testing.T) {
345
347
func TestMultistatusWriter (t * testing.T ) {
346
348
///The "section x.y.z" test cases come from section x.y.z of the spec at
347
349
// http://www.webdav.org/specs/rfc4918.html
348
- //
349
- // BUG:The following tests compare the actual and expected XML verbatim.
350
- // Minor tweaks in the marshalling output of either standard encoding/xml
351
- // or this package might break them. A more resilient approach could be
352
- // to normalize both actual and expected XML content before comparison.
353
- // This also would enhance readibility of the expected XML payload in the
354
- // wantXML field.
355
350
testCases := []struct {
356
351
desc string
357
352
responses []response
@@ -365,38 +360,43 @@ func TestMultistatusWriter(t *testing.T) {
365
360
Href : []string {"http://example.com/foo" },
366
361
Propstat : []propstat {{
367
362
Prop : []Property {{
368
- XMLName : xml.Name {Space : "http://ns.example.com/" , Local : "Authors" },
363
+ XMLName : xml.Name {
364
+ Space : "http://ns.example.com/" ,
365
+ Local : "Authors" ,
366
+ },
369
367
}},
370
368
Status : "HTTP/1.1 424 Failed Dependency" ,
371
369
}, {
372
370
Prop : []Property {{
373
- XMLName : xml.Name {Space : "http://ns.example.com/" , Local : "Copyright-Owner" },
371
+ XMLName : xml.Name {
372
+ Space : "http://ns.example.com/" ,
373
+ Local : "Copyright-Owner" ,
374
+ },
374
375
}},
375
376
Status : "HTTP/1.1 409 Conflict" ,
376
377
}},
377
- ResponseDescription : " Copyright Owner cannot be deleted or altered." ,
378
+ ResponseDescription : "Copyright Owner cannot be deleted or altered." ,
378
379
}},
379
- wantXML : `<?xml version="1.0" encoding="UTF-8"?>` +
380
- `<D:multistatus xmlns:D="DAV:">` +
381
- `<response xmlns="DAV:">` +
382
- `<href xmlns="DAV:">http://example.com/foo</href>` +
383
- `<propstat xmlns="DAV:">` +
384
- `<prop>` +
385
- `<Authors xmlns="http://ns.example.com/"></Authors>` +
386
- `</prop>` +
387
- `<status xmlns="DAV:">HTTP/1.1 424 Failed Dependency</status>` +
388
- `</propstat>` +
389
- `<propstat xmlns="DAV:">` +
390
- `<prop>` +
391
- `<Copyright-Owner xmlns="http://ns.example.com/"></Copyright-Owner>` +
392
- `</prop>` +
393
- `<status xmlns="DAV:">HTTP/1.1 409 Conflict</status>` +
394
- `</propstat>` +
395
- `<responsedescription xmlns="DAV:">` +
396
- ` Copyright Owner cannot be deleted or altered.` +
397
- `</responsedescription>` +
380
+ wantXML : `` +
381
+ `<?xml version="1.0" encoding="UTF-8"?>` +
382
+ `<multistatus xmlns="DAV:">` +
383
+ ` <response>` +
384
+ ` <href>http://example.com/foo</href>` +
385
+ ` <propstat>` +
386
+ ` <prop>` +
387
+ ` <Authors xmlns="http://ns.example.com/"></Authors>` +
388
+ ` </prop>` +
389
+ ` <status>HTTP/1.1 424 Failed Dependency</status>` +
390
+ ` </propstat>` +
391
+ ` <propstat xmlns="DAV:">` +
392
+ ` <prop>` +
393
+ ` <Copyright-Owner xmlns="http://ns.example.com/"></Copyright-Owner>` +
394
+ ` </prop>` +
395
+ ` <status>HTTP/1.1 409 Conflict</status>` +
396
+ ` </propstat>` +
397
+ ` <responsedescription>Copyright Owner cannot be deleted or altered.</responsedescription>` +
398
398
`</response>` +
399
- `</D: multistatus>` ,
399
+ `</multistatus>` ,
400
400
wantCode : StatusMulti ,
401
401
}, {
402
402
desc : "section 9.6.2 (lock-token-submitted)" ,
@@ -407,14 +407,15 @@ func TestMultistatusWriter(t *testing.T) {
407
407
InnerXML : []byte (`<lock-token-submitted xmlns="DAV:"/>` ),
408
408
},
409
409
}},
410
- wantXML : `<?xml version="1.0" encoding="UTF-8"?>` +
411
- `<D:multistatus xmlns:D="DAV:">` +
412
- `<response xmlns="DAV:">` +
413
- `<href xmlns="DAV:">http://example.com/foo</href>` +
414
- `<status xmlns="DAV:">HTTP/1.1 423 Locked</status>` +
415
- `<error xmlns="DAV:"><lock-token-submitted xmlns="DAV:"/></error>` +
416
- `</response>` +
417
- `</D:multistatus>` ,
410
+ wantXML : `` +
411
+ `<?xml version="1.0" encoding="UTF-8"?>` +
412
+ `<multistatus xmlns="DAV:">` +
413
+ ` <response>` +
414
+ ` <href>http://example.com/foo</href>` +
415
+ ` <status>HTTP/1.1 423 Locked</status>` +
416
+ ` <error><lock-token-submitted xmlns="DAV:"/></error>` +
417
+ ` </response>` +
418
+ `</multistatus>` ,
418
419
wantCode : StatusMulti ,
419
420
}, {
420
421
desc : "section 9.1.3" ,
@@ -442,42 +443,33 @@ func TestMultistatusWriter(t *testing.T) {
442
443
XMLName : xml.Name {Space : "http://ns.example.com/boxschema/" , Local : "Random" },
443
444
}},
444
445
Status : "HTTP/1.1 403 Forbidden" ,
445
- ResponseDescription : " The user does not have access to the DingALing property." ,
446
+ ResponseDescription : "The user does not have access to the DingALing property." ,
446
447
}},
447
448
}},
448
- respdesc : " There has been an access violation error." ,
449
- wantXML : `<?xml version="1.0" encoding="UTF-8"?>` +
450
- `<D:multistatus xmlns:D="DAV:">` +
451
- `<response xmlns="DAV:">` +
452
- `<href xmlns="DAV:">http://example.com/foo</href>` +
453
- `<propstat xmlns="DAV:">` +
454
- `<prop>` +
455
- `<bigbox xmlns="http://ns.example.com/boxschema/">` +
456
- `<BoxType xmlns="http://ns.example.com/boxschema/">Box type A</BoxType>` +
457
- `</bigbox>` +
458
- `<author xmlns="http://ns.example.com/boxschema/">` +
459
- `<Name xmlns="http://ns.example.com/boxschema/">J.J. Johnson</Name>` +
460
- `</author>` +
461
- `</prop>` +
462
- `<status xmlns="DAV:">HTTP/1.1 200 OK</status>` +
463
- `</propstat>` +
464
- `<propstat xmlns="DAV:">` +
465
- `<prop>` +
466
- `<DingALing xmlns="http://ns.example.com/boxschema/">` +
467
- `</DingALing>` +
468
- `<Random xmlns="http://ns.example.com/boxschema/">` +
469
- `</Random>` +
470
- `</prop>` +
471
- `<status xmlns="DAV:">HTTP/1.1 403 Forbidden</status>` +
472
- `<responsedescription xmlns="DAV:">` +
473
- ` The user does not have access to the DingALing property.` +
474
- `</responsedescription>` +
475
- `</propstat>` +
476
- `</response>` +
477
- `<D:responsedescription>` +
478
- ` There has been an access violation error.` +
479
- `</D:responsedescription>` +
480
- `</D:multistatus>` ,
449
+ respdesc : "There has been an access violation error." ,
450
+ wantXML : `` +
451
+ `<?xml version="1.0" encoding="UTF-8"?>` +
452
+ `<multistatus xmlns="DAV:">` +
453
+ ` <response>` +
454
+ ` <href>http://example.com/foo</href>` +
455
+ ` <propstat>` +
456
+ ` <prop>` +
457
+ ` <bigbox xmlns="http://ns.example.com/boxschema/"><BoxType xmlns="http://ns.example.com/boxschema/">Box type A</BoxType></bigbox>` +
458
+ ` <author xmlns="http://ns.example.com/boxschema/"><Name xmlns="http://ns.example.com/boxschema/">J.J. Johnson</Name></author>` +
459
+ ` </prop>` +
460
+ ` <status>HTTP/1.1 200 OK</status>` +
461
+ ` </propstat>` +
462
+ ` <propstat>` +
463
+ ` <prop>` +
464
+ ` <DingALing xmlns="http://ns.example.com/boxschema/"></DingALing>` +
465
+ ` <Random xmlns="http://ns.example.com/boxschema/"></Random>` +
466
+ ` </prop>` +
467
+ ` <status>HTTP/1.1 403 Forbidden</status>` +
468
+ ` <responsedescription>The user does not have access to the DingALing property.</responsedescription>` +
469
+ ` </propstat>` +
470
+ ` </response>` +
471
+ ` <responsedescription>There has been an access violation error.</responsedescription>` +
472
+ `</multistatus>` ,
481
473
wantCode : StatusMulti ,
482
474
}, {
483
475
desc : "bad: no response written" ,
@@ -493,7 +485,10 @@ func TestMultistatusWriter(t *testing.T) {
493
485
responses : []response {{
494
486
Propstat : []propstat {{
495
487
Prop : []Property {{
496
- XMLName : xml.Name {Space : "http://example.com/" , Local : "foo" },
488
+ XMLName : xml.Name {
489
+ Space : "http://example.com/" ,
490
+ Local : "foo" ,
491
+ },
497
492
}},
498
493
Status : "HTTP/1.1 200 OK" ,
499
494
}},
@@ -523,7 +518,10 @@ func TestMultistatusWriter(t *testing.T) {
523
518
Href : []string {"http://example.com/foo" },
524
519
Propstat : []propstat {{
525
520
Prop : []Property {{
526
- XMLName : xml.Name {Space : "http://example.com/" , Local : "foo" },
521
+ XMLName : xml.Name {
522
+ Space : "http://example.com/" ,
523
+ Local : "foo" ,
524
+ },
527
525
}},
528
526
Status : "HTTP/1.1 200 OK" ,
529
527
}},
@@ -535,10 +533,16 @@ func TestMultistatusWriter(t *testing.T) {
535
533
}, {
536
534
desc : "bad: multiple hrefs and propstat" ,
537
535
responses : []response {{
538
- Href : []string {"http://example.com/foo" , "http://example.com/bar" },
536
+ Href : []string {
537
+ "http://example.com/foo" ,
538
+ "http://example.com/bar" ,
539
+ },
539
540
Propstat : []propstat {{
540
541
Prop : []Property {{
541
- XMLName : xml.Name {Space : "http://example.com/" , Local : "foo" },
542
+ XMLName : xml.Name {
543
+ Space : "http://example.com/" ,
544
+ Local : "foo" ,
545
+ },
542
546
}},
543
547
Status : "HTTP/1.1 200 OK" ,
544
548
}},
@@ -547,29 +551,68 @@ func TestMultistatusWriter(t *testing.T) {
547
551
// default of http.responseWriter
548
552
wantCode : http .StatusOK ,
549
553
}}
554
+
550
555
loop:
551
556
for _ , tc := range testCases {
552
557
rec := httptest .NewRecorder ()
553
558
w := multistatusWriter {w : rec , responseDescription : tc .respdesc }
554
559
for _ , r := range tc .responses {
555
560
if err := w .write (& r ); err != nil {
556
561
if err != tc .wantErr {
557
- t .Errorf ("%s: got write error %v, want %v" , tc .desc , err , tc .wantErr )
562
+ t .Errorf ("%s: got write error %v, want %v" ,
563
+ tc .desc , err , tc .wantErr )
558
564
}
559
565
continue loop
560
566
}
561
567
}
562
568
if err := w .close (); err != tc .wantErr {
563
- t .Errorf ("%s: got close error %v, want %v" , tc .desc , err , tc .wantErr )
569
+ t .Errorf ("%s: got close error %v, want %v" ,
570
+ tc .desc , err , tc .wantErr )
564
571
continue
565
572
}
566
573
if rec .Code != tc .wantCode {
567
- t .Errorf ("%s: got HTTP status code %d, want %d\n " , tc .desc , rec .Code , tc .wantCode )
574
+ t .Errorf ("%s: got HTTP status code %d, want %d\n " ,
575
+ tc .desc , rec .Code , tc .wantCode )
568
576
continue
569
577
}
570
- if gotXML := rec .Body .String (); gotXML != tc .wantXML {
571
- t .Errorf ("%s: XML body\n got %q\n want %q" , tc .desc , gotXML , tc .wantXML )
572
- continue
578
+
579
+ // normalize returns the normalized XML content of s. In contrast to
580
+ // the WebDAV specification, it ignores whitespace within property
581
+ // values of mixed XML content.
582
+ normalize := func (s string ) string {
583
+ d := xml .NewDecoder (strings .NewReader (s ))
584
+ var b bytes.Buffer
585
+ e := xml .NewEncoder (& b )
586
+ for {
587
+ tok , err := d .Token ()
588
+ if err != nil {
589
+ if err == io .EOF {
590
+ break
591
+ }
592
+ t .Fatalf ("%s: Token %v" , tc .desc , err )
593
+ }
594
+ switch val := tok .(type ) {
595
+ case xml.Comment , xml.Directive , xml.ProcInst :
596
+ continue
597
+ case xml.CharData :
598
+ if len (bytes .TrimSpace (val )) == 0 {
599
+ continue
600
+ }
601
+ }
602
+ if err := e .EncodeToken (tok ); err != nil {
603
+ t .Fatalf ("%s: EncodeToken: %v" , tc .desc , err )
604
+ }
605
+ }
606
+ if err := e .Flush (); err != nil {
607
+ t .Fatalf ("%s: Flush: %v" , tc .desc , err )
608
+ }
609
+ return b .String ()
610
+ }
611
+
612
+ gotXML := normalize (rec .Body .String ())
613
+ wantXML := normalize (tc .wantXML )
614
+ if gotXML != wantXML {
615
+ t .Errorf ("%s: XML body\n got %q\n want %q" , tc .desc , gotXML , wantXML )
573
616
}
574
617
}
575
618
}
0 commit comments