Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 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 | |
| 19 | import pathlib |
| 20 | import shutil |
| 21 | import unittest |
| 22 | |
| 23 | import pandas as pd |
| 24 | |
| 25 | from changesummary import ChangeSummary |
| 26 | from changesummary import ChangeType |
| 27 | from changesummary import DirectoryDoesNotExist |
| 28 | |
| 29 | SCRIPTS_DIR = pathlib.Path(__file__).parent.resolve() |
| 30 | NEW_ARTIFACTS_DIR = SCRIPTS_DIR / "test_resources" / "new_artifacts_dir" |
| 31 | CURRENT_ARTIFACTS_DIR = SCRIPTS_DIR / "test_resources" / "current_artifacts_dir" |
| 32 | TEMP_DIR = SCRIPTS_DIR / "test_resources" / "temp" |
| 33 | |
| 34 | |
| 35 | class 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 | |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 99 | # There should be 4 unique key differences |
| 100 | self.assertEqual(len(df), 4) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 101 | |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 102 | # Expected Result for key 'schemas.FileList' |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 103 | # Key Added Deleted Name Version ChangeType Count |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 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]) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 107 | self.assertEqual( |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 108 | df[df["Key"] == "schemas.FileList"].ChangeType.iloc[0], ChangeType.ADDED |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 109 | ) |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 110 | self.assertEqual(df[df["Key"] == "schemas.FileList"].Count.iloc[0], 8) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 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 | |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 125 | # There should be 6 unique key differences |
| 126 | self.assertEqual(len(df), 6) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 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 |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 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) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 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() |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 183 | result = pd.read_csv(TEMP_DIR / "allapis.dataframe") |
| 184 | |
| 185 | # bigquery was added |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 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) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 189 | self.assertEqual( |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 190 | len(result[(result["Name"] == "bigquery") & result["Added"]]), 1 |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 191 | ) |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 192 | |
| 193 | # Confirm that key "schemas.ProjectReference.newkey" exists for bigquery |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 194 | self.assertEqual( |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 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 |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 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 |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 212 | # 6 key changes in total. All 6 key changes should be deletions. |
| 213 | self.assertEqual(len(result[result["Name"] == "cloudtasks"]), 6) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 214 | self.assertEqual( |
| 215 | len(result[(result["Name"] == "cloudtasks") & result["Added"]]), 0 |
| 216 | ) |
| 217 | self.assertEqual( |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 218 | len(result[(result["Name"] == "cloudtasks") & result["Deleted"]]), 6 |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 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 |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 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) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 233 | self.assertEqual( |
| 234 | len(result[(result["Name"] == "drive") & result["Deleted"]]), 0 |
| 235 | ) |
| 236 | self.assertTrue(result[(result["Name"] == "drive")].IsStable.all()) |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 237 | self.assertTrue(result[(result["Name"] == "drive")].IsFeatureAggregate.all()) |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 238 | self.assertEqual( |
| 239 | result[(result["Name"] == "drive")].Summary.iloc[0], |
| 240 | "feat(drive): update the api", |
| 241 | ) |
| 242 | |
Anthonios Partheniou | 6a88422 | 2021-05-10 22:14:57 -0400 | [diff] [blame] | 243 | |
Anthonios Partheniou | aff037a | 2021-04-21 11:00:09 -0400 | [diff] [blame] | 244 | if __name__ == "__main__": |
| 245 | unittest.main() |