|
1 | 1 | import responses |
2 | 2 | from urllib.parse import urlencode |
3 | | -from flask_testing import TestCase |
4 | 3 | from webapp.app import create_app |
| 4 | +from tests.base_test_cases import BaseFlaskTestCase |
| 5 | +from unittest.mock import patch |
| 6 | +from cache.cache_utility import redis_cache |
| 7 | + |
| 8 | +POPULAR_PATH = "webapp.store.views.snap_recommendations.get_popular" |
| 9 | +RECENT_PATH = "webapp.store.views.snap_recommendations.get_recent" |
| 10 | +TREND_PATH = "webapp.store.views.snap_recommendations.get_trending" |
| 11 | +TOP_PATH = "webapp.store.views.snap_recommendations.get_top_rated" |
| 12 | +CATEGORIES_PATH = "webapp.store.views.device_gateway.get_categories" |
5 | 13 |
|
6 | 14 |
|
7 | 15 | EMPTY_EXTRA_DETAILS_PAYLOAD = {"aliases": None, "package_name": "vault"} |
|
43 | 51 | } |
44 | 52 |
|
45 | 53 |
|
46 | | -class GetDetailsPageTest(TestCase): |
| 54 | +class GetDetailsPageTest(BaseFlaskTestCase): |
47 | 55 | def setUp(self): |
| 56 | + super().setUp() |
48 | 57 | self.snap_name = "toto" |
49 | 58 | self.api_url = "".join( |
50 | 59 | [ |
@@ -388,6 +397,188 @@ def test_extra_details(self): |
388 | 397 | ], |
389 | 398 | ) |
390 | 399 |
|
| 400 | + @responses.activate |
| 401 | + def test_explore_uses_redis_cache(self): |
| 402 | + """When Redis has cached explore data, the recommendation APIs |
| 403 | + and device gateway should not be called and the view should |
| 404 | + return successfully using the cached values. |
| 405 | + """ |
| 406 | + # seed redis |
| 407 | + popular = [ |
| 408 | + { |
| 409 | + "details": { |
| 410 | + "name": "/pop1", |
| 411 | + "icon": "", |
| 412 | + "title": "Pop 1", |
| 413 | + "publisher": "Pub 1", |
| 414 | + "developer_validation": None, |
| 415 | + "summary": "Popular snap", |
| 416 | + }, |
| 417 | + } |
| 418 | + ] |
| 419 | + recent = [ |
| 420 | + { |
| 421 | + "details": { |
| 422 | + "name": "/recent1", |
| 423 | + "icon": "", |
| 424 | + "title": "Recent 1", |
| 425 | + "publisher": "Pub 2", |
| 426 | + "developer_validation": None, |
| 427 | + "summary": "Recent snap", |
| 428 | + }, |
| 429 | + } |
| 430 | + ] |
| 431 | + trending = [ |
| 432 | + { |
| 433 | + "details": { |
| 434 | + "name": "/trend1", |
| 435 | + "icon": "", |
| 436 | + "title": "Trend 1", |
| 437 | + "publisher": "Pub 3", |
| 438 | + "developer_validation": None, |
| 439 | + "summary": "Trending snap", |
| 440 | + }, |
| 441 | + } |
| 442 | + ] |
| 443 | + top_rated = [ |
| 444 | + { |
| 445 | + "details": { |
| 446 | + "name": "/top1", |
| 447 | + "icon": "", |
| 448 | + "title": "Top 1", |
| 449 | + "publisher": "Pub 4", |
| 450 | + "developer_validation": None, |
| 451 | + "summary": "Top rated snap", |
| 452 | + }, |
| 453 | + } |
| 454 | + ] |
| 455 | + categories = [{"slug": "cat1", "name": "Cat 1"}] |
| 456 | + |
| 457 | + redis_cache.set("explore:popular-snaps", popular, ttl=3600) |
| 458 | + redis_cache.set("explore:recent-snaps", recent, ttl=3600) |
| 459 | + redis_cache.set("explore:trending-snaps", trending, ttl=3600) |
| 460 | + redis_cache.set("explore:top-rated-snaps", top_rated, ttl=3600) |
| 461 | + redis_cache.set("explore:categories", categories, ttl=3600) |
| 462 | + |
| 463 | + with patch(POPULAR_PATH) as mock_popular: |
| 464 | + with patch(RECENT_PATH) as mock_recent: |
| 465 | + with patch(TREND_PATH) as mock_trending: |
| 466 | + with patch(TOP_PATH) as mock_top_rated: |
| 467 | + with patch(CATEGORIES_PATH) as mock_categories: |
| 468 | + response = self.client.get("/explore") |
| 469 | + |
| 470 | + self.assert200(response) |
| 471 | + |
| 472 | + mock_popular.assert_not_called() |
| 473 | + mock_recent.assert_not_called() |
| 474 | + mock_trending.assert_not_called() |
| 475 | + mock_top_rated.assert_not_called() |
| 476 | + mock_categories.assert_not_called() |
| 477 | + |
| 478 | + @responses.activate |
| 479 | + def test_explore_populates_cache_when_empty(self): |
| 480 | + """When Redis cache is empty, the recommendation/device methods |
| 481 | + should be called and their results stored in Redis for subsequent |
| 482 | + requests. |
| 483 | + """ |
| 484 | + |
| 485 | + with patch( |
| 486 | + POPULAR_PATH, |
| 487 | + return_value=[ |
| 488 | + { |
| 489 | + "details": { |
| 490 | + "name": "/popx", |
| 491 | + "icon": "", |
| 492 | + "title": "Pop X", |
| 493 | + "publisher": "Pub X", |
| 494 | + "developer_validation": None, |
| 495 | + "summary": "Popular x", |
| 496 | + } |
| 497 | + } |
| 498 | + ], |
| 499 | + ) as mock_popular: |
| 500 | + with patch( |
| 501 | + RECENT_PATH, |
| 502 | + return_value=[ |
| 503 | + { |
| 504 | + "details": { |
| 505 | + "name": "/recentx", |
| 506 | + "icon": "", |
| 507 | + "title": "Recent X", |
| 508 | + "publisher": "Pub RX", |
| 509 | + "developer_validation": None, |
| 510 | + "summary": "Recent x", |
| 511 | + } |
| 512 | + } |
| 513 | + ], |
| 514 | + ) as mock_recent: |
| 515 | + with patch( |
| 516 | + TREND_PATH, |
| 517 | + return_value=[ |
| 518 | + { |
| 519 | + "details": { |
| 520 | + "name": "/trendx", |
| 521 | + "icon": "", |
| 522 | + "title": "Trend X", |
| 523 | + "publisher": "Pub TX", |
| 524 | + "developer_validation": None, |
| 525 | + "summary": "Trend x", |
| 526 | + } |
| 527 | + } |
| 528 | + ], |
| 529 | + ) as mock_trending: |
| 530 | + with patch( |
| 531 | + TOP_PATH, |
| 532 | + return_value=[ |
| 533 | + { |
| 534 | + "details": { |
| 535 | + "name": "/topx", |
| 536 | + "icon": "", |
| 537 | + "title": "Top X", |
| 538 | + "publisher": "Pub TX", |
| 539 | + "developer_validation": None, |
| 540 | + "summary": "Top x", |
| 541 | + } |
| 542 | + } |
| 543 | + ], |
| 544 | + ) as mock_top_rated: |
| 545 | + with patch( |
| 546 | + CATEGORIES_PATH, |
| 547 | + return_value=[{"slug": "c1", "name": "C1"}], |
| 548 | + ) as mock_categories: |
| 549 | + response = self.client.get("/explore") |
| 550 | + |
| 551 | + self.assert200(response) |
| 552 | + |
| 553 | + # ensure the methods were called to populate cache |
| 554 | + self.assertTrue(mock_popular.called) |
| 555 | + self.assertTrue(mock_recent.called) |
| 556 | + self.assertTrue(mock_trending.called) |
| 557 | + self.assertTrue(mock_top_rated.called) |
| 558 | + self.assertTrue(mock_categories.called) |
| 559 | + # cached values should now exist |
| 560 | + pop_cached = redis_cache.get( |
| 561 | + "explore:popular-snaps" |
| 562 | + ) |
| 563 | + recent_cached = redis_cache.get( |
| 564 | + "explore:recent-snaps" |
| 565 | + ) |
| 566 | + trend_cached = redis_cache.get( |
| 567 | + "explore:trending-snaps" |
| 568 | + ) |
| 569 | + top_cached = redis_cache.get( |
| 570 | + "explore:top-rated-snaps" |
| 571 | + ) |
| 572 | + categories_cached = redis_cache.get( |
| 573 | + "explore:categories" |
| 574 | + ) |
| 575 | + |
| 576 | + assert pop_cached is not None |
| 577 | + assert recent_cached is not None |
| 578 | + assert trend_cached is not None |
| 579 | + assert top_cached is not None |
| 580 | + assert categories_cached is not None |
| 581 | + |
391 | 582 |
|
392 | 583 | if __name__ == "__main__": |
393 | 584 | import unittest |
|
0 commit comments