Initial curated itworx-knowledge-hub source from 3b1751d0d9dee551aa6994eee0093704dc334817

This commit is contained in:
Public Source Publisher committed 2026-10-01 01:37:37 +02:00
commit ad8ad252a9
825 files changed
+124216

No files matched your search

@@ -0,0 +1,145 @@
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);
}
}