Repository: getmoto/moto

Issue:
iot.describe_thing_group() thingGroupMetadata incomplete
On the current implementation of moto, IoT->describe_thing_group() returns **thingGroupMetadata**  with a single field: **creationDate**.
According to the [docs](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/iot.html#IoT.Client.describe_thing_group), **thingGroupMetadata** must be:
```
'thingGroupMetadata': {
        'parentGroupName': 'string',
        'rootToParentThingGroups': [
            {
                'groupName': 'string',
                'groupArn': 'string'
            },
        ],
        'creationDate': datetime(2015, 1, 1)
    }
```

I forked the library and added the missing fields. I'm going to submit the pull request.

Also i fixed a typo in **creationDate** (it was misspelled as creationDat**a**) 

BTW, the latest version of BotoCore is failing every test because of some regex problem (`'^/confirmdestination/(?P<confirmationToken+>[^/]*)$'`). Its a bug with BotoCore.



Discussion:

Resolving patch:
diff --git a/moto/iot/models.py b/moto/iot/models.py
--- a/moto/iot/models.py
+++ b/moto/iot/models.py
@@ -55,7 +55,7 @@ def __init__(self, thing_type_name, thing_type_properties, region_name):
         self.thing_type_properties = thing_type_properties
         self.thing_type_id = str(uuid.uuid4())  # I don't know the rule of id
         t = time.time()
-        self.metadata = {"deprecated": False, "creationData": int(t * 1000) / 1000.0}
+        self.metadata = {"deprecated": False, "creationDate": int(t * 1000) / 1000.0}
         self.arn = "arn:aws:iot:%s:1:thingtype/%s" % (self.region_name, thing_type_name)
 
     def to_dict(self):
@@ -69,7 +69,12 @@ def to_dict(self):
 
 class FakeThingGroup(BaseModel):
     def __init__(
-        self, thing_group_name, parent_group_name, thing_group_properties, region_name
+        self,
+        thing_group_name,
+        parent_group_name,
+        thing_group_properties,
+        region_name,
+        thing_groups,
     ):
         self.region_name = region_name
         self.thing_group_name = thing_group_name
@@ -78,7 +83,32 @@ def __init__(
         self.parent_group_name = parent_group_name
         self.thing_group_properties = thing_group_properties or {}
         t = time.time()
-        self.metadata = {"creationData": int(t * 1000) / 1000.0}
+        self.metadata = {"creationDate": int(t * 1000) / 1000.0}
+        if parent_group_name:
+            self.metadata["parentGroupName"] = parent_group_name
+            # initilize rootToParentThingGroups
+            if "rootToParentThingGroups" not in self.metadata:
+                self.metadata["rootToParentThingGroups"] = []
+            # search for parent arn
+            for thing_group_arn, thing_group in thing_groups.items():
+                if thing_group.thing_group_name == parent_group_name:
+                    parent_thing_group_structure = thing_group
+                    break
+            # if parent arn found (should always be found)
+            if parent_thing_group_structure:
+                # copy parent's rootToParentThingGroups
+                if "rootToParentThingGroups" in parent_thing_group_structure.metadata:
+                    self.metadata["rootToParentThingGroups"].extend(
+                        parent_thing_group_structure.metadata["rootToParentThingGroups"]
+                    )
+                self.metadata["rootToParentThingGroups"].extend(
+                    [
+                        {
+                            "groupName": parent_group_name,
+                            "groupArn": parent_thing_group_structure.arn,
+                        }
+                    ]
+                )
         self.arn = "arn:aws:iot:%s:1:thinggroup/%s" % (
             self.region_name,
             thing_group_name,
@@ -639,6 +669,7 @@ def create_thing_group(
             parent_group_name,
             thing_group_properties,
             self.region_name,
+            self.thing_groups,
         )
         self.thing_groups[thing_group.arn] = thing_group
         return thing_group.thing_group_name, thing_group.arn, thing_group.thing_group_id


Test patch:
diff --git a/tests/test_iot/test_iot.py b/tests/test_iot/test_iot.py
--- a/tests/test_iot/test_iot.py
+++ b/tests/test_iot/test_iot.py
@@ -581,6 +581,278 @@ def test_delete_principal_thing():
     client.delete_certificate(certificateId=cert_id)
 
 
+@mock_iot
+def test_describe_thing_group_metadata_hierarchy():
+    client = boto3.client("iot", region_name="ap-northeast-1")
+    group_name_1a = "my-group-name-1a"
+    group_name_1b = "my-group-name-1b"
+    group_name_2a = "my-group-name-2a"
+    group_name_2b = "my-group-name-2b"
+    group_name_3a = "my-group-name-3a"
+    group_name_3b = "my-group-name-3b"
+    group_name_3c = "my-group-name-3c"
+    group_name_3d = "my-group-name-3d"
+
+    # --1a
+    #    |--2a
+    #    |   |--3a
+    #    |   |--3b
+    #    |
+    #    |--2b
+    #        |--3c
+    #        |--3d
+    # --1b
+
+    # create thing groups tree
+    # 1
+    thing_group1a = client.create_thing_group(thingGroupName=group_name_1a)
+    thing_group1a.should.have.key("thingGroupName").which.should.equal(group_name_1a)
+    thing_group1a.should.have.key("thingGroupArn")
+    thing_group1b = client.create_thing_group(thingGroupName=group_name_1b)
+    thing_group1b.should.have.key("thingGroupName").which.should.equal(group_name_1b)
+    thing_group1b.should.have.key("thingGroupArn")
+    # 2
+    thing_group2a = client.create_thing_group(
+        thingGroupName=group_name_2a, parentGroupName=group_name_1a
+    )
+    thing_group2a.should.have.key("thingGroupName").which.should.equal(group_name_2a)
+    thing_group2a.should.have.key("thingGroupArn")
+    thing_group2b = client.create_thing_group(
+        thingGroupName=group_name_2b, parentGroupName=group_name_1a
+    )
+    thing_group2b.should.have.key("thingGroupName").which.should.equal(group_name_2b)
+    thing_group2b.should.have.key("thingGroupArn")
+    # 3
+    thing_group3a = client.create_thing_group(
+        thingGroupName=group_name_3a, parentGroupName=group_name_2a
+    )
+    thing_group3a.should.have.key("thingGroupName").which.should.equal(group_name_3a)
+    thing_group3a.should.have.key("thingGroupArn")
+    thing_group3b = client.create_thing_group(
+        thingGroupName=group_name_3b, parentGroupName=group_name_2a
+    )
+    thing_group3b.should.have.key("thingGroupName").which.should.equal(group_name_3b)
+    thing_group3b.should.have.key("thingGroupArn")
+    thing_group3c = client.create_thing_group(
+        thingGroupName=group_name_3c, parentGroupName=group_name_2b
+    )
+    thing_group3c.should.have.key("thingGroupName").which.should.equal(group_name_3c)
+    thing_group3c.should.have.key("thingGroupArn")
+    thing_group3d = client.create_thing_group(
+        thingGroupName=group_name_3d, parentGroupName=group_name_2b
+    )
+    thing_group3d.should.have.key("thingGroupName").which.should.equal(group_name_3d)
+    thing_group3d.should.have.key("thingGroupArn")
+
+    # describe groups
+    # groups level 1
+    # 1a
+    thing_group_description1a = client.describe_thing_group(
+        thingGroupName=group_name_1a
+    )
+    thing_group_description1a.should.have.key("thingGroupName").which.should.equal(
+        group_name_1a
+    )
+    thing_group_description1a.should.have.key("thingGroupProperties")
+    thing_group_description1a.should.have.key("thingGroupMetadata")
+    thing_group_description1a["thingGroupMetadata"].should.have.key("creationDate")
+    thing_group_description1a.should.have.key("version")
+    # 1b
+    thing_group_description1b = client.describe_thing_group(
+        thingGroupName=group_name_1b
+    )
+    thing_group_description1b.should.have.key("thingGroupName").which.should.equal(
+        group_name_1b
+    )
+    thing_group_description1b.should.have.key("thingGroupProperties")
+    thing_group_description1b.should.have.key("thingGroupMetadata")
+    thing_group_description1b["thingGroupMetadata"].should.have.length_of(1)
+    thing_group_description1b["thingGroupMetadata"].should.have.key("creationDate")
+    thing_group_description1b.should.have.key("version")
+    # groups level 2
+    # 2a
+    thing_group_description2a = client.describe_thing_group(
+        thingGroupName=group_name_2a
+    )
+    thing_group_description2a.should.have.key("thingGroupName").which.should.equal(
+        group_name_2a
+    )
+    thing_group_description2a.should.have.key("thingGroupProperties")
+    thing_group_description2a.should.have.key("thingGroupMetadata")
+    thing_group_description2a["thingGroupMetadata"].should.have.length_of(3)
+    thing_group_description2a["thingGroupMetadata"].should.have.key(
+        "parentGroupName"
+    ).being.equal(group_name_1a)
+    thing_group_description2a["thingGroupMetadata"].should.have.key(
+        "rootToParentThingGroups"
+    )
+    thing_group_description2a["thingGroupMetadata"][
+        "rootToParentThingGroups"
+    ].should.have.length_of(1)
+    thing_group_description2a["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupName"
+    ].should.match(group_name_1a)
+    thing_group_description2a["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupArn"
+    ].should.match(thing_group1a["thingGroupArn"])
+    thing_group_description2a.should.have.key("version")
+    # 2b
+    thing_group_description2b = client.describe_thing_group(
+        thingGroupName=group_name_2b
+    )
+    thing_group_description2b.should.have.key("thingGroupName").which.should.equal(
+        group_name_2b
+    )
+    thing_group_description2b.should.have.key("thingGroupProperties")
+    thing_group_description2b.should.have.key("thingGroupMetadata")
+    thing_group_description2b["thingGroupMetadata"].should.have.length_of(3)
+    thing_group_description2b["thingGroupMetadata"].should.have.key(
+        "parentGroupName"
+    ).being.equal(group_name_1a)
+    thing_group_description2b["thingGroupMetadata"].should.have.key(
+        "rootToParentThingGroups"
+    )
+    thing_group_description2b["thingGroupMetadata"][
+        "rootToParentThingGroups"
+    ].should.have.length_of(1)
+    thing_group_description2b["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupName"
+    ].should.match(group_name_1a)
+    thing_group_description2b["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupArn"
+    ].should.match(thing_group1a["thingGroupArn"])
+    thing_group_description2b.should.have.key("version")
+    # groups level 3
+    # 3a
+    thing_group_description3a = client.describe_thing_group(
+        thingGroupName=group_name_3a
+    )
+    thing_group_description3a.should.have.key("thingGroupName").which.should.equal(
+        group_name_3a
+    )
+    thing_group_description3a.should.have.key("thingGroupProperties")
+    thing_group_description3a.should.have.key("thingGroupMetadata")
+    thing_group_description3a["thingGroupMetadata"].should.have.length_of(3)
+    thing_group_description3a["thingGroupMetadata"].should.have.key(
+        "parentGroupName"
+    ).being.equal(group_name_2a)
+    thing_group_description3a["thingGroupMetadata"].should.have.key(
+        "rootToParentThingGroups"
+    )
+    thing_group_description3a["thingGroupMetadata"][
+        "rootToParentThingGroups"
+    ].should.have.length_of(2)
+    thing_group_description3a["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupName"
+    ].should.match(group_name_1a)
+    thing_group_description3a["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupArn"
+    ].should.match(thing_group1a["thingGroupArn"])
+    thing_group_description3a["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupName"
+    ].should.match(group_name_2a)
+    thing_group_description3a["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupArn"
+    ].should.match(thing_group2a["thingGroupArn"])
+    thing_group_description3a.should.have.key("version")
+    # 3b
+    thing_group_description3b = client.describe_thing_group(
+        thingGroupName=group_name_3b
+    )
+    thing_group_description3b.should.have.key("thingGroupName").which.should.equal(
+        group_name_3b
+    )
+    thing_group_description3b.should.have.key("thingGroupProperties")
+    thing_group_description3b.should.have.key("thingGroupMetadata")
+    thing_group_description3b["thingGroupMetadata"].should.have.length_of(3)
+    thing_group_description3b["thingGroupMetadata"].should.have.key(
+        "parentGroupName"
+    ).being.equal(group_name_2a)
+    thing_group_description3b["thingGroupMetadata"].should.have.key(
+        "rootToParentThingGroups"
+    )
+    thing_group_description3b["thingGroupMetadata"][
+        "rootToParentThingGroups"
+    ].should.have.length_of(2)
+    thing_group_description3b["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupName"
+    ].should.match(group_name_1a)
+    thing_group_description3b["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupArn"
+    ].should.match(thing_group1a["thingGroupArn"])
+    thing_group_description3b["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupName"
+    ].should.match(group_name_2a)
+    thing_group_description3b["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupArn"
+    ].should.match(thing_group2a["thingGroupArn"])
+    thing_group_description3b.should.have.key("version")
+    # 3c
+    thing_group_description3c = client.describe_thing_group(
+        thingGroupName=group_name_3c
+    )
+    thing_group_description3c.should.have.key("thingGroupName").which.should.equal(
+        group_name_3c
+    )
+    thing_group_description3c.should.have.key("thingGroupProperties")
+    thing_group_description3c.should.have.key("thingGroupMetadata")
+    thing_group_description3c["thingGroupMetadata"].should.have.length_of(3)
+    thing_group_description3c["thingGroupMetadata"].should.have.key(
+        "parentGroupName"
+    ).being.equal(group_name_2b)
+    thing_group_description3c["thingGroupMetadata"].should.have.key(
+        "rootToParentThingGroups"
+    )
+    thing_group_description3c["thingGroupMetadata"][
+        "rootToParentThingGroups"
+    ].should.have.length_of(2)
+    thing_group_description3c["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupName"
+    ].should.match(group_name_1a)
+    thing_group_description3c["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupArn"
+    ].should.match(thing_group1a["thingGroupArn"])
+    thing_group_description3c["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupName"
+    ].should.match(group_name_2b)
+    thing_group_description3c["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupArn"
+    ].should.match(thing_group2b["thingGroupArn"])
+    thing_group_description3c.should.have.key("version")
+    # 3d
+    thing_group_description3d = client.describe_thing_group(
+        thingGroupName=group_name_3d
+    )
+    thing_group_description3d.should.have.key("thingGroupName").which.should.equal(
+        group_name_3d
+    )
+    thing_group_description3d.should.have.key("thingGroupProperties")
+    thing_group_description3d.should.have.key("thingGroupMetadata")
+    thing_group_description3d["thingGroupMetadata"].should.have.length_of(3)
+    thing_group_description3d["thingGroupMetadata"].should.have.key(
+        "parentGroupName"
+    ).being.equal(group_name_2b)
+    thing_group_description3d["thingGroupMetadata"].should.have.key(
+        "rootToParentThingGroups"
+    )
+    thing_group_description3d["thingGroupMetadata"][
+        "rootToParentThingGroups"
+    ].should.have.length_of(2)
+    thing_group_description3d["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupName"
+    ].should.match(group_name_1a)
+    thing_group_description3d["thingGroupMetadata"]["rootToParentThingGroups"][0][
+        "groupArn"
+    ].should.match(thing_group1a["thingGroupArn"])
+    thing_group_description3d["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupName"
+    ].should.match(group_name_2b)
+    thing_group_description3d["thingGroupMetadata"]["rootToParentThingGroups"][1][
+        "groupArn"
+    ].should.match(thing_group2b["thingGroupArn"])
+    thing_group_description3d.should.have.key("version")
+
+
 @mock_iot
 def test_thing_groups():
     client = boto3.client("iot", region_name="ap-northeast-1")