Repository: lundberg/respx

Issue:
Cannot create a lookup to match empty params
https://github.com/lundberg/respx/blob/c76f632690cdfb2c878fe8fea999df0a26083eb3/respx/patterns.py#L502-L507

E.g. `respx_mock.get("https://example.com", params__eq={}).mock(...)` matches request with any query params including empty.
Looks like the value check must be more accurate.

respx version 0.19.2


Discussion:
codecov-commenter: # [Codecov](https://codecov.io/gh/lundberg/respx/pull/206?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg) Report
> Merging [#206](https://codecov.io/gh/lundberg/respx/pull/206?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg) (d0a1750) into [master](https://codecov.io/gh/lundberg/respx/commit/019a8a839193af265b15c92a2b7f8dec67c6cb65?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg) (019a8a8) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff            @@
##            master      #206   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           20        20           
  Lines         2731      2735    +4     
  Branches       338       338           
=========================================
+ Hits          2731      2735    +4     
```


| [Impacted Files](https://codecov.io/gh/lundberg/respx/pull/206?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg) | Coverage Δ | |
|---|---|---|
| [respx/patterns.py](https://codecov.io/gh/lundberg/respx/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg#diff-cmVzcHgvcGF0dGVybnMucHk=) | `100.00% <100.00%> (ø)` | |
| [tests/test\_patterns.py](https://codecov.io/gh/lundberg/respx/pull/206/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg#diff-dGVzdHMvdGVzdF9wYXR0ZXJucy5weQ==) | `100.00% <100.00%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/lundberg/respx/pull/206?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/lundberg/respx/pull/206?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg). Last update [019a8a8...d0a1750](https://codecov.io/gh/lundberg/respx/pull/206?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Jonas+Lundberg).


Patch:
diff --git a/respx/patterns.py b/respx/patterns.py
index 37ab1d4..59a9e4d 100644
--- a/respx/patterns.py
+++ b/respx/patterns.py
@@ -504,9 +504,6 @@ def M(*patterns: Pattern, **lookups: Any) -> Optional[Pattern]:
     extras = None
 
     for pattern__lookup, value in lookups.items():
-        if not value:
-            continue
-
         # Handle url pattern
         if pattern__lookup == "url":
             extras = parse_url_patterns(value)
@@ -535,6 +532,10 @@ def M(*patterns: Pattern, **lookups: Any) -> Optional[Pattern]:
             lookup = Lookup(lookup_name) if lookup_name else None
             pattern = P(value, lookup=lookup)
 
+        # Skip patterns with no value, exept when using equal lookup
+        if not pattern.value and pattern.lookup is not Lookup.EQUAL:
+            continue
+
         patterns += (pattern,)
 
     # Combine and merge patterns


Test patch:
diff --git a/tests/test_patterns.py b/tests/test_patterns.py
index 0f06293..f90c561 100644
--- a/tests/test_patterns.py
+++ b/tests/test_patterns.py
@@ -66,6 +66,19 @@ def test_match_context():
     assert match.context == {"host": "foo.bar", "slug": "baz"}
 
 
+@pytest.mark.parametrize(
+    "kwargs,url,expected",
+    [
+        ({"params__eq": {}}, "https://foo.bar/", True),
+        ({"params__eq": {}}, "https://foo.bar/?x=y", False),
+        ({"params__contains": {}}, "https://foo.bar/?x=y", True),
+    ],
+)
+def test_m_pattern(kwargs, url, expected):
+    request = httpx.Request("GET", url)
+    assert bool(M(host="foo.bar", **kwargs).match(request)) is expected
+
+
 @pytest.mark.parametrize(
     "lookup,value,expected",
     [
@@ -217,6 +230,8 @@ def test_path_pattern():
         (Lookup.EQUAL, "y=2", "https://foo.bar/?x=1", False),
         (Lookup.EQUAL, {"x": ANY}, "https://foo.bar/?x=1", True),
         (Lookup.EQUAL, {"y": ANY}, "https://foo.bar/?x=1", False),
+        (Lookup.EQUAL, {}, "https://foo.bar/?x=1", False),
+        (Lookup.EQUAL, {}, "https://foo.bar/", True),
         (Lookup.EQUAL, "x=1&y=2", "https://foo.bar/?x=1", False),
         (Lookup.EQUAL, "y=2&x=1", "https://foo.bar/?x=1&y=2", True),
         (Lookup.EQUAL, "y=3&x=2&x=1", "https://foo.bar/?x=1&x=2&y=3", False),  # ordered