Repository: pallets/werkzeug (python)

Issue:
`Authorization` should support equality check
As shown in https://github.com/csernazs/pytest-httpserver/issues/241, it can be useful in tests to check if two `Authorization` or `WWWAuthenticate` objects are equal. Implement `__eq__` for both classes.

Pull request description:
`__eq__` for auth objects
Implement `__eq__` for `Authorization` and `WWWAuthenticate` objects.

fixes #2665 

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 234972c5..737fb7a6 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -8,6 +8,8 @@ Unreleased
 -   Percent-encode plus (+) when building URLs and in test requests. :issue:`2657`
 -   Cookie values don't quote characters defined in RFC 6265. :issue:`2659`
 -   Include ``pyi`` files for ``datastructures`` type annotations. :issue:`2660`
+-   ``Authorization`` and ``WWWAuthenticate`` objects can be compared for equality.
+    :issue:`2665`
 
 
 Version 2.3.0
diff --git a/src/werkzeug/datastructures/auth.py b/src/werkzeug/datastructures/auth.py
index 3eb6cdd1..afc719c7 100644
--- a/src/werkzeug/datastructures/auth.py
+++ b/src/werkzeug/datastructures/auth.py
@@ -78,6 +78,16 @@ class Authorization:
     def __contains__(self, key: str) -> bool:
         return key in self.parameters
 
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, Authorization):
+            return NotImplemented
+
+        return (
+            other.type == self.type
+            and other.token == self.token
+            and other.parameters == self.parameters
+        )
+
     @classmethod
     def from_header(cls, value: str | None) -> te.Self | None:
         """Parse an ``Authorization`` header value and return an instance, or ``None``
@@ -339,6 +349,16 @@ class WWWAuthenticate:
     def __contains__(self, key: str) -> bool:
         return key in self.parameters
 
+    def __eq__(self, other: object) -> bool:
+        if not isinstance(other, WWWAuthenticate):
+            return NotImplemented
+
+        return (
+            other.type == self.type
+            and other.token == self.token
+            and other.parameters == self.parameters
+        )
+
     def get(self, key: str, default: str | None = None) -> str | None:
         return self.parameters.get(key, default)
 


Test patch:
diff --git a/tests/test_http.py b/tests/test_http.py
index a0b80c73..e82f869d 100644
--- a/tests/test_http.py
+++ b/tests/test_http.py
@@ -208,6 +208,18 @@ class TestHTTPUtility:
         content = base64.b64encode(b"\xffser:pass").decode()
         assert Authorization.from_header(f"Basic {content}") is None
 
+    def test_authorization_eq(self):
+        basic1 = Authorization.from_header("Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==")
+        basic2 = Authorization(
+            "basic", {"username": "Aladdin", "password": "open sesame"}
+        )
+        assert basic1 == basic2
+        bearer1 = Authorization.from_header("Bearer abc")
+        bearer2 = Authorization("bearer", token="abc")
+        assert bearer1 == bearer2
+        assert basic1 != bearer1
+        assert basic1 != object()
+
     def test_www_authenticate_header(self):
         wa = WWWAuthenticate.from_header('Basic realm="WallyWorld"')
         assert wa.type == "basic"
@@ -230,6 +242,16 @@ class TestHTTPUtility:
         assert WWWAuthenticate.from_header("broken").type == "broken"
         assert WWWAuthenticate.from_header("") is None
 
+    def test_www_authenticate_eq(self):
+        basic1 = WWWAuthenticate.from_header("Basic realm=abc")
+        basic2 = WWWAuthenticate("basic", {"realm": "abc"})
+        assert basic1 == basic2
+        token1 = WWWAuthenticate.from_header("Token abc")
+        token2 = WWWAuthenticate("token", token="abc")
+        assert token1 == token2
+        assert basic1 != token1
+        assert basic1 != object()
+
     def test_etags(self):
         assert http.quote_etag("foo") == '"foo"'
         assert http.quote_etag("foo", True) == 'W/"foo"'