|
53 | 53 | MessagingServiceType, |
54 | 54 | RequestReceiptBodyParams, |
55 | 55 | RequestReviewDenyBodyParams, |
56 | | - SubjectIdentityVerificationBodyParams, |
57 | 56 | ) |
58 | 57 | from fides.api.schemas.policy import ActionType, CurrentStep, PolicyResponse |
59 | | -from fides.api.schemas.privacy_request import PrivacyRequestSource, PrivacyRequestStatus |
| 58 | +from fides.api.schemas.privacy_request import ( |
| 59 | + IdentityValue, |
| 60 | + PrivacyRequestResponse, |
| 61 | + PrivacyRequestSource, |
| 62 | + PrivacyRequestStatus, |
| 63 | +) |
60 | 64 | from fides.api.schemas.redis_cache import ( |
61 | 65 | CustomPrivacyRequestField, |
62 | 66 | Identity, |
@@ -1210,6 +1214,195 @@ def test_get_privacy_requests_with_identity( |
1210 | 1214 | assert resp["items"][0]["id"] == succeeded_privacy_request.id |
1211 | 1215 | assert resp["items"][0].get("identity") is None |
1212 | 1216 |
|
| 1217 | + @pytest.mark.parametrize( |
| 1218 | + "custom_identities,expected_identity_values", |
| 1219 | + [ |
| 1220 | + # Test case 1: List of integers |
| 1221 | + ( |
| 1222 | + { |
| 1223 | + "regi_id": LabeledIdentity(label="Regi ID", value=[12345678]), |
| 1224 | + }, |
| 1225 | + { |
| 1226 | + "regi_id": {"label": "Regi ID", "value": [12345678]}, |
| 1227 | + }, |
| 1228 | + ), |
| 1229 | + # Test case 2: List of strings |
| 1230 | + ( |
| 1231 | + { |
| 1232 | + "agent_id": LabeledIdentity( |
| 1233 | + label="Agent ID", value=["one", "two", "three"] |
| 1234 | + ), |
| 1235 | + }, |
| 1236 | + { |
| 1237 | + "agent_id": {"label": "Agent ID", "value": ["one", "two", "three"]}, |
| 1238 | + }, |
| 1239 | + ), |
| 1240 | + # Test case 3: Mixed list |
| 1241 | + ( |
| 1242 | + { |
| 1243 | + "user_id": LabeledIdentity( |
| 1244 | + label="User ID", value=[12345678, "one", "two", "three"] |
| 1245 | + ), |
| 1246 | + }, |
| 1247 | + { |
| 1248 | + "user_id": { |
| 1249 | + "label": "User ID", |
| 1250 | + "value": [12345678, "one", "two", "three"], |
| 1251 | + }, |
| 1252 | + }, |
| 1253 | + ), |
| 1254 | + # Test case 4: All three cases together |
| 1255 | + ( |
| 1256 | + { |
| 1257 | + "regi_id": LabeledIdentity(label="Regi ID", value=[12345678]), |
| 1258 | + "agent_id": LabeledIdentity( |
| 1259 | + label="Agent ID", value=["one", "two", "three"] |
| 1260 | + ), |
| 1261 | + "user_id": LabeledIdentity( |
| 1262 | + label="User ID", value=[12345678, "one", "two", "three"] |
| 1263 | + ), |
| 1264 | + }, |
| 1265 | + { |
| 1266 | + "regi_id": {"label": "Regi ID", "value": [12345678]}, |
| 1267 | + "agent_id": {"label": "Agent ID", "value": ["one", "two", "three"]}, |
| 1268 | + "user_id": { |
| 1269 | + "label": "User ID", |
| 1270 | + "value": [12345678, "one", "two", "three"], |
| 1271 | + }, |
| 1272 | + }, |
| 1273 | + ), |
| 1274 | + # Test case 5: Single integer in list |
| 1275 | + ( |
| 1276 | + { |
| 1277 | + "customer_id": LabeledIdentity(label="Customer ID", value=[999]), |
| 1278 | + }, |
| 1279 | + { |
| 1280 | + "customer_id": {"label": "Customer ID", "value": [999]}, |
| 1281 | + }, |
| 1282 | + ), |
| 1283 | + # Test case 6: Empty list |
| 1284 | + ( |
| 1285 | + { |
| 1286 | + "empty_list": LabeledIdentity(label="Empty List", value=[]), |
| 1287 | + }, |
| 1288 | + { |
| 1289 | + "empty_list": {"label": "Empty List", "value": []}, |
| 1290 | + }, |
| 1291 | + ), |
| 1292 | + # Test case 7: String value (not a list) |
| 1293 | + ( |
| 1294 | + { |
| 1295 | + "customer_name": LabeledIdentity( |
| 1296 | + label="Customer Name", value="John Doe" |
| 1297 | + ), |
| 1298 | + }, |
| 1299 | + { |
| 1300 | + "customer_name": {"label": "Customer Name", "value": "John Doe"}, |
| 1301 | + }, |
| 1302 | + ), |
| 1303 | + # Test case 8: Integer value (not a list) |
| 1304 | + ( |
| 1305 | + { |
| 1306 | + "account_number": LabeledIdentity( |
| 1307 | + label="Account Number", value=98765 |
| 1308 | + ), |
| 1309 | + }, |
| 1310 | + { |
| 1311 | + "account_number": {"label": "Account Number", "value": 98765}, |
| 1312 | + }, |
| 1313 | + ), |
| 1314 | + # Test case 9: Mixed types - string, int, and list |
| 1315 | + ( |
| 1316 | + { |
| 1317 | + "name": LabeledIdentity(label="Name", value="Jane Smith"), |
| 1318 | + "id": LabeledIdentity(label="ID", value=456789), |
| 1319 | + "tags": LabeledIdentity(label="Tags", value=["tag1", "tag2"]), |
| 1320 | + }, |
| 1321 | + { |
| 1322 | + "name": {"label": "Name", "value": "Jane Smith"}, |
| 1323 | + "id": {"label": "ID", "value": 456789}, |
| 1324 | + "tags": {"label": "Tags", "value": ["tag1", "tag2"]}, |
| 1325 | + }, |
| 1326 | + ), |
| 1327 | + ], |
| 1328 | + ) |
| 1329 | + def test_get_privacy_requests_with_custom_identities( |
| 1330 | + self, |
| 1331 | + api_client: TestClient, |
| 1332 | + url, |
| 1333 | + generate_auth_header, |
| 1334 | + db, |
| 1335 | + policy, |
| 1336 | + custom_identities, |
| 1337 | + expected_identity_values, |
| 1338 | + ): |
| 1339 | + """Test that privacy requests with custom identities containing various value types |
| 1340 | + can be retrieved and validated correctly. |
| 1341 | +
|
| 1342 | + This test would have caught the validation error where IdentityValue.value |
| 1343 | + was too restrictive and couldn't accept list values like [12345678] or |
| 1344 | + ['one', 'two', 'three']. |
| 1345 | +
|
| 1346 | + The test is parametrized to cover: |
| 1347 | + - List values |
| 1348 | + - String values |
| 1349 | + - Integer values |
| 1350 | + - Mixed types (string, int, list) |
| 1351 | +
|
| 1352 | + Note: LabeledIdentity only accepts MultiValue types (int, str, or list of int/str) |
| 1353 | + """ |
| 1354 | + # Create a privacy request |
| 1355 | + privacy_request = PrivacyRequest.create( |
| 1356 | + db=db, |
| 1357 | + data={ |
| 1358 | + "status": PrivacyRequestStatus.pending, |
| 1359 | + "policy_id": policy.id, |
| 1360 | + "client_id": policy.client_id, |
| 1361 | + }, |
| 1362 | + ) |
| 1363 | + |
| 1364 | + # Persist identity with custom fields that have various value types |
| 1365 | + identity_dict = { "email": "[email protected]", **custom_identities} |
| 1366 | + privacy_request.persist_identity(db=db, identity=Identity(**identity_dict)) |
| 1367 | + |
| 1368 | + auth_header = generate_auth_header(scopes=[PRIVACY_REQUEST_READ]) |
| 1369 | + response = api_client.get( |
| 1370 | + url + f"?include_identities=true", headers=auth_header |
| 1371 | + ) |
| 1372 | + assert response.status_code == 200 |
| 1373 | + resp = response.json() |
| 1374 | + |
| 1375 | + # Verify the response validates correctly |
| 1376 | + assert len(resp["items"]) == 1 |
| 1377 | + assert resp["items"][0]["id"] == privacy_request.id |
| 1378 | + |
| 1379 | + # Verify the identity field is present and contains the list values |
| 1380 | + identity = resp["items"][0]["identity"] |
| 1381 | + assert identity is not None |
| 1382 | + |
| 1383 | + # Verify standard identity field |
| 1384 | + assert identity["email"] == { |
| 1385 | + "label": "Email", |
| 1386 | + |
| 1387 | + } |
| 1388 | + |
| 1389 | + # Verify custom identity fields with various value types |
| 1390 | + for field_name, expected_value in expected_identity_values.items(): |
| 1391 | + assert identity[field_name] == expected_value |
| 1392 | + |
| 1393 | + validated_response = PrivacyRequestResponse(**resp["items"][0]) |
| 1394 | + assert validated_response.identity is not None |
| 1395 | + |
| 1396 | + # Verify each custom identity field can be accessed and has the correct value |
| 1397 | + for field_name, expected_value in expected_identity_values.items(): |
| 1398 | + assert field_name in validated_response.identity |
| 1399 | + identity_value = validated_response.identity[field_name] |
| 1400 | + assert isinstance(identity_value, IdentityValue) |
| 1401 | + assert identity_value.value == expected_value["value"] |
| 1402 | + assert identity_value.label == expected_value["label"] |
| 1403 | + |
| 1404 | + privacy_request.delete(db) |
| 1405 | + |
1213 | 1406 | def test_get_privacy_requests_with_custom_fields( |
1214 | 1407 | self, |
1215 | 1408 | api_client: TestClient, |
|
0 commit comments