This commit prepares the musiclink bot for production deployment on NixOS. Changes: - Refactored service layer to support Dependency Injection for API URLs. - Added 'internal/bot/bot_integration_test.go' for mock-based integration testing. - Added 'flake.nix' and 'flake.lock' for reproducible Nix builds. - Added 'README.md', 'LICENSE', and '.gitignore'. - Verified build and tests pass locally.
113 lines
2.8 KiB
Go
113 lines
2.8 KiB
Go
package bot_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gorilla/websocket"
|
|
"musiclink/internal/bot"
|
|
"musiclink/pkg/config"
|
|
)
|
|
|
|
// upgrader is used by the mock Matterbridge server
|
|
var upgrader = websocket.Upgrader{}
|
|
|
|
func TestBotIntegration(t *testing.T) {
|
|
// 1. Mock the Music API (idonthavespotify)
|
|
mockMusicAPI := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// Verify request
|
|
if r.Method != "POST" {
|
|
t.Errorf("API expected POST, got %s", r.Method)
|
|
}
|
|
|
|
// Return canned response
|
|
resp := map[string]interface{}{
|
|
"type": "song",
|
|
"title": "Test Song",
|
|
"links": []map[string]interface{}{
|
|
{"type": "spotify", "url": "https://spotify.com/track/123"},
|
|
{"type": "appleMusic", "url": "https://apple.com/track/123"},
|
|
{"type": "youtube", "url": "https://youtube.com/watch?v=123"},
|
|
},
|
|
}
|
|
json.NewEncoder(w).Encode(resp)
|
|
}))
|
|
defer mockMusicAPI.Close()
|
|
|
|
// 2. Mock Matterbridge WebSocket Server
|
|
done := make(chan struct{})
|
|
mockBridge := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
c, err := upgrader.Upgrade(w, r, nil)
|
|
if err != nil {
|
|
t.Errorf("upgrade: %v", err)
|
|
return
|
|
}
|
|
defer c.Close()
|
|
|
|
// 2a. Send a message with a link
|
|
msg := bot.Message{
|
|
Text: "Check out this song: https://open.spotify.com/track/123",
|
|
Username: "User",
|
|
Gateway: "discord",
|
|
Event: "", // Regular message
|
|
}
|
|
if err := c.WriteJSON(msg); err != nil {
|
|
t.Errorf("write: %v", err)
|
|
return
|
|
}
|
|
|
|
// 2b. Wait for the bot to respond
|
|
var response bot.Message
|
|
if err := c.ReadJSON(&response); err != nil {
|
|
t.Errorf("read: %v", err)
|
|
return
|
|
}
|
|
|
|
// 2c. Verify the response
|
|
if !strings.Contains(response.Text, "Test Song") {
|
|
t.Errorf("Expected response to contain title 'Test Song', got: %s", response.Text)
|
|
}
|
|
if !strings.Contains(response.Text, "Apple Music: https://apple.com/track/123") {
|
|
t.Errorf("Expected response to contain Apple Music link")
|
|
}
|
|
|
|
close(done)
|
|
}))
|
|
defer mockBridge.Close()
|
|
|
|
// 3. Configure and Start the Bot
|
|
cfg := config.MatterbridgeConfig{
|
|
URL: "ws://" + strings.TrimPrefix(mockBridge.URL, "http://"),
|
|
Token: "test-token",
|
|
Gateway: "discord",
|
|
Username: "MusicLink",
|
|
}
|
|
|
|
// Use the mock Music API URL
|
|
handler := bot.NewHandler(mockMusicAPI.URL)
|
|
b := bot.New(cfg, handler.Handle)
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
// Run bot in a goroutine
|
|
go func() {
|
|
if err := b.Run(ctx); err != nil && err != context.Canceled {
|
|
t.Logf("Bot stopped: %v", err)
|
|
}
|
|
}()
|
|
|
|
// 4. Wait for test completion
|
|
select {
|
|
case <-done:
|
|
// Success!
|
|
case <-ctx.Done():
|
|
t.Fatal("Test timed out waiting for bot response")
|
|
}
|
|
}
|