Skip to content

Commit f5a81b6

Browse files
committed
Add AccessRule repository integration tests
1 parent bf3ea9e commit f5a81b6

1 file changed

Lines changed: 88 additions & 0 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
using Bit.Core.Entities;
2+
using Bit.Core.Repositories;
3+
using Bit.Infrastructure.IntegrationTest.AdminConsole;
4+
using Bit.Pam.Entities;
5+
using Bit.Pam.Repositories;
6+
using Xunit;
7+
8+
namespace Bit.Infrastructure.IntegrationTest.Pam.Repositories;
9+
10+
public class AccessRuleRepositoryTests
11+
{
12+
[DatabaseTheory, DatabaseData]
13+
public async Task DeleteAsync_WithGovernedCollections_ClearsAssociationsAndKeepsCollections(
14+
IOrganizationRepository organizationRepository,
15+
ICollectionRepository collectionRepository,
16+
IAccessRuleRepository accessRuleRepository)
17+
{
18+
// Arrange
19+
var organization = await organizationRepository.CreateTestOrganizationAsync();
20+
21+
var rule = await accessRuleRepository.CreateAsync(new AccessRule
22+
{
23+
OrganizationId = organization.Id,
24+
Name = "Test Rule",
25+
Conditions = """{"kind":"human_approval"}""",
26+
});
27+
28+
var collection = new Collection
29+
{
30+
Name = "Governed Collection",
31+
OrganizationId = organization.Id,
32+
};
33+
await collectionRepository.CreateAsync(collection, [], []);
34+
35+
await accessRuleRepository.SetCollectionAssociationsAsync(
36+
organization.Id, rule.Id, [collection.Id], []);
37+
38+
// Sanity check: the collection is governed by the rule before deletion.
39+
var details = await accessRuleRepository.GetDetailsByIdAsync(rule.Id);
40+
Assert.NotNull(details);
41+
Assert.Contains(collection.Id, details.CollectionIds);
42+
43+
// Act
44+
await accessRuleRepository.DeleteAsync(rule);
45+
46+
// Assert: the rule is gone, but the collection survives with its association cleared.
47+
Assert.Null(await accessRuleRepository.GetByIdAsync(rule.Id));
48+
49+
var actualCollection = await collectionRepository.GetByIdAsync(collection.Id);
50+
Assert.NotNull(actualCollection);
51+
Assert.Null(actualCollection.AccessRuleId);
52+
}
53+
54+
[DatabaseTheory, DatabaseData]
55+
public async Task CreateAsync_ReusingNameOfDeletedRule_Succeeds(
56+
IOrganizationRepository organizationRepository,
57+
IAccessRuleRepository accessRuleRepository)
58+
{
59+
// Arrange
60+
var organization = await organizationRepository.CreateTestOrganizationAsync();
61+
62+
var original = await accessRuleRepository.CreateAsync(new AccessRule
63+
{
64+
OrganizationId = organization.Id,
65+
Name = "Reusable Name",
66+
Conditions = """{"kind":"human_approval"}""",
67+
});
68+
69+
// Act: delete the rule, then create a new one reusing its name. A hard delete removes the row, so the unique
70+
// index on (OrganizationId, Name) no longer reserves the name.
71+
await accessRuleRepository.DeleteAsync(original);
72+
73+
var recreated = await accessRuleRepository.CreateAsync(new AccessRule
74+
{
75+
OrganizationId = organization.Id,
76+
Name = "Reusable Name",
77+
Conditions = """{"kind":"human_approval"}""",
78+
});
79+
80+
// Assert: a distinct, live rule owns the name and the original stays gone.
81+
Assert.NotEqual(original.Id, recreated.Id);
82+
Assert.Null(await accessRuleRepository.GetByIdAsync(original.Id));
83+
84+
var live = await accessRuleRepository.GetByIdAsync(recreated.Id);
85+
Assert.NotNull(live);
86+
Assert.Equal("Reusable Name", live.Name);
87+
}
88+
}

0 commit comments

Comments
 (0)