Skip to content

Commit 399f8ab

Browse files
authored
docs: explain required role-permissions (#220)
* docs: explain required role-permissions * pr-fix: change cosmos db title * pr-fix: correct w/ possible 404 if already deleted * pr-fix: correct pk var * pr-fix: remove unnecessary 404 check * Update docs/preview/02-Features/04-Storage/02-cosmos.mdx
1 parent abea54d commit 399f8ab

File tree

6 files changed

+193
-39
lines changed

6 files changed

+193
-39
lines changed

docs/preview/02-Features/04-Storage/01-storage-account.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ The following functionality is available when installing this package:
1515
PM> Install-Package -Name Arcus.Testing.Storage.Blob
1616
```
1717

18+
> 🛡️ Make sure that the test client that interacts with the Blob storage has at least [`Storage Blob Data Contributor`](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/storage#storage-blob-data-contributor)-rights if the test needs to create/update/delete containers.
19+
1820
## Temporary Blob container
1921
The `TemporaryBlobContainer` provides a solution when the integration test requires a storage system (container) during the test run. An Azure Blob container is created upon the setup of the test fixture and is deleted again when the test fixture is disposed.
2022

@@ -171,6 +173,8 @@ The following functionality is available when installing this package:
171173
PM> Install-Package -Name Arcus.Testing.Storage.Table
172174
```
173175

176+
> 🛡️ Make sure that the test client that interacts with the Table storage has at least [`Storage Table Data Contributor`](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/storage#storage-table-data-contributor)-rights if the test needs to create/update/delete tables.
177+
174178
## Temporary Table
175179
The `TemporaryTable` provides a solution when the integration test requires a storage system (table) during the test run. An Azure Table is created upon the setup of the test fixture and is deleted again when the test fixture is disposed.
176180

docs/preview/02-Features/04-Storage/02-cosmos.mdx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import Tabs from '@theme/Tabs';
22
import TabItem from '@theme/TabItem';
33

4-
# CosmosDb storage
4+
# CosmosDb
55
The `Arcus.Testing.Storage.CosmosDb` package provides test fixtures to Azure CosmosDb storage. By using the common test practices 'clean environment', it provides a temporary collections and documents.
66

77
## Installation
@@ -14,6 +14,8 @@ PM> Install-Package -Name Arcus.Testing.Storage.Cosmos
1414
<Tabs groupId="storage-types">
1515
<TabItem value="mongodb" label="MongoDb" default>
1616

17+
> 🛡️ Make sure that the test client that interacts with the Cosmos storage has at least [`DocumentDB Account Contributor`](https://learn.microsoft.com/en-us/azure/role-based-access-control/built-in-roles/databases#documentdb-account-contributor)-rights if the test needs to create/update/delete collections.
18+
1719
### Temporary MongoDb collection
1820
The `TemporaryMongoDbCollection` provides a solution when the integration tes requires a storage system (collection) during the test run. An MongoDb collection is created upon setup of the test fixture and is deleted again when the test fixture disposed.
1921

@@ -124,6 +126,18 @@ BsonValue documentId = document.Id;
124126
</TabItem>
125127
<TabItem value="nosql" label="NoSql">
126128

129+
> 🛡️ Make sure that the test client that interacts with the Cosmos storage has at least [`Cosmos DB Built-in Data Contributor`](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/security/reference-data-plane-roles)-rights if the test needs to create/update/delete containers.
130+
>
131+
> **This role is not a built-in Azure RBAC, but a role specific for NoSql**. One can [assign this role with Azure CLI](https://learn.microsoft.com/en-us/cli/azure/cosmosdb/sql/role/assignment?view=azure-cli-latest#az-cosmosdb-sql-role-assignment-create):
132+
> ```shell
133+
> az cosmosdb sql role assignment create `
134+
> --account-name "CosmosDBAccountName" `
135+
> --resource-group "ResourceGroupName" `
136+
> --role-definition-name "Cosmos DB Built-in Data Contributor" `
137+
> --scope "/" `
138+
> --principal-id "UserOrPrincipalObjectId"
139+
> ```
140+
127141
### Temporary NoSql container
128142
The `TemporaryNoSqlContainer` provides a solution when the integration tes requires a storage system (container) during the test run. A NoSql container is created upon setup of the test fixture and is deleted again when the test fixture disposed.
129143

src/Arcus.Testing.Storage.Cosmos/TemporaryNoSqlContainer.cs

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.ObjectModel;
44
using System.IO;
55
using System.Linq;
6+
using System.Net;
67
using System.Text;
78
using System.Threading.Tasks;
89
using Azure;
@@ -547,7 +548,14 @@ private static async Task CleanContainerOnSetupAsync(Container container, Tempor
547548
await ForEachItemAsync(container, async (id, partitionKey, _) =>
548549
{
549550
logger.LogTrace("[Test:Setup] Delete Azure Cosmos NoSql item '{ItemId}' {PartitionKey} in container '{DatabaseName}/{ContainerName}'", id, partitionKey, container.Database.Id, container.Id);
550-
await container.DeleteItemStreamAsync(id, partitionKey);
551+
using ResponseMessage response = await container.DeleteItemStreamAsync(id, partitionKey);
552+
553+
if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.NotFound)
554+
{
555+
throw new RequestFailedException(
556+
$"[Test:Setup] Failed to delete Azure Cosmos NoSql item '{id}' {partitionKey} in container '{container.Database.Id}/{container.Id}' " +
557+
$"since the delete operation responded with a failure: {(int) response.StatusCode} {response.StatusCode}: {response.ErrorMessage}");
558+
}
551559
});
552560
}
553561
else if (options.OnSetup.Items is OnSetupNoSqlContainer.CleanIfMatched)
@@ -557,7 +565,14 @@ await ForEachItemAsync(container, async (id, key, doc) =>
557565
if (options.OnSetup.IsMatched(id, key, doc, container.Database.Client))
558566
{
559567
logger.LogTrace("[Test:Setup] Delete matched Azure Cosmos NoSql item '{ItemId}' {PartitionKey} in container '{DatabaseName}/{ContainerName}'", id, key, container.Database.Id, container.Id);
560-
await container.DeleteItemStreamAsync(id, key);
568+
using ResponseMessage response = await container.DeleteItemStreamAsync(id, key);
569+
570+
if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.NotFound)
571+
{
572+
throw new RequestFailedException(
573+
$"[Test:Setup] Failed to delete matched Azure Cosmos NoSql item '{id}' {key} in container '{container.Database.Id}/{container.Id}' " +
574+
$"since the delete operation responded with a failure: {(int) response.StatusCode} {response.StatusCode}: {response.ErrorMessage}");
575+
}
561576
}
562577
});
563578
}
@@ -594,18 +609,15 @@ public async ValueTask DisposeAsync()
594609
}
595610
else
596611
{
597-
disposables.Add(AsyncDisposable.Create(async () =>
598-
{
599-
await CleanContainerOnTeardownAsync();
600-
}));
612+
await CleanContainerOnTeardownAsync(disposables);
601613
}
602614

603615
disposables.Add(_resourceClient);
604616

605617
GC.SuppressFinalize(this);
606618
}
607619

608-
private async Task CleanContainerOnTeardownAsync()
620+
private async Task CleanContainerOnTeardownAsync(DisposableCollection disposables)
609621
{
610622
if (_options.OnTeardown.Items is OnTeardownNoSqlContainer.CleanIfCreated)
611623
{
@@ -614,21 +626,45 @@ private async Task CleanContainerOnTeardownAsync()
614626

615627
if (_options.OnTeardown.Items is OnTeardownNoSqlContainer.CleanAll)
616628
{
617-
await ForEachItemAsync(async (id, key, _) =>
629+
await ForEachItemAsync((id, key, _) =>
618630
{
619-
_logger.LogTrace("[Test:Teardown] Delete Azure Cosmos NoSql item '{ItemId}' {PartitionKey} in NoSql container '{DatabaseName}/{ContainerName}'", id, key, Client.Database.Id, Client.Id);
620-
await Client.DeleteItemStreamAsync(id, key);
631+
disposables.Add(AsyncDisposable.Create(async () =>
632+
{
633+
_logger.LogTrace("[Test:Teardown] Delete Azure Cosmos NoSql item '{ItemId}' {PartitionKey} in NoSql container '{DatabaseName}/{ContainerName}'", id, key, Client.Database.Id, Client.Id);
634+
using ResponseMessage response = await Client.DeleteItemStreamAsync(id, key);
635+
636+
if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.NotFound)
637+
{
638+
throw new RequestFailedException(
639+
$"[Test:Teardown] Failed to delete Azure Cosmos NoSql item '{id}' {key} in container '{Client.Database.Id}/{Client.Id}' " +
640+
$"since the delete operation responded with a failure: {(int) response.StatusCode} {response.StatusCode}: {response.ErrorMessage}");
641+
}
642+
}));
643+
644+
return Task.CompletedTask;
621645
});
622646
}
623647
else if (_options.OnTeardown.Items is OnTeardownNoSqlContainer.CleanIfMatched)
624648
{
625-
await ForEachItemAsync(async (id, key, doc) =>
649+
await ForEachItemAsync((id, key, doc) =>
626650
{
627-
if (_options.OnTeardown.IsMatched(id, key, doc, Client.Database.Client))
651+
disposables.Add(AsyncDisposable.Create(async () =>
628652
{
629-
_logger.LogTrace("[Test:Teardown] Delete Azure Cosmos NoSql item '{ItemId}' {PartitionKey} in NoSql container '{DatabaseName}/{ContainerName}'", id, key, Client.Database.Id, Client.Id);
630-
await Client.DeleteItemStreamAsync(id, key);
631-
}
653+
if (_options.OnTeardown.IsMatched(id, key, doc, Client.Database.Client))
654+
{
655+
_logger.LogTrace("[Test:Teardown] Delete Azure Cosmos NoSql item '{ItemId}' {PartitionKey} in NoSql container '{DatabaseName}/{ContainerName}'", id, key, Client.Database.Id, Client.Id);
656+
using ResponseMessage response = await Client.DeleteItemStreamAsync(id, key);
657+
658+
if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.NotFound)
659+
{
660+
throw new RequestFailedException(
661+
$"[Test:Teardown] Failed to delete matched Azure Cosmos NoSql item '{id}' {key} in container '{Client.Database.Id}/{Client.Id}' " +
662+
$"since the delete operation responded with a failure: {(int) response.StatusCode} {response.StatusCode}: {response.ErrorMessage}");
663+
}
664+
}
665+
}));
666+
667+
return Task.CompletedTask;
632668
});
633669
}
634670
}

src/Arcus.Testing.Storage.Cosmos/TemporaryNoSqlItem.cs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ private static async Task<TemporaryNoSqlItem> InsertNewItemAsync<TItem>(
118118
{
119119
logger.LogDebug("[Test:Setup] Insert new Azure Cosmos NoSql '{ItemType}' item '{ItemId}' to container '{DatabaseName}/{ContainerName}'", typeof(TItem).Name, itemId, container.Database.Id, container.Id);
120120
ItemResponse<TItem> response = await container.CreateItemAsync(item);
121-
121+
122122
if (response.StatusCode != HttpStatusCode.Created)
123123
{
124124
throw new RequestFailedException(
@@ -141,15 +141,29 @@ public async ValueTask DisposeAsync()
141141
disposables.Add(AsyncDisposable.Create(async () =>
142142
{
143143
_logger.LogDebug("[Test:Teardown] Delete Azure Cosmos NoSql '{ItemType}' item '{ItemId}' in container '{DatabaseName}/{ContainerName}'", _itemType.Name, Id, _container.Database.Id, _container.Id);
144-
await _container.DeleteItemStreamAsync(Id, PartitionKey);
144+
using ResponseMessage response = await _container.DeleteItemStreamAsync(Id, PartitionKey);
145+
146+
if (!response.IsSuccessStatusCode && response.StatusCode != HttpStatusCode.NotFound)
147+
{
148+
throw new RequestFailedException(
149+
$"[Test:Teardown] Failed to delete Azure Cosmos NoSql '{_itemType.Name}' item '{Id}' {PartitionKey} in container '{_container.Database.Id}/{_container.Id}' " +
150+
$"since the delete operation responded with a failure: {(int) response.StatusCode} {response.StatusCode}: {response.ErrorMessage}");
151+
}
145152
}));
146153
}
147154
else
148155
{
149156
disposables.Add(AsyncDisposable.Create(async () =>
150157
{
151158
_logger.LogDebug("[Test:Teardown] Revert replaced Azure Cosmos NoSql '{ItemType}' item '{ItemId}' in container '{DatabaseName}/{ContainerName}'", _itemType.Name, Id, _container.Database.Id, _container.Id);
152-
await _container.ReplaceItemStreamAsync(_originalItemStream, Id, PartitionKey);
159+
using ResponseMessage response = await _container.ReplaceItemStreamAsync(_originalItemStream, Id, PartitionKey);
160+
161+
if (!response.IsSuccessStatusCode)
162+
{
163+
throw new RequestFailedException(
164+
$"[Test:Teardown] Failed to revert Azure Cosmos NoSql '{_itemType.Name}' item '{Id}' {PartitionKey} in container '{_container.Database.Id}/{_container.Id}' " +
165+
$"since the replace operation responded with a failure: {(int) response.StatusCode} {response.StatusCode}: {response.ErrorMessage}");
166+
}
153167
}));
154168
disposables.Add(AsyncDisposable.Create(async () =>
155169
{

0 commit comments

Comments
 (0)