146 lines
6.5 KiB
C#
146 lines
6.5 KiB
C#
using System.Net;
|
|
using System.Net.Http.Json;
|
|
using ItWorx.KnowledgeHub.Contracts.Admin;
|
|
using ItWorx.KnowledgeHub.Contracts.Auth;
|
|
using ItWorx.KnowledgeHub.Contracts.Runbooks;
|
|
using ItWorx.KnowledgeHub.IntegrationTests.Infrastructure;
|
|
|
|
namespace ItWorx.KnowledgeHub.IntegrationTests.Projects;
|
|
|
|
[Collection(PostgresTestGroup.Name)]
|
|
public sealed class RunbookTests(PostgresContainerFixture postgres) : OwnerSetupTestBase(postgres)
|
|
{
|
|
private async Task<HttpClient> AuthenticatedClientAsync()
|
|
{
|
|
var client = Factory.CreateClient();
|
|
await SetupHelper.LoginAsync(client, OwnerEmail, OwnerPassword);
|
|
return client;
|
|
}
|
|
|
|
private static async Task<RunbookDetail> CreateRunbookAsync(HttpClient client, string title)
|
|
{
|
|
var response = await SetupHelper.PostWithCsrfAsync(
|
|
client, "/api/v1/runbooks",
|
|
new CreateRunbookRequest(null, title, "## Doel\n\nHerstel de dienst.", null, "High", true, 45));
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var runbook = await response.Content.ReadFromJsonAsync<RunbookDetail>();
|
|
Assert.NotNull(runbook);
|
|
return runbook;
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Creating_a_runbook_creates_a_backing_Runbook_page_with_a_profile_and_is_stale_until_validated()
|
|
{
|
|
using var client = await AuthenticatedClientAsync();
|
|
|
|
var runbook = await CreateRunbookAsync(client, "Databaseherstel");
|
|
|
|
Assert.Equal("Draft", runbook.Status);
|
|
Assert.Equal("High", runbook.RiskLevel);
|
|
Assert.True(runbook.IsStale);
|
|
Assert.Null(runbook.LastValidatedAt);
|
|
|
|
var pageResponse = await client.GetAsync($"/api/v1/pages/{runbook.PageId}");
|
|
var page = await pageResponse.Content.ReadFromJsonAsync<ItWorx.KnowledgeHub.Contracts.Knowledge.PageDetail>();
|
|
Assert.Equal("Runbook", page!.DocumentType);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Validating_a_runbook_against_its_current_revision_clears_staleness()
|
|
{
|
|
using var client = await AuthenticatedClientAsync();
|
|
var runbook = await CreateRunbookAsync(client, "Te valideren procedure");
|
|
|
|
var response = await SetupHelper.PostWithCsrfAndIfMatchAsync(
|
|
client, $"/api/v1/runbooks/{runbook.Id}/validate", new { }, runbook.ETag);
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var validated = await response.Content.ReadFromJsonAsync<RunbookDetail>();
|
|
Assert.False(validated!.IsStale);
|
|
Assert.NotNull(validated.LastValidatedAt);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Editing_the_underlying_page_after_validation_makes_the_runbook_stale_again()
|
|
{
|
|
using var client = await AuthenticatedClientAsync();
|
|
var runbook = await CreateRunbookAsync(client, "Procedure die verandert");
|
|
|
|
var validateResponse = await SetupHelper.PostWithCsrfAndIfMatchAsync(
|
|
client, $"/api/v1/runbooks/{runbook.Id}/validate", new { }, runbook.ETag);
|
|
var validated = await validateResponse.Content.ReadFromJsonAsync<RunbookDetail>();
|
|
Assert.False(validated!.IsStale);
|
|
|
|
var pageResponse = await client.GetFromJsonAsync<ItWorx.KnowledgeHub.Contracts.Knowledge.PageDetail>($"/api/v1/pages/{runbook.PageId}");
|
|
await SetupHelper.PutWithCsrfAsync(
|
|
client,
|
|
$"/api/v1/pages/{runbook.PageId}",
|
|
new ItWorx.KnowledgeHub.Contracts.Knowledge.UpdatePageRequest(
|
|
pageResponse!.Title, "## Doel\n\nBijgewerkte procedure.", "Runbook", "Active", "Procedure bijgewerkt."),
|
|
pageResponse.ETag);
|
|
|
|
var afterEdit = await client.GetFromJsonAsync<RunbookDetail>($"/api/v1/runbooks/{runbook.Id}");
|
|
Assert.True(afterEdit!.IsStale);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Updating_the_runbook_profile_changes_risk_level_and_status()
|
|
{
|
|
using var client = await AuthenticatedClientAsync();
|
|
var runbook = await CreateRunbookAsync(client, "Aan te passen profiel");
|
|
|
|
var response = await SetupHelper.PutWithCsrfAsync(
|
|
client, $"/api/v1/runbooks/{runbook.Id}/profile",
|
|
new UpdateRunbookProfileRequest(runbook.Owner, "Critical", "Active", true, 90, null),
|
|
runbook.ETag);
|
|
|
|
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
|
var updated = await response.Content.ReadFromJsonAsync<RunbookDetail>();
|
|
Assert.Equal("Critical", updated!.RiskLevel);
|
|
Assert.Equal("Active", updated.Status);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Runbooks_appear_in_the_workspace_runbook_list()
|
|
{
|
|
using var client = await AuthenticatedClientAsync();
|
|
var runbook = await CreateRunbookAsync(client, "Vindbare procedure");
|
|
|
|
var list = await client.GetFromJsonAsync<RunbookListResponse>("/api/v1/runbooks");
|
|
|
|
Assert.NotNull(list);
|
|
Assert.Contains(list.Items, item => item.Id == runbook.Id && item.Title == "Vindbare procedure");
|
|
}
|
|
|
|
// Reuses the exact reader-boundary pattern already established in
|
|
// ProjectHubTests.A_reader_cannot_create_or_mutate_projects_but_can_read_them —
|
|
// see DecisionTests' identical addition for the same V2-M5 rationale.
|
|
[Fact]
|
|
public async Task A_reader_can_read_runbooks_but_cannot_create_or_validate_them()
|
|
{
|
|
using var adminClient = await AuthenticatedClientAsync();
|
|
var runbook = await CreateRunbookAsync(adminClient, "Alleen-lezenprocedure");
|
|
|
|
const string readerEmail = "runbooklezer@itworx.test";
|
|
const string readerPassword = "reader-strong-password";
|
|
var createUserResponse = await SetupHelper.PostWithCsrfAsync(
|
|
adminClient, "/api/v1/admin/users", new CreateUserRequest("Testlezer", readerEmail, readerPassword, "Reader"));
|
|
Assert.Equal(HttpStatusCode.OK, createUserResponse.StatusCode);
|
|
|
|
using var readerClient = Factory.CreateClient();
|
|
await SetupHelper.LoginAsync(readerClient, readerEmail, readerPassword);
|
|
|
|
var readResponse = await readerClient.GetAsync($"/api/v1/runbooks/{runbook.Id}");
|
|
Assert.Equal(HttpStatusCode.OK, readResponse.StatusCode);
|
|
|
|
var createResponse = await SetupHelper.PostWithCsrfAsync(
|
|
readerClient, "/api/v1/runbooks",
|
|
new CreateRunbookRequest(null, "Ongeautoriseerde procedure", "## Doel\n\nX.", null, "Low", false, null));
|
|
Assert.Equal(HttpStatusCode.Forbidden, createResponse.StatusCode);
|
|
|
|
var validateResponse = await SetupHelper.PostWithCsrfAndIfMatchAsync(
|
|
readerClient, $"/api/v1/runbooks/{runbook.Id}/validate", new { }, runbook.ETag);
|
|
Assert.Equal(HttpStatusCode.Forbidden, validateResponse.StatusCode);
|
|
}
|
|
}
|