Skip to content

Commit 801caee

Browse files
Refactor URI construction and enhance test coverage (#126)
* Refactor URI construction and enhance test coverage Refactored `DeleteOperation` and `ReplaceOperation` to directly include `operation.ItemId` in URI construction via `MakeAbsoluteUri`. Updated `ExecutableOperation` to support an optional `itemId` parameter in `MakeAbsoluteUri`. Added `MakeAbsoluteUri_WorksWithId` theory test to validate various URI construction scenarios. * Refactor URI handling and update related tests Modified `MakeAbsoluteUri` in `ExecutableOperation.cs` to handle `itemId` more robustly by conditionally formatting it with a leading slash if it is not empty and avoiding trailing slashes before `itemId`. Updated test cases in `OfflineDbContext_Tests.cs`, `AddOperation_Tests.cs`, and `ExecutableOperation_Tests.cs` to reflect the new URI formatting rules, specifically removing trailing slashes before query parameters in the expected URIs. --------- Co-authored-by: Adrian Hall <[email protected]>
1 parent db1a7e2 commit 801caee

File tree

6 files changed

+89
-55
lines changed

6 files changed

+89
-55
lines changed

src/CommunityToolkit.Datasync.Client/Offline/Operations/DeleteOperation.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ internal class DeleteOperation(DatasyncOperation operation) : ExecutableOperatio
2121
/// <returns>The result of the push operation (async).</returns>
2222
internal override async Task<ServiceResponse> ExecuteAsync(EntityDatasyncOptions options, CancellationToken cancellationToken = default)
2323
{
24-
Uri endpoint = MakeAbsoluteUri(options.HttpClient.BaseAddress, options.Endpoint);
25-
using HttpRequestMessage request = new(HttpMethod.Delete, new Uri(endpoint, operation.ItemId));
24+
Uri endpoint = MakeAbsoluteUri(options.HttpClient.BaseAddress, options.Endpoint, operation.ItemId);
25+
using HttpRequestMessage request = new(HttpMethod.Delete, endpoint);
2626
if (!string.IsNullOrEmpty(operation.EntityVersion))
2727
{
2828
request.Headers.IfMatch.Add(new EntityTagHeaderValue($"\"{operation.EntityVersion}\""));

src/CommunityToolkit.Datasync.Client/Offline/Operations/ExecutableOperation.cs

+6-3
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,22 @@ internal abstract class ExecutableOperation
2222
/// </summary>
2323
/// <param name="baseAddress">The base address from the client.</param>
2424
/// <param name="relativeOrAbsoluteUri">A relative or absolute URI</param>
25+
/// <param name="itemId">The id of the item to append to the URI.</param>
2526
/// <returns></returns>
26-
internal static Uri MakeAbsoluteUri(Uri? baseAddress, Uri relativeOrAbsoluteUri)
27+
internal static Uri MakeAbsoluteUri(Uri? baseAddress, Uri relativeOrAbsoluteUri, string? itemId = null)
2728
{
29+
itemId = string.IsNullOrWhiteSpace(itemId) ? string.Empty : $"/{itemId}";
30+
2831
if (relativeOrAbsoluteUri.IsAbsoluteUri)
2932
{
30-
return new Uri($"{relativeOrAbsoluteUri.ToString().TrimEnd('/')}/");
33+
return new Uri($"{relativeOrAbsoluteUri.ToString().TrimEnd('/')}{itemId}");
3134
}
3235

3336
if (baseAddress != null)
3437
{
3538
if (baseAddress.IsAbsoluteUri)
3639
{
37-
return new Uri($"{new Uri(baseAddress, relativeOrAbsoluteUri).ToString().TrimEnd('/')}/");
40+
return new Uri($"{new Uri(baseAddress, relativeOrAbsoluteUri).ToString().TrimEnd('/')}{itemId}");
3841
}
3942
}
4043

src/CommunityToolkit.Datasync.Client/Offline/Operations/ReplaceOperation.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ internal class ReplaceOperation(DatasyncOperation operation) : ExecutableOperati
2121
/// <returns>The result of the push operation (async).</returns>
2222
internal override async Task<ServiceResponse> ExecuteAsync(EntityDatasyncOptions options, CancellationToken cancellationToken = default)
2323
{
24-
Uri endpoint = MakeAbsoluteUri(options.HttpClient.BaseAddress, options.Endpoint);
25-
using HttpRequestMessage request = new(HttpMethod.Put, new Uri(endpoint, operation.ItemId))
24+
Uri endpoint = MakeAbsoluteUri(options.HttpClient.BaseAddress, options.Endpoint, operation.ItemId);
25+
using HttpRequestMessage request = new(HttpMethod.Put, endpoint)
2626
{
2727
Content = new StringContent(operation.Item, JsonMediaType)
2828
};

tests/CommunityToolkit.Datasync.Client.Test/Offline/OfflineDbContext_Tests.cs

+37-37
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,10 @@ public async Task PullAsync_List_Works_InitialSync()
6767
actual.Should().BeEquivalentTo(expected);
6868

6969
this.context.Handler.Requests.Should().HaveCount(4);
70-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$orderby=updatedAt&$count=true&__includedeleted=true");
71-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
72-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
73-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
70+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$orderby=updatedAt&$count=true&__includedeleted=true");
71+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
72+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
73+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
7474

7575
DatasyncDeltaToken token = this.context.DatasyncDeltaTokens.Find(["CommunityToolkit.Datasync.TestCommon.Databases.ClientMovie"]);
7676
token.Should().NotBeNull();
@@ -103,10 +103,10 @@ public async Task PullAsync_DbSet_Works_InitialSync()
103103
actual.Should().BeEquivalentTo(expected);
104104

105105
this.context.Handler.Requests.Should().HaveCount(4);
106-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$orderby=updatedAt&$count=true&__includedeleted=true");
107-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
108-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
109-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
106+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$orderby=updatedAt&$count=true&__includedeleted=true");
107+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
108+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
109+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
110110

111111
DatasyncDeltaToken token = this.context.DatasyncDeltaTokens.Find(["CommunityToolkit.Datasync.TestCommon.Databases.ClientMovie"]);
112112
token.Should().NotBeNull();
@@ -147,10 +147,10 @@ public async Task PullAsync_Configurator_Works_InitialSync_Ver1()
147147
actual.Should().BeEquivalentTo(expected);
148148

149149
this.context.Handler.Requests.Should().HaveCount(4);
150-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$filter=startswith%28title%2C%27abc%27%29&$orderby=updatedAt&$count=true&__includedeleted=true");
151-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
152-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
153-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
150+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$filter=startswith%28title%2C%27abc%27%29&$orderby=updatedAt&$count=true&__includedeleted=true");
151+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
152+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
153+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
154154

155155
DatasyncDeltaToken token = this.context.DatasyncDeltaTokens.Find(["q-CommunityToolkit.Datasync.TestCommon.Databases.ClientMovie-abc"]);
156156
token.Should().NotBeNull();
@@ -191,10 +191,10 @@ public async Task PullAsync_Configurator_Works_InitialSync_Ver2()
191191
actual.Should().BeEquivalentTo(expected);
192192

193193
this.context.Handler.Requests.Should().HaveCount(4);
194-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$filter=startswith%28title%2C%27abc%27%29&$orderby=updatedAt&$count=true&__includedeleted=true");
195-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
196-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
197-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
194+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$filter=startswith%28title%2C%27abc%27%29&$orderby=updatedAt&$count=true&__includedeleted=true");
195+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
196+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
197+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
198198

199199
DatasyncDeltaToken token = this.context.DatasyncDeltaTokens.Find(["q-CommunityToolkit.Datasync.TestCommon.Databases.ClientMovie-a87ec01f71a5577199797b433e3bcc6b"]);
200200
token.Should().NotBeNull();
@@ -242,10 +242,10 @@ public async Task PullAsync_Configurator_Works_InitialSync_Ver3()
242242
actual.Should().BeEquivalentTo(expected);
243243

244244
this.context.Handler.Requests.Should().HaveCount(4);
245-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$filter=startswith%28title%2C%27abc%27%29&$orderby=updatedAt&$count=true&__includedeleted=true");
246-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
247-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
248-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
245+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$filter=startswith%28title%2C%27abc%27%29&$orderby=updatedAt&$count=true&__includedeleted=true");
246+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
247+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
248+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
249249

250250
DatasyncDeltaToken token = this.context.DatasyncDeltaTokens.Find(["q-CommunityToolkit.Datasync.TestCommon.Databases.ClientMovie-a87ec01f71a5577199797b433e3bcc6b"]);
251251
token.Should().NotBeNull();
@@ -281,10 +281,10 @@ public async Task PullAsync_Configurator_Works_InitialSync_Ver4()
281281
actual.Should().BeEquivalentTo(expected);
282282

283283
this.context.Handler.Requests.Should().HaveCount(4);
284-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$orderby=updatedAt&$count=true&__includedeleted=true");
285-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
286-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
287-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
284+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$orderby=updatedAt&$count=true&__includedeleted=true");
285+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
286+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
287+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
288288

289289
DatasyncDeltaToken token = this.context.DatasyncDeltaTokens.Find(["CommunityToolkit.Datasync.TestCommon.Databases.ClientMovie"]);
290290
token.Should().NotBeNull();
@@ -321,10 +321,10 @@ public async Task PullAsync_List_Works_FollowonSync()
321321
actual.Should().BeEquivalentTo(expected);
322322

323323
this.context.Handler.Requests.Should().HaveCount(4);
324-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$filter=%28updatedAt gt cast%282024-08-23T20%3A22%3A54.291Z%2CEdm.DateTimeOffset%29%29&$orderby=updatedAt&$count=true&__includedeleted=true");
325-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
326-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
327-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
324+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$filter=%28updatedAt gt cast%282024-08-23T20%3A22%3A54.291Z%2CEdm.DateTimeOffset%29%29&$orderby=updatedAt&$count=true&__includedeleted=true");
325+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
326+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
327+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
328328
}
329329

330330
[Fact]
@@ -362,10 +362,10 @@ public async Task PullAsync_List_Works_DoesntAddDeletions()
362362
actual.Should().BeEquivalentTo(expected);
363363

364364
this.context.Handler.Requests.Should().HaveCount(4);
365-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$filter=%28updatedAt gt cast%282024-08-23T20%3A22%3A54.291Z%2CEdm.DateTimeOffset%29%29&$orderby=updatedAt&$count=true&__includedeleted=true");
366-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
367-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
368-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
365+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$filter=%28updatedAt gt cast%282024-08-23T20%3A22%3A54.291Z%2CEdm.DateTimeOffset%29%29&$orderby=updatedAt&$count=true&__includedeleted=true");
366+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
367+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
368+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
369369
}
370370

371371
[Fact]
@@ -410,10 +410,10 @@ public async Task PullAsync_List_Works_DeletionsAndReplacements()
410410
actual.Should().BeEquivalentTo(expected);
411411

412412
this.context.Handler.Requests.Should().HaveCount(4);
413-
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$orderby=updatedAt&$count=true&__includedeleted=true");
414-
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=5");
415-
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=10");
416-
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/?$skip=15");
413+
this.context.Handler.Requests[0].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$orderby=updatedAt&$count=true&__includedeleted=true");
414+
this.context.Handler.Requests[1].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=5");
415+
this.context.Handler.Requests[2].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=10");
416+
this.context.Handler.Requests[3].RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies?$skip=15");
417417
}
418418

419419
[Fact]
@@ -426,7 +426,7 @@ public async Task PullAsync_List_FailedRequest()
426426
pullResult.IsSuccessful.Should().BeFalse();
427427
pullResult.FailedRequests.Should().HaveCount(1);
428428
KeyValuePair<Uri, ServiceResponse> kv = pullResult.FailedRequests.Single();
429-
kv.Key.Should().Be("https://test.zumo.net/tables/movies/?$orderby=updatedAt&$count=true&__includedeleted=true");
429+
kv.Key.Should().Be("https://test.zumo.net/tables/movies?$orderby=updatedAt&$count=true&__includedeleted=true");
430430
}
431431

432432
[Fact]

tests/CommunityToolkit.Datasync.Client.Test/Offline/Operations/AddOperation_Tests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ public async Task AddOperation_ExecuteAsync()
5353
HttpRequestMessage request = handler.Requests.SingleOrDefault();
5454
request.Should().NotBeNull();
5555
request.Method.Should().Be(HttpMethod.Post);
56-
request.RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies/");
56+
request.RequestUri.ToString().Should().Be("https://test.zumo.net/tables/movies");
5757
(await request.Content.ReadAsStringAsync()).Should().Be(itemJson);
5858

5959
response.Should().NotBeNull();

0 commit comments

Comments
 (0)