<think>

Okay, let's see. The problem is that the FastAPI app's login is failing because emails are normalized in one path but not another. So, the code in save.py and routes.py uses email addresses. In save_user, they call email.strip().lower(), which normalizes the email by removing whitespace and lowercasing. But in the login function, when they get the user, they just use the original email provided. So, if a user logs in with an email that's not stripped or lowercased, it won't match the stored one.

Wait, right. So when someone saves a user, the email is converted to lowercase and stripped of whitespace. But when they log in, they pass the email as-is. So if the login email is, say, " User@example.com ", it won't match the stored one. But if the login uses the same email format, maybe with different casing, it would fail. But the issue is that in one path (saving), the email is normalized, but in the login path, the email is not. So the problem is that the get_user function is using the original email (since it's just getting it from the USERS dict without any processing). But when saving, the key is the lowercased and stripped email. So when logging in, if the email is not stripped or lowercased, it won't find the correct entry.

So the fix would be to ensure that in both save and login, the email is processed the same way. Either in save, they should strip and lowercase, and in login, they should also do the same. But in the current code, save_user does the stripping and lowercasing, but login is just passing the email as is. So when someone logs in with an email that hasn't been stripped or lowercased, the stored password_hash won't be found.

Therefore, the solution is to modify the login function to process the email in the same way as when saving. So in routes.py, when getting the user, we should also apply strip and lower. Wait, but the get_user function is just returning USERS.get(email), which uses the original email. So the fix would be to adjust the login function to normalize the email before checking. So in the login function, when retrieving the stored user, we should also do email.strip().lower().

Wait, but the login function is called with the email parameter. So when someone logs in with an email, say "User@example.com", the code would get the stored email as "userexample.com" (if the original stored email was "User@example.com"). Wait no, because when saving, the key is email.strip().lower(). So if the original email is "User@example.com", then after stripping and lowercasing, it's "userexample.com". But when logging in, if the email passed is "user@example.com", then get_user would look for "user@example.com" in the USERS dict, which would not be present. Because the stored key is "userexample.com".

So the problem is that the login path is not normalizing the email. So to fix this, the login function should also process the email in the same way as when saving. So in the login function, when retrieving the stored user, we should do get_user(email.strip().lower()). But wait, the get_user function is just a wrapper around USERS.get(email). So modifying the login function to process the email before looking it up would fix the issue.

So the patch would be to change the login function in routes.py to normalize the email. So instead of:

def login(email, password_hash):
    stored = get_user(email)
    return stored == password_hash

It should be:

def login(email, password_hash):
    stored = get_user(email.strip().lower())
    return stored == password_hash

That way, both save and login process the email the same way. Then, when a user logs in with any email format, it's normalized to the same key as when they were saved. That should fix the problem.

So the exact changes are in routes.py, modify the login function to process the email with strip and lower before checking. Then, the regression test would check that logging in with an email that has different casing or whitespace still works. For example, save a user with email " User@example.com ", then log in with "user@example.com" and it should succeed.

Wait, but the original code for save_user does email.strip().lower() when saving. So when you save, the key is the lowercased and stripped email. So if the user is saved with " User@example.com ", the key is "userexample.com". Then, when logging in with "user@example.com", the code would look for "userexample.com" in the USERS dict, which would be present. So the login function, after normalizing the email, would retrieve the correct stored password_hash.

Yes. So the patch is to adjust the login function to normalize the email. Then, the regression test would be to verify that login works with emails that have varying cases and whitespace.

So the patch plan would be:

In routes.py, modify the login function to normalize the email by stripping whitespace and lowercasing before retrieving the stored password_hash.

Then, the regression test would be:

Test case: user signs up with email " User@example.com ", password_hash "abc". Then, login with "user@example.com" and expect success.

Original code would fail because the stored key is "userexample.com", but login uses "user@example.com" which isn't in the USERS dict. After patch, the login normalizes the email to "userexample.com", which matches the stored key, so returns True.

So the exact changes are in routes.py's login function. The other files (store.py) are correct as they are. So the patch is to modify routes.py's login function.

Another possible approach is to have the login function pass the email through the same normalization as save_user. So the patch is exactly as described.

So the final answer would be the patch to routes.py's login function, and the regression test case.

</think>

To resolve the issue of inconsistent email normalization between the user storage and login paths, apply the following patch:

```diff
--- save.py         --- routes.py        \
+--- HEAD           + diff                 \
+--- PATCH         + def login(email, password_hash): \
         ...                               +     email = email.strip().lower() \
         ...                               +     stored = get_user(email)       \
         ...                               +     return stored == password_hash
```

**Patch Explanation:**
- In `routes.py`, modify the `login` function to normalize the email address by stripping whitespace and converting to lowercase before checking the stored password hash. This ensures consistency with the normalization performed in `save_user`.

**Regression Test:**
- **Test Case:** Save a user with the email `" User@example.com "` and a password hash `abc`. Attempt to log in with the email `"user@example.com"` and verify that the login succeeds (returns `True`).
- **Expected Result:** The login should succeed because the email is normalized to `"userexample.com"` in both paths, matching the stored password hash.