BookmarkManagerTests.cs
  1  // Copyright (c) Microsoft Corporation
  2  // The Microsoft Corporation licenses this file to you under the MIT license.
  3  // See the LICENSE file in the project root for more information.
  4  
  5  using System;
  6  using System.Linq;
  7  using Microsoft.VisualStudio.TestTools.UnitTesting;
  8  
  9  namespace Microsoft.CmdPal.Ext.Bookmarks.UnitTests;
 10  
 11  [TestClass]
 12  public class BookmarkManagerTests
 13  {
 14      [TestMethod]
 15      public void BookmarkManager_CanBeInstantiated()
 16      {
 17          // Arrange & Act
 18          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource());
 19  
 20          // Assert
 21          Assert.IsNotNull(bookmarkManager);
 22      }
 23  
 24      [TestMethod]
 25      public void BookmarkManager_InitialBookmarksEmpty()
 26      {
 27          // Arrange
 28          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource());
 29  
 30          // Act
 31          var bookmarks = bookmarkManager.Bookmarks;
 32  
 33          // Assert
 34          Assert.IsNotNull(bookmarks);
 35          Assert.AreEqual(0, bookmarks.Count);
 36      }
 37  
 38      [TestMethod]
 39      public void BookmarkManager_InitialBookmarksCorruptedData()
 40      {
 41          // Arrange
 42          var json = "@*>$ß Corrupted data. Hey, this is not JSON!";
 43          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource(json));
 44  
 45          // Act
 46          var bookmarks = bookmarkManager.Bookmarks;
 47  
 48          // Assert
 49          Assert.IsNotNull(bookmarks);
 50          Assert.AreEqual(0, bookmarks.Count);
 51      }
 52  
 53      [TestMethod]
 54      public void BookmarkManager_InitializeWithExistingData()
 55      {
 56          // Arrange
 57          const string json = """
 58                              {
 59                                  "Data":[
 60                                      {"Id":"d290f1ee-6c54-4b01-90e6-d701748f0851","Name":"Bookmark1","Bookmark":"C:\\Path1"},
 61                                      {"Id":"c4a760a4-5b63-4c9e-b8b3-2c3f5f3e6f7a","Name":"Bookmark2","Bookmark":"D:\\Path2"}
 62                                  ]
 63                              }
 64                              """;
 65          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource(json));
 66  
 67          // Act
 68          var bookmarks = bookmarkManager.Bookmarks?.ToList();
 69  
 70          // Assert
 71          Assert.IsNotNull(bookmarks);
 72          Assert.AreEqual(2, bookmarks.Count);
 73  
 74          Assert.AreEqual("Bookmark1", bookmarks[0].Name);
 75          Assert.AreEqual("C:\\Path1", bookmarks[0].Bookmark);
 76          Assert.AreEqual(Guid.Parse("d290f1ee-6c54-4b01-90e6-d701748f0851"), bookmarks[0].Id);
 77  
 78          Assert.AreEqual("Bookmark2", bookmarks[1].Name);
 79          Assert.AreEqual("D:\\Path2", bookmarks[1].Bookmark);
 80          Assert.AreEqual(Guid.Parse("c4a760a4-5b63-4c9e-b8b3-2c3f5f3e6f7a"), bookmarks[1].Id);
 81      }
 82  
 83      [TestMethod]
 84      public void BookmarkManager_InitializeWithLegacyData_GeneratesIds()
 85      {
 86          // Arrange
 87          const string json = """
 88                              {
 89                                  "Data":
 90                                  [
 91                                      { "Name":"Bookmark1", "Bookmark":"C:\\Path1" },
 92                                      { "Name":"Bookmark2", "Bookmark":"D:\\Path2" }
 93                                  ]
 94                              }
 95                              """;
 96          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource(json));
 97  
 98          // Act
 99          var bookmarks = bookmarkManager.Bookmarks?.ToList();
100  
101          // Assert
102          Assert.IsNotNull(bookmarks);
103          Assert.AreEqual(2, bookmarks.Count);
104  
105          Assert.AreEqual("Bookmark1", bookmarks[0].Name);
106          Assert.AreEqual("C:\\Path1", bookmarks[0].Bookmark);
107          Assert.AreNotEqual(Guid.Empty, bookmarks[0].Id);
108  
109          Assert.AreEqual("Bookmark2", bookmarks[1].Name);
110          Assert.AreEqual("D:\\Path2", bookmarks[1].Bookmark);
111          Assert.AreNotEqual(Guid.Empty, bookmarks[1].Id);
112  
113          Assert.AreNotEqual(bookmarks[0].Id, bookmarks[1].Id);
114      }
115  
116      [TestMethod]
117      public void BookmarkManager_AddBookmark_WorksCorrectly()
118      {
119          // Arrange
120          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource());
121          var bookmarkAddedEventFired = false;
122          bookmarkManager.BookmarkAdded += (bookmark) =>
123          {
124              bookmarkAddedEventFired = true;
125              Assert.AreEqual("TestBookmark", bookmark.Name);
126              Assert.AreEqual("C:\\TestPath", bookmark.Bookmark);
127          };
128  
129          // Act
130          var addedBookmark = bookmarkManager.Add("TestBookmark", "C:\\TestPath");
131  
132          // Assert
133          var bookmarks = bookmarkManager.Bookmarks;
134          Assert.AreEqual(1, bookmarks.Count);
135          Assert.AreEqual(addedBookmark, bookmarks.First());
136          Assert.IsTrue(bookmarkAddedEventFired);
137      }
138  
139      [TestMethod]
140      public void BookmarkManager_RemoveBookmark_WorksCorrectly()
141      {
142          // Arrange
143          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource());
144          var addedBookmark = bookmarkManager.Add("TestBookmark", "C:\\TestPath");
145          var bookmarkRemovedEventFired = false;
146          bookmarkManager.BookmarkRemoved += (bookmark) =>
147          {
148              bookmarkRemovedEventFired = true;
149              Assert.AreEqual(addedBookmark, bookmark);
150          };
151  
152          // Act
153          var removeResult = bookmarkManager.Remove(addedBookmark.Id);
154  
155          // Assert
156          var bookmarks = bookmarkManager.Bookmarks;
157          Assert.IsTrue(removeResult);
158          Assert.AreEqual(0, bookmarks.Count);
159          Assert.IsTrue(bookmarkRemovedEventFired);
160      }
161  
162      [TestMethod]
163      public void BookmarkManager_UpdateBookmark_WorksCorrectly()
164      {
165          // Arrange
166          var bookmarkManager = new BookmarksManager(new MockBookmarkDataSource());
167          var addedBookmark = bookmarkManager.Add("TestBookmark", "C:\\TestPath");
168          var bookmarkUpdatedEventFired = false;
169          bookmarkManager.BookmarkUpdated += (data, bookmarkData) =>
170          {
171              bookmarkUpdatedEventFired = true;
172              Assert.AreEqual(addedBookmark, data);
173              Assert.AreEqual("UpdatedBookmark", bookmarkData.Name);
174              Assert.AreEqual("D:\\UpdatedPath", bookmarkData.Bookmark);
175          };
176  
177          // Act
178          var updatedBookmark = bookmarkManager.Update(addedBookmark.Id, "UpdatedBookmark", "D:\\UpdatedPath");
179  
180          // Assert
181          var bookmarks = bookmarkManager.Bookmarks;
182          Assert.IsNotNull(updatedBookmark);
183          Assert.AreEqual(1, bookmarks.Count);
184          Assert.AreEqual(updatedBookmark, bookmarks.First());
185          Assert.AreEqual("UpdatedBookmark", updatedBookmark.Name);
186          Assert.AreEqual("D:\\UpdatedPath", updatedBookmark.Bookmark);
187          Assert.IsTrue(bookmarkUpdatedEventFired);
188      }
189  
190      [TestMethod]
191      public void BookmarkManager_LegacyData_IdsArePersistedAcrossLoads()
192      {
193          // Arrange
194          const string json = """
195                              {
196                                  "Data": 
197                                  [
198                                      { "Name": "C:\\","Bookmark": "C:\\" },
199                                      { "Name": "Bing.com","Bookmark": "https://bing.com" }
200                                  ]
201                              }
202                              """;
203  
204          var dataSource = new MockBookmarkDataSource(json);
205  
206          // First load: IDs should be generated for legacy entries
207          var manager1 = new BookmarksManager(dataSource);
208          var firstLoad = manager1.Bookmarks.ToList();
209          Assert.AreEqual(2, firstLoad.Count);
210          Assert.AreNotEqual(Guid.Empty, firstLoad[0].Id);
211          Assert.AreNotEqual(Guid.Empty, firstLoad[1].Id);
212  
213          // Keep a name->id map to be insensitive to ordering
214          var firstIdsByName = firstLoad.ToDictionary(b => b.Name, b => b.Id);
215  
216          // Wait deterministically for async persistence to complete
217          var wasSaved = dataSource.WaitForSave(1, 5000);
218          Assert.IsTrue(wasSaved, "Data was not saved within the expected time.");
219  
220          // Second load: should read back the same IDs from persisted data
221          var manager2 = new BookmarksManager(dataSource);
222          var secondLoad = manager2.Bookmarks.ToList();
223          Assert.AreEqual(2, secondLoad.Count);
224  
225          var secondIdsByName = secondLoad.ToDictionary(b => b.Name, b => b.Id);
226  
227          foreach (var kvp in firstIdsByName)
228          {
229              Assert.IsTrue(secondIdsByName.ContainsKey(kvp.Key), $"Missing bookmark '{kvp.Key}' after reload.");
230              Assert.AreEqual(kvp.Value, secondIdsByName[kvp.Key], $"Bookmark '{kvp.Key}' upgraded ID was not persisted across loads.");
231          }
232      }
233  }