/ 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 }