changeset 398:9528e2a03d3c

adapt DICOMweb tests following fix of issue #196 (STOW-RS: Should return 200 only when successfully stored all instances)
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 05 May 2021 18:59:08 +0200
parents eb87ec525b53
children dd519677974d
files Database/Issue196.dcm Database/Issue196.dump Database/Issue196.dump~ Plugins/DicomWeb/DicomWeb.py Plugins/DicomWeb/Run.py Tests/Toolbox.py
diffstat 6 files changed, 75 insertions(+), 37 deletions(-) [+]
line wrap: on
line diff
Binary file Database/Issue196.dcm has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Database/Issue196.dump	Wed May 05 18:59:08 2021 +0200
@@ -0,0 +1,4 @@
+# dump2dcm --write-xfer-little Issue196.dump Issue196.dcm
+
+(0008,0018) UI [1.2.840.113619.2.176.2025.1499492.7040.1171286242.109] #  54, 1 SOPInstanceUID
+(0008,0016) UI =MRImageStorage                          #  26, 1 SOPClassUID
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Database/Issue196.dump~	Wed May 05 18:59:08 2021 +0200
@@ -0,0 +1,5 @@
+# dump2dcm +Ug --write-xfer-little Issue111.dump Issue111.dcm
+
+(0010,0020) LO A              # Patient ID
+(0010,0010) PN HELLO^A        # Patient Name
+(0008,0050) SH (no value available)  #  AccessionNumber
--- a/Plugins/DicomWeb/DicomWeb.py	Mon Apr 26 15:22:59 2021 +0200
+++ b/Plugins/DicomWeb/DicomWeb.py	Wed May 05 18:59:08 2021 +0200
@@ -39,7 +39,7 @@
         body += bytearray('\r\n', 'ascii')
 
 
-def SendStow(orthanc, uri, dicom):
+def SendStowRaw(orthanc, uri, dicom):
     # We do not use Python's "email" package, as it uses LF (\n) for line
     # endings instead of CRLF (\r\n) for binary messages, as required by
     # RFC 1341
@@ -65,7 +65,17 @@
         'Accept' : 'application/json',
     }
 
-    return DoPost(orthanc, uri, body, headers = headers)
+    (response, content) = DoPostRaw(orthanc, uri, body, headers = headers)
+
+    return (response.status, DecodeJson(content))
+
+
+def SendStow(orthanc, uri, dicom):
+    (status, content) = SendStowRaw(orthanc, uri, dicom)
+    if not (status in [ 200 ]):
+        raise Exception('Bad status: %d' % status)
+    else:
+        return content
 
 
 def DoGetMultipart(orthanc, uri, headers = {}, returnHeaders = False):
--- a/Plugins/DicomWeb/Run.py	Mon Apr 26 15:22:59 2021 +0200
+++ b/Plugins/DicomWeb/Run.py	Wed May 05 18:59:08 2021 +0200
@@ -649,41 +649,60 @@
 
         
     def test_stow_errors(self):
+        def CheckSequences(a):
+            self.assertEqual(3, len(a))
+            self.assertTrue('00080005' in a)
+            self.assertTrue('00081198' in a)
+            self.assertTrue('00081199' in a)
+            self.assertEqual('CS', a['00080005']['vr'])
+            self.assertEqual('SQ', a['00081198']['vr'])
+            self.assertEqual('SQ', a['00081199']['vr'])
+        
         # Pushing an instance to a study that is not its parent
-        a = SendStow(ORTHANC, args.dicomweb + '/studies/nope', GetDatabasePath('Phenix/IM-0001-0001.dcm'))
-        self.assertEqual(3, len(a))
-        self.assertTrue('00080005' in a)
-        self.assertEqual('CS', a['00080005']['vr'])
-        self.assertTrue('00081198' in a)
-        self.assertEqual('SQ', a['00081198']['vr'])
-        self.assertEqual(1, len(['00081198']))
-        self.assertTrue('00081199' in a)
-        self.assertEqual('SQ', a['00081199']['vr'])
-        self.assertEqual(1, len(['00081199']))
+        (status, a) = SendStowRaw(ORTHANC, args.dicomweb + '/studies/nope', GetDatabasePath('Phenix/IM-0001-0001.dcm'))
+        self.assertEqual(409, status)
+        CheckSequences(a)
+
+        self.assertFalse('Value' in a['00081199'])  # No success instance
+        
+        self.assertEqual(1, len(a['00081198']['Value']))  # One failed instance
+        self.assertEqual('1.2.840.10008.5.1.4.1.1.2',
+                         a['00081198']['Value'][0]['00081150']['Value'][0])
+        self.assertEqual('1.2.840.113704.7.1.1.6632.1127829031.2',
+                         a['00081198']['Value'][0]['00081155']['Value'][0])
+        self.assertEqual(0x0110,  # Processing failure
+                         a['00081198']['Value'][0]['00081197']['Value'][0])
 
         # Pushing an instance with missing tags
-        a = SendStow(ORTHANC, args.dicomweb + '/studies', GetDatabasePath('Issue111.dcm'))
-        self.assertEqual(3, len(a))
-        self.assertTrue('00080005' in a)
-        self.assertEqual('CS', a['00080005']['vr'])
-        self.assertTrue('00081198' in a)
-        self.assertEqual('SQ', a['00081198']['vr'])
-        self.assertEqual(1, len(['00081198']))
-        self.assertTrue('00081199' in a)
-        self.assertEqual('SQ', a['00081199']['vr'])
-        self.assertEqual(1, len(['00081199']))
+        (status, a) = SendStowRaw(ORTHANC, args.dicomweb + '/studies', GetDatabasePath('Issue111.dcm'))
+        self.assertEqual(400, status)
+        CheckSequences(a)
+
+        self.assertFalse('Value' in a['00081198'])  # No failed instance, as tags are missing
+        self.assertFalse('Value' in a['00081199'])  # No success instance
 
         # Pushing a file that is not in the DICOM format
-        a = SendStow(ORTHANC, args.dicomweb + '/studies', GetDatabasePath('Issue111.dump'))
-        self.assertEqual(3, len(a))
-        self.assertTrue('00080005' in a)
-        self.assertEqual('CS', a['00080005']['vr'])
-        self.assertTrue('00081198' in a)
-        self.assertEqual('SQ', a['00081198']['vr'])
-        self.assertEqual(1, len(['00081198']))
-        self.assertTrue('00081199' in a)
-        self.assertEqual('SQ', a['00081199']['vr'])
-        self.assertEqual(1, len(['00081199']))
+        (status, a) = SendStowRaw(ORTHANC, args.dicomweb + '/studies', GetDatabasePath('Issue111.dump'))
+        self.assertEqual(400, status)
+        CheckSequences(a)
+
+        self.assertFalse('Value' in a['00081198'])  # No failed instance, as non-DICOM
+        self.assertFalse('Value' in a['00081199'])  # No success instance
+
+        # Pushing a DICOM instance with only SOP class and instance UID
+        (status, a) = SendStowRaw(ORTHANC, args.dicomweb + '/studies', GetDatabasePath('Issue196.dcm'))
+        self.assertEqual(400, status)
+        CheckSequences(a)
+
+        self.assertFalse('Value' in a['00081199'])  # No success instance
+
+        self.assertEqual(1, len(a['00081198']['Value']))  # One failed instance
+        self.assertEqual('1.2.840.10008.5.1.4.1.1.4',
+                         a['00081198']['Value'][0]['00081150']['Value'][0])
+        self.assertEqual('1.2.840.113619.2.176.2025.1499492.7040.1171286242.109',
+                         a['00081198']['Value'][0]['00081155']['Value'][0])
+        self.assertEqual(0xC000,  # Error: Cannot understand (cannot understand certain Data Elements)
+                         a['00081198']['Value'][0]['00081197']['Value'][0])
 
 
     def test_allowed_methods(self):
--- a/Tests/Toolbox.py	Mon Apr 26 15:22:59 2021 +0200
+++ b/Tests/Toolbox.py	Wed May 05 18:59:08 2021 +0200
@@ -56,7 +56,7 @@
         from StringIO import StringIO
 
     
-def _DecodeJson(s):
+def DecodeJson(s):
     t = s
 
     if (sys.version_info >= (3, 0)):
@@ -121,7 +121,7 @@
     if not (resp.status in [ 200 ]):
         raise Exception(resp.status, resp)
     else:
-        return _DecodeJson(content)
+        return DecodeJson(content)
 
 def _DoPutOrPost(orthanc, uri, method, data, contentType, headers):
     http = httplib2.Http()
@@ -156,7 +156,7 @@
     if not (resp.status in [ 200 ]):
         raise Exception(resp.status, resp)
     else:
-        return _DecodeJson(content)
+        return DecodeJson(content)
 
 def DoPutRaw(orthanc, uri, data = {}, contentType = '', headers = {}):
     return _DoPutOrPost(orthanc, uri, 'PUT', data, contentType, headers)
@@ -166,7 +166,7 @@
     if not (resp.status in [ 200, 201, 302 ]):
         raise Exception(resp.status, resp)
     else:
-        return _DecodeJson(content)
+        return DecodeJson(content)
 
 def DoPostRaw(orthanc, uri, data = {}, contentType = '', headers = {}):
     return _DoPutOrPost(orthanc, uri, 'POST', data, contentType, headers)
@@ -176,7 +176,7 @@
     if not (resp.status in [ 200, 201, 302 ]):
         raise Exception(resp.status, resp)
     else:
-        return _DecodeJson(content)
+        return DecodeJson(content)
 
 def GetDatabasePath(filename):
     return os.path.join(os.path.dirname(__file__), '..', 'Database', filename)