/ src / modules / cmdpal / Tests / Microsoft.CmdPal.Ext.Bookmarks.UnitTests / BookmarkJsonParserTests.cs
BookmarkJsonParserTests.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.Collections.Generic;
  6  
  7  using Microsoft.CmdPal.Ext.Bookmarks.Persistence;
  8  using Microsoft.VisualStudio.TestTools.UnitTesting;
  9  
 10  namespace Microsoft.CmdPal.Ext.Bookmarks.UnitTests;
 11  
 12  [TestClass]
 13  public class BookmarkJsonParserTests
 14  {
 15      private BookmarkJsonParser _parser;
 16  
 17      [TestInitialize]
 18      public void Setup()
 19      {
 20          _parser = new BookmarkJsonParser();
 21      }
 22  
 23      [TestMethod]
 24      public void ParseBookmarks_ValidJson_ReturnsBookmarks()
 25      {
 26          // Arrange
 27          var json = """
 28          {
 29              "Data": [
 30                  {
 31                      "Name": "Google",
 32                      "Bookmark": "https://www.google.com"
 33                  },
 34                  {
 35                      "Name": "Local File",
 36                      "Bookmark": "C:\\temp\\file.txt"
 37                  }
 38              ]
 39          }
 40          """;
 41  
 42          // Act
 43          var result = _parser.ParseBookmarks(json);
 44  
 45          // Assert
 46          Assert.IsNotNull(result);
 47          Assert.AreEqual(2, result.Data.Count);
 48          Assert.AreEqual("Google", result.Data[0].Name);
 49          Assert.AreEqual("https://www.google.com", result.Data[0].Bookmark);
 50          Assert.AreEqual("Local File", result.Data[1].Name);
 51          Assert.AreEqual("C:\\temp\\file.txt", result.Data[1].Bookmark);
 52      }
 53  
 54      [TestMethod]
 55      public void ParseBookmarks_EmptyJson_ReturnsEmptyBookmarks()
 56      {
 57          // Arrange
 58          var json = "{}";
 59  
 60          // Act
 61          var result = _parser.ParseBookmarks(json);
 62  
 63          // Assert
 64          Assert.IsNotNull(result);
 65          Assert.AreEqual(0, result.Data.Count);
 66      }
 67  
 68      [TestMethod]
 69      public void ParseBookmarks_NullJson_ReturnsEmptyBookmarks()
 70      {
 71          // Act
 72          var result = _parser.ParseBookmarks(null);
 73  
 74          // Assert
 75          Assert.IsNotNull(result);
 76          Assert.AreEqual(0, result.Data.Count);
 77      }
 78  
 79      [TestMethod]
 80      public void ParseBookmarks_WhitespaceJson_ReturnsEmptyBookmarks()
 81      {
 82          // Act
 83          var result = _parser.ParseBookmarks("   ");
 84  
 85          // Assert
 86          Assert.IsNotNull(result);
 87          Assert.AreEqual(0, result.Data.Count);
 88      }
 89  
 90      [TestMethod]
 91      public void ParseBookmarks_EmptyString_ReturnsEmptyBookmarks()
 92      {
 93          // Act
 94          var result = _parser.ParseBookmarks(string.Empty);
 95  
 96          // Assert
 97          Assert.IsNotNull(result);
 98          Assert.AreEqual(0, result.Data.Count);
 99      }
100  
101      [TestMethod]
102      public void ParseBookmarks_InvalidJson_ReturnsEmptyBookmarks()
103      {
104          // Arrange
105          var invalidJson = "{invalid json}";
106  
107          // Act
108          var result = _parser.ParseBookmarks(invalidJson);
109  
110          // Assert
111          Assert.IsNotNull(result);
112          Assert.AreEqual(0, result.Data.Count);
113      }
114  
115      [TestMethod]
116      public void ParseBookmarks_MalformedJson_ReturnsEmptyBookmarks()
117      {
118          // Arrange
119          var malformedJson = """
120          {
121              "Data": [
122                  {
123                      "Name": "Google",
124                      "Bookmark": "https://www.google.com"
125                  },
126                  {
127                      "Name": "Incomplete entry"
128          """;
129  
130          // Act
131          var result = _parser.ParseBookmarks(malformedJson);
132  
133          // Assert
134          Assert.IsNotNull(result);
135          Assert.AreEqual(0, result.Data.Count);
136      }
137  
138      [TestMethod]
139      public void ParseBookmarks_JsonWithTrailingCommas_ParsesSuccessfully()
140      {
141          // Arrange - JSON with trailing commas (should be handled by AllowTrailingCommas option)
142          var json = """
143          {
144              "Data": [
145                  {
146                      "Name": "Google",
147                      "Bookmark": "https://www.google.com",
148                  },
149                  {
150                      "Name": "Local File",
151                      "Bookmark": "C:\\temp\\file.txt",
152                  },
153              ]
154          }
155          """;
156  
157          // Act
158          var result = _parser.ParseBookmarks(json);
159  
160          // Assert
161          Assert.IsNotNull(result);
162          Assert.AreEqual(2, result.Data.Count);
163          Assert.AreEqual("Google", result.Data[0].Name);
164          Assert.AreEqual("https://www.google.com", result.Data[0].Bookmark);
165      }
166  
167      [TestMethod]
168      public void ParseBookmarks_JsonWithDifferentCasing_ParsesSuccessfully()
169      {
170          // Arrange - JSON with different property name casing (should be handled by PropertyNameCaseInsensitive option)
171          var json = """
172          {
173              "data": [
174                  {
175                      "name": "Google",
176                      "bookmark": "https://www.google.com"
177                  }
178              ]
179          }
180          """;
181  
182          // Act
183          var result = _parser.ParseBookmarks(json);
184  
185          // Assert
186          Assert.IsNotNull(result);
187          Assert.AreEqual(1, result.Data.Count);
188          Assert.AreEqual("Google", result.Data[0].Name);
189          Assert.AreEqual("https://www.google.com", result.Data[0].Bookmark);
190      }
191  
192      [TestMethod]
193      public void SerializeBookmarks_ValidBookmarks_ReturnsJsonString()
194      {
195          // Arrange
196          var bookmarks = new BookmarksData
197          {
198              Data = new List<BookmarkData>
199              {
200                  new BookmarkData { Name = "Google", Bookmark = "https://www.google.com" },
201                  new BookmarkData { Name = "Local File", Bookmark = "C:\\temp\\file.txt" },
202              },
203          };
204  
205          // Act
206          var result = _parser.SerializeBookmarks(bookmarks);
207  
208          // Assert
209          Assert.IsNotNull(result);
210          Assert.IsTrue(result.Contains("Google"));
211          Assert.IsTrue(result.Contains("https://www.google.com"));
212          Assert.IsTrue(result.Contains("Local File"));
213          Assert.IsTrue(result.Contains("C:\\\\temp\\\\file.txt")); // Escaped backslashes in JSON
214          Assert.IsTrue(result.Contains("Data"));
215      }
216  
217      [TestMethod]
218      public void SerializeBookmarks_EmptyBookmarks_ReturnsValidJson()
219      {
220          // Arrange
221          var bookmarks = new BookmarksData();
222  
223          // Act
224          var result = _parser.SerializeBookmarks(bookmarks);
225  
226          // Assert
227          Assert.IsNotNull(result);
228          Assert.IsTrue(result.Contains("Data"));
229          Assert.IsTrue(result.Contains("[]"));
230      }
231  
232      [TestMethod]
233      public void SerializeBookmarks_NullBookmarks_ReturnsEmptyString()
234      {
235          // Act
236          var result = _parser.SerializeBookmarks(null);
237  
238          // Assert
239          Assert.AreEqual(string.Empty, result);
240      }
241  
242      [TestMethod]
243      public void ParseBookmarks_RoundTripSerialization_PreservesData()
244      {
245          // Arrange
246          var originalBookmarks = new BookmarksData
247          {
248              Data = new List<BookmarkData>
249              {
250                  new BookmarkData { Name = "Google", Bookmark = "https://www.google.com" },
251                  new BookmarkData { Name = "Local File", Bookmark = "C:\\temp\\file.txt" },
252                  new BookmarkData { Name = "Placeholder", Bookmark = "Open {file} in editor" },
253              },
254          };
255  
256          // Act - Serialize then parse
257          var serializedJson = _parser.SerializeBookmarks(originalBookmarks);
258          var parsedBookmarks = _parser.ParseBookmarks(serializedJson);
259  
260          // Assert
261          Assert.IsNotNull(parsedBookmarks);
262          Assert.AreEqual(originalBookmarks.Data.Count, parsedBookmarks.Data.Count);
263  
264          for (var i = 0; i < originalBookmarks.Data.Count; i++)
265          {
266              Assert.AreEqual(originalBookmarks.Data[i].Name, parsedBookmarks.Data[i].Name);
267              Assert.AreEqual(originalBookmarks.Data[i].Bookmark, parsedBookmarks.Data[i].Bookmark);
268          }
269      }
270  
271      [TestMethod]
272      public void ParseBookmarks_JsonWithPlaceholderBookmarks_CorrectlyIdentifiesPlaceholders()
273      {
274          // Arrange
275          var json = """
276          {
277              "Data": [
278                  {
279                      "Name": "Regular URL",
280                      "Bookmark": "https://www.google.com"
281                  },
282                  {
283                      "Name": "Placeholder Command",
284                      "Bookmark": "notepad {file}"
285                  },
286                  {
287                      "Name": "Multiple Placeholders",
288                      "Bookmark": "copy {source} {destination}"
289                  }
290              ]
291          }
292          """;
293  
294          // Act
295          var result = _parser.ParseBookmarks(json);
296  
297          // Assert
298          Assert.IsNotNull(result);
299          Assert.AreEqual(3, result.Data.Count);
300      }
301  
302      [TestMethod]
303      public void ParseBookmarks_IsPlaceholder_CorrectlyIdentifiesPlaceholders()
304      {
305          // Arrange
306          var json = """
307          {
308              "Data": [
309                  {
310                      "Name": "Simple Placeholder",
311                      "Bookmark": "notepad {file}"
312                  },
313                  {
314                      "Name": "Multiple Placeholders",
315                      "Bookmark": "copy {source} to {destination}"
316                  },
317                  {
318                      "Name": "Web URL with Placeholder",
319                      "Bookmark": "https://search.com?q={query}"
320                  },
321                  {
322                      "Name": "Complex Placeholder",
323                      "Bookmark": "cmd /c echo {message} > {output_file}"
324                  },
325                  {
326                      "Name": "No Placeholder - Regular URL",
327                      "Bookmark": "https://www.google.com"
328                  },
329                  {
330                      "Name": "No Placeholder - Local File",
331                      "Bookmark": "C:\\temp\\file.txt"
332                  },
333                  {
334                      "Name": "False Positive - Only Opening Brace",
335                      "Bookmark": "test { incomplete"
336                  },
337                  {
338                      "Name": "False Positive - Only Closing Brace",
339                      "Bookmark": "test } incomplete"
340                  },
341                  {
342                      "Name": "Empty Placeholder",
343                      "Bookmark": "command {}"
344                  }
345              ]
346          }
347          """;
348  
349          // Act
350          var result = _parser.ParseBookmarks(json);
351  
352          // Assert
353          Assert.IsNotNull(result);
354          Assert.AreEqual(9, result.Data.Count);
355      }
356  
357      [TestMethod]
358      public void ParseBookmarks_MixedProperties_CorrectlyIdentifiesPlaceholder()
359      {
360          // Arrange
361          var json = """
362          {
363              "Data": [
364                  {
365                      "Name": "Web URL with Placeholder",
366                      "Bookmark": "https://google.com/search?q={query}"
367                  },
368                  {
369                      "Name": "Web URL without Placeholder",
370                      "Bookmark": "https://github.com"
371                  },
372                  {
373                      "Name": "Local File with Placeholder",
374                      "Bookmark": "notepad {file}"
375                  },
376                  {
377                      "Name": "Local File without Placeholder",
378                      "Bookmark": "C:\\Windows\\notepad.exe"
379                  }
380              ]
381          }
382          """;
383  
384          // Act
385          var result = _parser.ParseBookmarks(json);
386  
387          // Assert
388          Assert.IsNotNull(result);
389          Assert.AreEqual(4, result.Data.Count);
390      }
391  }