Merge "Test for yang file archive limitation"
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/utils/MultipartFileUtil.java b/cps-rest/src/main/java/org/onap/cps/rest/utils/MultipartFileUtil.java
index 534077c..3e01f6e 100644
--- a/cps-rest/src/main/java/org/onap/cps/rest/utils/MultipartFileUtil.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/utils/MultipartFileUtil.java
@@ -2,6 +2,7 @@
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Pantheon.tech
  *  Modifications Copyright (C) 2021 Bell Canada.
+ *  Modifications Copyright (C) 2023 Nordix Foundation.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -127,16 +128,17 @@
     }
 
     private static String extractYangResourceContent(final ZipInputStream zipInputStream,
-        final ZipFileSizeValidator zipFileSizeValidator) throws IOException {
+                                                     final ZipFileSizeValidator zipFileSizeValidator)
+        throws IOException {
         try (final var byteArrayOutputStream = new ByteArrayOutputStream()) {
             var totalSizeEntry = 0;
             int numberOfBytesRead;
             final var buffer = new byte[READ_BUFFER_SIZE];
-            zipFileSizeValidator.incrementTotalEntryInArchive();
+            zipFileSizeValidator.incrementTotalYangFileEntryCountInArchive();
             while ((numberOfBytesRead = zipInputStream.read(buffer, 0, READ_BUFFER_SIZE)) > 0) {
                 byteArrayOutputStream.write(buffer, 0, numberOfBytesRead);
                 totalSizeEntry += numberOfBytesRead;
-                zipFileSizeValidator.updateTotalSizeArchive(numberOfBytesRead);
+                zipFileSizeValidator.updateTotalUncompressedSizeOfYangFilesInArchive(numberOfBytesRead);
                 zipFileSizeValidator.validateCompresssionRatio(totalSizeEntry);
             }
             return byteArrayOutputStream.toString(StandardCharsets.UTF_8);
diff --git a/cps-rest/src/main/java/org/onap/cps/rest/utils/ZipFileSizeValidator.java b/cps-rest/src/main/java/org/onap/cps/rest/utils/ZipFileSizeValidator.java
index d148fb7..2e303d1 100644
--- a/cps-rest/src/main/java/org/onap/cps/rest/utils/ZipFileSizeValidator.java
+++ b/cps-rest/src/main/java/org/onap/cps/rest/utils/ZipFileSizeValidator.java
@@ -1,6 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Bell Canada.
+ *  Modifications Copyright (C) 2023 Nordix Foundation.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -28,31 +29,33 @@
 public class ZipFileSizeValidator {
 
     private static final int THRESHOLD_ENTRIES = 10000;
-    private static final int THRESHOLD_SIZE = 100000000;
+    private static int THRESHOLD_SIZE = 100000000;
     private static final double THRESHOLD_RATIO = 40;
     private static final String INVALID_ZIP = "Invalid ZIP archive content.";
 
-    private int totalSizeArchive = 0;
-    private int totalEntryInArchive = 0;
+    private int totalUncompressedSizeOfYangFilesInArchive = 0;
+    private int totalYangFileEntriesInArchive = 0;
     private long compressedSize = 0;
 
     /**
      * Increment the totalEntryInArchive by 1.
      */
-    public void incrementTotalEntryInArchive() {
-        totalEntryInArchive++;
+    public void incrementTotalYangFileEntryCountInArchive() {
+        totalYangFileEntriesInArchive++;
     }
 
     /**
      * Update the totalSizeArchive by numberOfBytesRead.
+     *
      * @param numberOfBytesRead the number of bytes of each entry
      */
-    public void updateTotalSizeArchive(final int numberOfBytesRead) {
-        totalSizeArchive += numberOfBytesRead;
+    public void updateTotalUncompressedSizeOfYangFilesInArchive(final int numberOfBytesRead) {
+        totalUncompressedSizeOfYangFilesInArchive += numberOfBytesRead;
     }
 
     /**
      * Validate the total Compression size of the zip.
+     *
      * @param totalEntrySize the size of the unzipped entry.
      */
     public void validateCompresssionRatio(final int totalEntrySize) {
@@ -68,13 +71,14 @@
      * Validate the total Size and number of entries in the zip.
      */
     public void validateSizeAndEntries() {
-        if (totalSizeArchive > THRESHOLD_SIZE) {
+        if (totalUncompressedSizeOfYangFilesInArchive > THRESHOLD_SIZE) {
             throw new ModelValidationException(INVALID_ZIP,
-                String.format("The uncompressed data size exceeds the CPS limit %s bytes.", THRESHOLD_SIZE));
+                String.format("The total size of uncompressed yang files exceeds the CPS limit of %s bytes.",
+                    THRESHOLD_SIZE));
         }
-        if (totalEntryInArchive > THRESHOLD_ENTRIES) {
+        if (totalYangFileEntriesInArchive > THRESHOLD_ENTRIES) {
             throw new ModelValidationException(INVALID_ZIP,
-                String.format("The number of entries in the archive exceeds the CPS limit %s.",
+                String.format("The number of yang file entries in the archive exceeds the CPS limit %s.",
                     THRESHOLD_ENTRIES));
         }
     }
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy
index 3f4729e..67ee50e 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/utils/MultipartFileUtilSpec.groovy
@@ -1,6 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2020 Pantheon.tech
+ *  Modifications Copyright (C) 2023 Nordix Foundation.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -51,7 +52,7 @@
     def 'Extract yang resources from zip archive.'() {
         given: 'uploaded zip archive containing 2 yang files and 1 not yang (json) file'
             def multipartFile = new MockMultipartFile("file", "TEST.ZIP", "application/zip",
-                    getClass().getResource("/yang-files-set.zip").getBytes())
+                getClass().getResource("/yang-files-set.zip").getBytes())
         when: 'resources are extracted from zip file'
             def result = MultipartFileUtil.extractYangResourcesMap(multipartFile)
         then: 'information from yang files is extracted, not yang file (json) is ignored'
@@ -60,6 +61,32 @@
             assert result["component.yang"] == "fake component content 1\n"
     }
 
+    def 'Yang file limits in zip archive: #scenario for the bug reported in CPS-1477'() {
+        given: 'a yang file size (uncompressed) limit of #threshold bytes'
+            ZipFileSizeValidator.THRESHOLD_SIZE = threshold
+        and: 'an archive with a yang file of 1083 bytes'
+            def multipartFile = multipartZipFileFromResource('/yang-files-set-total-1083-bytes.zip')
+        when: 'attempt to extract yang files'
+            def thrownException = null
+            try {
+                MultipartFileUtil.extractYangResourcesMap(multipartFile)
+            } catch (Exception e) {
+                thrownException  = e
+            }
+        then: 'ModelValidationException indicating size limit is only thrown when threshold exceeded'
+            if (thresholdExceeded) {
+                assert thrownException instanceof ModelValidationException
+                assert thrownException.details.contains('limit of ' + threshold + ' bytes')
+            } else {
+                assert thrownException == null
+            }
+        where:
+            scenario          | threshold || thresholdExceeded
+            'exceed limit'    | 1082      || true
+            'equals to limit' | 1083      || false
+            'within limit'    | 1084      || false
+    }
+
     def 'Extract resources from zip archive having #caseDescriptor.'() {
         when: 'attempt to extract resources from zip file is performed'
             MultipartFileUtil.extractYangResourcesMap(multipartFile)
@@ -91,7 +118,7 @@
 
     def multipartZipFileFromResource(resourcePath) {
         return new MockMultipartFile("file", "TEST.ZIP", "application/zip",
-                getClass().getResource(resourcePath).getBytes())
+            getClass().getResource(resourcePath).getBytes())
     }
 
     def multipartFileForIOException(extension) {
diff --git a/cps-rest/src/test/groovy/org/onap/cps/rest/utils/ZipFileSizeValidatorSpec.groovy b/cps-rest/src/test/groovy/org/onap/cps/rest/utils/ZipFileSizeValidatorSpec.groovy
index 16fbf98..60ecb2e 100644
--- a/cps-rest/src/test/groovy/org/onap/cps/rest/utils/ZipFileSizeValidatorSpec.groovy
+++ b/cps-rest/src/test/groovy/org/onap/cps/rest/utils/ZipFileSizeValidatorSpec.groovy
@@ -1,6 +1,7 @@
 /*
  *  ============LICENSE_START=======================================================
  *  Copyright (C) 2021 Bell Canada.
+ *  Modifications Copyright (C) 2023 Nordix Foundation.
  *  ================================================================================
  *  Licensed under the Apache License, Version 2.0 (the "License");
  *  you may not use this file except in compliance with the License.
@@ -32,25 +33,25 @@
     def compressedFileSize = 100
 
     def setup() {
-        objectUnderTest.setTotalEntryInArchive(0)
-        objectUnderTest.setTotalSizeArchive(0)
+        objectUnderTest.setTotalYangFileEntriesInArchive(0)
+        objectUnderTest.setTotalUncompressedSizeOfYangFilesInArchive(0)
         objectUnderTest.setCompressedSize(compressedFileSize)
     }
 
-    def 'Increment the total entries in Archive.'() {
-        when: 'the totalEntriesInArchive value is incremented'
-            objectUnderTest.incrementTotalEntryInArchive()
-        then: 'the totalEntriesInArchive is incremented by 1'
-            assert objectUnderTest.totalEntryInArchive == old(objectUnderTest.totalEntryInArchive) + 1
+    def 'Increment the total yang file entry count in Archive.'() {
+        when: 'the totalYangFileEntryInArchive value is incremented'
+            objectUnderTest.incrementTotalYangFileEntryCountInArchive()
+        then: 'the totalYangFileEntryInArchive is incremented by 1'
+            assert objectUnderTest.totalYangFileEntriesInArchive == old(objectUnderTest.totalYangFileEntriesInArchive) + 1
     }
 
-    def 'Update the total size of Archive.'() {
+    def 'Update the total uncompressed size of yang files in Archive.'() {
         given: 'the size of an entry of archive'
             def entrySize = 100
-        when: 'the totalSizeArchive is to be updated with the latest entry Size'
-            objectUnderTest.updateTotalSizeArchive(entrySize)
-        then: 'the totalSizeArchive is updated as expected'
-            assert objectUnderTest.totalSizeArchive == old(objectUnderTest.totalSizeArchive) + entrySize
+        when: 'the totalUncompressedSizeOfYangFilesInArchive is to be updated with the latest entry Size'
+            objectUnderTest.updateTotalUncompressedSizeOfYangFilesInArchive(entrySize)
+        then: 'the totalUncompressedSizeOfYangFilesInArchive is updated as expected'
+            assert objectUnderTest.totalUncompressedSizeOfYangFilesInArchive == old(objectUnderTest.totalUncompressedSizeOfYangFilesInArchive) + entrySize
     }
 
     def 'Validate the zip archive for compression ratio less that threshold compression ratio.'() {
@@ -73,29 +74,29 @@
 
     def 'Validate the zip archive for thresholdSize and thresholdEntries #caseDescriptor.'() {
         given:
-            objectUnderTest.setTotalEntryInArchive(totalEntriesInArchive)
-            objectUnderTest.setTotalSizeArchive(totalSizeArchive)
+            objectUnderTest.setTotalYangFileEntriesInArchive(totalYangEntriesInArchive)
+            objectUnderTest.setTotalUncompressedSizeOfYangFilesInArchive(totalUncompressedSizeofYangArchive)
         when: 'the validation is performed against the threshold size and threshold Entries count'
             objectUnderTest.validateSizeAndEntries()
         then: 'validation passes and no exception is thrown'
             noExceptionThrown()
         where: 'following cases are tested'
-            caseDescriptor              | totalSizeArchive  | totalEntriesInArchive
-            'less than threshold value' | thresholdSize - 1 | thresholdEntries - 1
-            'at threshold value'        | thresholdSize     | thresholdEntries
+            caseDescriptor              | totalUncompressedSizeofYangArchive | totalYangEntriesInArchive
+            'less than threshold value' | thresholdSize - 1                  | thresholdEntries - 1
+            'at threshold value'        | thresholdSize                      | thresholdEntries
     }
 
     def 'Validate the zip archive for thresholdSize and thresholdEntries with #caseDescriptor.'() {
         given:
-            objectUnderTest.setTotalEntryInArchive(totalEntriesInArchive)
-            objectUnderTest.setTotalSizeArchive(totalSizeArchive)
+            objectUnderTest.setTotalYangFileEntriesInArchive(totalYangEntriesInArchive)
+            objectUnderTest.setTotalUncompressedSizeOfYangFilesInArchive(totalUncompressedSizeofYangArchive)
         when: 'the validation is performed against the threshold size and threshold Entries count'
             objectUnderTest.validateSizeAndEntries()
         then: 'validation fails and exception is thrown'
             thrown ModelValidationException
         where: 'following cases are tested'
-            caseDescriptor                                  | totalSizeArchive  | totalEntriesInArchive
-            'totalEntriesInArchive exceeds threshold value' | thresholdSize     | thresholdEntries + 1
-            'totalSizeArchive exceeds threshold value'      | thresholdSize + 1 | thresholdEntries
+            caseDescriptor                                  | totalUncompressedSizeofYangArchive | totalYangEntriesInArchive
+            'totalEntriesInArchive exceeds threshold value' | thresholdSize                      | thresholdEntries + 1
+            'totalSizeArchive exceeds threshold value'      | thresholdSize + 1                  | thresholdEntries
     }
 }
diff --git a/cps-rest/src/test/resources/yang-files-set-total-1083-bytes.zip b/cps-rest/src/test/resources/yang-files-set-total-1083-bytes.zip
new file mode 100644
index 0000000..9908055
--- /dev/null
+++ b/cps-rest/src/test/resources/yang-files-set-total-1083-bytes.zip
Binary files differ