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
|