tests: fix unit test failure when auto_populated_fields is set#16944
tests: fix unit test failure when auto_populated_fields is set#16944
Conversation
There was a problem hiding this comment.
Code Review
This pull request implements auto-population for the request_id field in CreateJob, CancelJob, and DeleteJob methods, as specified in the service configuration and verified in unit tests. Feedback indicates that the auto-population logic should be moved from the sample code into the generated RPC methods to fully comply with AIP-4235. Furthermore, the unit tests should be updated to use re.fullmatch for more robust UUID validation and to use empty strings instead of None when resetting proto3 string fields.
| if not request.request_id: | ||
| request.request_id = str(uuid.uuid4()) |
There was a problem hiding this comment.
According to AIP-4235, auto-population of fields should be handled by the client library's RPC methods. If the generator is updated to support auto_populated_fields, this logic should ideally be placed within the generated RPC methods rather than the sample code. This keeps the samples clean and ensures the feature is available to all users of the library, not just those following the samples.
References
- According to AIP-4235, auto-population of fields should be handled by the client library's RPC methods. (link)
| assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", args[0].request_id) | ||
| # clear UUID field so that the check below succeeds | ||
| args[0].request_id = None |
There was a problem hiding this comment.
There are two improvements that can be made here for better robustness and adherence to proto3 standards:
- Use
re.fullmatchinstead ofre.matchto ensure the entirerequest_idstring conforms to the UUID v4 format, preventing false positives from longer strings. - Set the field to an empty string
""instead ofNone. In proto3, string fields default to"". Whileproto-plusmight handleNoneby converting it to the default, it is not valid for standardgoogle.protobufmessages and can lead toTypeErroror unexpected behavior in equality checks.
| assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", args[0].request_id) | |
| # clear UUID field so that the check below succeeds | |
| args[0].request_id = None | |
| assert re.fullmatch(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", args[0].request_id) | |
| # clear UUID field so that the check below succeeds | |
| args[0].request_id = "" |
References
- In proto3, string fields default to an empty string (""). Using None is not valid for standard google.protobuf messages. (link)
|
The error in https://github.com/googleapis/google-cloud-python/actions/runs/25388575659/job/74456652962?pr=16944 matches what we have in b/508597813 |
| if not request.request_id: | ||
| request.request_id = str(uuid.uuid4()) | ||
|
|
There was a problem hiding this comment.
nit: can this be introduced as a helper once in the client which can simply be called in each methods?
There was a problem hiding this comment.
This file does not touch this template at all. IOW, this is an existing issue at main. I'll open a separate PR to address it.
There was a problem hiding this comment.
This will be addressed in a separate PR #17004
| call.assert_called() | ||
| _, args, _ = call.mock_calls[0] | ||
| # Ensure that the uuid4 field is set according to AIP 4235 | ||
| assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", args[0].request_id) |
There was a problem hiding this comment.
nit: can the regex be defined once and re-used everywhere?
There was a problem hiding this comment.
This is already defined as a macro. You only need to change it in one place
| if isinstance(request, dict): | ||
| request['request_id'] = "explicit value for autopopulate-able field" | ||
| else: | ||
| request.request_id = "explicit value for autopopulate-able field" |
There was a problem hiding this comment.
nit: Can this be included as part of the parametrize above?
There was a problem hiding this comment.
This PR doesn't touch this code. This is an existing issue at main. I'll open a separate PR to address it.
| found_field = None | ||
| for i, (key, value) in enumerate(req.call_args.kwargs['params']): | ||
| if key == "requestId": | ||
| assert re.match(r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}", value) | ||
| found_field = i | ||
| break | ||
| if found_field is not None: | ||
| del req.call_args.kwargs['params'][found_field] |
There was a problem hiding this comment.
I don't know how I feel about deleting a field in a mock test.
A couple of options:
- We include
request_idwithinexpected_paramsand have the value set tomock.ANYsince we're already asserting what the value should look like against the regex later on. - We can filter the params we want to compare against the expected_params.
Fixes b/508597813