Publish curated Knowledge Hub source from 2ed833f290b439a7bc91e5fd19b042c269e3bee9
This commit is contained in:
1 parent
10f2edcd29
commit
59f15b2d08
218 files changed
+23995
-1849
No files matched your search
+147
@@ -0,0 +1,147 @@
|
||||
using System.Net;
|
||||
using System.Text.Json;
|
||||
using ItWorx.KnowledgeHub.IntegrationTests.Infrastructure;
|
||||
|
||||
namespace ItWorx.KnowledgeHub.IntegrationTests.Platform;
|
||||
|
||||
[Collection(PostgresTestGroup.Name)]
|
||||
public sealed class UserPreferencesApiTests(PostgresContainerFixture postgres) : OwnerSetupTestBase(postgres)
|
||||
{
|
||||
private sealed record PreferencesPayload(
|
||||
string Theme,
|
||||
string Density,
|
||||
bool PrimaryNavigationCollapsed,
|
||||
bool ContextPanelOpen,
|
||||
string[] NavigationOrder,
|
||||
string[] DashboardWidgetOrder,
|
||||
string[] HiddenDashboardWidgets,
|
||||
string DefaultLandingPage,
|
||||
string CatalogView,
|
||||
int ContextPanelWidth,
|
||||
int NavigationWidth);
|
||||
|
||||
private static PreferencesPayload DefaultPreferences(string theme = "system") => new(
|
||||
theme,
|
||||
"comfortable",
|
||||
false,
|
||||
false,
|
||||
["/", "/infrastructure", "/infrastructure/services", "/projects", "/runbooks", "/knowledge", "/activity", "/search"],
|
||||
["attention", "server", "projects", "activity", "runbooks", "favorites", "quick-actions"],
|
||||
[],
|
||||
"/",
|
||||
"list",
|
||||
304,
|
||||
224);
|
||||
|
||||
private async Task<HttpClient> AuthenticatedClientAsync()
|
||||
{
|
||||
var client = Factory.CreateClient();
|
||||
await SetupHelper.LoginAsync(client, OwnerEmail, OwnerPassword);
|
||||
return client;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Missing_preferences_return_the_shared_defaults_without_persisting_a_row()
|
||||
{
|
||||
using var client = await AuthenticatedClientAsync();
|
||||
|
||||
using var first = await client.GetAsync("/api/v1/me/preferences");
|
||||
using var firstJson = JsonDocument.Parse(await first.Content.ReadAsStringAsync());
|
||||
using var second = await client.GetAsync("/api/v1/me/preferences");
|
||||
using var secondJson = JsonDocument.Parse(await second.Content.ReadAsStringAsync());
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, first.StatusCode);
|
||||
Assert.Equal("\"0\"", first.Headers.ETag?.Tag);
|
||||
Assert.Equal("0", firstJson.RootElement.GetProperty("eTag").GetString());
|
||||
Assert.True(firstJson.RootElement.GetProperty("isDefault").GetBoolean());
|
||||
Assert.Equal(1, firstJson.RootElement.GetProperty("schemaVersion").GetInt32());
|
||||
Assert.Equal("system", firstJson.RootElement.GetProperty("theme").GetString());
|
||||
Assert.Equal("comfortable", firstJson.RootElement.GetProperty("density").GetString());
|
||||
Assert.Equal(304, firstJson.RootElement.GetProperty("contextPanelWidth").GetInt32());
|
||||
Assert.Equal(224, firstJson.RootElement.GetProperty("navigationWidth").GetInt32());
|
||||
Assert.Equal("0", secondJson.RootElement.GetProperty("eTag").GetString());
|
||||
Assert.True(secondJson.RootElement.GetProperty("isDefault").GetBoolean());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Full_replacement_with_the_current_etag_persists_for_the_signed_in_user()
|
||||
{
|
||||
using var client = await AuthenticatedClientAsync();
|
||||
var request = new PreferencesPayload(
|
||||
"dark", "compact", true, true,
|
||||
["/projects", "/", "/knowledge"], ["projects", "attention", "server"], ["server"],
|
||||
"/projects", "grid", 420, 248);
|
||||
|
||||
using var updated = await SetupHelper.PutWithCsrfAsync(client, "/api/v1/me/preferences", request, "0");
|
||||
using var updatedJson = JsonDocument.Parse(await updated.Content.ReadAsStringAsync());
|
||||
using var fetched = await client.GetAsync("/api/v1/me/preferences");
|
||||
using var fetchedJson = JsonDocument.Parse(await fetched.Content.ReadAsStringAsync());
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, updated.StatusCode);
|
||||
Assert.Equal("\"1\"", updated.Headers.ETag?.Tag);
|
||||
Assert.Equal("1", updatedJson.RootElement.GetProperty("eTag").GetString());
|
||||
Assert.False(updatedJson.RootElement.GetProperty("isDefault").GetBoolean());
|
||||
Assert.Equal("dark", fetchedJson.RootElement.GetProperty("theme").GetString());
|
||||
Assert.Equal("/projects", fetchedJson.RootElement.GetProperty("defaultLandingPage").GetString());
|
||||
Assert.Equal(420, fetchedJson.RootElement.GetProperty("contextPanelWidth").GetInt32());
|
||||
Assert.Equal("1", fetchedJson.RootElement.GetProperty("eTag").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_missing_if_match_is_rejected_before_updating_preferences()
|
||||
{
|
||||
using var client = await AuthenticatedClientAsync();
|
||||
var request = DefaultPreferences("light");
|
||||
|
||||
using var response = await SetupHelper.PutWithCsrfAsync(client, "/api/v1/me/preferences", request);
|
||||
|
||||
Assert.Equal((HttpStatusCode)428, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task A_stale_if_match_is_rejected_without_overwriting_a_newer_preference_version()
|
||||
{
|
||||
using var client = await AuthenticatedClientAsync();
|
||||
var request = DefaultPreferences("dark");
|
||||
|
||||
using var first = await SetupHelper.PutWithCsrfAsync(client, "/api/v1/me/preferences", request, "0");
|
||||
using var stale = await SetupHelper.PutWithCsrfAsync(client, "/api/v1/me/preferences", request with { Theme = "light" }, "0");
|
||||
using var fetched = await client.GetAsync("/api/v1/me/preferences");
|
||||
using var fetchedJson = JsonDocument.Parse(await fetched.Content.ReadAsStringAsync());
|
||||
|
||||
Assert.Equal(HttpStatusCode.OK, first.StatusCode);
|
||||
Assert.Equal(HttpStatusCode.PreconditionFailed, stale.StatusCode);
|
||||
Assert.Equal("dark", fetchedJson.RootElement.GetProperty("theme").GetString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Preferences_reject_unknown_navigation_and_widget_ids()
|
||||
{
|
||||
using var client = await AuthenticatedClientAsync();
|
||||
var request = DefaultPreferences() with
|
||||
{
|
||||
NavigationOrder = ["/admin/users/delete"],
|
||||
DashboardWidgetOrder = ["unexpected-widget"],
|
||||
DefaultLandingPage = "/admin/users/delete"
|
||||
};
|
||||
|
||||
using var response = await SetupHelper.PutWithCsrfAsync(client, "/api/v1/me/preferences", request, "0");
|
||||
|
||||
Assert.Equal(HttpStatusCode.UnprocessableEntity, response.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Concurrent_first_writes_with_the_default_etag_have_one_winner()
|
||||
{
|
||||
using var firstClient = await AuthenticatedClientAsync();
|
||||
using var secondClient = await AuthenticatedClientAsync();
|
||||
var firstWrite = SetupHelper.PutWithCsrfAsync(firstClient, "/api/v1/me/preferences", DefaultPreferences("dark"), "0");
|
||||
var secondWrite = SetupHelper.PutWithCsrfAsync(secondClient, "/api/v1/me/preferences", DefaultPreferences("light"), "0");
|
||||
|
||||
using var firstResponse = await firstWrite;
|
||||
using var secondResponse = await secondWrite;
|
||||
|
||||
Assert.Equal(1, new[] { firstResponse.StatusCode, secondResponse.StatusCode }.Count(status => status == HttpStatusCode.OK));
|
||||
Assert.Equal(1, new[] { firstResponse.StatusCode, secondResponse.StatusCode }.Count(status => status == HttpStatusCode.PreconditionFailed));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user