1# Copyright 2021 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""ChangeSummary tests.""" 16 17__author__ = "partheniou@google.com (Anthonios Partheniou)" 18 19import pathlib 20import shutil 21import unittest 22 23import pandas as pd 24 25from changesummary import ChangeSummary 26from changesummary import ChangeType 27from changesummary import DirectoryDoesNotExist 28 29SCRIPTS_DIR = pathlib.Path(__file__).parent.resolve() 30NEW_ARTIFACTS_DIR = SCRIPTS_DIR / "test_resources" / "new_artifacts_dir" 31CURRENT_ARTIFACTS_DIR = SCRIPTS_DIR / "test_resources" / "current_artifacts_dir" 32TEMP_DIR = SCRIPTS_DIR / "test_resources" / "temp" 33 34 35class TestChangeSummary(unittest.TestCase): 36 def setUp(self): 37 # Clear temporary directory 38 shutil.rmtree(TEMP_DIR, ignore_errors=True) 39 # Create temporary directory 40 pathlib.Path(TEMP_DIR).mkdir() 41 42 self.cs = ChangeSummary(NEW_ARTIFACTS_DIR, CURRENT_ARTIFACTS_DIR, TEMP_DIR, []) 43 44 def test_raises_on_directory_not_found_new_artifacts_dir(self): 45 with self.assertRaises(DirectoryDoesNotExist): 46 ChangeSummary( 47 "invalid_artifact_dir", CURRENT_ARTIFACTS_DIR, TEMP_DIR, [] 48 ).detect_discovery_changes() 49 50 def test_raises_on_directory_not_found_current_artifacts_dir(self): 51 with self.assertRaises(DirectoryDoesNotExist): 52 ChangeSummary( 53 NEW_ARTIFACTS_DIR, "invalid_artifact_dir", TEMP_DIR, [] 54 ).detect_discovery_changes() 55 56 def test_raises_on_directory_not_found_temp_dir(self): 57 # Remove temporary directory 58 shutil.rmtree(TEMP_DIR, ignore_errors=True) 59 60 with self.assertRaises(DirectoryDoesNotExist): 61 ChangeSummary( 62 NEW_ARTIFACTS_DIR, CURRENT_ARTIFACTS_DIR, "invalid_temp_dir", [] 63 ).detect_discovery_changes() 64 65 # Create temporary directory 66 pathlib.Path(TEMP_DIR).mkdir() 67 68 ChangeSummary( 69 NEW_ARTIFACTS_DIR, CURRENT_ARTIFACTS_DIR, TEMP_DIR, [] 70 ).detect_discovery_changes() 71 72 def test_raises_on_directory_not_found(self): 73 with self.assertRaises(DirectoryDoesNotExist): 74 self.cs._raise_if_directory_not_found(directory="invalid_dir") 75 76 def test_load_json_to_dataframe_returns_empty_df_if_file_path_invalid(self): 77 df = self.cs._load_json_to_dataframe(file_path="invalid_path") 78 self.assertTrue(df.empty) 79 80 def test_load_json_to_dataframe_returns_expected_data(self): 81 doc_path = NEW_ARTIFACTS_DIR / "drive.v3.json" 82 df = self.cs._load_json_to_dataframe(file_path=doc_path) 83 self.assertEqual(df["name"].iloc[0], "drive") 84 self.assertEqual(df["version"].iloc[0], "v3") 85 86 def test_get_discovery_differences_for_new_doc_returns_expected_dataframe(self): 87 df = self.cs._get_discovery_differences("drive.v3.json") 88 # Assume that `drive.v3.json` is a new discovery artifact that doesn't 89 # exist in `CURRENT_ARTIFACTS_DIR`. 90 self.assertEqual(df["Name"].iloc[0], "drive") 91 self.assertEqual(df["Version"].iloc[0], "v3") 92 93 # All rows in the dataframe should have `True` in the `Added` column 94 # and `False` in the `Deleted` column. 95 # pd.Dataframe().all() will return `True` if all elements are `True`. 96 self.assertTrue(df["Added"].all()) 97 self.assertTrue((~df["Deleted"]).all()) 98 99 # There should be 4 unique key differences 100 self.assertEqual(len(df), 4) 101 102 # Expected Result for key 'schemas.FileList' 103 # Key Added Deleted Name Version ChangeType Count 104 # schemas.FileList True False drive v3 2 8 105 self.assertTrue(df[df["Key"] == "schemas.FileList"].Added.iloc[0]) 106 self.assertFalse(df[df["Key"] == "schemas.FileList"].Deleted.iloc[0]) 107 self.assertEqual( 108 df[df["Key"] == "schemas.FileList"].ChangeType.iloc[0], ChangeType.ADDED 109 ) 110 self.assertEqual(df[df["Key"] == "schemas.FileList"].Count.iloc[0], 8) 111 112 def test_get_discovery_differences_for_deleted_doc_returns_expected_dataframe(self): 113 df = self.cs._get_discovery_differences("cloudtasks.v2.json") 114 # Assuming that `cloudtasks.v2.json` is a discovery artifact that doesn't 115 # exist in `NEW_ARTIFACTS_DIR`. 116 self.assertEqual(df["Name"].iloc[0], "cloudtasks") 117 self.assertEqual(df["Version"].iloc[0], "v2") 118 119 # All rows in the dataframe should have `False` in the `Added` column 120 # and `True` in the `Deleted` column. 121 # pd.Dataframe().all() will return `True` if all elements are `True`. 122 self.assertTrue((~df["Added"]).all()) 123 self.assertTrue(df["Deleted"].all()) 124 125 # There should be 6 unique key differences 126 self.assertEqual(len(df), 6) 127 128 # Expected Result for key 'schemas.Task' 129 # Key Added Deleted Name Version ChangeType Count 130 # schemas.Task False True cloudtasks v2 1 18 131 self.assertFalse(df[df["Key"] == "schemas.Task"].Added.iloc[0]) 132 self.assertTrue(df[df["Key"] == "schemas.Task"].Deleted.iloc[0]) 133 self.assertEqual( 134 df[df["Key"] == "schemas.Task"].ChangeType.iloc[0], ChangeType.DELETED 135 ) 136 self.assertEqual(df[df["Key"] == "schemas.Task"].Count.iloc[0], 18) 137 138 def test_get_discovery_differences_for_changed_doc_returns_expected_dataframe(self): 139 # Assuming that `bigquery.v2.json` is a discovery artifact has 140 # changed. There will be a mix of keys being added, changed or deleted. 141 df = self.cs._get_discovery_differences("bigquery.v2.json") 142 143 self.assertEqual(df["Name"].iloc[0], "bigquery") 144 self.assertEqual(df["Version"].iloc[0], "v2") 145 146 # There should be 28 unique key differences 147 # 1 unique keys changed, 1 unique keys added, 2 unique keys deleted 148 self.assertEqual(len(df), 4) 149 self.assertEqual(len(df[df["ChangeType"] == ChangeType.CHANGED]), 1) 150 self.assertEqual(len(df[df["ChangeType"] == ChangeType.ADDED]), 1) 151 self.assertEqual(len(df[df["ChangeType"] == ChangeType.DELETED]), 2) 152 153 # Expected Result for key 'schemas.PrincipalComponentInfo' 154 # Key Added Deleted Name Version ChangeType Count 155 # schemas.PrincipalComponentInfo False True bigquery v2 1 10 156 key = "schemas.PrincipalComponentInfo" 157 self.assertFalse(df[df["Key"] == key].Added.iloc[0]) 158 self.assertTrue(df[df["Key"] == key].Deleted.iloc[0]) 159 self.assertEqual(df[df["Key"] == key].ChangeType.iloc[0], ChangeType.DELETED) 160 self.assertEqual(df[df["Key"] == key].Count.iloc[0], 10) 161 162 def test_build_summary_message_returns_expected_result(self): 163 msg = self.cs._build_summary_message(api_name="bigquery", is_feature=True) 164 self.assertEqual(msg, "feat(bigquery): update the api") 165 msg = self.cs._build_summary_message(api_name="bigquery", is_feature=False) 166 self.assertEqual(msg, "fix(bigquery): update the api") 167 168 def test_get_stable_versions(self): 169 # These versions should be considered stable 170 s = pd.Series(["v1", "v1.4", "v1.4.5"]) 171 self.assertTrue(self.cs._get_stable_versions(s).all().iloc[0]) 172 173 # These versions should not be considered stable 174 s = pd.Series(["v1b1", "v1alpha", "v1beta1"]) 175 self.assertTrue((~self.cs._get_stable_versions(s)).all().iloc[0]) 176 177 def test_detect_discovery_changes(self): 178 files_changed = ["bigquery.v2.json", "cloudtasks.v2.json", "drive.v3.json"] 179 cs = ChangeSummary( 180 NEW_ARTIFACTS_DIR, CURRENT_ARTIFACTS_DIR, TEMP_DIR, files_changed 181 ) 182 cs.detect_discovery_changes() 183 result = pd.read_csv(TEMP_DIR / "allapis.dataframe") 184 185 # bigquery was added 186 # 4 key changes in total. 187 # 1 unique keys changed, 1 unique keys added, 2 unique keys deleted 188 self.assertEqual(len(result[result["Name"] == "bigquery"]), 4) 189 self.assertEqual( 190 len(result[(result["Name"] == "bigquery") & result["Added"]]), 1 191 ) 192 193 # Confirm that key "schemas.ProjectReference.newkey" exists for bigquery 194 self.assertEqual( 195 result[ 196 (result["Name"] == "bigquery") & (result["Added"]) & (result["Count"] == 1) 197 ]["Key"].iloc[0], 198 "schemas.ProjectReference.newkey", 199 ) 200 201 self.assertEqual( 202 len(result[(result["Name"] == "bigquery") & result["Deleted"]]), 2 203 ) 204 self.assertTrue(result[result["Name"] == "bigquery"].IsStable.all()) 205 self.assertTrue(result[result["Name"] == "bigquery"].IsFeatureAggregate.all()) 206 self.assertEqual( 207 result[result["Name"] == "bigquery"].Summary.iloc[0], 208 "feat(bigquery): update the api", 209 ) 210 211 # cloudtasks was deleted 212 # 6 key changes in total. All 6 key changes should be deletions. 213 self.assertEqual(len(result[result["Name"] == "cloudtasks"]), 6) 214 self.assertEqual( 215 len(result[(result["Name"] == "cloudtasks") & result["Added"]]), 0 216 ) 217 self.assertEqual( 218 len(result[(result["Name"] == "cloudtasks") & result["Deleted"]]), 6 219 ) 220 self.assertTrue(result[(result["Name"] == "cloudtasks")].IsStable.all()) 221 self.assertTrue( 222 result[(result["Name"] == "cloudtasks")].IsFeatureAggregate.all() 223 ) 224 self.assertEqual( 225 result[(result["Name"] == "cloudtasks")].Summary.iloc[0], 226 "feat(cloudtasks): update the api", 227 ) 228 229 # drive was updated 230 # 4 key changes in total. All 4 key changes should be additions 231 self.assertEqual(len(result[result["Name"] == "drive"]), 4) 232 self.assertEqual(len(result[(result["Name"] == "drive") & result["Added"]]), 4) 233 self.assertEqual( 234 len(result[(result["Name"] == "drive") & result["Deleted"]]), 0 235 ) 236 self.assertTrue(result[(result["Name"] == "drive")].IsStable.all()) 237 self.assertTrue(result[(result["Name"] == "drive")].IsFeatureAggregate.all()) 238 self.assertEqual( 239 result[(result["Name"] == "drive")].Summary.iloc[0], 240 "feat(drive): update the api", 241 ) 242 243 244if __name__ == "__main__": 245 unittest.main() 246