Files
go-telegram-bot/rate_limiter_test.go
2024-10-22 14:54:39 +02:00

193 lines
5.4 KiB
Go

package main
import (
"strings"
"testing"
"time"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
// TestCheckRateLimits tests the checkRateLimits method of the Bot.
// It verifies that users are allowed or denied based on their message rates.
func TestCheckRateLimits(t *testing.T) {
// Create a mock clock starting at a fixed time
mockClock := &MockClock{
currentTime: time.Date(2023, 10, 1, 0, 0, 0, 0, time.UTC),
}
// Create a mock configuration with reduced timeframes for testing
config := BotConfig{
ID: "bot1",
MemorySize: 10,
MessagePerHour: 5, // Allow 5 messages per hour
MessagePerDay: 10, // Allow 10 messages per day
TempBanDuration: "1m", // Temporary ban duration of 1 minute for testing
SystemPrompts: make(map[string]string),
TelegramToken: "YOUR_TELEGRAM_BOT_TOKEN",
OwnerTelegramID: 123456789,
}
// Initialize the Bot with mock data and MockClock
bot := &Bot{
config: config,
userLimiters: make(map[int64]*userLimiter),
clock: mockClock,
}
userID := int64(12345)
// Helper function to simulate message sending
sendMessage := func() bool {
return bot.checkRateLimits(userID)
}
// Send 5 messages within the hourly limit
for i := 0; i < config.MessagePerHour; i++ {
if !sendMessage() {
t.Errorf("Expected message %d to be allowed", i+1)
}
}
// 6th message should exceed the hourly limit and trigger a ban
if sendMessage() {
t.Errorf("Expected message to be denied due to hourly limit exceeded")
}
// Attempt to send another message immediately, should still be banned
if sendMessage() {
t.Errorf("Expected message to be denied while user is banned")
}
// Fast-forward time by TempBanDuration to lift the ban
mockClock.Advance(time.Minute) // Banned for 1 minute
// Advance time to allow hourly limiter to replenish
mockClock.Advance(time.Hour) // Advance by 1 hour
// Send another message, should be allowed now
if !sendMessage() {
t.Errorf("Expected message to be allowed after ban duration")
}
// Send additional messages to reach the daily limit
for i := 0; i < config.MessagePerDay-config.MessagePerHour-1; i++ {
if !sendMessage() {
t.Errorf("Expected message %d to be allowed towards daily limit", i+1)
}
}
// Attempt to exceed the daily limit
if sendMessage() {
t.Errorf("Expected message to be denied due to daily limit exceeded")
}
}
func TestOwnerAssignment(t *testing.T) {
// Initialize in-memory database for testing
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
if err != nil {
t.Fatalf("Failed to open in-memory database: %v", err)
}
// Migrate the schema
err = db.AutoMigrate(&BotModel{}, &ConfigModel{}, &Message{}, &User{}, &Role{})
if err != nil {
t.Fatalf("Failed to migrate database schema: %v", err)
}
// Create default roles
err = createDefaultRoles(db)
if err != nil {
t.Fatalf("Failed to create default roles: %v", err)
}
// Create a bot configuration
config := BotConfig{
ID: "test_bot",
TelegramToken: "TEST_TELEGRAM_TOKEN",
MemorySize: 10,
MessagePerHour: 5,
MessagePerDay: 10,
TempBanDuration: "1m",
SystemPrompts: make(map[string]string),
Active: true,
OwnerTelegramID: 111111111,
}
// Initialize MockClock
mockClock := &MockClock{
currentTime: time.Now(),
}
// Create the bot
bot, err := NewBot(db, config, mockClock)
if err != nil {
t.Fatalf("Failed to create bot: %v", err)
}
// Verify that the owner exists
var owner User
err = db.Where("telegram_id = ? AND bot_id = ? AND is_owner = ?", config.OwnerTelegramID, bot.botID, true).First(&owner).Error
if err != nil {
t.Fatalf("Owner was not created: %v", err)
}
// Attempt to create another owner for the same bot
_, err = bot.getOrCreateUser(222222222, "AnotherOwner", true)
if err == nil {
t.Fatalf("Expected error when creating a second owner, but got none")
}
// Verify that the error message is appropriate
expectedErrorMsg := "an owner already exists for this bot"
if err.Error() != expectedErrorMsg && !strings.Contains(err.Error(), "unique index") {
t.Fatalf("Unexpected error message: %v", err)
}
// Assign admin role to a new user
adminUser, err := bot.getOrCreateUser(333333333, "AdminUser", false)
if err != nil {
t.Fatalf("Failed to create admin user: %v", err)
}
if adminUser.Role.Name != "admin" {
t.Fatalf("Expected role 'admin', got '%s'", adminUser.Role.Name)
}
// Assign owner role to a user from a different bot
otherBotConfig := BotConfig{
ID: "other_bot",
TelegramToken: "OTHER_TELEGRAM_TOKEN",
MemorySize: 10,
MessagePerHour: 5,
MessagePerDay: 10,
TempBanDuration: "1m",
SystemPrompts: make(map[string]string),
Active: true,
OwnerTelegramID: 444444444,
}
otherBot, err := NewBot(db, otherBotConfig, mockClock)
if err != nil {
t.Fatalf("Failed to create other bot: %v", err)
}
_, err = otherBot.getOrCreateUser(config.OwnerTelegramID, "OwnerOfOtherBot", true)
if err != nil {
t.Fatalf("Failed to assign existing owner to another bot: %v", err)
}
// Verify multiple bots can have the same owner telegram ID
var ownerOfOtherBot User
err = db.Where("telegram_id = ? AND bot_id = ? AND is_owner = ?", config.OwnerTelegramID, otherBot.botID, true).First(&ownerOfOtherBot).Error
if err != nil {
t.Fatalf("Owner of other bot was not created: %v", err)
}
}
// To ensure thread safety and avoid race conditions during testing,
// you can run the tests with the `-race` flag:
// go test -race -v