Repository: getmoto/moto
Pull request #6329 (2023-05-17 10:53:58)

Core: add date to response metadata
Closes https://github.com/getmoto/moto/issues/6321

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/getmoto/moto/pull/6329?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#6329](https://app.codecov.io/gh/getmoto/moto/pull/6329?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (c50fb80) into [master](https://app.codecov.io/gh/getmoto/moto/commit/61c475fef7eebc260b4369547aeea086f5208b6a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (61c475f) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #6329   +/-   ##
=======================================
  Coverage   96.27%   96.27%           
=======================================
  Files         792      792           
  Lines       77221    77221           
=======================================
  Hits        74341    74341           
  Misses       2880     2880           
```

| Flag | Coverage Δ | |
|---|---|---|
| servertests | `36.92% <100.00%> (ø)` | |
| unittests | `96.21% <100.00%> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://app.codecov.io/gh/getmoto/moto/pull/6329?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [moto/core/responses.py](https://app.codecov.io/gh/getmoto/moto/pull/6329?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-bW90by9jb3JlL3Jlc3BvbnNlcy5weQ==) | `95.98% <100.00%> (ø)` | |


rafcio19: @bblommers ready for review

Patch:
diff --git a/moto/core/responses.py b/moto/core/responses.py
index 8fc794261..a50aa80a4 100644
--- a/moto/core/responses.py
+++ b/moto/core/responses.py
@@ -306,7 +306,10 @@ class BaseResponse(_TemplateEnvironmentMixin, ActionAuthenticatorMixin):
         self.headers = request.headers
         if "host" not in self.headers:
             self.headers["host"] = urlparse(full_url).netloc
-        self.response_headers = {"server": "amazon.com"}
+        self.response_headers = {
+            "server": "amazon.com",
+            "date": datetime.datetime.now().strftime("%a, %d %b %Y %H:%M:%S GMT"),
+        }
 
         # Register visit with IAM
         from moto.iam.models import mark_account_as_visited
diff --git a/moto/iam/responses.py b/moto/iam/responses.py
index ac321c912..60ab3b508 100644
--- a/moto/iam/responses.py
+++ b/moto/iam/responses.py
@@ -1738,6 +1738,10 @@ GET_GROUP_TEMPLATE = """<GetGroupResponse>
             <UserName>{{ user.name }}</UserName>
             <UserId>{{ user.id }}</UserId>
             <Arn>{{ user.arn }}</Arn>
+            <CreateDate>{{ user.created_iso_8601 }}</CreateDate>
+            {% if user.password_last_used_iso_8601 %}
+              <PasswordLastUsed>{{ user.password_last_used_iso_8601 }}</PasswordLastUsed>
+            {% endif %}
           </member>
         {% endfor %}
       </Users>


Test patch:
diff --git a/tests/test_core/test_responses.py b/tests/test_core/test_responses.py
index 728f6012d..f306348ff 100644
--- a/tests/test_core/test_responses.py
+++ b/tests/test_core/test_responses.py
@@ -1,3 +1,4 @@
+import datetime
 from unittest import SkipTest, mock
 import sure  # noqa # pylint: disable=unused-import
 
@@ -8,6 +9,7 @@ from botocore.awsrequest import AWSPreparedRequest
 from moto.core.responses import AWSServiceSpec, BaseResponse
 from moto.core.responses import flatten_json_request_body
 from moto import settings
+from freezegun import freeze_time
 
 
 def test_flatten_json_request_body():
@@ -225,3 +227,21 @@ def test_jinja_render_prettify(m_env_var):
     xml_string = template.render()
     assert xml_string == expected_output
     assert m_env_var
+
+
+def test_response_metadata():
+    # Setup
+    frozen_time = datetime.datetime(
+        2023, 5, 20, 10, 20, 30, tzinfo=datetime.timezone.utc
+    )
+    request = AWSPreparedRequest("GET", "http://request", {}, None, False)
+
+    # Execute
+    with freeze_time(frozen_time):
+        bc = BaseResponse()
+        bc.setup_class(request, request.url, request.headers)
+
+    # Verify
+    assert "date" in bc.response_headers
+    if not settings.TEST_SERVER_MODE:
+        assert bc.response_headers["date"] == "Sat, 20 May 2023 10:20:30 GMT"
diff --git a/tests/test_iam/test_iam_groups.py b/tests/test_iam/test_iam_groups.py
index 332d4cf56..58976757f 100644
--- a/tests/test_iam/test_iam_groups.py
+++ b/tests/test_iam/test_iam_groups.py
@@ -6,8 +6,11 @@ import json
 
 import pytest
 from botocore.exceptions import ClientError
-from moto import mock_iam
+from moto import mock_iam, settings
 from moto.core import DEFAULT_ACCOUNT_ID as ACCOUNT_ID
+from moto.backends import get_backend
+from freezegun import freeze_time
+from dateutil.tz import tzlocal
 
 MOCK_POLICY = """
 {
@@ -107,10 +110,34 @@ def test_add_user_to_unknown_group():
 
 @mock_iam
 def test_add_user_to_group():
-    conn = boto3.client("iam", region_name="us-east-1")
-    conn.create_group(GroupName="my-group")
-    conn.create_user(UserName="my-user")
-    conn.add_user_to_group(GroupName="my-group", UserName="my-user")
+    # Setup
+    frozen_time = datetime(2023, 5, 20, 10, 20, 30, tzinfo=tzlocal())
+
+    group = "my-group"
+    user = "my-user"
+    with freeze_time(frozen_time):
+        conn = boto3.client("iam", region_name="us-east-1")
+        conn.create_group(GroupName=group)
+        conn.create_user(UserName=user)
+        conn.add_user_to_group(GroupName=group, UserName=user)
+
+        # use internal api to set password, doesn't work in servermode
+        if not settings.TEST_SERVER_MODE:
+            iam_backend = get_backend("iam")[ACCOUNT_ID]["global"]
+            iam_backend.users[user].password_last_used = datetime.utcnow()
+    # Execute
+    result = conn.get_group(GroupName=group)
+
+    # Verify
+    assert len(result["Users"]) == 1
+
+    # if in servermode then we can't test for password because we can't
+    # manipulate the backend with internal an api
+    if settings.TEST_SERVER_MODE:
+        assert "CreateDate" in result["Users"][0]
+        return
+    assert result["Users"][0]["CreateDate"] == frozen_time
+    assert result["Users"][0]["PasswordLastUsed"] == frozen_time
 
 
 @mock_iam