|
1 | 1 | import datetime |
2 | 2 | import random |
3 | 3 |
|
| 4 | +import grpc |
4 | 5 | import pytest |
5 | 6 | from django.conf import settings |
6 | 7 |
|
|
24 | 25 | LEARNING_RESOURCE_TYPES, |
25 | 26 | PROGRAM_TYPE, |
26 | 27 | ) |
| 28 | +from learning_resources_search.exceptions import RetryError |
27 | 29 | from main.utils import now_in_utc |
28 | 30 | from vector_search.tasks import ( |
29 | 31 | embed_learning_resources_by_id, |
30 | 32 | embed_new_content_files, |
31 | 33 | embed_new_learning_resources, |
32 | 34 | embed_run_content_files, |
33 | 35 | embeddings_healthcheck, |
| 36 | + generate_embeddings, |
| 37 | + remove_embeddings, |
34 | 38 | remove_run_content_files, |
35 | 39 | remove_unpublished_run_content_files, |
36 | 40 | start_embed_resources, |
|
40 | 44 | pytestmark = pytest.mark.django_db |
41 | 45 |
|
42 | 46 |
|
| 47 | +def _rpc_error(code): |
| 48 | + """Build a grpc.RpcError carrying a status code, like qdrant's gRPC failures.""" |
| 49 | + err = grpc.RpcError() |
| 50 | + err.code = lambda: code |
| 51 | + return err |
| 52 | + |
| 53 | + |
43 | 54 | @pytest.mark.parametrize("index", list(LEARNING_RESOURCE_TYPES)) |
44 | 55 | def test_start_embed_resources(mocker, mocked_celery, index): |
45 | 56 | """ |
@@ -780,3 +791,63 @@ def test_embeddings_healthcheck_missing_summaries(mocker): |
780 | 791 | mock_sentry.mock_calls[0].args[0] |
781 | 792 | == "Warning: 1 missing content file summaries detected" |
782 | 793 | ) |
| 794 | + |
| 795 | + |
| 796 | +def test_generate_embeddings_raises_retryerror_on_grpc_deadline(mocker): |
| 797 | + """A DEADLINE_EXCEEDED gRPC error becomes a RetryError (autoretry_for picks it up).""" |
| 798 | + mocker.patch( |
| 799 | + "vector_search.tasks.embed_learning_resources", |
| 800 | + side_effect=_rpc_error(grpc.StatusCode.DEADLINE_EXCEEDED), |
| 801 | + ) |
| 802 | + with pytest.raises(RetryError): |
| 803 | + generate_embeddings([1], COURSE_TYPE, overwrite=False) |
| 804 | + |
| 805 | + |
| 806 | +def test_generate_embeddings_reraises_other_grpc_errors(mocker): |
| 807 | + """Non-transient gRPC errors propagate (task fails) rather than retrying.""" |
| 808 | + mocker.patch( |
| 809 | + "vector_search.tasks.embed_learning_resources", |
| 810 | + side_effect=_rpc_error(grpc.StatusCode.INVALID_ARGUMENT), |
| 811 | + ) |
| 812 | + with pytest.raises(grpc.RpcError): |
| 813 | + generate_embeddings([1], COURSE_TYPE, overwrite=False) |
| 814 | + |
| 815 | + |
| 816 | +def test_generate_embeddings_does_not_swallow_errors(mocker): |
| 817 | + """Unhandled errors propagate so the task fails instead of reporting success.""" |
| 818 | + mocker.patch( |
| 819 | + "vector_search.tasks.embed_learning_resources", |
| 820 | + side_effect=ValueError("boom"), |
| 821 | + ) |
| 822 | + with pytest.raises(ValueError, match="boom"): |
| 823 | + generate_embeddings([1], COURSE_TYPE, overwrite=False) |
| 824 | + |
| 825 | + |
| 826 | +def test_remove_embeddings_raises_retryerror_on_grpc_deadline(mocker): |
| 827 | + """remove_embeddings retries on DEADLINE_EXCEEDED rather than swallowing it.""" |
| 828 | + mocker.patch( |
| 829 | + "vector_search.tasks.remove_qdrant_records", |
| 830 | + side_effect=_rpc_error(grpc.StatusCode.DEADLINE_EXCEEDED), |
| 831 | + ) |
| 832 | + with pytest.raises(RetryError): |
| 833 | + remove_embeddings([1], COURSE_TYPE) |
| 834 | + |
| 835 | + |
| 836 | +def test_remove_embeddings_reraises_other_grpc_errors(mocker): |
| 837 | + """Non-transient gRPC errors propagate (task fails) rather than retrying.""" |
| 838 | + mocker.patch( |
| 839 | + "vector_search.tasks.remove_qdrant_records", |
| 840 | + side_effect=_rpc_error(grpc.StatusCode.INVALID_ARGUMENT), |
| 841 | + ) |
| 842 | + with pytest.raises(grpc.RpcError): |
| 843 | + remove_embeddings([1], COURSE_TYPE) |
| 844 | + |
| 845 | + |
| 846 | +def test_remove_embeddings_does_not_swallow_errors(mocker): |
| 847 | + """Unhandled errors propagate so the task fails instead of reporting success.""" |
| 848 | + mocker.patch( |
| 849 | + "vector_search.tasks.remove_qdrant_records", |
| 850 | + side_effect=ValueError("boom"), |
| 851 | + ) |
| 852 | + with pytest.raises(ValueError, match="boom"): |
| 853 | + remove_embeddings([1], COURSE_TYPE) |
0 commit comments