diff --git a/src/WayfarerMobile.Core/Interfaces/IGroupsService.cs b/src/WayfarerMobile.Core/Interfaces/IGroupsService.cs index 716619d..0ee7339 100644 --- a/src/WayfarerMobile.Core/Interfaces/IGroupsService.cs +++ b/src/WayfarerMobile.Core/Interfaces/IGroupsService.cs @@ -49,7 +49,7 @@ Task> GetLatestLocationsAsync( /// /// Updates the current user's peer visibility setting. - /// PATCH /api/mobile/groups/{groupId}/peer-visibility + /// POST /api/mobile/groups/{groupId}/peer-visibility /// /// The group ID. /// Whether peer visibility should be disabled. diff --git a/src/WayfarerMobile.Core/Models/GroupLocationModels.cs b/src/WayfarerMobile.Core/Models/GroupLocationModels.cs index d691684..2e91eec 100644 --- a/src/WayfarerMobile.Core/Models/GroupLocationModels.cs +++ b/src/WayfarerMobile.Core/Models/GroupLocationModels.cs @@ -135,7 +135,7 @@ public class GroupLocationResult /// /// Request to update peer visibility. -/// PATCH /api/mobile/groups/{groupId}/peer-visibility +/// POST /api/mobile/groups/{groupId}/peer-visibility /// public class PeerVisibilityUpdateRequest { diff --git a/src/WayfarerMobile/Services/GroupsService.cs b/src/WayfarerMobile/Services/GroupsService.cs index d8a71ee..162fb26 100644 --- a/src/WayfarerMobile/Services/GroupsService.cs +++ b/src/WayfarerMobile/Services/GroupsService.cs @@ -287,7 +287,7 @@ public async Task UpdatePeerVisibilityAsync( try { - var request = CreateRequest(new HttpMethod("PATCH"), $"/api/mobile/groups/{groupId}/peer-visibility"); + var request = CreateRequest(HttpMethod.Post, $"/api/mobile/groups/{groupId}/peer-visibility"); request.Content = JsonContent.Create(new PeerVisibilityUpdateRequest { Disabled = disabled }, options: JsonOptions); var response = await HttpClientInstance.SendAsync(request, cancellationToken); diff --git a/tests/WayfarerMobile.Tests/Unit/Services/ProductionGroupsServiceTests.cs b/tests/WayfarerMobile.Tests/Unit/Services/ProductionGroupsServiceTests.cs new file mode 100644 index 0000000..474bd3c --- /dev/null +++ b/tests/WayfarerMobile.Tests/Unit/Services/ProductionGroupsServiceTests.cs @@ -0,0 +1,110 @@ +using System.Net; +using System.Text.Json; +using Microsoft.Extensions.Logging; +using Moq.Protected; +using WayfarerMobile.Services; + +namespace WayfarerMobile.Tests.Unit.Services; + +/// +/// Regression tests for the production peer-visibility request. +/// +public class ProductionGroupsServiceTests +{ + [Theory] + [InlineData(true, HttpStatusCode.OK, true)] + [InlineData(false, HttpStatusCode.Forbidden, false)] + public async Task UpdatePeerVisibilityAsync_SendsOneAuthenticatedPostAndReturnsStatusResult( + bool disabled, + HttpStatusCode statusCode, + bool expectedResult) + { + var groupId = Guid.Parse("11111111-2222-3333-4444-555555555555"); + HttpMethod? capturedMethod = null; + Uri? capturedUri = null; + string? capturedAuthorization = null; + string? capturedContent = null; + var contactCount = 0; + var handler = new Mock(); + handler + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .Returns(async (request, token) => + { + contactCount++; + capturedMethod = request.Method; + capturedUri = request.RequestUri; + capturedAuthorization = request.Headers.Authorization?.ToString(); + capturedContent = await request.Content!.ReadAsStringAsync(token); + + return new HttpResponseMessage(statusCode); + }); + + using var httpClient = new HttpClient(handler.Object); + var httpClientFactory = new Mock(); + httpClientFactory.Setup(factory => factory.CreateClient("WayfarerApi")).Returns(httpClient); + var settings = new Mock(); + settings.Setup(value => value.IsConfigured).Returns(true); + settings.Setup(value => value.ServerUrl).Returns("https://api.example.com"); + settings.Setup(value => value.ApiToken).Returns("test-token-123"); + var service = new GroupsService( + settings.Object, + Mock.Of>(), + httpClientFactory.Object); + + var result = await service.UpdatePeerVisibilityAsync(groupId, disabled); + + result.Should().Be(expectedResult); + contactCount.Should().Be(1); + capturedMethod.Should().Be(HttpMethod.Post); + capturedUri.Should().Be(new Uri($"https://api.example.com/api/mobile/groups/{groupId}/peer-visibility")); + capturedAuthorization.Should().Be("Bearer test-token-123"); + using var content = JsonDocument.Parse(capturedContent!); + content.RootElement.GetProperty("disabled").GetBoolean().Should().Be(disabled); + } + + [Fact] + public async Task UpdatePeerVisibilityAsync_ForwardsCancellationToSendAsync() + { + var sendStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + var sendCompletion = new TaskCompletionSource( + TaskCreationOptions.RunContinuationsAsynchronously); + var capturedCancellationToken = CancellationToken.None; + var handler = new Mock(); + handler + .Protected() + .Setup>( + "SendAsync", + ItExpr.IsAny(), + ItExpr.IsAny()) + .Returns((_, token) => + { + capturedCancellationToken = token; + sendStarted.SetResult(); + return sendCompletion.Task; + }); + + using var httpClient = new HttpClient(handler.Object); + var httpClientFactory = new Mock(); + httpClientFactory.Setup(factory => factory.CreateClient("WayfarerApi")).Returns(httpClient); + var settings = new Mock(); + settings.Setup(value => value.IsConfigured).Returns(true); + settings.Setup(value => value.ServerUrl).Returns("https://api.example.com"); + var service = new GroupsService( + settings.Object, + Mock.Of>(), + httpClientFactory.Object); + using var cancellationSource = new CancellationTokenSource(); + + var resultTask = service.UpdatePeerVisibilityAsync(Guid.NewGuid(), disabled: true, cancellationSource.Token); + await sendStarted.Task; + cancellationSource.Cancel(); + + capturedCancellationToken.IsCancellationRequested.Should().BeTrue(); + sendCompletion.SetCanceled(capturedCancellationToken); + (await resultTask).Should().BeFalse(); + } +} diff --git a/tests/WayfarerMobile.Tests/WayfarerMobile.Tests.csproj b/tests/WayfarerMobile.Tests/WayfarerMobile.Tests.csproj index 2629738..cb7fef8 100644 --- a/tests/WayfarerMobile.Tests/WayfarerMobile.Tests.csproj +++ b/tests/WayfarerMobile.Tests/WayfarerMobile.Tests.csproj @@ -25,6 +25,7 @@ +