summary refs log tree commit diff
diff options
context:
space:
mode:
authorRory& <root@rory.gay>2026-06-05 15:48:02 +0200
committerRory& <root@rory.gay>2026-06-05 15:48:02 +0200
commit31ca17d53602fc4d6078f51595a8f41a6ba4d4b2 (patch)
tree498b799407fde2bcf4f5e82fc902e994694c22bf
parentMore C# models, split basic client SDK from client (diff)
downloadserver-ts-31ca17d53602fc4d6078f51595a8f41a6ba4d4b2.tar.xz
Some basic unit tests
-rw-r--r--extra/admin-api/SpacebarAdminAPI.slnx3
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Abstractions/UserAbstraction.cs24
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Config.cs11
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertExtensions.cs15
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertHttpExtensions.cs38
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Fixtures/TestFixture.cs29
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/GlobalUsings.cs1
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Spacebar.Tests.csproj44
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Tests/AuthenticationTests.cs95
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Tests/BasicWellKnownTests.cs44
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Tests/ChannelTests.cs65
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Tests/GuildTests.cs47
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/Tests/Meta/UserAbstractionTests.cs16
-rw-r--r--extra/admin-api/Tests/Spacebar.Tests/appsettings.json5
14 files changed, 437 insertions, 0 deletions
diff --git a/extra/admin-api/SpacebarAdminAPI.slnx b/extra/admin-api/SpacebarAdminAPI.slnx

index 44498b0f..491568cd 100644 --- a/extra/admin-api/SpacebarAdminAPI.slnx +++ b/extra/admin-api/SpacebarAdminAPI.slnx
@@ -25,6 +25,9 @@ <Project Path="Models/Spacebar.Models.Gateway/Spacebar.Models.Gateway.csproj" /> <Project Path="Models/Spacebar.Models.Generic/Spacebar.Models.Generic.csproj" /> </Folder> + <Folder Name="/Tests/"> + <Project Path="Tests/Spacebar.Tests/Spacebar.Tests.csproj" /> + </Folder> <Folder Name="/Utilities/"> <Project Path="Utilities/ConfigTest/ConfigTest.csproj" /> <Project Path="Utilities/DiscordEmojiConverter/DiscordEmojiConverter.csproj" /> diff --git a/extra/admin-api/Tests/Spacebar.Tests/Abstractions/UserAbstraction.cs b/extra/admin-api/Tests/Spacebar.Tests/Abstractions/UserAbstraction.cs new file mode 100644
index 00000000..b6cef1ac --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Abstractions/UserAbstraction.cs
@@ -0,0 +1,24 @@ +using Spacebar.Sdk.Core; + +namespace Spacebar.Tests.Abstractions; + +public class UserAbstraction(Config _config, SpacebarClientProviderService _clientProvider) { + public async Task<AuthenticatedSpacebarClient> GetFreshUser(bool withAutojoinGuilds = false) { + var ua = await _clientProvider.GetUnauthenticatedClientAsync(_config.TestInstance); + var tokenResponse = await ua.RegisterAsync(new() { + Email = $"{Guid.NewGuid().ToString()}@{Guid.NewGuid().ToString()}.tld", + Username = Guid.NewGuid().ToString(), + Password = Guid.NewGuid().ToString(), + DateOfBirth = new(), + Consent = true + }); + var client = await _clientProvider.GetAuthenticatedClientAsync(_config.TestInstance, tokenResponse.Token); + + if (!withAutojoinGuilds) { + + } + + return client; + } + +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Config.cs b/extra/admin-api/Tests/Spacebar.Tests/Config.cs new file mode 100644
index 00000000..d1aa9de0 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Config.cs
@@ -0,0 +1,11 @@ +using Microsoft.Extensions.Configuration; + +namespace Spacebar.Tests; + +public class Config { + public Config(IConfiguration? config) { + config.GetSection("Configuration").Bind(this); + } + + public string TestInstance { get; set; } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertExtensions.cs b/extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertExtensions.cs new file mode 100644
index 00000000..fa146f78 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertExtensions.cs
@@ -0,0 +1,15 @@ +namespace Spacebar.Tests.Extensions; + +public static class AssertExtensions { + extension(Assert) { + public static void StringNotNullOrEmpty(string? str) { + Assert.NotNull(str); + Assert.NotEqual("", str); + } + + public static void StringNotNullOrWhitespace(string? str) { + StringNotNullOrEmpty(str); + Assert.Matches(".+", str); + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertHttpExtensions.cs b/extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertHttpExtensions.cs new file mode 100644
index 00000000..f5e99dc9 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Extensions/AssertHttpExtensions.cs
@@ -0,0 +1,38 @@ +using System.Net.Http.Json; +using System.Text.Json.Nodes; + +namespace Spacebar.Tests.Extensions; + +public static class AssertHttpExtensions { + private static readonly HttpClient Hc = new(); + + public static async Task<string> GetFormattedErrorDetails(HttpResponseMessage res) { + return res.Content.Headers.ContentType?.MediaType == "application/json" + ? (await res.Content.ReadFromJsonAsync<JsonObject>())!.ToJsonString(new() { + WriteIndented = true + }) + : await res.Content.ReadAsStringAsync(); + } + + extension(Assert) { + public static async Task<HttpResponseMessage> SuccessfullyHttpGetAsync(string url) { + var res = await Hc.GetAsync(url); + Assert.True(res.IsSuccessStatusCode, $"Could not get {url}: {res.StatusCode}\n{await GetFormattedErrorDetails(res)}"); + return res; + } + + public static async Task<HttpResponseMessage> SuccessfullyHttpPostAsJsonAsync<TValue>(string url, TValue obj) { + var res = await Hc.PostAsJsonAsync(url, obj); + if (!res.IsSuccessStatusCode) + Assert.True(res.IsSuccessStatusCode, $"Could not POST JSON to {url}: {(int)res.StatusCode} {res.StatusCode}\n{await GetFormattedErrorDetails(res)}"); + return res; + } + + public static async Task<HttpResponseMessage> HttpSuccess(HttpResponseMessage res) { + if (!res.IsSuccessStatusCode) + Assert.True(res.IsSuccessStatusCode, + $"Could not {res.RequestMessage!.Method.Method.ToUpper()} to {res.RequestMessage!.RequestUri!.ToString()}: {(int)res.StatusCode} {res.StatusCode}\n{await GetFormattedErrorDetails(res)}"); + return res; + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Fixtures/TestFixture.cs b/extra/admin-api/Tests/Spacebar.Tests/Fixtures/TestFixture.cs new file mode 100644
index 00000000..cc9ce056 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Fixtures/TestFixture.cs
@@ -0,0 +1,29 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Spacebar.Sdk.Core; +using Spacebar.Tests.Abstractions; +using Xunit.Microsoft.DependencyInjection; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace Spacebar.Tests.Fixtures; + +public class TestFixture : TestBedFixture { + protected override void AddServices(IServiceCollection services, IConfiguration configuration) { + services.AddSingleton(configuration); + services.AddLogging(); + + services.AddSingleton<Config>(); + services.AddSingleton<UserAbstraction>(); + + services.AddSingleton<SpacebarClientWellKnownResolverService>(); + services.AddSingleton<SpacebarClientProviderService>(); + + } + + protected override ValueTask DisposeAsyncCore() + => new(); + + protected override IEnumerable<TestAppSettings> GetTestAppSettings() { + yield return new TestAppSettings { Filename = "appsettings.json", IsOptional = true }; + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/GlobalUsings.cs b/extra/admin-api/Tests/Spacebar.Tests/GlobalUsings.cs new file mode 100644
index 00000000..8c927eb7 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/GlobalUsings.cs
@@ -0,0 +1 @@ +global using Xunit; \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Spacebar.Tests.csproj b/extra/admin-api/Tests/Spacebar.Tests/Spacebar.Tests.csproj new file mode 100644
index 00000000..b521b61c --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Spacebar.Tests.csproj
@@ -0,0 +1,44 @@ +<Project Sdk="Microsoft.NET.Sdk"> + + <PropertyGroup> + <TargetFramework>net10.0</TargetFramework> + <ImplicitUsings>enable</ImplicitUsings> + <Nullable>enable</Nullable> + + <IsPackable>false</IsPackable> + <IsTestProject>true</IsTestProject> + </PropertyGroup> + + <ItemGroup> + <PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.8" /> + + <PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" /> + <PackageReference Include="Xunit.Microsoft.DependencyInjection" Version="10.0.4" /> + <PackageReference Include="xunit.runner.visualstudio" Version="3.1.5"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="coverlet.collector" Version="10.0.1"> + <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> + <PrivateAssets>all</PrivateAssets> + </PackageReference> + <PackageReference Include="xunit.v3" Version="3.2.2" /> + </ItemGroup> + + <ItemGroup> + <ProjectReference Include="..\..\Models\Spacebar.Models.AdminApi\Spacebar.Models.AdminApi.csproj" /> + <ProjectReference Include="..\..\Models\Spacebar.Models.Api\Spacebar.Models.Api.csproj" /> + <ProjectReference Include="..\..\Models\Spacebar.Models.Config\Spacebar.Models.Config.csproj" /> + <ProjectReference Include="..\..\Models\Spacebar.Models.Db\Spacebar.Models.Db.csproj" /> + <ProjectReference Include="..\..\Models\Spacebar.Models.Gateway\Spacebar.Models.Gateway.csproj" /> + <ProjectReference Include="..\..\Models\Spacebar.Models.Generic\Spacebar.Models.Generic.csproj" /> + <ProjectReference Include="..\..\Utilities\Spacebar.Sdk\Spacebar.Sdk.csproj" /> + </ItemGroup> + + <ItemGroup> + <Content Include="appsettings*.json"> + <CopyToOutputDirectory>Always</CopyToOutputDirectory> + </Content> + </ItemGroup> + +</Project> diff --git a/extra/admin-api/Tests/Spacebar.Tests/Tests/AuthenticationTests.cs b/extra/admin-api/Tests/Spacebar.Tests/Tests/AuthenticationTests.cs new file mode 100644
index 00000000..04bd5f91 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Tests/AuthenticationTests.cs
@@ -0,0 +1,95 @@ +using System.Net.Http.Json; +using System.Text.Json.Nodes; +using ArcaneLibs.Extensions; +using Spacebar.Models.Api; +using Spacebar.Sdk.Core; +using Spacebar.Tests.Extensions; +using Spacebar.Tests.Fixtures; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace Spacebar.Tests.Tests; + +public class AuthenticationTests(ITestOutputHelper testOutputHelper, TestFixture fixture) : TestBed<TestFixture>(testOutputHelper, fixture) { + private readonly Config _config = fixture.GetService<Config>(testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + + private readonly SpacebarClientWellKnownResolverService _wellKnownResolver = fixture.GetService<SpacebarClientWellKnownResolverService>(testOutputHelper) ?? + throw new InvalidOperationException( + $"Failed to get {nameof(SpacebarClientWellKnownResolverService)}"); + + private readonly SpacebarClientProviderService _clientProvider = fixture.GetService<SpacebarClientProviderService>(testOutputHelper) ?? + throw new InvalidOperationException($"Failed to get {nameof(SpacebarClientProviderService)}"); + + [Fact] + public async Task RegisterUser() { + var res = await Assert.SuccessfullyHttpPostAsJsonAsync($"{_config.TestInstance}/api/v9/auth/register", new RegisterRequest() { + Email = $"{Guid.NewGuid().ToString()}@{Guid.NewGuid().ToString()}.tld", + Username = Guid.NewGuid().ToString(), + Password = Guid.NewGuid().ToString(), + DateOfBirth = new(), + Consent = true + }); + } + + [Fact] + public async Task ConcurrentRegister50Users() { + var tasks = Enumerable.Range(0, 50).Select(async _ => { + var rr = new RegisterRequest() { + Email = $"{Guid.NewGuid().ToString()}@{Guid.NewGuid().ToString()}.tld", + Username = Guid.NewGuid().ToString(), + Password = "password", + DateOfBirth = new(), + Consent = true + }; + return (rr, await Assert.SuccessfullyHttpPostAsJsonAsync($"{_config.TestInstance}/api/v9/auth/register", rr)); + }).ToList(); + await Task.WhenAll(tasks); + + testOutputHelper.WriteLine("Waiting for server to settle..."); + await Task.Delay(2500, TestContext.Current.CancellationToken); + + testOutputHelper.WriteLine("Cleaning up users..."); + var cleanupTasks = tasks.Select(x => x.Result).Select(async res => { + var resp = await res.Item2.Content.ReadFromJsonAsync<RegisterResponse>(); + var c = await _clientProvider.GetAuthenticatedClientAsync(_config.TestInstance, resp.Token); + var dresp = (await c.ApiHttpClient.PostAsJsonAsync("/api/v9/users/@me/delete", new JsonObject() { + { "password", "password" } + }, cancellationToken: TestContext.Current.CancellationToken)); + // TODO: figure out why this fails with "invalid password" + if (!dresp.IsSuccessStatusCode) + testOutputHelper.WriteLine("Failed to delete user: " + await AssertHttpExtensions.GetFormattedErrorDetails(dresp)); + }).ToList(); + await Task.WhenAll(cleanupTasks); + } + + [Fact] + public async Task LoginUser() { + var rr = new RegisterRequest() { + Email = $"{Guid.NewGuid().ToString()}@{Guid.NewGuid().ToString()}.tld", + Username = Guid.NewGuid().ToString(), + Password = Guid.NewGuid().ToString(), + DateOfBirth = new(), + Consent = true + }; + var rrRes = await Assert.SuccessfullyHttpPostAsJsonAsync($"{_config.TestInstance}/api/v9/auth/register", rr); + var loginRes = await Assert.SuccessfullyHttpPostAsJsonAsync($"{_config.TestInstance}/api/v9/auth/login", new LoginRequest() { + Login = rr.Email, + Password = rr.Password + }); + } + + [Fact] + public async Task WhoAmI() { + var rr = new RegisterRequest() { + Email = $"{Guid.NewGuid().ToString()}@{Guid.NewGuid().ToString()}.tld", + Username = Guid.NewGuid().ToString(), + Password = Guid.NewGuid().ToString(), + DateOfBirth = new(), + Consent = true + }; + var rrRes = await Assert.SuccessfullyHttpPostAsJsonAsync($"{_config.TestInstance}/api/v9/auth/register", rr); + var res = await rrRes.Content.ReadFromJsonAsync<RegisterResponse>(); + var client = await _clientProvider.GetAuthenticatedClientAsync(_config.TestInstance, res.Token); + var waRes = await Assert.HttpSuccess(await client.ApiHttpClient.GetAsync("/api/v9/auth/whoami")); + // TODO: finish test once model exists + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Tests/BasicWellKnownTests.cs b/extra/admin-api/Tests/Spacebar.Tests/Tests/BasicWellKnownTests.cs new file mode 100644
index 00000000..56254a62 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Tests/BasicWellKnownTests.cs
@@ -0,0 +1,44 @@ +using Spacebar.Sdk.Core; +using Spacebar.Tests.Extensions; +using Spacebar.Tests.Fixtures; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace Spacebar.Tests.Tests; + +public class BasicWellKnownTests(ITestOutputHelper testOutputHelper, TestFixture fixture) : TestBed<TestFixture>(testOutputHelper, fixture) { + private readonly Config _config = fixture.GetService<Config>(testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + + private readonly SpacebarClientWellKnownResolverService _wellKnownResolver = fixture.GetService<SpacebarClientWellKnownResolverService>(testOutputHelper) ?? + throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + + [Fact] + public async Task ValidateTestConfig() { + Assert.NotNull(_config.TestInstance); + Assert.NotEmpty(_config.TestInstance); + } + + [Fact] + public async Task CanReachInstance() => await Assert.SuccessfullyHttpGetAsync($"{_config.TestInstance}/api/v9/ping"); + + [Fact] + public async Task CanGetOldWellknown() { + await Assert.SuccessfullyHttpGetAsync($"{_config.TestInstance}/.well-known/spacebar"); + await Assert.SuccessfullyHttpGetAsync($"{_config.TestInstance}/api/v9/policies/instance/domains"); + } + + [Fact] + public async Task CanGetNewWellknown() => await Assert.SuccessfullyHttpGetAsync($"{_config.TestInstance}/.well-known/spacebar/client"); + + [Fact] + public async Task SdkCanGetWellKnown() { + testOutputHelper.WriteLine("instance: " + _config.TestInstance); + var res = await _wellKnownResolver.ResolveClientWellKnown(_config.TestInstance); + Assert.StringNotNullOrWhitespace(res.Api.BaseUrl); + Assert.StringNotNullOrWhitespace(res.Cdn.BaseUrl); + Assert.StringNotNullOrWhitespace(res.Gateway.BaseUrl); + Assert.NotEmpty(res.Api.ApiVersions.Active); + Assert.StringNotNullOrWhitespace(res.Api.ApiVersions.Default); + Assert.NotEmpty(res.Gateway.Compression); + Assert.NotEmpty(res.Gateway.Encoding); + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Tests/ChannelTests.cs b/extra/admin-api/Tests/Spacebar.Tests/Tests/ChannelTests.cs new file mode 100644
index 00000000..d04ee994 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Tests/ChannelTests.cs
@@ -0,0 +1,65 @@ +using System.Net.Http.Json; +using Spacebar.Models.Generic; +using Spacebar.Sdk.Core; +using Spacebar.Tests.Abstractions; +using Spacebar.Tests.Extensions; +using Spacebar.Tests.Fixtures; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace Spacebar.Tests.Tests; + +public class ChannelTests(ITestOutputHelper testOutputHelper, TestFixture fixture) : TestBed<TestFixture>(testOutputHelper, fixture) { + private readonly Config _config = fixture.GetService<Config>(testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + + private readonly SpacebarClientWellKnownResolverService _wellKnownResolver = fixture.GetService<SpacebarClientWellKnownResolverService>(testOutputHelper) ?? + throw new InvalidOperationException( + $"Failed to get {nameof(SpacebarClientWellKnownResolverService)}"); + + private readonly SpacebarClientProviderService _clientProvider = fixture.GetService<SpacebarClientProviderService>(testOutputHelper) ?? + throw new InvalidOperationException($"Failed to get {nameof(SpacebarClientProviderService)}"); + + private readonly UserAbstraction _userAbstraction = fixture.GetService<UserAbstraction>(testOutputHelper) ?? + throw new InvalidOperationException($"Failed to get {nameof(SpacebarClientProviderService)}"); + + [Fact] + public async Task CreateChannel() { + var client = await _userAbstraction.GetFreshUser(); + var guild = await client.CreateGuild(new() { + Name = "Test guild" + }); + + Assert.Equal("Test guild", guild.Name); + + var channel = await client.GetGuild(guild.Id).CreateChannelAsync(new() { + Name = "test", + Type = 0 + }); + + Assert.Equal("test", channel.Name); + } + + [Fact] + public async Task GetChannel() { + var client = await _userAbstraction.GetFreshUser(); + var guild = await client.CreateGuild(new() { + Name = "Test guild" + }); + + Assert.Equal("Test guild", guild.Name); + + var channel = await client.GetGuild(guild.Id).CreateChannelAsync(new() { + Name = "test", + Type = 0 + }); + + Assert.Equal("test", channel.Name); + + var res = await client.ApiHttpClient.GetAsync("channels/" + channel.Id, TestContext.Current.CancellationToken); + await Assert.HttpSuccess(res); + + var channelResp = await res.Content.ReadFromJsonAsync<Channel>(cancellationToken: TestContext.Current.CancellationToken); + Assert.Equal(channel.Name, channelResp!.Name); + Assert.Equal(channel.Id, channelResp!.Id); + + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Tests/GuildTests.cs b/extra/admin-api/Tests/Spacebar.Tests/Tests/GuildTests.cs new file mode 100644
index 00000000..12e566f0 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Tests/GuildTests.cs
@@ -0,0 +1,47 @@ +using Spacebar.Sdk.Core; +using Spacebar.Tests.Abstractions; +using Spacebar.Tests.Extensions; +using Spacebar.Tests.Fixtures; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace Spacebar.Tests.Tests; + +public class GuildTests(ITestOutputHelper testOutputHelper, TestFixture fixture) : TestBed<TestFixture>(testOutputHelper, fixture) { + private readonly Config _config = fixture.GetService<Config>(testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(Config)}"); + + private readonly SpacebarClientWellKnownResolverService _wellKnownResolver = fixture.GetService<SpacebarClientWellKnownResolverService>(testOutputHelper) ?? + throw new InvalidOperationException( + $"Failed to get {nameof(SpacebarClientWellKnownResolverService)}"); + + private readonly SpacebarClientProviderService _clientProvider = fixture.GetService<SpacebarClientProviderService>(testOutputHelper) ?? + throw new InvalidOperationException($"Failed to get {nameof(SpacebarClientProviderService)}"); + + private readonly UserAbstraction _userAbstraction = fixture.GetService<UserAbstraction>(testOutputHelper) ?? + throw new InvalidOperationException($"Failed to get {nameof(SpacebarClientProviderService)}"); + + [Fact] + public async Task CreateGuild() { + var client = await _userAbstraction.GetFreshUser(); + var guild = await client.CreateGuild(new() { + Name = "Test guild" + }); + + Assert.Equal("Test guild", guild.Name); + } + + [Fact] + public async Task GetChannels() { + var client = await _userAbstraction.GetFreshUser(); + var guild = await client.CreateGuild(new() { + Name = "Test guild" + }); + + Assert.Equal("Test guild", guild.Name); + + var channels = await client.GetGuild(guild.Id).GetChannelsAsync(); + Assert.NotEmpty(channels); + foreach (var channel in channels) { + Assert.StringNotNullOrWhitespace(channel.Name); + } + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/Tests/Meta/UserAbstractionTests.cs b/extra/admin-api/Tests/Spacebar.Tests/Tests/Meta/UserAbstractionTests.cs new file mode 100644
index 00000000..81f53f7f --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/Tests/Meta/UserAbstractionTests.cs
@@ -0,0 +1,16 @@ +using Spacebar.Tests.Abstractions; +using Spacebar.Tests.Extensions; +using Spacebar.Tests.Fixtures; +using Xunit.Microsoft.DependencyInjection.Abstracts; + +namespace Spacebar.Tests.Tests.Meta; + +public class UserAbstractionTests(ITestOutputHelper testOutputHelper, TestFixture fixture) : TestBed<TestFixture>(testOutputHelper, fixture) { + private readonly UserAbstraction _config = fixture.GetService<UserAbstraction>(testOutputHelper) ?? throw new InvalidOperationException($"Failed to get {nameof(UserAbstraction)}"); + + [Fact] + public async Task CanGetUser() { + var res = await _config.GetFreshUser(); + Assert.StringNotNullOrWhitespace(res.ApiHttpClient.BaseAddress!.ToString()); + } +} \ No newline at end of file diff --git a/extra/admin-api/Tests/Spacebar.Tests/appsettings.json b/extra/admin-api/Tests/Spacebar.Tests/appsettings.json new file mode 100644
index 00000000..d1d45a93 --- /dev/null +++ b/extra/admin-api/Tests/Spacebar.Tests/appsettings.json
@@ -0,0 +1,5 @@ +{ + "Configuration": { + "TestInstance": "http://localhost:3001" + } +} \ No newline at end of file