diff --git a/internal/elements.go b/internal/elements.go index db7d960..3837507 100644 --- a/internal/elements.go +++ b/internal/elements.go @@ -377,7 +377,15 @@ type GetETag struct { type ETag string func (etag *ETag) UnmarshalText(b []byte) error { - s, err := strconv.Unquote(string(b)) + s := string(b) + // support for providers with non-strict https://datatracker.ietf.org/doc/html/rfc2616#section-3.11 implementation + if !strings.HasPrefix(s, "\"") { + *etag = ETag(s) + return nil + } + + var err error + s, err = strconv.Unquote(s) if err != nil { return fmt.Errorf("webdav: failed to unquote ETag: %v", err) } diff --git a/internal/elements_test.go b/internal/elements_test.go index 73a6ef5..2f28c9a 100644 --- a/internal/elements_test.go +++ b/internal/elements_test.go @@ -63,3 +63,153 @@ func TestTimeRoundTrip(t *testing.T) { t.Fatalf("invalid round-trip:\ngot= %s\nwant=%s", got, want) } } + +func TestETag_UnmarshalText(t *testing.T) { + type args struct { + b []byte + } + tests := []struct { + name string + etag ETag + args args + wantErr bool + wantETag ETag + }{ + { + name: "double quoted string", + etag: "", + args: args{ + b: []byte("\"1692394723948\""), + }, + wantErr: false, + wantETag: "1692394723948", + }, + { + name: "empty double quoted string", + etag: "", + args: args{ + b: []byte("\"\""), + }, + wantErr: false, + wantETag: "", + }, + { + name: "unquoted string", + etag: "", + args: args{ + b: []byte("1692394723948"), + }, + wantErr: false, + wantETag: "1692394723948", + }, + { + name: "empty string", + etag: "", + args: args{ + b: []byte(""), + }, + wantErr: false, + wantETag: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.etag.UnmarshalText(tt.args.b); (err != nil) != tt.wantErr { + t.Errorf("UnmarshalText() error = %v, wantErr %v", err, tt.wantErr) + } + + if tt.etag != tt.wantETag { + t.Errorf("UnmarshalText() want %s, got %s", tt.wantETag, tt.etag) + } + }) + } +} + +func TestETag_String(t *testing.T) { + tests := []struct { + name string + etag ETag + want string + }{ + { + name: "string with double-quote", + etag: "162392347123", + want: "\"162392347123\"", + }, + { + name: "empty string with double-quote", + etag: "", + want: "\"\"", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.etag.String(); got != tt.want { + t.Errorf("String() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestETag_UnmarshalAndMarshalText(t *testing.T) { + tests := []struct { + name string + // initial ETag + etag ETag + // first value for ETag UnmarshalText + unmarshalValue []byte + // expected MarshalText() result after UnmarshalText() + wantMarshalled []byte + // expected UnmarshalText() result after MarshalText() with unmarshalValue + wantUnmarshalledETag ETag + }{ + { + name: "string", + etag: "", + unmarshalValue: []byte("162392347123"), + wantMarshalled: []byte("\"162392347123\""), + wantUnmarshalledETag: "162392347123", + }, + { + name: "double-quoted", + etag: "", + unmarshalValue: []byte("\"162392347123\""), + wantMarshalled: []byte("\"162392347123\""), + wantUnmarshalledETag: "162392347123", + }, + { + name: "empty string", + etag: "", + unmarshalValue: []byte(""), + wantMarshalled: []byte("\"\""), + wantUnmarshalledETag: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if err := tt.etag.UnmarshalText(tt.unmarshalValue); err != nil { + t.Errorf("UnmarshalText() = %v", err) + } + + m, err := tt.etag.MarshalText() + if err != nil { + t.Errorf("MarshalText() = %v", err) + } + + if !bytes.Equal(m, tt.wantMarshalled) { + t.Errorf("MarshalText() want %s, got %s", tt.wantMarshalled, m) + } + + if err := tt.etag.UnmarshalText(m); err != nil { + t.Errorf("UnmarshalText() got error after MarshalText() = %v", err) + } + + if tt.etag != tt.wantUnmarshalledETag { + t.Errorf("UnmarshalText() after MarshalText() want %s, got %s", tt.wantUnmarshalledETag, tt.etag) + } + }) + } +}