(T domainObject, bool recursive = false)
33 | {
34 | var props = domainObject.GetType().GetProperties();
35 | foreach (var prop in props)
36 | {
37 | var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
38 | try
39 | {
40 | object propObj = null;
41 | object data;
42 | switch (propType.Name.ToLower())
43 | {
44 | case "string":
45 | data = "test";
46 | break;
47 | case "int":
48 | case "int32":
49 | data = 2;
50 | break;
51 | case "datetime":
52 | data = DateTime.Now;
53 | break;
54 | case "bool":
55 | case "boolean":
56 | data = true;
57 | break;
58 | case "decimal":
59 | data = decimal.Parse("1.23");
60 | break;
61 | case "double":
62 | data = double.Parse("3.21");
63 | break;
64 | default:
65 | if (propType.IsInterface)
66 | {
67 | propObj = null;
68 | }
69 | else if (propType.IsArray)
70 | {
71 | var elementType = propType.GetElementType();
72 | propObj = Array.CreateInstance(elementType, 1);
73 | }
74 | else
75 | {
76 | var ctr = propType.GetConstructors()[0];
77 | propObj = ctr.GetParameters().Any() ? null : Activator.CreateInstance(propType);
78 | }
79 | data = propObj;
80 | break;
81 | }
82 | if (data != null && prop.CanWrite)
83 | {
84 | prop.SetValue(domainObject, data);
85 | Assert.AreEqual(data, prop.GetValue(domainObject));
86 | }
87 | if (recursive && propObj != null)
88 | {
89 | if (propType.IsGenericType)
90 | {
91 | var myListElementType = propType.GetGenericArguments().Single();
92 | propObj = Activator.CreateInstance(myListElementType);
93 | }
94 | SetProperties(propObj, recursive);
95 | }
96 | }
97 | catch (Exception ex)
98 | {
99 | var msg = string.Format(
100 | "Error creating instance of {0} because: {1}",
101 | propType.Name, ex.Message);
102 | Assert.IsNotNull(prop.GetValue(domainObject), msg);
103 | }
104 | }
105 | return domainObject;
106 | }
107 | }
108 | public class TestHelpers
109 | {
110 | public static string RandomString(int size)
111 | {
112 | StringBuilder builder = new StringBuilder();
113 | char ch;
114 | for (int i = 0; i < size; i++)
115 | {
116 | ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));
117 | builder.Append(ch);
118 | }
119 | return builder.ToString();
120 | }
121 | private static readonly Random random = new((int)DateTime.Now.Ticks);
122 | }
123 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Core.Tests/SnakeCaseContractResolverTests.cs:
--------------------------------------------------------------------------------
1 | using BookmarkSync.Core.Json;
2 |
3 | namespace BookmarkSync.Core.Tests;
4 |
5 | [TestClass]
6 | public class SnakeCaseContractResolverTests
7 | {
8 | [DataTestMethod]
9 | [DataRow("clientId", "client_id")]
10 | public void ResolvePropertyName_IsSnakeCase(string input, string expected)
11 | {
12 | // Arrange
13 | var resolver = SnakeCaseContractResolver.Instance;
14 |
15 | // Act
16 | var actual = resolver.GetResolvedPropertyName(input);
17 |
18 | // Assert
19 | Assert.AreEqual(expected, actual);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Core.Tests/Utilities/HtmlUtilitiesTests.cs:
--------------------------------------------------------------------------------
1 | using BookmarkSync.Core.Utilities;
2 |
3 | namespace BookmarkSync.Core.Tests.Utilities;
4 |
5 | [TestClass]
6 | public class HtmlUtilitiesTests
7 | {
8 | [TestMethod]
9 | public void ConvertToPlainText_Success()
10 | {
11 | // Arrange
12 | const string
13 | html = "example paragraph
",
14 | expected = "\r\nexample paragraph";
15 |
16 | // Act
17 | string actual = HtmlUtilities.ConvertToPlainText(html);
18 |
19 | // Assert
20 | Assert.AreEqual(expected, actual);
21 | }
22 | [TestMethod]
23 | public void ConvertToPlainText_WithBr()
24 | {
25 | // Arrange
26 | const string
27 | html = "example paragraph
with breaks
",
28 | expected = "\r\nexample paragraph\r\nwith breaks";
29 |
30 | // Act
31 | string actual = HtmlUtilities.ConvertToPlainText(html);
32 |
33 | // Assert
34 | Assert.AreEqual(expected, actual);
35 | }
36 | [TestMethod]
37 | public void ConvertToPlainText_WithComments()
38 | {
39 | // Arrange
40 | const string
41 | html = """
42 |
43 | html document
44 | """,
45 | expected = "html document";
46 |
47 | // Act
48 | string actual = HtmlUtilities.ConvertToPlainText(html).Trim();
49 |
50 | // Assert
51 | Assert.AreEqual(expected, actual);
52 | }
53 |
54 | [TestMethod]
55 | public void ConvertToPlainText_WithScripts()
56 | {
57 | // Arrange
58 | const string
59 | html = """
60 | html document
61 |
64 | """,
65 | expected = "html document";
66 |
67 | // Act
68 | string actual = HtmlUtilities.ConvertToPlainText(html).Trim();
69 |
70 | // Assert
71 | Assert.AreEqual(expected, actual);
72 | }
73 | [TestMethod]
74 | public void ConvertToPlainText_WithStyle()
75 | {
76 | // Arrange
77 | const string
78 | html = """
79 |
85 | html document
86 | """,
87 | expected = "html document";
88 |
89 | // Act
90 | string actual = HtmlUtilities.ConvertToPlainText(html).Trim();
91 |
92 | // Assert
93 | Assert.AreEqual(expected, actual);
94 | }
95 | }
--------------------------------------------------------------------------------
/tests/BookmarkSync.Core.Tests/Utilities/UriUtilitiesTests.cs:
--------------------------------------------------------------------------------
1 | using BookmarkSync.Core.Utilities;
2 |
3 | namespace BookmarkSync.Core.Tests.Utilities;
4 |
5 | [TestClass]
6 | public class UriUtilitiesTests
7 | {
8 | [DataTestMethod]
9 | [DataRow("https://example.com")]
10 | [DataRow("https://example.com/")]
11 | public void UriUtilities_HttpsProto(string uri)
12 | {
13 | string actual = uri.RemoveProto();
14 | Assert.AreEqual("example.com", actual);
15 | }
16 | [DataTestMethod]
17 | [DataRow("http://example.com")]
18 | [DataRow("http://example.com/")]
19 | public void UriUtilities_HttpProto(string uri)
20 | {
21 | string actual = uri.RemoveProto();
22 | Assert.AreEqual("example.com", actual);
23 | }
24 | [DataTestMethod]
25 | [DataRow("example.com")]
26 | [DataRow("example.com/")]
27 | public void UriUtilities_NoProto(string uri)
28 | {
29 | string actual = uri.RemoveProto();
30 | Assert.AreEqual("example.com", actual);
31 | }
32 | [TestMethod]
33 | [ExpectedException(typeof(ArgumentException))]
34 | public void UrlUtilities_RemoveProto_EmptyString()
35 | {
36 | // Act
37 | string.Empty.RemoveProto();
38 |
39 | // Assert - Exception: ArgumentException
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/BookmarkSync.Infrastructure.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | enable
6 | enable
7 |
8 | false
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 |
18 |
19 |
20 | all
21 | runtime; build; native; contentfiles; analyzers; buildtransitive
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/GlobalUsings.cs:
--------------------------------------------------------------------------------
1 | // Global using directives
2 |
3 | global using BookmarkSync.Core.Configuration;
4 | global using BookmarkSync.Infrastructure.Services.Bookmarking;
5 | global using BookmarkSync.Infrastructure.Services.Bookmarking.Briefkasten;
6 | global using BookmarkSync.Infrastructure.Services.Bookmarking.LinkAce;
7 | global using BookmarkSync.Infrastructure.Services.Bookmarking.Linkding;
8 | global using BookmarkSync.Infrastructure.Services.Bookmarking.Pinboard;
9 | global using Microsoft.Extensions.Configuration;
10 | global using Microsoft.VisualStudio.TestTools.UnitTesting;
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/Services/BookmarkingServiceTests.cs:
--------------------------------------------------------------------------------
1 | namespace BookmarkSync.Infrastructure.Tests.Services;
2 |
3 | [TestClass]
4 | public class BookmarkingServiceTests
5 | {
6 | [TestMethod]
7 | [ExpectedException(typeof(InvalidOperationException))]
8 | public void GetBookmarkingService_Exception()
9 | {
10 | // Arrange
11 | var config = new Dictionary
12 | {
13 | {
14 | "App:Bookmarking:Service", "Nonsense"
15 | }
16 | };
17 | var configuration = new ConfigurationBuilder()
18 | .AddInMemoryCollection(config)
19 | .Build();
20 |
21 | IConfigManager configManager = new ConfigManager(configuration);
22 | HttpClient httpClient = new();
23 |
24 | // Act
25 | var unused = BookmarkingService.GetBookmarkingService(configManager, httpClient);
26 |
27 | // Assert - Exception
28 | }
29 | [TestMethod]
30 | public void GetBookmarkingService_Briefkasten()
31 | {
32 | // Arrange
33 | var config = new Dictionary
34 | {
35 | {
36 | "App:Bookmarking:Service", "Briefkasten"
37 | },
38 | {
39 | "App:Bookmarking:ApiToken", "ABC123DEF456"
40 | },
41 | {
42 | "App:Bookmarking:BriefkastenUri", "https://briefkastenhq.com"
43 | }
44 | };
45 | var configuration = new ConfigurationBuilder()
46 | .AddInMemoryCollection(config)
47 | .Build();
48 |
49 | IConfigManager configManager = new ConfigManager(configuration);
50 | HttpClient httpClient = new();
51 |
52 | // Act
53 | var obj = BookmarkingService.GetBookmarkingService(configManager, httpClient);
54 |
55 | // Assert
56 | Assert.AreEqual(typeof(BriefkastenBookmarkingService), obj.GetType());
57 | Assert.IsInstanceOfType(obj, typeof(BriefkastenBookmarkingService));
58 | }
59 | [TestMethod]
60 | public void GetBookmarkingService_LinkAce()
61 | {
62 | // Arrange
63 | var config = new Dictionary
64 | {
65 | {
66 | "App:Bookmarking:Service", "LinkAce"
67 | },
68 | {
69 | "App:Bookmarking:ApiToken", "secret:123456789"
70 | },
71 | {
72 | "App:Bookmarking:LinkAceUri", "https://your-linkace-url.com"
73 | }
74 | };
75 | var configuration = new ConfigurationBuilder()
76 | .AddInMemoryCollection(config)
77 | .Build();
78 |
79 | IConfigManager configManager = new ConfigManager(configuration);
80 | HttpClient httpClient = new();
81 |
82 | // Act
83 | var obj = BookmarkingService.GetBookmarkingService(configManager, httpClient);
84 |
85 | // Assert
86 | Assert.AreEqual(typeof(LinkAceBookmarkingService), obj.GetType());
87 | Assert.IsInstanceOfType(obj, typeof(LinkAceBookmarkingService));
88 | }
89 | [TestMethod]
90 | public void GetBookmarkingService_Linkding()
91 | {
92 | // Arrange
93 | var config = new Dictionary
94 | {
95 | {
96 | "App:Bookmarking:Service", "linkding"
97 | },
98 | {
99 | "App:Bookmarking:ApiToken", "3781368521fd4fae365dac100f28dbce533a4f9a"
100 | },
101 | {
102 | "App:Bookmarking:LinkdingUri", "https://your-linkace-url.com"
103 | }
104 | };
105 | var configuration = new ConfigurationBuilder()
106 | .AddInMemoryCollection(config)
107 | .Build();
108 |
109 | IConfigManager configManager = new ConfigManager(configuration);
110 | HttpClient httpClient = new();
111 |
112 | // Act
113 | var obj = BookmarkingService.GetBookmarkingService(configManager, httpClient);
114 |
115 | // Assert
116 | Assert.AreEqual(typeof(LinkdingBookmarkingService), obj.GetType());
117 | Assert.IsInstanceOfType(obj, typeof(LinkdingBookmarkingService));
118 | }
119 | [TestMethod]
120 | public void GetBookmarkingService_Pinboard()
121 | {
122 | // Arrange
123 | var config = new Dictionary
124 | {
125 | {
126 | "App:Bookmarking:Service", "Pinboard"
127 | },
128 | {
129 | "App:Bookmarking:ApiToken", "secret:123456789"
130 | }
131 | };
132 | var configuration = new ConfigurationBuilder()
133 | .AddInMemoryCollection(config)
134 | .Build();
135 |
136 | IConfigManager configManager = new ConfigManager(configuration);
137 | HttpClient httpClient = new();
138 |
139 | // Act
140 | var obj = BookmarkingService.GetBookmarkingService(configManager, httpClient);
141 |
142 | // Assert
143 | Assert.AreEqual(typeof(PinboardBookmarkingService), obj.GetType());
144 | Assert.IsInstanceOfType(obj, typeof(PinboardBookmarkingService));
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/Services/Briefkasten/BriefkastenBookmarkingServiceTests.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using BookmarkSync.Core.Entities;
3 | using Moq;
4 | using Moq.Protected;
5 |
6 | namespace BookmarkSync.Infrastructure.Tests.Services.Briefkasten;
7 |
8 | [TestClass]
9 | public class BriefkastenBookmarkingServiceTests
10 | {
11 | [TestInitialize]
12 | public void Init()
13 | {
14 | var config = new Dictionary
15 | {
16 | {
17 | "App:Bookmarking:Service", "Briefkasten"
18 | },
19 | {
20 | "App:Bookmarking:ApiToken", "token123456"
21 | },
22 | {
23 | "App:Bookmarking:BriefkastenUri", "https://briefkastenhq.com/"
24 | }
25 | };
26 | var configuration = new ConfigurationBuilder()
27 | .AddInMemoryCollection(config)
28 | .Build();
29 | IConfigManager configManager = new ConfigManager(configuration);
30 | _configManager = configManager;
31 | }
32 | private IConfigManager _configManager;
33 | [TestMethod]
34 | public async Task Briefkasten_Save_Success()
35 | {
36 | // Arrange
37 | var httpResponse = new HttpResponseMessage();
38 | httpResponse.StatusCode = HttpStatusCode.OK;
39 |
40 | Mock mockHandler = new();
41 | mockHandler.Protected()
42 | .Setup>("SendAsync",
43 | ItExpr.Is(r =>
44 | r.Method == HttpMethod.Post && r.RequestUri.ToString()
45 | .StartsWith(_configManager.GetConfigValue("App:Bookmarking:BriefkastenUri"))),
46 | ItExpr.IsAny())
47 | .ReturnsAsync(httpResponse);
48 |
49 | var httpClient = new HttpClient(mockHandler.Object);
50 | var bookmarkingService = BookmarkingService.GetBookmarkingService(_configManager, httpClient);
51 | var bookmark = new Bookmark
52 | {
53 | Uri = "https://example.com"
54 | };
55 |
56 | // Act
57 | var result = await bookmarkingService.Save(bookmark);
58 |
59 | // Assert
60 | Assert.IsNotNull(result);
61 | Assert.IsTrue(result.IsSuccessStatusCode);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/Services/LinkAce/LinkAceBookmarkingServiceTests.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Mime;
3 | using System.Text;
4 | using BookmarkSync.Core.Entities;
5 | using Moq;
6 | using Moq.Protected;
7 |
8 | namespace BookmarkSync.Infrastructure.Tests.Services.LinkAce;
9 |
10 | [TestClass]
11 | public class LinkAceBookmarkingServiceTests
12 | {
13 | [TestInitialize]
14 | public void Init()
15 | {
16 | var config = new Dictionary
17 | {
18 | {
19 | "App:Bookmarking:Service", "LinkAce"
20 | },
21 | {
22 | "App:Bookmarking:ApiToken", "token123456"
23 | },
24 | {
25 | "App:Bookmarking:LinkAceUri", "https://links.example.com/"
26 | }
27 | };
28 | var configuration = new ConfigurationBuilder()
29 | .AddInMemoryCollection(config)
30 | .Build();
31 | IConfigManager configManager = new ConfigManager(configuration);
32 | _configManager = configManager;
33 | }
34 | private IConfigManager _configManager;
35 | [TestMethod]
36 | public void LinkAce_Save_Success()
37 | {
38 | // Arrange
39 | var getHttpResponse = new HttpResponseMessage();
40 | getHttpResponse.StatusCode = HttpStatusCode.OK;
41 | getHttpResponse.Content = new StringContent(@"
42 | {
43 | ""current_page"": 1,
44 | ""data"": [],
45 | ""first_page_url"": ""https://links.fminus.co/api/v1/search/links?page=1"",
46 | ""from"": null,
47 | ""last_page"": 1,
48 | ""last_page_url"": ""https://links.fminus.co/api/v1/search/links?page=1"",
49 | ""links"": [
50 | {
51 | ""url"": null,
52 | ""label"": ""« Previous"",
53 | ""active"": false
54 | },
55 | {
56 | ""url"": ""https://links.fminus.co/api/v1/search/links?page=1"",
57 | ""label"": ""1"",
58 | ""active"": true
59 | },
60 | {
61 | ""url"": null,
62 | ""label"": ""Next »"",
63 | ""active"": false
64 | }
65 | ],
66 | ""next_page_url"": null,
67 | ""path"": ""https://links.fminus.co/api/v1/search/links"",
68 | ""per_page"": 24,
69 | ""prev_page_url"": null,
70 | ""to"": null,
71 | ""total"": 0
72 | }
73 | ", Encoding.UTF8, MediaTypeNames.Application.Json);
74 | var postHttpResponse = new HttpResponseMessage();
75 | postHttpResponse.StatusCode = HttpStatusCode.OK;
76 |
77 | Mock mockHandler = new();
78 | // GET
79 | mockHandler.Protected()
80 | .Setup>("SendAsync",
81 | ItExpr.Is(r =>
82 | r.Method == HttpMethod.Get && r.RequestUri.ToString()
83 | .StartsWith(_configManager.GetConfigValue("App:Bookmarking:LinkAceUri"))),
84 | ItExpr.IsAny())
85 | .ReturnsAsync(getHttpResponse);
86 | // POST
87 | mockHandler.Protected()
88 | .Setup>("SendAsync",
89 | ItExpr.Is(r =>
90 | r.Method == HttpMethod.Post && r.RequestUri.ToString()
91 | .StartsWith(_configManager.GetConfigValue("App:Bookmarking:LinkAceUri"))),
92 | ItExpr.IsAny())
93 | .ReturnsAsync(postHttpResponse);
94 |
95 | var httpClient = new HttpClient(mockHandler.Object);
96 | var bookmarkingService = BookmarkingService.GetBookmarkingService(_configManager, httpClient);
97 | var bookmark = new Bookmark
98 | {
99 | Uri = "https://example.com"
100 | };
101 |
102 | // Act
103 | var result = bookmarkingService.Save(bookmark).Result;
104 |
105 | // Assert
106 | Assert.IsNotNull(result);
107 | Assert.IsTrue(result.IsSuccessStatusCode);
108 | }
109 | [TestMethod]
110 | public void LinkAce_Save_Success_Link_Exists()
111 | {
112 | // Arrange
113 | var getHttpResponse = new HttpResponseMessage();
114 | getHttpResponse.StatusCode = HttpStatusCode.OK;
115 | getHttpResponse.Content = new StringContent(@"
116 | {
117 | ""current_page"": 1,
118 | ""data"": [
119 | {
120 | ""id"": 1098,
121 | ""user_id"": 1,
122 | ""url"": ""https://photos.jrgnsn.net/album/classic-atreus-build"",
123 | ""title"": ""Classic Atreus Build - jphotos"",
124 | ""description"": ""Pictures from my Atreus keyboard build"",
125 | ""is_private"": false,
126 | ""created_at"": ""2020-09-21T21:39:11.000000Z"",
127 | ""updated_at"": ""2023-06-09T16:16:37.000000Z"",
128 | ""deleted_at"": null,
129 | ""icon"": ""link"",
130 | ""status"": 1,
131 | ""check_disabled"": false,
132 | ""thumbnail"": null,
133 | ""tags"": [
134 | {
135 | ""id"": 603,
136 | ""user_id"": 1,
137 | ""name"": ""atreus"",
138 | ""is_private"": false,
139 | ""created_at"": ""2023-06-09T16:16:36.000000Z"",
140 | ""updated_at"": ""2023-06-09T16:16:36.000000Z"",
141 | ""deleted_at"": null,
142 | ""pivot"": {
143 | ""link_id"": 1098,
144 | ""tag_id"": 603
145 | }
146 | },
147 | {
148 | ""id"": 234,
149 | ""user_id"": 1,
150 | ""name"": ""diy"",
151 | ""is_private"": false,
152 | ""created_at"": ""2023-06-09T16:07:30.000000Z"",
153 | ""updated_at"": ""2023-06-09T16:07:30.000000Z"",
154 | ""deleted_at"": null,
155 | ""pivot"": {
156 | ""link_id"": 1098,
157 | ""tag_id"": 234
158 | }
159 | },
160 | {
161 | ""id"": 605,
162 | ""user_id"": 1,
163 | ""name"": ""keyboard"",
164 | ""is_private"": false,
165 | ""created_at"": ""2023-06-09T16:16:37.000000Z"",
166 | ""updated_at"": ""2023-06-09T16:16:37.000000Z"",
167 | ""deleted_at"": null,
168 | ""pivot"": {
169 | ""link_id"": 1098,
170 | ""tag_id"": 605
171 | }
172 | }
173 | ]
174 | }
175 | ],
176 | ""first_page_url"": ""https://links.fminus.co/api/v1/search/links?page=1"",
177 | ""from"": 1,
178 | ""last_page"": 1,
179 | ""last_page_url"": ""https://links.fminus.co/api/v1/search/links?page=1"",
180 | ""links"": [
181 | {
182 | ""url"": null,
183 | ""label"": ""« Previous"",
184 | ""active"": false
185 | },
186 | {
187 | ""url"": ""https://links.fminus.co/api/v1/search/links?page=1"",
188 | ""label"": ""1"",
189 | ""active"": true
190 | },
191 | {
192 | ""url"": null,
193 | ""label"": ""Next »"",
194 | ""active"": false
195 | }
196 | ],
197 | ""next_page_url"": null,
198 | ""path"": ""https://links.fminus.co/api/v1/search/links"",
199 | ""per_page"": 24,
200 | ""prev_page_url"": null,
201 | ""to"": 1,
202 | ""total"": 1
203 | }
204 | ", Encoding.UTF8, MediaTypeNames.Application.Json);
205 | var postHttpResponse = new HttpResponseMessage();
206 | postHttpResponse.StatusCode = HttpStatusCode.OK;
207 |
208 | Mock mockHandler = new();
209 | // GET
210 | mockHandler.Protected()
211 | .Setup>("SendAsync",
212 | ItExpr.Is(r =>
213 | r.Method == HttpMethod.Get && r.RequestUri.ToString()
214 | .StartsWith(_configManager.GetConfigValue("App:Bookmarking:LinkAceUri"))),
215 | ItExpr.IsAny())
216 | .ReturnsAsync(getHttpResponse);
217 | // POST
218 | mockHandler.Protected()
219 | .Setup>("SendAsync",
220 | ItExpr.Is(r =>
221 | r.Method == HttpMethod.Patch && r.RequestUri.ToString()
222 | .StartsWith(_configManager.GetConfigValue("App:Bookmarking:LinkAceUri"))),
223 | ItExpr.IsAny())
224 | .ReturnsAsync(postHttpResponse);
225 |
226 | var httpClient = new HttpClient(mockHandler.Object);
227 | var bookmarkingService = BookmarkingService.GetBookmarkingService(_configManager, httpClient);
228 | var bookmark = new Bookmark
229 | {
230 | Uri = "https://photos.jrgnsn.net/album/classic-atreus-build"
231 | };
232 |
233 | // Act
234 | var result = bookmarkingService.Save(bookmark).Result;
235 |
236 | // Assert
237 | Assert.IsNotNull(result);
238 | Assert.IsTrue(result.IsSuccessStatusCode);
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/Services/Linkding/LinkdingBookmarkingServiceTests.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using BookmarkSync.Core.Entities;
3 | using Moq;
4 | using Moq.Protected;
5 |
6 | namespace BookmarkSync.Infrastructure.Tests.Services.Linkding;
7 |
8 | [TestClass]
9 | public class LinkdingBookmarkingServiceTests
10 | {
11 | [TestInitialize]
12 | public void Init()
13 | {
14 | var config = new Dictionary
15 | {
16 | {
17 | "App:Bookmarking:Service", "linkding"
18 | },
19 | {
20 | "App:Bookmarking:ApiToken", "token123456"
21 | },
22 | {
23 | "App:Bookmarking:LinkdingUri", "https://links.example.com"
24 | }
25 | };
26 | var configuration = new ConfigurationBuilder()
27 | .AddInMemoryCollection(config)
28 | .Build();
29 | IConfigManager configManager = new ConfigManager(configuration);
30 | _configManager = configManager;
31 | }
32 | private IConfigManager _configManager;
33 | [TestMethod]
34 | public async Task Linkding_Save_Success()
35 | {
36 | // Arrange
37 | var httpResponse = new HttpResponseMessage();
38 | httpResponse.StatusCode = HttpStatusCode.OK;
39 |
40 | Mock mockHandler = new();
41 | mockHandler.Protected()
42 | .Setup>("SendAsync",
43 | ItExpr.Is(r =>
44 | r.Method == HttpMethod.Post && r.RequestUri.ToString()
45 | .StartsWith(_configManager.GetConfigValue("App:Bookmarking:LinkdingUri"))),
46 | ItExpr.IsAny())
47 | .ReturnsAsync(httpResponse);
48 |
49 | var httpClient = new HttpClient(mockHandler.Object);
50 | var bookmarkingService = BookmarkingService.GetBookmarkingService(_configManager, httpClient);
51 | var bookmark = new Bookmark
52 | {
53 | Uri = "https://example.com"
54 | };
55 |
56 | // Act
57 | var result = await bookmarkingService.Save(bookmark);
58 |
59 | // Assert
60 | Assert.IsNotNull(result);
61 | Assert.IsTrue(result.IsSuccessStatusCode);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/Services/Mastodon/MastodonServiceTests.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Mime;
3 | using System.Text;
4 | using BookmarkSync.Core.Entities;
5 | using BookmarkSync.Core.Entities.Config;
6 | using BookmarkSync.Infrastructure.Services.Mastodon;
7 | using Moq;
8 | using Moq.Protected;
9 | using Newtonsoft.Json;
10 | using Serilog;
11 | using Serilog.Events;
12 | using Serilog.Sinks.TestCorrelator;
13 |
14 | namespace BookmarkSync.Infrastructure.Tests.Services.Mastodon;
15 |
16 | [TestClass]
17 | public class MastodonServiceTests
18 | {
19 | [AssemblyInitialize]
20 | public static void ConfigureGlobalLogger(TestContext testContext)
21 | {
22 | Log.Logger = new LoggerConfiguration()
23 | .MinimumLevel.Debug()
24 | .WriteTo.TestCorrelator().CreateLogger();
25 | }
26 | [TestMethod]
27 | public async Task MastodonService_Delete_Bookmark_Success()
28 | {
29 | // Arrange
30 | var httpResponse = new HttpResponseMessage();
31 | httpResponse.StatusCode = HttpStatusCode.OK;
32 | var instance = new Instance
33 | {
34 | Uri = "https://localhost:3000"
35 | };
36 |
37 | Mock mockHandler = new();
38 | mockHandler.Protected()
39 | .Setup>("SendAsync",
40 | ItExpr.Is(r =>
41 | r.Method == HttpMethod.Post && r.RequestUri.ToString().StartsWith(instance.Uri)),
42 | ItExpr.IsAny())
43 | .ReturnsAsync(httpResponse);
44 |
45 | var httpClient = new HttpClient(mockHandler.Object);
46 |
47 | using (TestCorrelator.CreateContext())
48 | {
49 | var mastodonService = new MastodonService(httpClient);
50 | mastodonService.SetInstance(instance);
51 |
52 | // Act
53 | await mastodonService.DeleteBookmark(new Bookmark
54 | {
55 | Id = "123456"
56 | });
57 |
58 | // Assert
59 | var logs = TestCorrelator.GetLogEventsFromCurrentContext();
60 | Assert.IsNotNull(logs);
61 | var logEvents = logs.ToList();
62 | Assert.AreEqual(2, logEvents.Count);
63 | var logMessage = logEvents.Where(e => e.Level == LogEventLevel.Information).ToList()[0];
64 | Assert.AreEqual(LogEventLevel.Information, logMessage.Level);
65 | Assert.IsTrue(logMessage.MessageTemplate.ToString().Contains("deleted successfully"));
66 | }
67 | }
68 | [TestMethod]
69 | public async Task MastodonService_Failed_To_Delete_Bookmark()
70 | {
71 | // Arrange
72 | var httpResponse = new HttpResponseMessage();
73 | httpResponse.StatusCode = HttpStatusCode.BadRequest;
74 | var instance = new Instance
75 | {
76 | Uri = "https://localhost:3000"
77 | };
78 |
79 | Mock mockHandler = new();
80 | mockHandler.Protected()
81 | .Setup>("SendAsync",
82 | ItExpr.Is(r =>
83 | r.Method == HttpMethod.Post && r.RequestUri.ToString().StartsWith(instance.Uri)),
84 | ItExpr.IsAny())
85 | .ReturnsAsync(httpResponse);
86 |
87 | var httpClient = new HttpClient(mockHandler.Object);
88 |
89 | using (TestCorrelator.CreateContext())
90 | {
91 | var mastodonService = new MastodonService(httpClient);
92 | mastodonService.SetInstance(instance);
93 |
94 | // Act
95 | await mastodonService.DeleteBookmark(new Bookmark
96 | {
97 | Id = "123456"
98 | });
99 |
100 | // Assert
101 | var logs = TestCorrelator.GetLogEventsFromCurrentContext();
102 | Assert.IsNotNull(logs);
103 | var logEvents = logs.ToList();
104 | Assert.AreEqual(2, logEvents.Count);
105 | var logMessage = logEvents.Where(e => e.Level == LogEventLevel.Warning).ToList()[0];
106 | Assert.AreEqual(LogEventLevel.Warning, logMessage.Level);
107 | Assert.IsTrue(logMessage.MessageTemplate.ToString().Contains("Couldn't delete bookmark"));
108 | Assert.IsTrue(logMessage.Properties.TryGetValue("StatusCode", out var statusCodeValue));
109 | var statusCode = (HttpStatusCode?)((ScalarValue)statusCodeValue).Value;
110 | Assert.AreEqual(httpResponse.StatusCode, statusCode);
111 | }
112 | }
113 | [TestMethod]
114 | public async Task MastodonService_Failed_To_Delete_Bookmark_403_Forbidden()
115 | {
116 | // Arrange
117 | var httpResponse = new HttpResponseMessage();
118 | httpResponse.StatusCode = HttpStatusCode.Forbidden;
119 | var instance = new Instance
120 | {
121 | Uri = "https://localhost:3000"
122 | };
123 |
124 | Mock mockHandler = new();
125 | mockHandler.Protected()
126 | .Setup>("SendAsync",
127 | ItExpr.Is(r =>
128 | r.Method == HttpMethod.Post && r.RequestUri.ToString().StartsWith(instance.Uri)),
129 | ItExpr.IsAny())
130 | .ReturnsAsync(httpResponse);
131 |
132 | var httpClient = new HttpClient(mockHandler.Object);
133 |
134 | using (TestCorrelator.CreateContext())
135 | {
136 | var mastodonService = new MastodonService(httpClient);
137 | mastodonService.SetInstance(instance);
138 |
139 | // Act
140 | await mastodonService.DeleteBookmark(new Bookmark
141 | {
142 | Id = "123456"
143 | });
144 |
145 | // Assert
146 | var logs = TestCorrelator.GetLogEventsFromCurrentContext();
147 | Assert.IsNotNull(logs);
148 | var logEvents = logs.ToList();
149 | Assert.AreEqual(2, logEvents.Count);
150 | var logMessage = logEvents.Where(e => e.Level == LogEventLevel.Warning).ToList()[0];
151 | Assert.AreEqual(LogEventLevel.Warning, logMessage.Level);
152 | Assert.IsTrue(logMessage.MessageTemplate.ToString().Contains("403 Forbidden"));
153 | }
154 | }
155 | [TestMethod]
156 | public async Task MastodonService_GetBookmarks_Success_Empty_List()
157 | {
158 | // Arrange
159 | var expectedBookmarkList = new List();
160 | string json = JsonConvert.SerializeObject(expectedBookmarkList);
161 |
162 | var httpResponse = new HttpResponseMessage();
163 | httpResponse.StatusCode = HttpStatusCode.OK;
164 | httpResponse.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
165 | var instance = new Instance
166 | {
167 | Uri = "https://localhost:3000"
168 | };
169 |
170 | Mock mockHandler = new();
171 | mockHandler.Protected()
172 | .Setup>("SendAsync",
173 | ItExpr.Is(r =>
174 | r.Method == HttpMethod.Get && r.RequestUri.ToString().StartsWith(instance.Uri)),
175 | ItExpr.IsAny())
176 | .ReturnsAsync(httpResponse);
177 |
178 | var httpClient = new HttpClient(mockHandler.Object);
179 |
180 | using (TestCorrelator.CreateContext())
181 | {
182 | var mastodonService = new MastodonService(httpClient);
183 | mastodonService.SetInstance(instance);
184 |
185 | // Act
186 | var result = await mastodonService.GetBookmarks();
187 |
188 | // Assert
189 | var logs = TestCorrelator.GetLogEventsFromCurrentContext();
190 | Assert.IsNotNull(logs);
191 | var logEvents = logs.ToList();
192 | Assert.AreEqual(1, logEvents.Count);
193 | var logMessage = logEvents[0];
194 | Assert.AreEqual(LogEventLevel.Debug, logMessage.Level);
195 |
196 | Assert.AreEqual(0, result.Count);
197 | }
198 | }
199 | [TestMethod]
200 | public async Task MastodonService_GetBookmarks_Success_Non_Empty_List()
201 | {
202 | // Arrange
203 | var expectedBookmarkList = new List
204 | {
205 | new(),
206 | new()
207 | };
208 | string json = JsonConvert.SerializeObject(expectedBookmarkList);
209 |
210 | var httpResponse = new HttpResponseMessage();
211 | httpResponse.StatusCode = HttpStatusCode.OK;
212 | httpResponse.Content = new StringContent(json, Encoding.UTF8, MediaTypeNames.Application.Json);
213 | var instance = new Instance
214 | {
215 | Uri = "https://localhost:3000"
216 | };
217 |
218 | Mock mockHandler = new();
219 | mockHandler.Protected()
220 | .Setup>("SendAsync",
221 | ItExpr.Is(r =>
222 | r.Method == HttpMethod.Get && r.RequestUri.ToString().StartsWith(instance.Uri)),
223 | ItExpr.IsAny())
224 | .ReturnsAsync(httpResponse);
225 |
226 | var httpClient = new HttpClient(mockHandler.Object);
227 |
228 | using (TestCorrelator.CreateContext())
229 | {
230 | var mastodonService = new MastodonService(httpClient);
231 | mastodonService.SetInstance(instance);
232 |
233 | // Act
234 | var result = await mastodonService.GetBookmarks();
235 |
236 | // Assert
237 | var logs = TestCorrelator.GetLogEventsFromCurrentContext();
238 | Assert.IsNotNull(logs);
239 | var logEvents = logs.ToList();
240 | Assert.AreEqual(1, logEvents.Count);
241 | var logMessage = logEvents[0];
242 | Assert.AreEqual(LogEventLevel.Debug, logMessage.Level);
243 |
244 | Assert.AreEqual(2, result.Count);
245 | }
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/tests/BookmarkSync.Infrastructure.Tests/Services/Pinboard/PinboardBookmarkingServiceTests.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using BookmarkSync.Core.Entities;
3 | using Moq;
4 | using Moq.Protected;
5 |
6 | namespace BookmarkSync.Infrastructure.Tests.Services.Pinboard;
7 |
8 | [TestClass]
9 | public class PinboardBookmarkingServiceTests
10 | {
11 | [TestInitialize]
12 | public void Init()
13 | {
14 | var config = new Dictionary
15 | {
16 | {
17 | "App:Bookmarking:Service", "Pinboard"
18 | },
19 | {
20 | "App:Bookmarking:ApiToken", "token123456"
21 | }
22 | };
23 | var configuration = new ConfigurationBuilder()
24 | .AddInMemoryCollection(config)
25 | .Build();
26 | IConfigManager configManager = new ConfigManager(configuration);
27 | _configManager = configManager;
28 | }
29 | private IConfigManager _configManager;
30 | private readonly string _pinboardUri = "https://api.pinboard.in";
31 | [TestMethod]
32 | public async Task Pinboard_Save_Success()
33 | {
34 | // Arrange
35 | var httpResponse = new HttpResponseMessage();
36 | httpResponse.StatusCode = HttpStatusCode.OK;
37 |
38 | Mock mockHandler = new();
39 | mockHandler.Protected()
40 | .Setup>("SendAsync",
41 | ItExpr.Is(r =>
42 | r.Method == HttpMethod.Get && r.RequestUri.ToString()
43 | .StartsWith(_pinboardUri)),
44 | ItExpr.IsAny())
45 | .ReturnsAsync(httpResponse);
46 |
47 | var httpClient = new HttpClient(mockHandler.Object);
48 | var bookmarkingService = BookmarkingService.GetBookmarkingService(_configManager, httpClient);
49 | var bookmark = new Bookmark
50 | {
51 | Uri = "https://example.com"
52 | };
53 |
54 | // Act
55 | var result = await bookmarkingService.Save(bookmark);
56 |
57 | // Assert
58 | Assert.IsNotNull(result);
59 | Assert.IsTrue(result.IsSuccessStatusCode);
60 | }
61 | [TestMethod]
62 | public async Task Pinboard_Save_Success_Long_Title()
63 | {
64 | // Arrange
65 | var httpResponse = new HttpResponseMessage();
66 | httpResponse.StatusCode = HttpStatusCode.OK;
67 |
68 | Mock mockHandler = new();
69 | mockHandler.Protected()
70 | .Setup>("SendAsync",
71 | ItExpr.Is(r =>
72 | r.Method == HttpMethod.Get && r.RequestUri.ToString()
73 | .StartsWith(_pinboardUri)),
74 | ItExpr.IsAny())
75 | .ReturnsAsync(httpResponse);
76 |
77 | var httpClient = new HttpClient(mockHandler.Object);
78 | var bookmarkingService = BookmarkingService.GetBookmarkingService(_configManager, httpClient);
79 | var bookmark = new Bookmark
80 | {
81 | Uri = "https://example.com",
82 | // ReSharper disable StringLiteralTypo
83 | Content =
84 | "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus suscipit, urna quis consectetur sodales, ante enim auctor leo, ut tincidunt nibh tortor ac magna. Aenean suscipit tincidunt tincidunt. Curabitur et eros elit. Duis consequat felis justo, sit amet semper est scelerisque ac. Aenean ac. "
85 | // ReSharper restore StringLiteralTypo
86 | };
87 |
88 | // Act
89 | var result = await bookmarkingService.Save(bookmark);
90 |
91 | // Assert
92 | Assert.IsNotNull(result);
93 | Assert.IsTrue(result.IsSuccessStatusCode);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------