Skip to content

Commit 75bb09c

Browse files
committed
[PM-38749] Add tests
1 parent 7029f2a commit 75bb09c

3 files changed

Lines changed: 293 additions & 0 deletions

File tree

test/Api.IntegrationTest/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,61 @@ public async Task CreateThenUpdateThenGet_AsOwner_ReturnsCreatedAndOkAndOk()
183183
Assert.Equal(["example.com", "new.com"], content.AllowedDomains);
184184
}
185185

186+
[Fact]
187+
public async Task UpdateInviteSupportConfirmThenGet_AsOwner_UpdatesOnlyInviteAndSupportsConfirmation()
188+
{
189+
// Arrange
190+
var createRequest = new CreateOrganizationInviteLinkRequestModel
191+
{
192+
AllowedDomains = ["acme.com"],
193+
Invite = _invite,
194+
SupportsConfirmation = false,
195+
};
196+
197+
var createResponse = await _client.PostAsJsonAsync(
198+
$"/organizations/{_organization.Id}/invite-link", createRequest);
199+
200+
Assert.Equal(HttpStatusCode.Created, createResponse.StatusCode);
201+
202+
var created = await createResponse.Content.ReadFromJsonAsync<OrganizationInviteLinkResponseModel>();
203+
Assert.NotNull(created);
204+
205+
const string updatedInvite = "updated-invite-blob";
206+
var updateRequest = new UpdateInviteSupportConfirmRequestModel
207+
{
208+
Invite = updatedInvite,
209+
SupportsConfirmation = true,
210+
};
211+
212+
// Act
213+
var updateResponse = await _client.PutAsJsonAsync(
214+
$"/organizations/{_organization.Id}/invite-link/support-confirm", updateRequest);
215+
216+
// Assert
217+
Assert.Equal(HttpStatusCode.OK, updateResponse.StatusCode);
218+
219+
var updated = await updateResponse.Content.ReadFromJsonAsync<OrganizationInviteLinkResponseModel>();
220+
Assert.NotNull(updated);
221+
Assert.Equal(created.Id, updated.Id);
222+
Assert.Equal(created.Code, updated.Code);
223+
Assert.Equal(_organization.Id, updated.OrganizationId);
224+
Assert.Equal(updatedInvite, updated.Invite);
225+
Assert.True(updated.SupportsConfirmation);
226+
Assert.Equal(["acme.com"], updated.AllowedDomains);
227+
228+
var getResponse = await _client.GetAsync($"/organizations/{_organization.Id}/invite-link");
229+
230+
Assert.Equal(HttpStatusCode.OK, getResponse.StatusCode);
231+
232+
var content = await getResponse.Content.ReadFromJsonAsync<OrganizationInviteLinkResponseModel>();
233+
Assert.NotNull(content);
234+
Assert.Equal(created.Id, content.Id);
235+
Assert.Equal(created.Code, content.Code);
236+
Assert.Equal(updatedInvite, content.Invite);
237+
Assert.True(content.SupportsConfirmation);
238+
Assert.Equal(["acme.com"], content.AllowedDomains);
239+
}
240+
186241
[Fact]
187242
public async Task Delete_AsOwner_ReturnsNoContentAndRemovesLink()
188243
{

test/Api.Test/AdminConsole/Controllers/OrganizationInviteLinksControllerTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,91 @@ public async Task Update_WithValidationError_Returns400(
222222
Assert.NotNull(badRequestResult.Value);
223223
}
224224

225+
[Theory, BitAutoData]
226+
public async Task UpdateInviteSupportConfirm_WithValidInput_ReturnsOk(
227+
Guid orgId,
228+
OrganizationInviteLink inviteLink,
229+
SutProvider<OrganizationInviteLinksController> sutProvider)
230+
{
231+
// Arrange
232+
inviteLink.OrganizationId = orgId;
233+
inviteLink.AllowedDomains = "[\"acme.com\"]";
234+
235+
var model = new UpdateInviteSupportConfirmRequestModel
236+
{
237+
Invite = "new-invite-blob",
238+
SupportsConfirmation = true,
239+
};
240+
241+
sutProvider.GetDependency<IUpdateInviteSupportConfirmCommand>()
242+
.UpdateAsync(Arg.Any<UpdateInviteSupportConfirmRequest>())
243+
.Returns(new CommandResult<OrganizationInviteLink>(inviteLink));
244+
245+
// Act
246+
var result = await sutProvider.Sut.UpdateInviteSupportConfirm(orgId, model);
247+
248+
// Assert
249+
var okResult = Assert.IsType<Ok<OrganizationInviteLinkResponseModel>>(result);
250+
Assert.NotNull(okResult.Value);
251+
Assert.Equal(inviteLink.Id, okResult.Value.Id);
252+
Assert.Equal(orgId, okResult.Value.OrganizationId);
253+
254+
await sutProvider.GetDependency<IUpdateInviteSupportConfirmCommand>()
255+
.Received(1)
256+
.UpdateAsync(Arg.Is<UpdateInviteSupportConfirmRequest>(r =>
257+
r.OrganizationId == orgId &&
258+
r.Invite == "new-invite-blob" &&
259+
r.SupportsConfirmation == true));
260+
}
261+
262+
[Theory, BitAutoData]
263+
public async Task UpdateInviteSupportConfirm_WhenNoLinkExists_ReturnsNotFound(
264+
Guid orgId,
265+
SutProvider<OrganizationInviteLinksController> sutProvider)
266+
{
267+
// Arrange
268+
var model = new UpdateInviteSupportConfirmRequestModel
269+
{
270+
Invite = "new-invite-blob",
271+
SupportsConfirmation = true,
272+
};
273+
274+
sutProvider.GetDependency<IUpdateInviteSupportConfirmCommand>()
275+
.UpdateAsync(Arg.Any<UpdateInviteSupportConfirmRequest>())
276+
.Returns(new CommandResult<OrganizationInviteLink>(new InviteLinkNotFound()));
277+
278+
// Act
279+
var result = await sutProvider.Sut.UpdateInviteSupportConfirm(orgId, model);
280+
281+
// Assert
282+
var notFoundResult = Assert.IsType<NotFound<ErrorResponseModel>>(result);
283+
Assert.NotNull(notFoundResult.Value);
284+
}
285+
286+
[Theory, BitAutoData]
287+
public async Task UpdateInviteSupportConfirm_WhenInviteLinkNotAvailable_Returns400(
288+
Guid orgId,
289+
SutProvider<OrganizationInviteLinksController> sutProvider)
290+
{
291+
// Arrange
292+
var model = new UpdateInviteSupportConfirmRequestModel
293+
{
294+
Invite = "new-invite-blob",
295+
SupportsConfirmation = true,
296+
};
297+
298+
sutProvider.GetDependency<IUpdateInviteSupportConfirmCommand>()
299+
.UpdateAsync(Arg.Any<UpdateInviteSupportConfirmRequest>())
300+
.Returns(new CommandResult<OrganizationInviteLink>(new InviteLinkNotAvailable()));
301+
302+
// Act
303+
var result = await sutProvider.Sut.UpdateInviteSupportConfirm(orgId, model);
304+
305+
// Assert
306+
var badRequestResult = Assert.IsType<BadRequest<ErrorResponseModel>>(result);
307+
Assert.NotNull(badRequestResult.Value);
308+
}
309+
225310
[Theory, BitAutoData]
226311
public async Task GetStatus_WithValidQuery_Success(
227312
GetOrganizationInviteLinkStatusRequestModel model,
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
using Bit.Core.AdminConsole.AbilitiesCache;
2+
using Bit.Core.AdminConsole.Entities;
3+
using Bit.Core.AdminConsole.OrganizationFeatures.InviteLinks;
4+
using Bit.Core.AdminConsole.Repositories;
5+
using Bit.Core.Models.Data.Organizations;
6+
using Bit.Test.Common.AutoFixture;
7+
using Bit.Test.Common.AutoFixture.Attributes;
8+
using Microsoft.Extensions.Time.Testing;
9+
using NSubstitute;
10+
using Xunit;
11+
12+
namespace Bit.Core.Test.AdminConsole.OrganizationFeatures.InviteLinks;
13+
14+
[SutProviderCustomize]
15+
public class UpdateInviteSupportConfirmCommandTests
16+
{
17+
private static SutProvider<UpdateInviteSupportConfirmCommand> GetSutProvider() =>
18+
new SutProvider<UpdateInviteSupportConfirmCommand>()
19+
.WithFakeTimeProvider()
20+
.Create();
21+
22+
[Theory, BitAutoData]
23+
public async Task UpdateAsync_WithValidInput_UpdatesOnlyInviteAndSupportsConfirmation(Guid organizationId)
24+
{
25+
// Arrange
26+
var sutProvider = GetSutProvider();
27+
var now = new DateTime(2026, 7, 16, 12, 0, 0, DateTimeKind.Utc);
28+
sutProvider.GetDependency<FakeTimeProvider>().SetUtcNow(now);
29+
30+
SetupAbility(sutProvider, organizationId);
31+
32+
var originalCreationDate = now.AddDays(-5);
33+
var existingLink = new OrganizationInviteLink
34+
{
35+
Id = Guid.NewGuid(),
36+
Code = Guid.NewGuid(),
37+
OrganizationId = organizationId,
38+
Invite = "old-invite-blob",
39+
SupportsConfirmation = false,
40+
CreationDate = originalCreationDate,
41+
RevisionDate = originalCreationDate,
42+
};
43+
existingLink.SetAllowedDomains(["acme.com"]);
44+
var originalAllowedDomains = existingLink.AllowedDomains;
45+
46+
sutProvider.GetDependency<IOrganizationInviteLinkRepository>()
47+
.GetByOrganizationIdAsync(organizationId)
48+
.Returns(existingLink);
49+
50+
var request = CreateRequest(organizationId, "new-invite-blob", supportsConfirmation: true);
51+
52+
// Act
53+
var result = await sutProvider.Sut.UpdateAsync(request);
54+
55+
// Assert
56+
Assert.True(result.IsSuccess);
57+
var link = result.AsSuccess;
58+
Assert.Same(existingLink, link);
59+
Assert.Equal("new-invite-blob", link.Invite);
60+
Assert.True(link.SupportsConfirmation);
61+
Assert.Equal(now, link.RevisionDate);
62+
Assert.Equal(originalCreationDate, link.CreationDate);
63+
Assert.Equal(originalAllowedDomains, link.AllowedDomains);
64+
65+
await sutProvider.GetDependency<IOrganizationInviteLinkRepository>()
66+
.Received(1)
67+
.ReplaceAsync(existingLink);
68+
}
69+
70+
[Theory, BitAutoData]
71+
public async Task UpdateAsync_WithNoExistingLink_ReturnsNotFoundError(Guid organizationId)
72+
{
73+
// Arrange
74+
var sutProvider = GetSutProvider();
75+
SetupAbility(sutProvider, organizationId);
76+
sutProvider.GetDependency<IOrganizationInviteLinkRepository>()
77+
.GetByOrganizationIdAsync(organizationId)
78+
.Returns((OrganizationInviteLink?)null);
79+
80+
var request = CreateRequest(organizationId, "new-invite-blob", supportsConfirmation: true);
81+
82+
// Act
83+
var result = await sutProvider.Sut.UpdateAsync(request);
84+
85+
// Assert
86+
Assert.True(result.IsError);
87+
Assert.IsType<InviteLinkNotFound>(result.AsError);
88+
89+
await sutProvider.GetDependency<IOrganizationInviteLinkRepository>()
90+
.DidNotReceiveWithAnyArgs()
91+
.ReplaceAsync(default!);
92+
}
93+
94+
[Theory, BitAutoData]
95+
public async Task UpdateAsync_WithoutUseInviteLinksAbility_ReturnsNotAvailableError(Guid organizationId)
96+
{
97+
// Arrange
98+
var sutProvider = GetSutProvider();
99+
SetupAbility(sutProvider, organizationId, useInviteLinks: false);
100+
101+
var request = CreateRequest(organizationId, "new-invite-blob", supportsConfirmation: true);
102+
103+
// Act
104+
var result = await sutProvider.Sut.UpdateAsync(request);
105+
106+
// Assert
107+
Assert.True(result.IsError);
108+
Assert.IsType<InviteLinkNotAvailable>(result.AsError);
109+
110+
await sutProvider.GetDependency<IOrganizationInviteLinkRepository>()
111+
.DidNotReceiveWithAnyArgs()
112+
.GetByOrganizationIdAsync(default);
113+
}
114+
115+
[Theory, BitAutoData]
116+
public async Task UpdateAsync_WithNullAbility_ReturnsNotAvailableError(Guid organizationId)
117+
{
118+
// Arrange
119+
var sutProvider = GetSutProvider();
120+
sutProvider.GetDependency<IOrganizationAbilityCacheService>()
121+
.GetOrganizationAbilityAsync(organizationId)
122+
.Returns((OrganizationAbility?)null);
123+
124+
var request = CreateRequest(organizationId, "new-invite-blob", supportsConfirmation: true);
125+
126+
// Act
127+
var result = await sutProvider.Sut.UpdateAsync(request);
128+
129+
// Assert
130+
Assert.True(result.IsError);
131+
Assert.IsType<InviteLinkNotAvailable>(result.AsError);
132+
}
133+
134+
private static void SetupAbility(
135+
SutProvider<UpdateInviteSupportConfirmCommand> sutProvider,
136+
Guid organizationId,
137+
bool useInviteLinks = true)
138+
{
139+
sutProvider.GetDependency<IOrganizationAbilityCacheService>()
140+
.GetOrganizationAbilityAsync(organizationId)
141+
.Returns(new OrganizationAbility { UseInviteLinks = useInviteLinks });
142+
}
143+
144+
private static UpdateInviteSupportConfirmRequest CreateRequest(
145+
Guid organizationId,
146+
string invite,
147+
bool supportsConfirmation) => new()
148+
{
149+
OrganizationId = organizationId,
150+
Invite = invite,
151+
SupportsConfirmation = supportsConfirmation,
152+
};
153+
}

0 commit comments

Comments
 (0)