Implement SxS GraphQL users tests

This commit is contained in:
Jordan Dominion
2024-09-22 19:14:02 -04:00
parent 92b007dc4d
commit 8b09eb7fb5
26 changed files with 1272 additions and 241 deletions
@@ -0,0 +1,16 @@
mutation CreateSystemUserWithPermissionSet($systemIdentifier: String!) {
createUserBySystemIDAndPermissionSet(
input: { permissionSet: {}, systemIdentifier: $systemIdentifier }
) {
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
user {
id
}
}
}
@@ -0,0 +1,14 @@
mutation CreateUserFromOAuthConnection($name: String!, $oAuthConnections: [OAuthConnectionInput!]!) {
createUserByOAuthAndPermissionSet(input: { name: $name, oAuthConnections: $oAuthConnections }) {
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
user {
id
}
}
}
@@ -0,0 +1,40 @@
mutation CreateUserGroup($name: String!) {
createUserGroup(input: { name: $name }) {
userGroup {
id
name
permissionSet {
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
}
}
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
}
}
@@ -0,0 +1,40 @@
mutation CreateUserGroupWithInstanceListPerm($name: String!) {
createUserGroup(input: { name: $name, permissionSet: { instanceManagerRights: { canList: true } } }) {
userGroup {
id
name
permissionSet {
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
}
}
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
}
}
@@ -0,0 +1,14 @@
mutation CreateUserWithPassword($name: String!, $password: String!) {
createUserByPasswordAndPermissionSet(input: { name: $name, password: $password }) {
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
user {
id
}
}
}
@@ -0,0 +1,18 @@
mutation CreateUserWithPasswordSelectOAuthConnections($name: String!, $password: String!) {
createUserByPasswordAndPermissionSet(input: { name: $name, password: $password }) {
user {
id
oAuthConnections {
externalUserId
provider
}
}
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
}
}
@@ -0,0 +1,11 @@
mutation DeleteUserGroup($id: ID!) {
deleteEmptyUserGroup(input: { id: $id }) {
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
}
}
@@ -0,0 +1,69 @@
mutation SetFullPermsOnUserGroup($id: ID!) {
updateUserGroup(
input: {
id: $id
newPermissionSet: {
administrationRights: {
canChangeVersion: true
canDownloadLogs: true
canEditOwnOAuthConnections: true
canEditOwnPassword: true
canReadUsers: true
canWriteUsers: true
canUploadVersion: true
canRestartHost: true
}
instanceManagerRights: {
canCreate: true
canDelete: true
canGrantPermissions: true
canSetOnline: true
canSetConfiguration: true
canSetChatBotLimit: true
canSetAutoUpdate: true
canRename: true
canRead: true
canList: true
canRelocate: true
}
}
}
) {
userGroup {
id
name
permissionSet {
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
}
}
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
}
}
@@ -0,0 +1,41 @@
mutation SetUserGroup($id: ID!, $newGroupId: ID!) {
updateUserSetGroup(input: { id: $id, newGroupId: $newGroupId }) {
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
user {
ownedPermissionSet {
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
}
group {
id
}
}
}
}
@@ -0,0 +1,25 @@
mutation SetUserOAuthConnections($id: ID!, $newOAuthConnections: [OAuthConnectionInput!]!) {
updateUser(
input: { id: $id, newOAuthConnections: $newOAuthConnections }
) {
user {
canonicalName
createdAt
enabled
id
name
systemIdentifier
oAuthConnections {
externalUserId
provider
}
}
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
}
}
@@ -0,0 +1,66 @@
mutation SetUserPermissionSet($id: ID!, $permissionSet: PermissionSetInput!) {
updateUserSetOwnedPermissionSet(input: { newPermissionSet: $permissionSet, id: $id }) {
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
user {
effectivePermissionSet {
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
}
ownedPermissionSet {
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
}
group {
id
}
}
}
}
@@ -0,0 +1,18 @@
mutation UpdateUserOAuthConnections($id: ID!, $newOAuthConnections: [OAuthConnectionInput!]) {
updateUser(input: { id: $id, newOAuthConnections: $newOAuthConnections }) {
user {
id
oAuthConnections {
externalUserId
provider
}
}
errors {
... on ErrorMessageError {
additionalData
errorCode
message
}
}
}
}
@@ -0,0 +1,41 @@
query GetSomeGroupInfo($id: ID!) {
swarm {
users {
groups {
byId(id: $id) {
permissionSet {
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
}
queryableUsersByGroup(first: 1) {
totalCount
nodes {
id
}
}
}
}
}
}
}
@@ -0,0 +1,14 @@
query GetUserById($id: ID!) {
swarm {
users {
byId(id: $id) {
canonicalName
createdAt
enabled
id
name
systemIdentifier
}
}
}
}
@@ -0,0 +1,8 @@
query GetUserNameByNodeId($id: ID!) {
node(id: $id) {
... on UserName {
id
name
}
}
}
@@ -0,0 +1,14 @@
query ListUserGroups {
swarm {
users {
groups {
queryableGroups {
totalCount
nodes {
id
}
}
}
}
}
}
@@ -0,0 +1,12 @@
query ListUsers {
swarm {
users {
queryableUsers {
nodes {
id
}
totalCount
}
}
}
}
@@ -0,0 +1,16 @@
query PageUserIds($first: Int, $after: String) {
swarm {
users {
queryableUsers(first: $first, after: $after) {
pageInfo {
endCursor
hasNextPage
}
totalCount
nodes {
id
}
}
}
}
}
@@ -0,0 +1,51 @@
query ReadCurrentUser {
swarm {
users {
current {
canonicalName
createdAt
enabled
id
name
systemIdentifier
group {
id
name
}
oAuthConnections {
externalUserId
provider
}
effectivePermissionSet {
administrationRights {
canChangeVersion
canDownloadLogs
canEditOwnOAuthConnections
canEditOwnPassword
canReadUsers
canRestartHost
canUploadVersion
canWriteUsers
}
instanceManagerRights {
canCreate
canDelete
canGrantPermissions
canList
canRead
canRelocate
canRename
canSetAutoUpdate
canSetChatBotLimit
canSetConfiguration
canSetOnline
}
}
createdBy {
id
name
}
}
}
}
}
+31 -5
View File
@@ -1,9 +1,15 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Tgstation.Server.Api.Models;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StrawberryShake;
using Tgstation.Server.Client;
using Tgstation.Server.Client.GraphQL;
namespace Tgstation.Server.Tests.Live
{
@@ -19,7 +25,7 @@ namespace Tgstation.Server.Tests.Live
/// <param name="action">A <see cref="Func{TResult}"/> resulting in a <see cref="Task"/>.</param>
/// <param name="expectedErrorCode">The expected <see cref="ApiException.ErrorCode"/>.</param>
/// <returns>A <see cref="Task"/> representing the running operation,</returns>
public static async ValueTask ThrowsException<TApiException>(Func<ValueTask> action, ErrorCode? expectedErrorCode = null)
public static async ValueTask ThrowsException<TApiException>(Func<ValueTask> action, Api.Models.ErrorCode? expectedErrorCode = null)
where TApiException : ApiException
{
try
@@ -43,7 +49,7 @@ namespace Tgstation.Server.Tests.Live
/// <param name="action">A <see cref="Func{TResult}"/> resulting in a <see cref="Task"/>.</param>
/// <param name="expectedErrorCode">The expected <see cref="ApiException.ErrorCode"/>.</param>
/// <returns>A <see cref="Task"/> representing the running operation,</returns>
public static async ValueTask ThrowsException<TApiException, TResult>(Func<ValueTask<TResult>> action, ErrorCode? expectedErrorCode = null)
public static async ValueTask ThrowsException<TApiException, TResult>(Func<ValueTask<TResult>> action, Api.Models.ErrorCode? expectedErrorCode = null)
where TApiException : ApiException
{
try
@@ -58,5 +64,25 @@ namespace Tgstation.Server.Tests.Live
Assert.Fail($"Expected exception {typeof(TApiException)}!");
}
public static async ValueTask OperationFails<TResultData, TPayload>(
IGraphQLServerClient client,
Func<IGraphQLClient, Task<IOperationResult<TResultData>>> operationInvoker,
Func<TResultData, TPayload> payloadSelector,
Client.GraphQL.ErrorCode expectedErrorCode,
CancellationToken cancellationToken)
where TResultData : class
{
var operationResult = await client.RunOperation(operationInvoker, cancellationToken);
operationResult.EnsureNoErrors();
var payload = payloadSelector(operationResult.Data);
var payloadErrors = (IEnumerable<object>)payload.GetType().GetProperty("Errors").GetValue(payload);
var error = payloadErrors.Single();
var errorCode = (ErrorCode)error.GetType().GetProperty("ErrorCode").GetValue(error);
Assert.AreEqual(expectedErrorCode, errorCode);
}
}
}
@@ -0,0 +1,47 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StrawberryShake;
using Tgstation.Server.Client.GraphQL;
namespace Tgstation.Server.Tests.Live
{
static class GraphQLServerClientExtensions
{
public static async ValueTask<TResultData> RunQueryEnsureNoErrors<TResultData>(
this IGraphQLServerClient serverClient,
Func<IGraphQLClient, Task<IOperationResult<TResultData>>> operationExecutor,
CancellationToken cancellationToken)
where TResultData : class
{
var result = await serverClient.RunOperation(operationExecutor, cancellationToken);
result.EnsureNoErrors();
return result.Data;
}
public static async ValueTask<TPayload> RunMutationEnsureNoErrors<TResultData, TPayload>(
this IGraphQLServerClient serverClient,
Func<IGraphQLClient, Task<IOperationResult<TResultData>>> operationExecutor,
Func<TResultData, TPayload> payloadSelector,
CancellationToken cancellationToken)
where TResultData : class
{
var result = await serverClient.RunOperation(operationExecutor, cancellationToken);
result.EnsureNoErrors();
var data = payloadSelector(result.Data);
var errorsObject = data.GetType().GetProperty("Errors").GetValue(data);
if (errorsObject != null)
{
var errorsCount = (int)errorsObject.GetType().GetProperty("Count").GetValue(errorsObject);
Assert.AreEqual(0, errorsCount);
}
return data;
}
}
}
@@ -0,0 +1,23 @@
using StrawberryShake;
using System.Threading.Tasks;
using System.Threading;
using System;
using Tgstation.Server.Client.GraphQL;
using Tgstation.Server.Client;
namespace Tgstation.Server.Tests.Live
{
interface IMultiServerClient
{
ValueTask Execute(
Func<IRestServerClient, ValueTask> restAction,
Func<IGraphQLServerClient, ValueTask> graphQLAction);
ValueTask<(TRestResult, TGraphQLResult)> ExecuteReadOnlyConfirmEquivalence<TRestResult, TGraphQLResult>(
Func<IRestServerClient, ValueTask<TRestResult>> restAction,
Func<IGraphQLClient, Task<IOperationResult<TGraphQLResult>>> graphQLAction,
Func<TRestResult, TGraphQLResult, bool> comparison,
CancellationToken cancellationToken)
where TGraphQLResult : class;
}
}
@@ -34,10 +34,10 @@ namespace Tgstation.Server.Tests.Live.Instance
long? permlessPsId;
public JobsHubTests(IRestServerClient permedUser, IRestServerClient permlessUser)
public JobsHubTests(MultiServerClient permedUser, MultiServerClient permlessUser)
{
this.permedUser = permedUser;
this.permlessUser = permlessUser;
this.permedUser = permedUser.RestClient;
this.permlessUser = permlessUser.RestClient;
Assert.AreNotSame(permedUser, permlessUser);
@@ -1,4 +1,4 @@
using System;
using System;
using System.Threading;
using System.Threading.Tasks;
@@ -12,7 +12,7 @@ using Tgstation.Server.Common.Extensions;
namespace Tgstation.Server.Tests.Live
{
sealed class MultiServerClient : IAsyncDisposable
sealed class MultiServerClient : IMultiServerClient, IAsyncDisposable
{
public IRestServerClient RestClient { get; }
public IGraphQLServerClient GraphQLClient { get; }
@@ -40,7 +40,7 @@ namespace Tgstation.Server.Tests.Live
return restAction(RestClient);
}
public async ValueTask ExecuteReadOnlyConfirmEquivalence<TRestResult, TGraphQLResult>(
public async ValueTask<(TRestResult, TGraphQLResult)> ExecuteReadOnlyConfirmEquivalence<TRestResult, TGraphQLResult>(
Func<IRestServerClient, ValueTask<TRestResult>> restAction,
Func<IGraphQLClient, Task<IOperationResult<TGraphQLResult>>> graphQLAction,
Func<TRestResult, TGraphQLResult, bool> comparison,
@@ -50,8 +50,13 @@ namespace Tgstation.Server.Tests.Live
var restTask = restAction(RestClient);
var graphQLResult = await GraphQLClient.RunOperation(graphQLAction, cancellationToken);
graphQLResult.EnsureNoErrors();
var restResult = await restTask;
Assert.IsTrue(comparison(restResult, graphQLResult.Data), "REST/GraphQL results differ!");
var comparisonResult = comparison(restResult, graphQLResult.Data);
Assert.IsTrue(comparisonResult, "REST/GraphQL results differ!");
return (restResult, graphQLResult.Data);
}
}
}
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@@ -1379,20 +1379,25 @@ namespace Tgstation.Server.Tests.Live
await unAuthedMultiClient.ExecuteReadOnlyConfirmEquivalence(
restClient => restClient.ServerInformation(cancellationToken),
gqlClient => gqlClient.UnauthenticatedServerInformation.ExecuteAsync(cancellationToken),
(restServerInfo, gqlServerInfo) => restServerInfo.ApiVersion.Major == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.MajorApiVersion
&& (restServerInfo.OAuthProviderInfos == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos
|| restServerInfo.OAuthProviderInfos.All(kvp =>
{
var info = gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos.FirstOrDefault(x => (int)x.Key == (int)kvp.Key);
return info != null
&& info.Value.ServerUrl == kvp.Value.ServerUrl
&& info.Value.ClientId == kvp.Value.ClientId
&& info.Value.RedirectUri == kvp.Value.RedirectUri;
})),
(restServerInfo, gqlServerInfo) =>
{
var result = restServerInfo.ApiVersion.Major == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.MajorApiVersion
&& (restServerInfo.OAuthProviderInfos == gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos
|| restServerInfo.OAuthProviderInfos.All(kvp =>
{
var info = gqlServerInfo.Swarm.CurrentNode.Gateway.Information.OAuthProviderInfos.FirstOrDefault(x => (int)x.Key == (int)kvp.Key);
return info != null
&& info.Value.ServerUrl == kvp.Value.ServerUrl
&& info.Value.ClientId == kvp.Value.ClientId
&& info.Value.RedirectUri == kvp.Value.RedirectUri;
}));
return result;
},
cancellationToken);
}
async ValueTask<IRestServerClient> CreateUserWithNoInstancePerms()
async ValueTask<MultiServerClient> CreateUserWithNoInstancePerms()
{
var createRequest = new UserCreateRequest()
{
@@ -1408,10 +1413,10 @@ namespace Tgstation.Server.Tests.Live
var user = await firstAdminRestClient.Users.Create(createRequest, cancellationToken);
Assert.IsTrue(user.Enabled);
return await restClientFactory.CreateFromLogin(server.RootUrl, createRequest.Name, createRequest.Password, cancellationToken: cancellationToken);
return await CreateClient(server.RootUrl, createRequest.Name, createRequest.Password, false, cancellationToken);
}
var jobsHubTest = new JobsHubTests(firstAdminRestClient, await CreateUserWithNoInstancePerms());
var jobsHubTest = new JobsHubTests(firstAdminMultiClient, await CreateUserWithNoInstancePerms());
Task jobsHubTestTask;
{
if (server.DumpOpenApiSpecpath)
@@ -1449,7 +1454,7 @@ namespace Tgstation.Server.Tests.Live
jobsHubTestTask = FailFast(await jobsHubTest.Run(cancellationToken)); // returns Task<Task>
var rootTest = FailFast(RawRequestTests.Run(restClientFactory, firstAdminRestClient, cancellationToken));
var adminTest = FailFast(new AdministrationTest(firstAdminRestClient.Administration).Run(cancellationToken));
var usersTest = FailFast(new UsersTest(firstAdminRestClient).Run(cancellationToken));
var usersTest = FailFast(new UsersTest(firstAdminMultiClient).Run(cancellationToken).AsTask());
var instanceManagerTest = new InstanceManagerTest(firstAdminRestClient, server.Directory);
var compatInstanceTask = instanceManagerTest.CreateTestInstance("CompatTestsInstance", cancellationToken);
@@ -1836,7 +1841,15 @@ namespace Tgstation.Server.Tests.Live
await serverTask;
}
async ValueTask<MultiServerClient> CreateAdminClient(Uri url, CancellationToken cancellationToken)
ValueTask<MultiServerClient> CreateAdminClient(Uri url, CancellationToken cancellationToken)
=> CreateClient(url, DefaultCredentials.AdminUserName, DefaultCredentials.DefaultAdminUserPassword, true, cancellationToken);
async ValueTask<MultiServerClient> CreateClient(
Uri url,
string username,
string password,
bool retry,
CancellationToken cancellationToken = default)
{
url = new Uri(url.ToString().Replace(Routes.ApiRoot, String.Empty));
var giveUpAt = DateTimeOffset.UtcNow.AddMinutes(2);
@@ -1850,14 +1863,14 @@ namespace Tgstation.Server.Tests.Live
restClientTask = restClientFactory.CreateFromLogin(
url,
DefaultCredentials.AdminUserName,
DefaultCredentials.DefaultAdminUserPassword,
username,
password,
cancellationToken: cancellationToken);
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
graphQLClientTask = graphQLClientFactory.CreateFromLogin(
url,
DefaultCredentials.AdminUserName,
DefaultCredentials.DefaultAdminUserPassword,
username,
password,
cancellationToken: cts.Token);
IRestServerClient restClient;
@@ -1898,14 +1911,14 @@ namespace Tgstation.Server.Tests.Live
catch (HttpRequestException)
{
//migrating, to be expected
if (DateTimeOffset.UtcNow > giveUpAt)
if (DateTimeOffset.UtcNow > giveUpAt || !retry)
throw;
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
catch (ServiceUnavailableException)
{
// migrating, to be expected
if (DateTimeOffset.UtcNow > giveUpAt)
if (DateTimeOffset.UtcNow > giveUpAt || !retry)
throw;
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
+596 -207
View File
@@ -1,5 +1,11 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Elastic.CommonSchema;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using StrawberryShake;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
@@ -10,23 +16,25 @@ using Tgstation.Server.Api.Models.Request;
using Tgstation.Server.Api.Models.Response;
using Tgstation.Server.Api.Rights;
using Tgstation.Server.Client;
using Tgstation.Server.Client.GraphQL;
using Tgstation.Server.Common.Extensions;
using Tgstation.Server.Host.Controllers;
using Tgstation.Server.Host.System;
namespace Tgstation.Server.Tests.Live
{
sealed class UsersTest
{
readonly IRestServerClient serverClient;
readonly IMultiServerClient serverClient;
public UsersTest(IRestServerClient serverClient)
public UsersTest(IMultiServerClient serverClient)
{
this.serverClient = serverClient ?? throw new ArgumentNullException(nameof(serverClient));
}
public async Task Run(CancellationToken cancellationToken)
public async ValueTask Run(CancellationToken cancellationToken)
{
await Task.WhenAll(
await ValueTaskExtensions.WhenAll(
BasicTests(cancellationToken),
TestCreateSysUser(cancellationToken),
TestSpamCreation(cancellationToken));
@@ -34,214 +42,525 @@ namespace Tgstation.Server.Tests.Live
await TestPagination(cancellationToken);
}
async Task BasicTests(CancellationToken cancellationToken)
async ValueTask BasicTests(CancellationToken cancellationToken)
{
var user = await serverClient.Users.Read(cancellationToken);
Assert.IsNotNull(user);
Assert.AreEqual("Admin", user.Name);
Assert.IsNull(user.SystemIdentifier);
Assert.AreEqual(true, user.Enabled);
Assert.IsNotNull(user.OAuthConnections);
Assert.IsNotNull(user.PermissionSet);
Assert.IsNotNull(user.PermissionSet.Id);
Assert.IsNotNull(user.PermissionSet.InstanceManagerRights);
Assert.IsNotNull(user.PermissionSet.AdministrationRights);
var (restUser, gqlUser) = await serverClient.ExecuteReadOnlyConfirmEquivalence(
client => client.Users.Read(cancellationToken),
client => client.ReadCurrentUser.ExecuteAsync(cancellationToken),
(restResult, graphQLResult) =>
{
var gqlUser = graphQLResult.Swarm.Users.Current;
return restResult.Enabled == gqlUser.Enabled
&& restResult.Name == gqlUser.Name
&& (restResult.CreatedAt.Value.Ticks / 1000000) == (gqlUser.CreatedAt.Ticks / 1000000)
&& restResult.SystemIdentifier == gqlUser.SystemIdentifier
&& restResult.CreatedBy.Name == gqlUser.CreatedBy.Name;
},
cancellationToken);
var systemUser = user.CreatedBy;
Assert.IsNotNull(restUser);
Assert.AreEqual("Admin", restUser.Name);
Assert.IsNull(restUser.SystemIdentifier);
Assert.AreEqual(true, restUser.Enabled);
Assert.IsNotNull(restUser.OAuthConnections);
Assert.IsNotNull(restUser.PermissionSet);
Assert.IsNotNull(restUser.PermissionSet.Id);
Assert.IsNotNull(restUser.PermissionSet.InstanceManagerRights);
Assert.IsNotNull(restUser.PermissionSet.AdministrationRights);
var systemUser = restUser.CreatedBy;
Assert.IsNotNull(systemUser);
Assert.AreEqual("TGS", systemUser.Name);
var users = await serverClient.Users.List(null, cancellationToken);
Assert.IsTrue(users.Count > 0);
Assert.IsFalse(users.Any(x => x.Id == systemUser.Id));
await ApiAssert.ThrowsException<InsufficientPermissionsException, UserResponse>(() => serverClient.Users.GetId(systemUser, cancellationToken));
await ApiAssert.ThrowsException<InsufficientPermissionsException, UserResponse>(() => serverClient.Users.Update(new UserUpdateRequest
{
Id = systemUser.Id
}, cancellationToken));
var sampleOAuthConnections = new List<OAuthConnection>
{
new OAuthConnection
await serverClient.Execute(
async client =>
{
ExternalUserId = "asdfasdf",
Provider = OAuthProvider.Discord
}
};
await ApiAssert.ThrowsException<ApiConflictException, UserResponse>(() => serverClient.Users.Update(new UserUpdateRequest
{
Id = user.Id,
OAuthConnections = sampleOAuthConnections
}, cancellationToken), ErrorCode.AdminUserCannotOAuth);
var users = await client.Users.List(null, cancellationToken);
Assert.IsTrue(users.Count > 0);
Assert.IsFalse(users.Any(x => x.Id == systemUser.Id));
var testUser = await serverClient.Users.Create(
new UserCreateRequest
{
Name = $"BasicTestUser",
Password = "asdfasdjfhauwiehruiy273894234jhndjkwh"
await ApiAssert.ThrowsException<InsufficientPermissionsException, UserResponse>(() => client.Users.GetId(systemUser, cancellationToken));
await ApiAssert.ThrowsException<InsufficientPermissionsException, UserResponse>(() => client.Users.Update(new UserUpdateRequest
{
Id = systemUser.Id
}, cancellationToken));
var sampleOAuthConnections = new List<OAuthConnection>
{
new()
{
ExternalUserId = "asdfasdf",
Provider = Api.Models.OAuthProvider.Discord
}
};
await ApiAssert.ThrowsException<ApiConflictException, UserResponse>(() => client.Users.Update(new UserUpdateRequest
{
Id = restUser.Id,
OAuthConnections = sampleOAuthConnections
}, cancellationToken), Api.Models.ErrorCode.AdminUserCannotOAuth);
var testUser = await client.Users.Create(
new UserCreateRequest
{
Name = $"BasicTestUser",
Password = "asdfasdjfhauwiehruiy273894234jhndjkwh"
},
cancellationToken);
Assert.IsNotNull(testUser.OAuthConnections);
testUser = await client.Users.Update(
new UserUpdateRequest
{
Id = testUser.Id,
OAuthConnections = sampleOAuthConnections
},
cancellationToken);
Assert.AreEqual(1, testUser.OAuthConnections.Count);
Assert.AreEqual(sampleOAuthConnections.First().ExternalUserId, testUser.OAuthConnections.First().ExternalUserId);
Assert.AreEqual(sampleOAuthConnections.First().Provider, testUser.OAuthConnections.First().Provider);
var group = await client.Groups.Create(
new UserGroupCreateRequest
{
Name = "TestGroup"
},
cancellationToken);
Assert.AreEqual(group.Name, "TestGroup");
Assert.IsNotNull(group.PermissionSet);
Assert.IsNotNull(group.PermissionSet.Id);
Assert.AreEqual(AdministrationRights.None, group.PermissionSet.AdministrationRights);
Assert.AreEqual(InstanceManagerRights.None, group.PermissionSet.InstanceManagerRights);
var group2 = await client.Groups.Create(new UserGroupCreateRequest
{
Name = "TestGroup2",
PermissionSet = new PermissionSet
{
InstanceManagerRights = InstanceManagerRights.List
}
}, cancellationToken);
Assert.AreEqual(AdministrationRights.None, group2.PermissionSet.AdministrationRights);
Assert.AreEqual(InstanceManagerRights.List, group2.PermissionSet.InstanceManagerRights);
var groups = await client.Groups.List(null, cancellationToken);
Assert.AreEqual(2, groups.Count);
foreach (var igroup in groups)
{
Assert.IsNotNull(igroup.Users);
Assert.IsNotNull(igroup.PermissionSet);
}
await client.Groups.Delete(group2, cancellationToken);
groups = await client.Groups.List(null, cancellationToken);
Assert.AreEqual(1, groups.Count);
group = await client.Groups.Update(new UserGroupUpdateRequest
{
Id = groups[0].Id,
PermissionSet = new PermissionSet
{
InstanceManagerRights = RightsHelper.AllRights<InstanceManagerRights>(),
AdministrationRights = RightsHelper.AllRights<AdministrationRights>(),
}
}, cancellationToken);
Assert.AreEqual(RightsHelper.AllRights<AdministrationRights>(), group.PermissionSet.AdministrationRights);
Assert.AreEqual(RightsHelper.AllRights<InstanceManagerRights>(), group.PermissionSet.InstanceManagerRights);
UserUpdateRequest testUserUpdate = new UserCreateRequest
{
Name = "TestUserWithNoPassword",
Password = string.Empty
};
await ApiAssert.ThrowsException<ApiConflictException, UserResponse>(() => client.Users.Create((UserCreateRequest)testUserUpdate, cancellationToken), Api.Models.ErrorCode.UserPasswordLength);
testUserUpdate.OAuthConnections =
[
new()
{
ExternalUserId = "asdf",
Provider = Api.Models.OAuthProvider.GitHub
}
];
var testUser2 = await client.Users.Create((UserCreateRequest)testUserUpdate, cancellationToken);
testUserUpdate = new UserUpdateRequest
{
Id = testUser2.Id,
PermissionSet = testUser2.PermissionSet,
Group = new Api.Models.Internal.UserGroup
{
Id = group.Id
},
};
await ApiAssert.ThrowsException<ApiConflictException, UserResponse>(
() => client.Users.Update(
testUserUpdate,
cancellationToken),
Api.Models.ErrorCode.UserGroupAndPermissionSet);
testUserUpdate.PermissionSet = null;
testUser2 = await client.Users.Update(testUserUpdate, cancellationToken);
Assert.IsNull(testUser2.PermissionSet);
Assert.IsNotNull(testUser2.Group);
Assert.AreEqual(group.Id, testUser2.Group.Id);
var group4 = await client.Groups.GetId(group, cancellationToken);
Assert.IsNotNull(group4.Users);
Assert.AreEqual(1, group4.Users.Count);
Assert.AreEqual(testUser2.Id, group4.Users.First().Id);
Assert.IsNotNull(group4.PermissionSet);
testUserUpdate.Group = null;
testUserUpdate.PermissionSet = new PermissionSet
{
AdministrationRights = RightsHelper.AllRights<AdministrationRights>(),
InstanceManagerRights = RightsHelper.AllRights<InstanceManagerRights>(),
};
testUser2 = await client.Users.Update(testUserUpdate, cancellationToken);
Assert.IsNull(testUser2.Group);
Assert.IsNotNull(testUser2.PermissionSet);
},
cancellationToken);
Assert.IsNotNull(testUser.OAuthConnections);
testUser = await serverClient.Users.Update(
new UserUpdateRequest
{
Id = testUser.Id,
OAuthConnections = sampleOAuthConnections
},
cancellationToken);
Assert.AreEqual(1, testUser.OAuthConnections.Count);
Assert.AreEqual(sampleOAuthConnections.First().ExternalUserId, testUser.OAuthConnections.First().ExternalUserId);
Assert.AreEqual(sampleOAuthConnections.First().Provider, testUser.OAuthConnections.First().Provider);
var group = await serverClient.Groups.Create(
new UserGroupCreateRequest
async client =>
{
Name = "TestGroup"
},
cancellationToken);
Assert.AreEqual(group.Name, "TestGroup");
Assert.IsNotNull(group.PermissionSet);
Assert.IsNotNull(group.PermissionSet.Id);
Assert.AreEqual(AdministrationRights.None, group.PermissionSet.AdministrationRights);
Assert.AreEqual(InstanceManagerRights.None, group.PermissionSet.InstanceManagerRights);
var result = await client.RunOperation(gql => gql.ListUsers.ExecuteAsync(cancellationToken), cancellationToken);
result.EnsureNoErrors();
var users = result.Data.Swarm.Users.QueryableUsers;
Assert.IsTrue(users.TotalCount > 0);
Assert.AreEqual(Math.Min(ApiController.DefaultPageSize, users.TotalCount), users.Nodes.Count);
var group2 = await serverClient.Groups.Create(new UserGroupCreateRequest
{
Name = "TestGroup2",
PermissionSet = new PermissionSet
{
InstanceManagerRights = InstanceManagerRights.List
}
}, cancellationToken);
Assert.AreEqual(AdministrationRights.None, group2.PermissionSet.AdministrationRights);
Assert.AreEqual(InstanceManagerRights.List, group2.PermissionSet.InstanceManagerRights);
var tgsUserResult = await client.RunOperation(gql => gql.GetUserNameByNodeId.ExecuteAsync(gqlUser.Swarm.Users.Current.CreatedBy.Id, cancellationToken), cancellationToken);
tgsUserResult.EnsureNoErrors();
var tgsUserResult2 = await client.RunOperation(gql => gql.GetUserById.ExecuteAsync(gqlUser.Swarm.Users.Current.CreatedBy.Id, cancellationToken), cancellationToken);
Assert.IsTrue(tgsUserResult2.IsErrorResult());
var groups = await serverClient.Groups.List(null, cancellationToken);
Assert.AreEqual(2, groups.Count);
var sampleOAuthConnections = new List<OAuthConnectionInput>
{
new()
{
ExternalUserId = "asdfasdf",
Provider = Client.GraphQL.OAuthProvider.Discord,
}
};
foreach (var igroup in groups)
{
Assert.IsNotNull(igroup.Users);
Assert.IsNotNull(igroup.PermissionSet);
}
await ApiAssert.OperationFails(
client,
gql => gql.SetUserOAuthConnections.ExecuteAsync(
gqlUser.Swarm.Users.Current.Id,
sampleOAuthConnections,
cancellationToken),
data => data.UpdateUser,
Client.GraphQL.ErrorCode.AdminUserCannotOAuth,
cancellationToken);
await serverClient.Groups.Delete(group2, cancellationToken);
var testUserResult = await client.RunMutationEnsureNoErrors(
gql => gql.CreateUserWithPasswordSelectOAuthConnections.ExecuteAsync("BasicTestUser", "asdfasdjfhauwiehruiy273894234jhndjkwh", cancellationToken),
data => data.CreateUserByPasswordAndPermissionSet,
cancellationToken);
groups = await serverClient.Groups.List(null, cancellationToken);
Assert.AreEqual(1, groups.Count);
var testUserResult2 = await client.RunMutationEnsureNoErrors(
gql => gql.UpdateUserOAuthConnections.ExecuteAsync(
testUserResult.User.Id,
sampleOAuthConnections,
cancellationToken),
data => data.UpdateUser,
cancellationToken);
group = await serverClient.Groups.Update(new UserGroupUpdateRequest
{
Id = groups[0].Id,
PermissionSet = new PermissionSet
{
InstanceManagerRights = RightsHelper.AllRights<InstanceManagerRights>(),
AdministrationRights = RightsHelper.AllRights<AdministrationRights>(),
}
}, cancellationToken);
var testUser = testUserResult2.User;
Assert.IsNotNull(testUser.OAuthConnections);
Assert.AreEqual(1, testUser.OAuthConnections.Count);
Assert.AreEqual(sampleOAuthConnections.First().ExternalUserId, testUser.OAuthConnections[0].ExternalUserId);
Assert.AreEqual(sampleOAuthConnections.First().Provider, testUser.OAuthConnections[0].Provider);
Assert.AreEqual(RightsHelper.AllRights<AdministrationRights>(), group.PermissionSet.AdministrationRights);
Assert.AreEqual(RightsHelper.AllRights<InstanceManagerRights>(), group.PermissionSet.InstanceManagerRights);
var groupResult = await client.RunMutationEnsureNoErrors(
gql => gql.CreateUserGroup.ExecuteAsync("TestGroup", cancellationToken),
data => data.CreateUserGroup,
cancellationToken);
UserUpdateRequest testUserUpdate = new UserCreateRequest
{
Name = "TestUserWithNoPassword",
Password = string.Empty
};
var group = groupResult.UserGroup;
Assert.AreEqual(group.Name, "TestGroup");
Assert.IsNotNull(group.PermissionSet);
await ApiAssert.ThrowsException<ApiConflictException, UserResponse>(() => serverClient.Users.Create((UserCreateRequest)testUserUpdate, cancellationToken), ErrorCode.UserPasswordLength);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanSetOnline);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanRelocate);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanRename);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanSetConfiguration);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanRead);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanCreate);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanDelete);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanGrantPermissions);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanList);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanSetAutoUpdate);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanSetChatBotLimit);
Assert.IsFalse(group.PermissionSet.InstanceManagerRights.CanSetConfiguration);
testUserUpdate.OAuthConnections = new List<OAuthConnection>
{
new OAuthConnection
{
ExternalUserId = "asdf",
Provider = OAuthProvider.GitHub
}
};
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanChangeVersion);
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanDownloadLogs);
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanEditOwnOAuthConnections);
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanEditOwnPassword);
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanReadUsers);
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanRestartHost);
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanUploadVersion);
Assert.IsFalse(group.PermissionSet.AdministrationRights.CanWriteUsers);
var testUser2 = await serverClient.Users.Create((UserCreateRequest)testUserUpdate, cancellationToken);
var group2Result = await client.RunMutationEnsureNoErrors(
gql => gql.CreateUserGroupWithInstanceListPerm.ExecuteAsync("TestGroup2", cancellationToken),
data => data.CreateUserGroup,
cancellationToken);
testUserUpdate = new UserUpdateRequest
{
Id = testUser2.Id,
PermissionSet = testUser2.PermissionSet,
Group = new Api.Models.Internal.UserGroup
{
Id = group.Id
},
};
await ApiAssert.ThrowsException<ApiConflictException, UserResponse>(
() => serverClient.Users.Update(
testUserUpdate,
cancellationToken),
ErrorCode.UserGroupAndPermissionSet);
var group2 = group2Result.UserGroup;
testUserUpdate.PermissionSet = null;
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanSetOnline);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanRelocate);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanRename);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanSetConfiguration);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanRead);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanCreate);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanDelete);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanGrantPermissions);
Assert.IsTrue(group2.PermissionSet.InstanceManagerRights.CanList);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanSetAutoUpdate);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanSetChatBotLimit);
Assert.IsFalse(group2.PermissionSet.InstanceManagerRights.CanSetConfiguration);
testUser2 = await serverClient.Users.Update(testUserUpdate, cancellationToken);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanChangeVersion);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanDownloadLogs);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanEditOwnOAuthConnections);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanEditOwnPassword);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanReadUsers);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanRestartHost);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanUploadVersion);
Assert.IsFalse(group2.PermissionSet.AdministrationRights.CanWriteUsers);
Assert.IsNull(testUser2.PermissionSet);
Assert.IsNotNull(testUser2.Group);
Assert.AreEqual(group.Id, testUser2.Group.Id);
var groupsResult = await client.RunQueryEnsureNoErrors(
gql => gql.ListUserGroups.ExecuteAsync(cancellationToken),
cancellationToken);
group = await serverClient.Groups.GetId(group, cancellationToken);
Assert.IsNotNull(group.Users);
Assert.AreEqual(1, group.Users.Count);
Assert.AreEqual(testUser2.Id, group.Users.First().Id);
Assert.IsNotNull(group.PermissionSet);
var groups = groupsResult.Swarm.Users.Groups.QueryableGroups;
Assert.AreEqual(2, groups.TotalCount);
testUserUpdate.Group = null;
testUserUpdate.PermissionSet = new PermissionSet
{
AdministrationRights = RightsHelper.AllRights<AdministrationRights>(),
InstanceManagerRights = RightsHelper.AllRights<InstanceManagerRights>(),
};
foreach (var igroup in groups.Nodes)
Assert.IsNotNull(igroup.Id);
testUser2 = await serverClient.Users.Update(testUserUpdate, cancellationToken);
Assert.IsNull(testUser2.Group);
Assert.IsNotNull(testUser2.PermissionSet);
var deleteResult = await client.RunMutationEnsureNoErrors(
gql => gql.DeleteUserGroup.ExecuteAsync(group2.Id, cancellationToken),
data => data.DeleteEmptyUserGroup,
cancellationToken);
groupsResult = await client.RunQueryEnsureNoErrors(
gql => gql.ListUserGroups.ExecuteAsync(cancellationToken),
cancellationToken);
groups = groupsResult.Swarm.Users.Groups.QueryableGroups;
Assert.AreEqual(1, groups.TotalCount);
foreach (var igroup in groups.Nodes)
Assert.IsNotNull(igroup.Id);
var group3Result = await client.RunMutationEnsureNoErrors(
gql => gql.SetFullPermsOnUserGroup.ExecuteAsync(group.Id, cancellationToken),
data => data.UpdateUserGroup,
cancellationToken);
var group3 = group3Result.UserGroup;
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanSetOnline);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanRelocate);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanRename);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanSetConfiguration);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanRead);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanCreate);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanDelete);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanGrantPermissions);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanList);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanSetAutoUpdate);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanSetChatBotLimit);
Assert.IsTrue(group3.PermissionSet.InstanceManagerRights.CanSetConfiguration);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanChangeVersion);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanDownloadLogs);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanEditOwnOAuthConnections);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanEditOwnPassword);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanReadUsers);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanRestartHost);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanUploadVersion);
Assert.IsTrue(group3.PermissionSet.AdministrationRights.CanWriteUsers);
await ApiAssert.OperationFails(
client,
gql => gql.CreateUserWithPassword.ExecuteAsync("TestUserWithNoPassword", String.Empty, cancellationToken),
data => data.CreateUserByPasswordAndPermissionSet,
Client.GraphQL.ErrorCode.ModelValidationFailure,
cancellationToken);
await ApiAssert.OperationFails(
client,
gql => gql.CreateUserWithPassword.ExecuteAsync("TestUserWithShortPassword", "a", cancellationToken),
data => data.CreateUserByPasswordAndPermissionSet,
Client.GraphQL.ErrorCode.UserPasswordLength,
cancellationToken);
var oAuthCreateResult = await client.RunMutationEnsureNoErrors(
gql => gql.CreateUserFromOAuthConnection.ExecuteAsync(
"TestUserWithNoPassword",
[
new()
{
ExternalUserId = "asdf",
Provider = Client.GraphQL.OAuthProvider.GitHub,
}
],
cancellationToken),
data => data.CreateUserByOAuthAndPermissionSet,
cancellationToken);
var testUser2 = oAuthCreateResult.User;
var testUser22Result = await client.RunMutationEnsureNoErrors(
gql => gql.SetUserGroup.ExecuteAsync(testUser2.Id, group.Id, cancellationToken),
data => data.UpdateUserSetGroup,
cancellationToken);
var testUser22 = testUser22Result.User;
Assert.IsNull(testUser22.OwnedPermissionSet);
Assert.IsNotNull(testUser22.Group);
Assert.AreEqual(group.Id, testUser22.Group.Id);
var group4Result = await client.RunQueryEnsureNoErrors(
gql => gql.GetSomeGroupInfo.ExecuteAsync(group.Id, cancellationToken),
cancellationToken);
var group4 = group4Result.Swarm.Users.Groups.ById;
Assert.IsNotNull(group4.QueryableUsersByGroup.Nodes);
Assert.AreEqual(1, group4.QueryableUsersByGroup.TotalCount);
Assert.AreEqual(testUser2.Id, group4.QueryableUsersByGroup.Nodes[0].Id);
Assert.IsNotNull(group4.PermissionSet);
var testUser4Result = await client.RunMutationEnsureNoErrors(
gql => gql.SetUserPermissionSet.ExecuteAsync(
testUser2.Id,
new PermissionSetInput
{
AdministrationRights = new AdministrationRightsFlagsInput
{
CanChangeVersion = true,
CanDownloadLogs = true,
CanEditOwnOAuthConnections = true,
CanEditOwnPassword = true,
CanReadUsers = true,
CanRestartHost = true,
CanUploadVersion = true,
CanWriteUsers = true,
},
InstanceManagerRights = new InstanceManagerRightsFlagsInput
{
CanCreate = true,
CanDelete = true,
CanGrantPermissions = true,
CanList = true,
CanRead = true,
CanRelocate = true,
CanRename = true,
CanSetAutoUpdate = true,
CanSetChatBotLimit = true,
CanSetConfiguration = true,
CanSetOnline = true,
}
},
cancellationToken),
data => data.UpdateUserSetOwnedPermissionSet,
cancellationToken);
var testUser4 = testUser4Result.User;
Assert.IsNull(testUser4.Group);
Assert.IsNotNull(testUser4.OwnedPermissionSet);
});
}
async Task TestCreateSysUser(CancellationToken cancellationToken)
ValueTask TestCreateSysUser(CancellationToken cancellationToken)
{
var sysId = Environment.UserName;
var update = new UserCreateRequest
{
SystemIdentifier = sysId
};
if (new PlatformIdentifier().IsWindows)
await serverClient.Users.Create(update, cancellationToken);
else
await ApiAssert.ThrowsException<MethodNotSupportedException, UserResponse>(() => serverClient.Users.Create(update, cancellationToken), ErrorCode.RequiresPosixSystemIdentity);
return serverClient.Execute(
async restClient =>
{
var update = new UserCreateRequest
{
SystemIdentifier = sysId
};
if (new PlatformIdentifier().IsWindows)
await restClient.Users.Create(update, cancellationToken);
else
await ApiAssert.ThrowsException<MethodNotSupportedException, UserResponse>(() => restClient.Users.Create(update, cancellationToken), Api.Models.ErrorCode.RequiresPosixSystemIdentity);
},
async graphQLClient =>
{
if (new PlatformIdentifier().IsWindows)
{
await graphQLClient.RunMutationEnsureNoErrors(
gql => gql.CreateSystemUserWithPermissionSet.ExecuteAsync(sysId, cancellationToken),
data => data.CreateUserBySystemIDAndPermissionSet,
cancellationToken);
}
else
await ApiAssert.OperationFails(
graphQLClient,
gql => gql.CreateSystemUserWithPermissionSet.ExecuteAsync(sysId, cancellationToken),
data => data.CreateUserBySystemIDAndPermissionSet,
Client.GraphQL.ErrorCode.RequiresPosixSystemIdentity,
cancellationToken);
});
}
async Task TestSpamCreation(CancellationToken cancellationToken)
async ValueTask TestSpamCreation(CancellationToken cancellationToken)
{
// Careful with this, very easy to overload the thread pool
const int RepeatCount = 100;
var tasks = new List<ValueTask<UserResponse>>(RepeatCount);
var tasks = new List<ValueTask>(RepeatCount);
ThreadPool.GetMaxThreads(out var defaultMaxWorker, out var defaultMaxCompletion);
ThreadPool.GetMinThreads(out var defaultMinWorker, out var defaultMinCompletion);
ConcurrentBag<object> ids = new ConcurrentBag<object>();
try
{
ThreadPool.SetMinThreads(Math.Min(RepeatCount * 4, defaultMaxWorker), Math.Min(RepeatCount * 4, defaultMaxCompletion));
for (int i = 0; i < RepeatCount; ++i)
{
var task =
serverClient.Users.Create(
new UserCreateRequest
var iLocal = i;
ValueTask CreateSpamUser()
=> serverClient.Execute(
async restClient =>
{
Name = $"SpamTestUser_{i}",
Password = "asdfasdjfhauwiehruiy273894234jhndjkwh"
var user = await restClient.Users.Create(
new UserCreateRequest
{
Name = $"SpamTestUser_{iLocal}",
Password = "asdfasdjfhauwiehruiy273894234jhndjkwh"
},
cancellationToken);
ids.Add(user.Id);
},
cancellationToken);
tasks.Add(task);
async graphQLClient =>
{
var result = await graphQLClient.RunMutationEnsureNoErrors(
gql => gql.CreateUserWithPassword.ExecuteAsync($"SpamTestUser_{iLocal}", "asdfasdjfhauwiehruiy273894234jhndjkwh", cancellationToken),
data => data.CreateUserByPasswordAndPermissionSet,
cancellationToken);
ids.Add(result.User.Id);
});
tasks.Add(CreateSpamUser());
}
await ValueTaskExtensions.WhenAll(tasks);
@@ -251,57 +570,127 @@ namespace Tgstation.Server.Tests.Live
ThreadPool.SetMinThreads(defaultMinWorker, defaultMinCompletion);
}
Assert.AreEqual(RepeatCount, tasks.Select(task => task.Result.Id).Distinct().Count(), "Did not receive expected number of unique user IDs!");
Assert.AreEqual(RepeatCount, ids.Distinct().Count(), "Did not receive expected number of unique user IDs!");
}
async Task TestPagination(CancellationToken cancellationToken)
ValueTask TestPagination(CancellationToken cancellationToken)
{
// we test pagination here b/c it's the only spot we have a decent amount of entities
var nullSettings = await serverClient.Users.List(null, cancellationToken);
var emptySettings = await serverClient.Users.List(
new PaginationSettings
const int ExpectedCount = 106;
return serverClient.Execute(
async restClient =>
{
}, cancellationToken);
// we test pagination here b/c it's the only spot we have a decent amount of entities
var nullSettings = await restClient.Users.List(null, cancellationToken);
var emptySettings = await restClient.Users.List(
new PaginationSettings
{
}, cancellationToken);
Assert.AreEqual(nullSettings.Count, emptySettings.Count);
Assert.IsTrue(nullSettings.All(x => emptySettings.SingleOrDefault(y => x.Id == y.Id) != null));
Assert.AreEqual(nullSettings.Count, emptySettings.Count);
Assert.AreEqual(ExpectedCount, nullSettings.Count);
Assert.IsTrue(nullSettings.All(x => emptySettings.SingleOrDefault(y => x.Id == y.Id) != null));
await ApiAssert.ThrowsException<ApiConflictException, List<UserResponse>>(() => serverClient.Users.List(
new PaginationSettings
{
PageSize = -2143
}, cancellationToken), ErrorCode.ApiInvalidPageOrPageSize);
await ApiAssert.ThrowsException<ApiConflictException, List<UserResponse>>(() => serverClient.Users.List(
new PaginationSettings
{
PageSize = int.MaxValue
}, cancellationToken), ErrorCode.ApiPageTooLarge);
await ApiAssert.ThrowsException<ApiConflictException, List<UserResponse>>(() => restClient.Users.List(
new PaginationSettings
{
PageSize = -2143
}, cancellationToken), Api.Models.ErrorCode.ApiInvalidPageOrPageSize);
await ApiAssert.ThrowsException<ApiConflictException, List<UserResponse>>(() => restClient.Users.List(
new PaginationSettings
{
PageSize = ApiController.MaximumPageSize + 1,
}, cancellationToken), Api.Models.ErrorCode.ApiPageTooLarge);
await serverClient.Users.List(
new PaginationSettings
{
PageSize = 50
var users = await restClient.Users.List(
new PaginationSettings
{
PageSize = ApiController.MaximumPageSize,
RetrieveCount = ApiController.MaximumPageSize,
},
cancellationToken);
Assert.AreEqual(ApiController.MaximumPageSize, users.Count);
var skipped = await restClient.Users.List(new PaginationSettings
{
Offset = 50,
RetrieveCount = 5
}, cancellationToken);
Assert.AreEqual(5, skipped.Count);
var allAfterSkipped = await restClient.Users.List(new PaginationSettings
{
Offset = 50,
}, cancellationToken);
Assert.IsTrue(5 < allAfterSkipped.Count);
var limited = await restClient.Users.List(new PaginationSettings
{
RetrieveCount = 12,
}, cancellationToken);
Assert.AreEqual(12, limited.Count);
},
cancellationToken);
graphQLClient =>
{
async ValueTask TestPageSize(int? inputPageSize)
{
var outputPageSize = inputPageSize ?? ApiController.DefaultPageSize;
var skipped = await serverClient.Users.List(new PaginationSettings
{
Offset = 50,
RetrieveCount = 5
}, cancellationToken);
Assert.AreEqual(5, skipped.Count);
string cursor = null;
var allAfterSkipped = await serverClient.Users.List(new PaginationSettings
{
Offset = 50,
}, cancellationToken);
Assert.IsTrue(5 < allAfterSkipped.Count);
var exactMatch = (ExpectedCount % outputPageSize) == 0;
var expectedIterations = (ExpectedCount / outputPageSize) + (exactMatch ? 0 : 1);
for (int i = 0; i < expectedIterations; ++i)
{
var isLastIteration = i == expectedIterations - 1;
var queryable = (await graphQLClient.RunQueryEnsureNoErrors(
gql => gql.PageUserIds.ExecuteAsync(inputPageSize, cursor, cancellationToken),
cancellationToken))
.Swarm
.Users
.QueryableUsers;
var limited = await serverClient.Users.List(new PaginationSettings
{
RetrieveCount = 12,
}, cancellationToken);
Assert.AreEqual(12, limited.Count);
if (!isLastIteration || exactMatch)
Assert.AreEqual(outputPageSize, queryable.Nodes.Count);
else
Assert.AreEqual(ExpectedCount % outputPageSize, queryable.Nodes.Count);
Assert.AreEqual(!isLastIteration, queryable.PageInfo.HasNextPage);
Assert.IsNotNull(queryable.PageInfo.EndCursor);
cursor = queryable.PageInfo.EndCursor;
}
}
async ValueTask TestBadPageSize(int size)
{
var result = await graphQLClient.RunOperation(
gql => gql.PageUserIds.ExecuteAsync(size, null, cancellationToken),
cancellationToken);
if (size == 0)
{
// special case
result.EnsureNoErrors();
Assert.AreEqual(0, result.Data.Swarm.Users.QueryableUsers.Nodes.Count);
return;
}
var errored = result.IsErrorResult();
Assert.IsTrue(errored);
}
return ValueTaskExtensions.WhenAll(
TestPageSize(null),
TestPageSize(ApiController.DefaultPageSize),
TestPageSize(ApiController.DefaultPageSize / 2),
TestPageSize(ApiController.MaximumPageSize),
TestPageSize(1),
TestBadPageSize(-1),
TestBadPageSize(0),
TestBadPageSize(ApiController.MaximumPageSize + 1),
TestBadPageSize(Int32.MinValue),
TestBadPageSize(Int32.MaxValue));
});
}
}
}