| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/notifications/notification_database.h" |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <optional> |
| |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/test/bind.h" |
| #include "base/uuid.h" |
| #include "content/public/browser/notification_database_data.h" |
| #include "content/public/test/browser_task_environment.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/common/notifications/notification_resources.h" |
| #include "third_party/blink/public/common/notifications/platform_notification_data.h" |
| #include "third_party/blink/public/mojom/notifications/notification.mojom.h" |
| #include "third_party/leveldatabase/src/include/leveldb/db.h" |
| #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| |
| const int kExampleServiceWorkerRegistrationId = 42; |
| |
| const struct { |
| const char* origin; |
| const char* tag; |
| int64_t service_worker_registration_id; |
| } kExampleNotificationData[] = { |
| {"https://example.com", "" /* tag */, 0}, |
| {"https://example.com", "" /* tag */, kExampleServiceWorkerRegistrationId}, |
| {"https://example.com", "" /* tag */, kExampleServiceWorkerRegistrationId}, |
| {"https://example.com", "" /* tag */, |
| kExampleServiceWorkerRegistrationId + 1}, |
| {"https://chrome.com", "" /* tag */, 0}, |
| {"https://chrome.com", "" /* tag */, 0}, |
| {"https://chrome.com", "" /* tag */, kExampleServiceWorkerRegistrationId}, |
| {"https://chrome.com", "foo" /* tag */, 0}}; |
| |
| class NotificationDatabaseTest : public ::testing::Test { |
| public: |
| NotificationDatabaseTest() |
| : task_environment_(BrowserTaskEnvironment::IO_MAINLOOP) {} |
| |
| protected: |
| // Creates a new NotificationDatabase instance in memory. |
| NotificationDatabase* CreateDatabaseInMemory() { |
| return new NotificationDatabase(base::FilePath(), callback()); |
| } |
| |
| // Creates a new NotificationDatabase instance in |path|. |
| NotificationDatabase* CreateDatabaseOnFileSystem(const base::FilePath& path) { |
| return new NotificationDatabase(path, callback()); |
| } |
| |
| // Creates a new notification for |service_worker_registration_id| belonging |
| // to |origin| and writes it to the database. The written notification id |
| // will be stored in |notification_id|. |
| void CreateAndWriteNotification(NotificationDatabase* database, |
| const GURL& origin, |
| const std::string& tag, |
| bool is_shown_by_browser, |
| int64_t service_worker_registration_id, |
| std::string* notification_id) { |
| DCHECK(notification_id); |
| |
| NotificationDatabaseData database_data; |
| database_data.notification_id = GenerateNotificationId(); |
| database_data.origin = origin; |
| database_data.service_worker_registration_id = |
| service_worker_registration_id; |
| database_data.notification_data.tag = tag; |
| database_data.is_shown_by_browser = is_shown_by_browser; |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| *notification_id = database_data.notification_id; |
| } |
| |
| // Populates |database| with a series of example notifications that differ in |
| // their origin and Service Worker registration id. |
| void PopulateDatabaseWithExampleData(NotificationDatabase* database) { |
| std::string notification_id; |
| for (const auto& notification_data : kExampleNotificationData) { |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database, GURL(notification_data.origin), notification_data.tag, |
| false /* is_shown_by_browser */, |
| notification_data.service_worker_registration_id, ¬ification_id)); |
| } |
| } |
| |
| // Returns if |database| has been opened. |
| bool IsDatabaseOpen(NotificationDatabase* database) { |
| return database->IsOpen(); |
| } |
| |
| // Returns if |database| is an in-memory only database. |
| bool IsInMemoryDatabase(NotificationDatabase* database) { |
| return database->IsInMemoryDatabase(); |
| } |
| |
| // Writes a LevelDB key-value pair directly to the LevelDB backing the |
| // notification database in |database|. |
| void WriteLevelDBKeyValuePair(NotificationDatabase* database, |
| const std::string& key, |
| const std::string& value) { |
| leveldb::Status status = |
| database->GetDBForTesting()->Put(leveldb::WriteOptions(), key, value); |
| ASSERT_TRUE(status.ok()); |
| } |
| |
| // Generates a random notification ID. The format of the ID is opaque. |
| std::string GenerateNotificationId() { |
| return base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| } |
| |
| NotificationDatabase::UkmCallback callback() { return callback_; } |
| |
| BrowserTaskEnvironment task_environment_; // Must be first member. |
| |
| NotificationDatabase::UkmCallback callback_; |
| }; |
| |
| TEST_F(NotificationDatabaseTest, OpenCloseMemory) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| |
| // Should return false because the database does not exist in memory. |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->Open(false /* create_if_missing */)); |
| |
| // Should return true, indicating that the database could be created. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| EXPECT_TRUE(IsDatabaseOpen(database.get())); |
| EXPECT_TRUE(IsInMemoryDatabase(database.get())); |
| |
| // Verify that in-memory databases do not persist when being re-created. |
| database.reset(CreateDatabaseInMemory()); |
| |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->Open(false /* create_if_missing */)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, OpenCloseFileSystem) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| |
| std::unique_ptr<NotificationDatabase> database( |
| CreateDatabaseOnFileSystem(database_dir.GetPath())); |
| |
| // Should return false because the database does not exist on the file system. |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->Open(false /* create_if_missing */)); |
| |
| // Should return true, indicating that the database could be created. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| EXPECT_TRUE(IsDatabaseOpen(database.get())); |
| EXPECT_FALSE(IsInMemoryDatabase(database.get())); |
| |
| // Close the database, and re-open it without attempting to create it because |
| // the files on the file system should still exist as expected. |
| database.reset(CreateDatabaseOnFileSystem(database_dir.GetPath())); |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(false /* create_if_missing */)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DestroyDatabase) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| |
| std::unique_ptr<NotificationDatabase> database( |
| CreateDatabaseOnFileSystem(database_dir.GetPath())); |
| |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| EXPECT_TRUE(IsDatabaseOpen(database.get())); |
| |
| // Destroy the database. This will immediately close it as well. |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, database->Destroy()); |
| EXPECT_FALSE(IsDatabaseOpen(database.get())); |
| |
| // Try to re-open the database (but not re-create it). This should fail as |
| // the files associated with the database should have been blown away. |
| database.reset(CreateDatabaseOnFileSystem(database_dir.GetPath())); |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->Open(false /* create_if_missing */)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, NotificationIdIncrements) { |
| base::ScopedTempDir database_dir; |
| ASSERT_TRUE(database_dir.CreateUniqueTempDir()); |
| |
| std::unique_ptr<NotificationDatabase> database( |
| CreateDatabaseOnFileSystem(database_dir.GetPath())); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| std::string notification_id; |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, "" /* tag */, false /* is_shown_by_browser */, |
| 0 /* sw_registration_id */, ¬ification_id)); |
| |
| database.reset(CreateDatabaseOnFileSystem(database_dir.GetPath())); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(false /* create_if_missing */)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, NotificationIdIncrementsStorage) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| NotificationDatabaseData database_data, read_database_data; |
| database_data.notification_id = GenerateNotificationId(); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationData(database_data.notification_id, |
| origin, &read_database_data)); |
| |
| EXPECT_EQ(database_data.notification_id, read_database_data.notification_id); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadInvalidNotificationData) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| NotificationDatabaseData database_data; |
| |
| // Reading the notification data for a notification that does not exist should |
| // return the ERROR_NOT_FOUND status code. |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadNotificationData("bad-id", GURL("https://chrome.com"), |
| &database_data)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadNotificationDataDifferentOrigin) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| NotificationDatabaseData database_data, read_database_data; |
| database_data.notification_id = GenerateNotificationId(); |
| database_data.notification_data.title = u"My Notification"; |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| // Reading the notification from the database when given a different origin |
| // should return the ERROR_NOT_FOUND status code. |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadNotificationData(database_data.notification_id, |
| GURL("https://chrome.com"), |
| &read_database_data)); |
| |
| // However, reading the notification from the database with the same origin |
| // should return STATUS_OK and the associated notification data. |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationData(database_data.notification_id, |
| origin, &read_database_data)); |
| |
| EXPECT_EQ(database_data.notification_data.title, |
| read_database_data.notification_data.title); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadNotificationDataReflection) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| blink::PlatformNotificationData notification_data; |
| notification_data.title = u"My Notification"; |
| notification_data.direction = |
| blink::mojom::NotificationDirection::RIGHT_TO_LEFT; |
| notification_data.lang = "nl-NL"; |
| notification_data.body = u"Hello, world!"; |
| notification_data.tag = "replace id"; |
| notification_data.icon = GURL("https://example.com/icon.png"); |
| notification_data.silent = true; |
| |
| NotificationDatabaseData database_data; |
| database_data.notification_id = GenerateNotificationId(); |
| database_data.origin = origin; |
| database_data.service_worker_registration_id = 42; |
| database_data.notification_data = notification_data; |
| |
| // Write the constructed notification to the database, and then immediately |
| // read it back from the database again as well. |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| NotificationDatabaseData read_database_data; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationData(database_data.notification_id, |
| origin, &read_database_data)); |
| |
| // Verify that all members retrieved from the database are exactly the same |
| // as the ones that were written to it. This tests the serialization behavior. |
| |
| EXPECT_EQ(database_data.notification_id, read_database_data.notification_id); |
| |
| EXPECT_EQ(database_data.origin, read_database_data.origin); |
| EXPECT_EQ(database_data.service_worker_registration_id, |
| read_database_data.service_worker_registration_id); |
| |
| const blink::PlatformNotificationData& read_notification_data = |
| read_database_data.notification_data; |
| |
| EXPECT_EQ(notification_data.title, read_notification_data.title); |
| EXPECT_EQ(notification_data.direction, read_notification_data.direction); |
| EXPECT_EQ(notification_data.lang, read_notification_data.lang); |
| EXPECT_EQ(notification_data.body, read_notification_data.body); |
| EXPECT_EQ(notification_data.tag, read_notification_data.tag); |
| EXPECT_EQ(notification_data.icon, read_notification_data.icon); |
| EXPECT_EQ(notification_data.silent, read_notification_data.silent); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadInvalidNotificationResources) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| blink::NotificationResources database_resources; |
| |
| // Reading the notification resources for a notification that does not exist |
| // should return the ERROR_NOT_FOUND status code. |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadNotificationResources( |
| "bad-id", GURL("https://chrome.com"), &database_resources)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadNotificationResourcesDifferentOrigin) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| NotificationDatabaseData database_data; |
| blink::NotificationResources database_resources; |
| database_data.notification_id = GenerateNotificationId(); |
| database_data.notification_data.title = u"My Notification"; |
| database_data.notification_resources = blink::NotificationResources(); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| // Reading the notification resources from the database when given a different |
| // origin should return the ERROR_NOT_FOUND status code. |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadNotificationResources(database_data.notification_id, |
| GURL("https://chrome.com"), |
| &database_resources)); |
| |
| // However, reading the notification from the database with the same origin |
| // should return STATUS_OK and the associated notification data. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationResources(database_data.notification_id, |
| origin, &database_resources)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadNotificationResourcesReflection) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| blink::NotificationResources notification_resources; |
| NotificationDatabaseData database_data; |
| database_data.notification_id = GenerateNotificationId(); |
| database_data.origin = origin; |
| database_data.service_worker_registration_id = 42; |
| database_data.notification_resources = notification_resources; |
| |
| // Write the constructed notification to the database, and then immediately |
| // read it back from the database again as well. |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| NotificationDatabaseData read_database_data; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationData(database_data.notification_id, |
| origin, &read_database_data)); |
| |
| // Verify that all members retrieved from the database are exactly the same |
| // as the ones that were written to it. This tests the serialization behavior. |
| |
| EXPECT_EQ(database_data.notification_id, read_database_data.notification_id); |
| |
| EXPECT_EQ(database_data.origin, read_database_data.origin); |
| EXPECT_EQ(database_data.service_worker_registration_id, |
| read_database_data.service_worker_registration_id); |
| |
| // We do not populate the resources when reading from the database. |
| EXPECT_FALSE(read_database_data.notification_resources.has_value()); |
| |
| blink::NotificationResources read_notification_resources; |
| EXPECT_EQ( |
| NotificationDatabase::STATUS_OK, |
| database->ReadNotificationResources(database_data.notification_id, origin, |
| &read_notification_resources)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadWriteMultipleNotificationData) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| std::vector<std::string> notification_ids; |
| |
| // Write ten notifications to the database, each with a unique title and |
| // notification id (it is the responsibility of the user to increment this). |
| for (int i = 1; i <= 10; ++i) { |
| std::string notification_id; |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, "" /* tag */, false /* is_shown_by_browser */, |
| i /* sw_registration_id */, ¬ification_id)); |
| |
| EXPECT_FALSE(notification_id.empty()); |
| |
| notification_ids.push_back(notification_id); |
| } |
| |
| NotificationDatabaseData database_data; |
| |
| int64_t service_worker_registration_id = 1; |
| |
| // Read the ten notifications from the database, and verify that the titles |
| // of each of them matches with how they were created. |
| for (const std::string& notification_id : notification_ids) { |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationData(notification_id, origin, |
| &database_data)); |
| |
| EXPECT_EQ(service_worker_registration_id++, |
| database_data.service_worker_registration_id); |
| } |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadNotificationUpdateInteraction) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| NotificationDatabaseData database_data, read_database_data; |
| database_data.notification_id = GenerateNotificationId(); |
| database_data.notification_data.title = u"My Notification"; |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| // Check that the time deltas have not yet been set. |
| EXPECT_EQ(false, |
| read_database_data.time_until_first_click_millis.has_value()); |
| EXPECT_EQ(false, read_database_data.time_until_last_click_millis.has_value()); |
| EXPECT_EQ(false, read_database_data.time_until_close_millis.has_value()); |
| |
| // Check that when a notification has an interaction, the appropriate field is |
| // updated on the read. |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationDataAndRecordInteraction( |
| database_data.notification_id, origin, |
| PlatformNotificationContext::Interaction::CLICKED, |
| &read_database_data)); |
| EXPECT_EQ(1, read_database_data.num_clicks); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationDataAndRecordInteraction( |
| database_data.notification_id, origin, |
| PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED, |
| &read_database_data)); |
| EXPECT_EQ(1, read_database_data.num_action_button_clicks); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationDataAndRecordInteraction( |
| database_data.notification_id, origin, |
| PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED, |
| &read_database_data)); |
| EXPECT_EQ(2, read_database_data.num_action_button_clicks); |
| |
| // Check that the click timestamps are correctly updated. |
| EXPECT_EQ(true, read_database_data.time_until_first_click_millis.has_value()); |
| EXPECT_EQ(true, read_database_data.time_until_last_click_millis.has_value()); |
| |
| // Check that when a read with a CLOSED interaction occurs, the correct |
| // field is updated. |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationDataAndRecordInteraction( |
| database_data.notification_id, origin, |
| PlatformNotificationContext::Interaction::CLOSED, |
| &read_database_data)); |
| EXPECT_EQ(true, read_database_data.time_until_close_millis.has_value()); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteInvalidNotificationData) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| // Deleting non-existing notifications is not considered to be a failure. |
| ASSERT_EQ( |
| NotificationDatabase::STATUS_OK, |
| database->DeleteNotificationData("bad-id", GURL("https://chrome.com"))); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteNotificationDataSameOrigin) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| const std::string notification_id = GenerateNotificationId(); |
| |
| NotificationDatabaseData database_data; |
| database_data.notification_id = notification_id; |
| |
| GURL origin("https://example.com"); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| // Reading a notification after writing one should succeed. |
| EXPECT_EQ( |
| NotificationDatabase::STATUS_OK, |
| database->ReadNotificationData(notification_id, origin, &database_data)); |
| |
| // Delete the notification which was just written to the database, and verify |
| // that reading it again will fail. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteNotificationData(notification_id, origin)); |
| EXPECT_EQ( |
| NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadNotificationData(notification_id, origin, &database_data)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteNotificationResourcesSameOrigin) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| const std::string notification_id = GenerateNotificationId(); |
| |
| blink::NotificationResources notification_resources; |
| NotificationDatabaseData database_data; |
| database_data.notification_id = notification_id; |
| database_data.notification_resources = notification_resources; |
| |
| GURL origin("https://example.com"); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| // Reading notification resources after writing should succeed. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationResources(notification_id, origin, |
| ¬ification_resources)); |
| |
| // Delete the notification which was just written to the database, and verify |
| // that reading the resources again will fail. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteNotificationData(notification_id, origin)); |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadNotificationResources(notification_id, origin, |
| ¬ification_resources)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteNotificationDataDifferentOrigin) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| const std::string notification_id = GenerateNotificationId(); |
| |
| NotificationDatabaseData database_data; |
| database_data.notification_id = notification_id; |
| |
| GURL origin("https://example.com"); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| // Attempting to delete the notification with a different origin, but with the |
| // same |notification_id|, should not return an error (the notification could |
| // not be found, but that's not considered a failure). However, it should not |
| // remove the notification either. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteNotificationData(notification_id, |
| GURL("https://chrome.com"))); |
| |
| EXPECT_EQ( |
| NotificationDatabase::STATUS_OK, |
| database->ReadNotificationData(notification_id, origin, &database_data)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteInvalidNotificationResources) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| // Deleting non-existing resources is not considered to be a failure. |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteNotificationResources("bad-id", |
| GURL("https://chrome.com"))); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteNotificationResources) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| const std::string notification_id = GenerateNotificationId(); |
| |
| blink::NotificationResources notification_resources; |
| NotificationDatabaseData database_data; |
| database_data.notification_id = notification_id; |
| database_data.notification_resources = notification_resources; |
| |
| GURL origin("https://example.com"); |
| |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->WriteNotificationData(origin, database_data)); |
| |
| // Reading notification resources after writing should succeed. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadNotificationResources(notification_id, origin, |
| ¬ification_resources)); |
| |
| // Delete the notification resources for the notification which was just |
| // written to the database, and verify that reading them again will fail. |
| EXPECT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteNotificationResources(notification_id, origin)); |
| EXPECT_EQ(NotificationDatabase::STATUS_ERROR_NOT_FOUND, |
| database->ReadNotificationResources(notification_id, origin, |
| ¬ification_resources)); |
| } |
| |
| TEST_F(NotificationDatabaseTest, |
| ForEachNotificationDataForServiceWorkerRegistration) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get())); |
| |
| GURL origin("https://example.com:443"); |
| |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ForEachNotificationDataForServiceWorkerRegistration( |
| origin, kExampleServiceWorkerRegistrationId, |
| base::BindLambdaForTesting( |
| [¬ifications](const NotificationDatabaseData& data) { |
| notifications.push_back(data); |
| }))); |
| |
| EXPECT_EQ(2u, notifications.size()); |
| } |
| |
| TEST_F(NotificationDatabaseTest, ReadAllNotificationDataForOrigin) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get())); |
| |
| GURL origin("https://example.com"); |
| |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForOrigin(origin, ¬ifications)); |
| |
| EXPECT_EQ(4u, notifications.size()); |
| } |
| |
| TEST_F(NotificationDatabaseTest, |
| ReadAllNotificationDataForServiceWorkerRegistration) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get())); |
| |
| GURL origin("https://example.com:443"); |
| |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForServiceWorkerRegistration( |
| origin, kExampleServiceWorkerRegistrationId, |
| std::nullopt /* is_shown_by_browser */, ¬ifications)); |
| |
| EXPECT_EQ(2u, notifications.size()); |
| } |
| |
| TEST_F(NotificationDatabaseTest, |
| ReadAllNotificationDataForServiceWorkerRegistrationShownByBrowser) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| GURL origin("https://example.com:443"); |
| std::string kTag = "tag"; |
| |
| std::string non_browser_notification_id; |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, kTag, false /* is_shown_by_browser */, |
| kExampleServiceWorkerRegistrationId, &non_browser_notification_id)); |
| |
| std::string browser_notification_id; |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, kTag, true /* is_shown_by_browser */, |
| kExampleServiceWorkerRegistrationId, &browser_notification_id)); |
| |
| { |
| // Expect to be able to read notification shown by the browser. |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForServiceWorkerRegistration( |
| origin, kExampleServiceWorkerRegistrationId, |
| true /* is_shown_by_browser */, ¬ifications)); |
| ASSERT_EQ(1u, notifications.size()); |
| EXPECT_EQ(browser_notification_id, notifications[0].notification_id); |
| } |
| |
| { |
| // Expect to be able to read notification not shown by the browser. |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForServiceWorkerRegistration( |
| origin, kExampleServiceWorkerRegistrationId, |
| false /* is_shown_by_browser */, ¬ifications)); |
| ASSERT_EQ(1u, notifications.size()); |
| EXPECT_EQ(non_browser_notification_id, notifications[0].notification_id); |
| } |
| |
| { |
| // Expect to be able to read notification not shown by anyone. |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForServiceWorkerRegistration( |
| origin, kExampleServiceWorkerRegistrationId, |
| std::nullopt /* is_shown_by_browser */, ¬ifications)); |
| ASSERT_EQ(2u, notifications.size()); |
| } |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteAllNotificationDataForOrigin) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get())); |
| |
| GURL origin("https://example.com:443"); |
| |
| std::set<std::string> deleted_notification_ids; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteAllNotificationDataForOrigin( |
| origin, "" /* tag */, std::nullopt /* is_shown_by_browser */, |
| &deleted_notification_ids)); |
| |
| EXPECT_EQ(4u, deleted_notification_ids.size()); |
| |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForOrigin(origin, ¬ifications)); |
| |
| EXPECT_EQ(0u, notifications.size()); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteAllNotificationDataForOriginWithTag) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get())); |
| |
| GURL origin("https://chrome.com"); |
| |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForOrigin(origin, ¬ifications)); |
| |
| const std::string& tag = "foo"; |
| |
| size_t notifications_with_tag = 0; |
| size_t notifications_without_tag = 0; |
| |
| for (const auto& database_data : notifications) { |
| if (database_data.notification_data.tag == tag) |
| ++notifications_with_tag; |
| else |
| ++notifications_without_tag; |
| } |
| |
| ASSERT_GT(notifications_with_tag, 0u); |
| ASSERT_GT(notifications_without_tag, 0u); |
| |
| std::set<std::string> deleted_notification_ids; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteAllNotificationDataForOrigin( |
| origin, "foo" /* tag */, std::nullopt /* is_shown_by_browser */, |
| &deleted_notification_ids)); |
| |
| EXPECT_EQ(notifications_with_tag, deleted_notification_ids.size()); |
| |
| std::vector<NotificationDatabaseData> updated_notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForOrigin(origin, |
| &updated_notifications)); |
| |
| EXPECT_EQ(notifications_without_tag, updated_notifications.size()); |
| |
| size_t updated_notifications_with_tag = 0; |
| size_t updated_notifications_without_tag = 0; |
| |
| for (const auto& database_data : updated_notifications) { |
| if (database_data.notification_data.tag == tag) |
| ++updated_notifications_with_tag; |
| else |
| ++updated_notifications_without_tag; |
| } |
| |
| EXPECT_EQ(0u, updated_notifications_with_tag); |
| EXPECT_EQ(notifications_without_tag, updated_notifications_without_tag); |
| } |
| |
| TEST_F(NotificationDatabaseTest, DeleteAllNotificationDataForOriginEmpty) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| GURL origin("https://example.com"); |
| |
| std::set<std::string> deleted_notification_ids; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteAllNotificationDataForOrigin( |
| origin, "" /* tag */, std::nullopt /* is_shown_by_browser */, |
| &deleted_notification_ids)); |
| |
| EXPECT_EQ(0u, deleted_notification_ids.size()); |
| } |
| |
| TEST_F(NotificationDatabaseTest, |
| DeleteAllNotificationDataForOriginShownByBrowser) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| GURL origin("https://example.com:443"); |
| std::string kTag = "tag"; |
| |
| std::string non_browser_notification_id; |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, kTag, false /* is_shown_by_browser */, |
| kExampleServiceWorkerRegistrationId, &non_browser_notification_id)); |
| |
| std::string browser_notification_id; |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, kTag, true /* is_shown_by_browser */, |
| kExampleServiceWorkerRegistrationId, &browser_notification_id)); |
| |
| { |
| // Expect two notifications in the database for |origin|. |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ( |
| NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForOrigin(origin, ¬ifications)); |
| EXPECT_EQ(2u, notifications.size()); |
| } |
| |
| { |
| // Expect to be able to delete only notifications from the browser. |
| std::set<std::string> deleted_notification_ids; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteAllNotificationDataForOrigin( |
| origin, kTag, true /* is_shown_by_browser */, |
| &deleted_notification_ids)); |
| EXPECT_EQ(1u, deleted_notification_ids.size()); |
| EXPECT_EQ(1u, deleted_notification_ids.count(browser_notification_id)); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, kTag, true /* is_shown_by_browser */, |
| kExampleServiceWorkerRegistrationId, &browser_notification_id)); |
| |
| { |
| // Expect to be able to delete only notifications not from the browser. |
| std::set<std::string> deleted_notification_ids; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteAllNotificationDataForOrigin( |
| origin, kTag, false /* is_shown_by_browser */, |
| &deleted_notification_ids)); |
| EXPECT_EQ(1u, deleted_notification_ids.size()); |
| EXPECT_EQ(1u, deleted_notification_ids.count(non_browser_notification_id)); |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(CreateAndWriteNotification( |
| database.get(), origin, kTag, false /* is_shown_by_browser */, |
| kExampleServiceWorkerRegistrationId, &non_browser_notification_id)); |
| |
| { |
| // Expect to be able to delete notifications from the browser or not. |
| std::set<std::string> deleted_notification_ids; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteAllNotificationDataForOrigin( |
| origin, kTag, std::nullopt /* is_shown_by_browser */, |
| &deleted_notification_ids)); |
| EXPECT_EQ(2u, deleted_notification_ids.size()); |
| EXPECT_EQ(1u, deleted_notification_ids.count(browser_notification_id)); |
| EXPECT_EQ(1u, deleted_notification_ids.count(non_browser_notification_id)); |
| } |
| |
| // Expect no remaining notifications. |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForOrigin(origin, ¬ifications)); |
| EXPECT_EQ(0u, notifications.size()); |
| } |
| |
| TEST_F(NotificationDatabaseTest, |
| DeleteAllNotificationDataForServiceWorkerRegistration) { |
| std::unique_ptr<NotificationDatabase> database(CreateDatabaseInMemory()); |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->Open(true /* create_if_missing */)); |
| |
| ASSERT_NO_FATAL_FAILURE(PopulateDatabaseWithExampleData(database.get())); |
| |
| GURL origin("https://example.com:443"); |
| std::set<std::string> deleted_notification_ids; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->DeleteAllNotificationDataForServiceWorkerRegistration( |
| origin, kExampleServiceWorkerRegistrationId, |
| &deleted_notification_ids)); |
| |
| EXPECT_EQ(2u, deleted_notification_ids.size()); |
| |
| std::vector<NotificationDatabaseData> notifications; |
| ASSERT_EQ(NotificationDatabase::STATUS_OK, |
| database->ReadAllNotificationDataForServiceWorkerRegistration( |
| origin, kExampleServiceWorkerRegistrationId, |
| std::nullopt /* is_shown_by_browser */, ¬ifications)); |
| |
| EXPECT_EQ(0u, notifications.size()); |
| } |
| |
| } // namespace content |