mirror of
https://github.com/spacebarchat/server.git
synced 2026-06-07 15:21:47 +00:00
Unit tests for webhooks
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Spacebar.Models.Api;
|
||||
|
||||
public class CreateWebhookRequest {
|
||||
[JsonPropertyName("name")]
|
||||
public required string Name { get; set; }
|
||||
|
||||
[JsonPropertyName("avatar")]
|
||||
public string? AvatarData { get; set; }
|
||||
}
|
||||
@@ -14,6 +14,8 @@ public class SpacebarApiException : Exception {
|
||||
public JsonObject? Errors { get; set; }
|
||||
|
||||
public JsonObject?[]? AjvErrors { get; set; }
|
||||
|
||||
public JsonObject? OriginalErrorData { get; init; }
|
||||
|
||||
public class FieldErrorList {
|
||||
// public
|
||||
@@ -32,6 +34,7 @@ public class SpacebarApiException : Exception {
|
||||
}
|
||||
|
||||
var ex = new SpacebarApiException(msg) {
|
||||
OriginalErrorData = resp,
|
||||
Code = resp["code"]!.GetValue<int>(),
|
||||
ErrorMessage = resp["message"]!.GetValue<string>(),
|
||||
Request = resp["request"]?.GetValue<string>(),
|
||||
@@ -42,7 +45,7 @@ public class SpacebarApiException : Exception {
|
||||
return ex;
|
||||
}
|
||||
|
||||
public JsonObject AsJsonObject() => new() {
|
||||
public JsonObject AsJsonObject() => OriginalErrorData?.DeepClone().AsObject() ?? new() {
|
||||
{ "message", Message },
|
||||
{ "code", Code },
|
||||
{ "request", Request },
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Spacebar.Models.Generic;
|
||||
|
||||
public class Webhook {
|
||||
[JsonPropertyName("id"), JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public required long Id { get; set; }
|
||||
|
||||
[JsonPropertyName("type")]
|
||||
public WebhookType WebhookType { get; set; }
|
||||
|
||||
[JsonPropertyName("guild_id"), JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public long? GuildId { get; set; }
|
||||
|
||||
[JsonPropertyName("channel_id"), JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public long? ChannelId { get; set; }
|
||||
|
||||
[JsonPropertyName("user")]
|
||||
public PartialUser? User { get; set; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string? Name { get; set; }
|
||||
|
||||
[JsonPropertyName("avatar")]
|
||||
public string? AvatarData { get; set; }
|
||||
|
||||
[JsonPropertyName("token")]
|
||||
public string? Token { get; set; }
|
||||
|
||||
[JsonPropertyName("application_id"), JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
|
||||
public long? ApplicationId { get; set; }
|
||||
|
||||
[JsonPropertyName("source_guild")]
|
||||
public JsonObject? SourceGuild { get; set; } // TODO type
|
||||
|
||||
[JsonPropertyName("source_channel")]
|
||||
public JsonObject? SourceChannel { get; set; } // TODO type
|
||||
|
||||
[JsonPropertyName("url")]
|
||||
public string? Url { get; set; }
|
||||
}
|
||||
|
||||
public enum WebhookType : byte {
|
||||
Incomming = 1,
|
||||
ChannelFollower = 2,
|
||||
Application = 3
|
||||
}
|
||||
@@ -15,10 +15,12 @@ public class UserAbstraction(Config _config, SpacebarClientProviderService _clie
|
||||
var client = await _clientProvider.GetAuthenticatedClientAsync(_config.TestInstance, tokenResponse.Token);
|
||||
|
||||
if (!withAutojoinGuilds) {
|
||||
|
||||
await Task.Delay(1000);
|
||||
var leaves = (await client.GetJoinedGuilds()).Select(x => client.GetGuild(x.Id).LeaveAsync()).ToList();
|
||||
await Task.WhenAll(leaves);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Net.Http.Json;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using ArcaneLibs.Extensions;
|
||||
using Spacebar.Models.Api;
|
||||
@@ -33,6 +34,7 @@ public class AuthenticationTests(ITestOutputHelper testOutputHelper, TestFixture
|
||||
[Fact]
|
||||
public async Task ConcurrentRegister50Users() {
|
||||
var tasks = Enumerable.Range(0, 50).Select(async _ => {
|
||||
var sw = Stopwatch.StartNew();
|
||||
var rr = new RegisterRequest() {
|
||||
Email = $"{Guid.NewGuid().ToString()}@{Guid.NewGuid().ToString()}.tld",
|
||||
Username = Guid.NewGuid().ToString(),
|
||||
@@ -40,7 +42,10 @@ public class AuthenticationTests(ITestOutputHelper testOutputHelper, TestFixture
|
||||
DateOfBirth = new(),
|
||||
Consent = true
|
||||
};
|
||||
return (rr, await Assert.SuccessfullyHttpPostAsJsonAsync($"{_config.TestInstance}/api/v9/auth/register", rr));
|
||||
|
||||
var result = await Assert.SuccessfullyHttpPostAsJsonAsync($"{_config.TestInstance}/api/v9/auth/register", rr);
|
||||
testOutputHelper.WriteLine($"Registered {rr.Email} in {sw.Elapsed}...");
|
||||
return (rr, result);
|
||||
}).ToList();
|
||||
await Task.WhenAll(tasks);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ public class ChannelTests(ITestOutputHelper testOutputHelper, TestFixture fixtur
|
||||
|
||||
[Fact]
|
||||
public async Task CreateChannel() {
|
||||
var client = await _userAbstraction.GetFreshUser();
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
var guild = await client.CreateGuild(new() {
|
||||
Name = "Test guild"
|
||||
});
|
||||
@@ -40,13 +40,14 @@ public class ChannelTests(ITestOutputHelper testOutputHelper, TestFixture fixtur
|
||||
|
||||
[Fact]
|
||||
public async Task GetChannel() {
|
||||
var client = await _userAbstraction.GetFreshUser();
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
var guild = await client.CreateGuild(new() {
|
||||
Name = "Test guild"
|
||||
});
|
||||
|
||||
Assert.Equal("Test guild", guild.Name);
|
||||
|
||||
|
||||
// await Task.Delay(1000, TestContext.Current.CancellationToken); // TODO: unflake
|
||||
var channel = await client.GetGuild(guild.Id).CreateChannelAsync(new() {
|
||||
Name = "test",
|
||||
Type = 0
|
||||
@@ -54,6 +55,7 @@ public class ChannelTests(ITestOutputHelper testOutputHelper, TestFixture fixtur
|
||||
|
||||
Assert.Equal("test", channel.Name);
|
||||
|
||||
// await Task.Delay(1000, TestContext.Current.CancellationToken); // TODO: unflake
|
||||
var res = await client.ApiHttpClient.GetAsync("channels/" + channel.Id, TestContext.Current.CancellationToken);
|
||||
await Assert.HttpSuccess(res);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ public class GuildTests(ITestOutputHelper testOutputHelper, TestFixture fixture)
|
||||
|
||||
[Fact]
|
||||
public async Task CreateGuild() {
|
||||
var client = await _userAbstraction.GetFreshUser();
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
var guild = await client.CreateGuild(new() {
|
||||
Name = "Test guild"
|
||||
});
|
||||
@@ -31,7 +31,7 @@ public class GuildTests(ITestOutputHelper testOutputHelper, TestFixture fixture)
|
||||
|
||||
[Fact]
|
||||
public async Task GetChannels() {
|
||||
var client = await _userAbstraction.GetFreshUser();
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
var guild = await client.CreateGuild(new() {
|
||||
Name = "Test guild"
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ public class UserAbstractionTests(ITestOutputHelper testOutputHelper, TestFixtur
|
||||
|
||||
[Fact]
|
||||
public async Task CanGetUser() {
|
||||
var res = await _config.GetFreshUser();
|
||||
var res = await _config.GetFreshUser(withAutojoinGuilds: true);
|
||||
Assert.StringNotNullOrWhitespace(res.ApiHttpClient.BaseAddress!.ToString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
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 WebhookTests(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 CreateWebhook() {
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
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 cChannel = client.GetChannel(channel.Id);
|
||||
var wh = await cChannel.CreateWebhookAsync(new() {
|
||||
Name = "meow"
|
||||
});
|
||||
|
||||
Assert.Equal("meow", wh.Name);
|
||||
Assert.StringNotNullOrWhitespace(wh.Url);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateMultipleWebhooks() {
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
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 cChannel = client.GetChannel(channel.Id);
|
||||
|
||||
var count = Random.Shared.Next(10);
|
||||
testOutputHelper.WriteLine($"Creating {count} webhooks...");
|
||||
await Task.WhenAll(Enumerable.Range(0, count).Select(i => cChannel.CreateWebhookAsync(new() {
|
||||
Name = "meow" + i
|
||||
})).ToList());
|
||||
|
||||
var wh = await cChannel.GetWebhooksAsync();
|
||||
Assert.All(wh, h => Assert.StartsWith("meow", h.Name));
|
||||
Assert.All(wh, h => Assert.StringNotNullOrWhitespace(h.Url));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendWebhookMessageWithWait() {
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
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 cChannel = client.GetChannel(channel.Id);
|
||||
var wh = await cChannel.CreateWebhookAsync(new() {
|
||||
Name = "meow"
|
||||
});
|
||||
|
||||
Assert.Equal("meow", wh.Name);
|
||||
Assert.StringNotNullOrWhitespace(wh.Url);
|
||||
|
||||
await Assert.SuccessfullyHttpPostAsJsonAsync(wh.Url + "?wait=true", new JsonObject() {
|
||||
{ "content", "meow" }
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SendWebhookMessage() {
|
||||
var client = await _userAbstraction.GetFreshUser(withAutojoinGuilds: true);
|
||||
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 cChannel = client.GetChannel(channel.Id);
|
||||
var wh = await cChannel.CreateWebhookAsync(new() {
|
||||
Name = "meow"
|
||||
});
|
||||
|
||||
Assert.Equal("meow", wh.Name);
|
||||
Assert.StringNotNullOrWhitespace(wh.Url);
|
||||
|
||||
await Assert.SuccessfullyHttpPostAsJsonAsync(wh.Url, new JsonObject() {
|
||||
{ "content", "meow" }
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,13 @@ public class AuthenticatedSpacebarClient {
|
||||
if (!resp.IsSuccessStatusCode) throw SpacebarApiException.FromJson((await resp.Content.ReadFromJsonAsync<JsonObject>())!);
|
||||
return (await resp.Content.ReadFromJsonAsync<Guild>())!;
|
||||
}
|
||||
|
||||
public async Task<List<Guild>> GetJoinedGuilds() {
|
||||
var resp = await ApiHttpClient.GetAsync("users/@me/guilds");
|
||||
// TODO: abstract out
|
||||
if (!resp.IsSuccessStatusCode) throw SpacebarApiException.FromJson((await resp.Content.ReadFromJsonAsync<JsonObject>())!);
|
||||
return (await resp.Content.ReadFromJsonAsync<List<Guild>>())!;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpacebarClientChannel(AuthenticatedSpacebarClient client, long channelId) {
|
||||
@@ -91,6 +98,22 @@ public class SpacebarClientChannel(AuthenticatedSpacebarClient client, long chan
|
||||
Console.WriteLine(data.ToJson(indent: false, ignoreNull: true));
|
||||
return data.Select(x => x.Deserialize<Message>()).ToList();
|
||||
}
|
||||
|
||||
public async Task<Webhook> CreateWebhookAsync(CreateWebhookRequest req) {
|
||||
var resp = await client.ApiHttpClient.PostAsJsonAsync($"channels/{channelId}/webhooks", req, new JsonSerializerOptions() {
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
});
|
||||
// TODO: abstract out
|
||||
if (!resp.IsSuccessStatusCode) throw SpacebarApiException.FromJson((await resp.Content.ReadFromJsonAsync<JsonObject>())!);
|
||||
return (await resp.Content.ReadFromJsonAsync<Webhook>())!;
|
||||
}
|
||||
|
||||
public async Task<List<Webhook>> GetWebhooksAsync() {
|
||||
var resp = await client.ApiHttpClient.GetAsync($"channels/{channelId}/webhooks");
|
||||
// TODO: abstract out
|
||||
if (!resp.IsSuccessStatusCode) throw SpacebarApiException.FromJson((await resp.Content.ReadFromJsonAsync<JsonObject>())!);
|
||||
return (await resp.Content.ReadFromJsonAsync<List<Webhook>>())!;
|
||||
}
|
||||
}
|
||||
|
||||
public class SpacebarClientGuild(AuthenticatedSpacebarClient client, long guildId) {
|
||||
@@ -115,6 +138,19 @@ public class SpacebarClientGuild(AuthenticatedSpacebarClient client, long guildI
|
||||
if (!resp.IsSuccessStatusCode) throw SpacebarApiException.FromJson((await resp.Content.ReadFromJsonAsync<JsonObject>())!);
|
||||
return (await resp.Content.ReadFromJsonAsync<Channel>())!;
|
||||
}
|
||||
|
||||
public async Task LeaveAsync(bool lurking = false) {
|
||||
var req = new HttpRequestMessage(HttpMethod.Delete, $"users/@me/guilds/{guildId}") {
|
||||
Content = new StringContent(new JsonObject() {
|
||||
{ "lurking", lurking }
|
||||
}.ToJsonString(new JsonSerializerOptions() {
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
}))
|
||||
};
|
||||
var resp = await client.ApiHttpClient.SendAsync(req);
|
||||
// TODO: abstract out
|
||||
if (!resp.IsSuccessStatusCode) throw SpacebarApiException.FromJson((await resp.Content.ReadFromJsonAsync<JsonObject>())!);
|
||||
}
|
||||
}
|
||||
|
||||
public class AuthenticatedSpacebarGatewayClient(ILogger<AuthenticatedSpacebarGatewayClient> logger, SpacebarClientWellKnown wellKnown, string token) {
|
||||
|
||||
Reference in New Issue
Block a user