-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve TADriver interface and implementations #1032
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
✅ All tests successful. No failed tests found.
Additional details and impacted files@@ Coverage Diff @@
## main #1032 +/- ##
==========================================
+ Coverage 97.48% 97.52% +0.03%
==========================================
Files 459 462 +3
Lines 37279 37956 +677
==========================================
+ Hits 36341 37015 +674
- Misses 938 941 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
Codecov ReportAttention: Patch coverage is ✅ All tests successful. No failed tests found.
📢 Thoughts on this report? Let us know! |
❌ 2 Tests Failed:
View the top 2 failed tests by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
❌ 2 Tests Failed:
View the top 2 failed tests by shortest run time
To view more test analytics, go to the Test Analytics Dashboard |
✅ All tests successful. No failed tests were found. 📣 Thoughts on this report? Let Codecov know! | Powered by Codecov |
Improve TADriver interface - add some more methods to the TADriver - implement a base constructor - modify the write_testruns interface - implement all methods in BQ and PG - improve BQ and PG tests - modify use of TADriver interface in processor and finishers - update django settings to include new settings - TODO: modify requirements to suitable shared version - create ta_utils to replace test_results in the future - the reason for this is that we want a slightly different implementation of the test results notifier for the new TA pipeline
6c6004b
to
4df5375
Compare
This PR includes changes to |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there are a few simplifications possible here, otherwise this looks good.
I am quite unhappy with the tests though. We are once again relying on overly mocked "whitebox" tests. the only thing they are asserting is that you call the mocks with certain parameters, and that the function returns the mocked data unmodified. I don’t think these tests provide any kind of value :-(
@@ -12,11 +12,23 @@ | |||
if "timeseries" in DATABASES: | |||
DATABASES["timeseries"]["AUTOCOMMIT"] = False | |||
|
|||
if "test_analytics" in DATABASES: | |||
DATABASES["test_analytics"]["AUTOCOMMIT"] = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should really consider changing this in general, but that is a completely different discussion.
max_backtick_count = curr_backtick_count | ||
|
||
backticks = "`" * (max_backtick_count + 1) | ||
return f"{backticks}python\n{content}\n{backticks}" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
setting the codeblock language to python
might be the wrong thing to do in general
not sure if we have any way to detect the language? maybe depending on the testsuite (which should really be named the "test runner")?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this was a product / design decision, we just wanted to have some highlighting in the failure message displayed in the comment, i can't remember if this decision was made before or after we had framework detection but either way i can bring this up to them
testruns_written = [ | ||
MessageToDict( | ||
ta_testrun_pb2.TestRun.FromString(testrun_bytes), | ||
preserving_proto_field_name=True, | ||
) | ||
for testrun_bytes in mock_bigquery_service.mock_calls[0][1][3] | ||
] | ||
assert snapshot("json") == sorted(testruns_written, key=lambda x: x["name"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you are asserting that the mocked function is being called with the arguments you put in above.
IMO the value of such tests is very low. All you are asserting here is that the serialization to protobuf is working correctly?
ScalarQueryParameter("repoid", "INT64", 1), | ||
ScalarQueryParameter("commit_sha", "STRING", "abc123"), | ||
] | ||
assert snapshot("json") == result |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
here also, the snapshot just contains the mock_bigquery_service.query.return_value
return [ | ||
flake | ||
for flake in Flake.objects.filter( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return [ | |
flake | |
for flake in Flake.objects.filter( | |
return list(Flake.objects.filter( |
query, params = mock_bigquery_service.query.call_args[0] | ||
assert snapshot("txt") == query | ||
assert params == [ | ||
ScalarQueryParameter("repoid", "INT64", 1), | ||
ScalarQueryParameter("commit_sha", "STRING", "abc123"), | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general, I doubt asserting the generated SQL here is providing any value.
You are building these queries using trivial string concatenation.
This is very different from the way I assert the generated queries in the deletion code, which are fully dynamically created by the ORM, where I have very little insight into how it works under the hood.
pg = PGDriver(repoid, db_session, flaky_test_set) | ||
if settings.BIGQUERY_WRITE_ENABLED: | ||
bq = BQDriver(repoid) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instead of duplicating this code, how about taking advantage of the new driver interface you have introduced:
drivers = [pg, bq] if BQ_ENABLED else [pg] # or any kind of combination based on feature flags
for driver in drivers:
driver.bulk_write_testruns(parsing_info)
return [ | ||
{ | ||
"branch_name": result["branch_name"], | ||
"timestamp": result["timestamp"], | ||
"outcome": result["outcome"], | ||
"test_id": result["test_id"], | ||
"flags_hash": result["flags_hash"], | ||
} | ||
for result in query_result | ||
] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
are you picking only specific fields from the result? how is this different from just returning query_result
or list(query_result)
in case its an iterator?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the reason for this was typing, since i wanted the return type to be a typed dict, but there was no guarantee on the result of query having those keys, i ended up doing this, which is no better than doing cast(query_result, list[TypedDict])
tbh, but i do want it to be a TypedDict
flakes = list(self.flake_dict.values()) | ||
|
||
flake_dict = { | ||
( | ||
bytes(flake.test_id), | ||
bytes(flake.flags_id) if flake.flags_id else None, | ||
): flake | ||
for flake in flakes | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is just flake_dict = self.flake_dict
, is it?
for flake in Flake.objects.raw( | ||
"SELECT * FROM flake WHERE repoid = %s AND (test_id, flags_id) IN %s AND end_date IS NULL AND count != recent_passes_count + fail_count", | ||
[self.repo_id, test_ids], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is there a particular reason to use a raw query here?
IMO, using the query builder would be simpler, as the if test_ids
would just be simply adding another filter
to the base query set, which seems to be the same in both branches?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we use the query builder here:
from django.db.models import Q
query = Q()
for id, author in filter_data:
query |= Q(id=id, author=author)
books = Book.objects.filter(query)
would be the way to do it, which i wasn't a fan of, because the sql would be a bunch of (id, author) = (value, value) OR
concatenated together, so i just went with the raw sql to express it exactly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh, so the problem here is that a IN
query with a tuple is not expressible with the django query builder?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ohhh, so the problem here is that a IN query with a tuple is not expressible with the django query builder?
yes, the only way to get the behaviour we want through the query builder is by having a where expression with a bunch of equality checks joined by OR
@Swatinem i think you're right. my idea was to ship this quickly and start validating in prod on our own repos and have minimal automated testing because the option of "find a way to connect to BQ in CI" sounded like it would take too long and the option of "use a third party emulator" sounded like a bad idea. the goal of the tests right now is not to validate that anything works but that changes that devs make in the future don't break the existing behaviour. I thought this might have some value but i think you're right that we want tests that validate that things work and that these tests are basically just here to make sure that devs review their changes to this code through the snapshots. what i can do right now is use https://github.com/goccy/bigquery-emulator but i still don't think it's a good idea to use a third party emulator in the long term and that it we would be best if we find a way to connect to some dev deployment of BQ in the CI at some point in the future |
similar to what I proposed a while back to test real GCS access, we can use secrets in CI to run this with a proper test project in GCP, and just skip these tests when the credentials are not available either locally or for forks in CI (though maybe still fail in CI on missing credentials anyway) I think that would be a very reasonable thing to do |
depends on: codecov/shared#484