Repository: rucio/rucio

Issue:
`list_content` call never raises DataIdentifierNotFound
### Description

Due to the change to the new SQLAlchemy2 syntax with scalars, this query will never raise the exception.DataIdentifierNotFound, but instead just return an empty scalar.

https://github.com/rucio/rucio/blame/6eac5dcc30cb6dac427cbc5070d46c170ae7fedf/lib/rucio/core/did.py#L2035

### Steps to reproduce

```
-bash-4.2$ rucio list-dids mc23_13p6TeV.601188.PhPy8EG_AZNLOqsdqsdqsd
+--------------+--------------+
| SCOPE:NAME | [DID TYPE] |
|--------------+--------------|
+--------------+--------------+
-bash-4.2$ rucio list-content mc23_13p6TeV.601188.PhPy8EG_AZNLOqsdqsdqsd
+--------------+--------------+
| SCOPE:NAME | [DID TYPE] |
|--------------+--------------|
+--------------+--------------+
```

### Rucio Version

master

### Additional Information

Best would probably be to join with did table and check if the did exists in the first place. Another option would be, if no result is found, to check for a dataidentifier then.


Discussion:

Resolving patch:
diff --git a/lib/rucio/core/did.py b/lib/rucio/core/did.py
--- a/lib/rucio/core/did.py
+++ b/lib/rucio/core/did.py
@@ -1655,20 +1655,22 @@ def list_content(scope, name, *, session: "Session"):
     :param name: The data identifier name.
     :param session: The database session in use.
     """
-    try:
-        stmt = select(
-            models.DataIdentifierAssociation
-        ).with_hint(
-            models.DataIdentifierAssociation, "INDEX(CONTENTS CONTENTS_PK)", 'oracle'
-        ).filter_by(
-            scope=scope,
-            name=name
-        )
-        for tmp_did in session.execute(stmt).yield_per(5).scalars():
-            yield {'scope': tmp_did.child_scope, 'name': tmp_did.child_name, 'type': tmp_did.child_type,
-                   'bytes': tmp_did.bytes, 'adler32': tmp_did.adler32, 'md5': tmp_did.md5}
-    except NoResultFound:
-        raise exception.DataIdentifierNotFound(f"Data identifier '{scope}:{name}' not found")
+    stmt = select(
+        models.DataIdentifierAssociation
+    ).with_hint(
+        models.DataIdentifierAssociation, "INDEX(CONTENTS CONTENTS_PK)", 'oracle'
+    ).filter_by(
+        scope=scope,
+        name=name
+    )
+    children_found = False
+    for tmp_did in session.execute(stmt).yield_per(5).scalars():
+        children_found = True
+        yield {'scope': tmp_did.child_scope, 'name': tmp_did.child_name, 'type': tmp_did.child_type,
+               'bytes': tmp_did.bytes, 'adler32': tmp_did.adler32, 'md5': tmp_did.md5}
+    if not children_found:
+        # Raise exception if the did doesn't exist
+        __get_did(scope=scope, name=name, session=session)
 
 
 @stream_session


Test patch:
diff --git a/tests/test_did.py b/tests/test_did.py
--- a/tests/test_did.py
+++ b/tests/test_did.py
@@ -876,6 +876,9 @@ def test_list_content(self, did_client, rse_factory):
         files1 = [{'scope': scope, 'name': did_name_generator('file'), 'bytes': 1, 'adler32': '0cc737eb'} for i in range(nbfiles)]
         files2 = [{'scope': scope, 'name': did_name_generator('file'), 'bytes': 1, 'adler32': '0cc737eb'} for i in range(nbfiles)]
 
+        with pytest.raises(DataIdentifierNotFound):
+            did_client.list_content(scope, dataset1)
+
         did_client.add_dataset(scope, dataset1)
 
         with pytest.raises(DataIdentifierAlreadyExists):
diff --git a/tests/test_reaper.py b/tests/test_reaper.py
--- a/tests/test_reaper.py
+++ b/tests/test_reaper.py
@@ -359,7 +359,8 @@ def __get_archive_contents_history_count(archive, *, session=None):
     assert len(list(did_core.list_content(**dataset1))) == 2
     with pytest.raises(DataIdentifierNotFound):
         did_core.get_did(**dataset2)
-    assert len(list(did_core.list_content(**dataset2))) == 0
+    with pytest.raises(DataIdentifierNotFound):
+        list(did_core.list_content(**dataset2))
     assert len(list(did_core.list_archive_content(**archive2))) == 0
     assert __get_archive_contents_history_count(archive1) == 4
     assert __get_archive_contents_history_count(archive2) == 3