veza/scripts/smoke_test.go

276 lines
6.9 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"path/filepath"
"strings"
"time"
)
// Configuration
var (
baseURL = getEnv("API_URL", "http://localhost:8080/api/v1")
testEmail = getEnv("TEST_EMAIL", "test@veza.local")
testPassword = getEnv("TEST_PASSWORD", "TestPassword123!")
)
func main() {
fmt.Println("🚀 Starting Veza Smoke Tests...")
fmt.Printf("Target: %s\n", baseURL)
// 1. Authentication
fmt.Println("\n🔐 Authenticating...")
token, err := authenticate()
if err != nil {
fatal("Authentication failed: %v", err)
}
fmt.Println("✅ Authentication successful")
// 2. Create Dummy Audio File
fmt.Println("\n🎵 Creating dummy audio file...")
filePath, err := createDummyAudioFile()
if err != nil {
fatal("Failed to create dummy file: %v", err)
}
defer os.Remove(filePath)
fmt.Printf("✅ Created dummy audio file: %s\n", filePath)
// 3. Upload Track
fmt.Println("\n⬆ Uploading track...")
trackID, err := uploadTrack(token, filePath)
if err != nil {
fatal("Track upload failed: %v", err)
}
fmt.Printf("✅ Track uploaded successfully (ID: %v)\n", trackID)
// 4. Verify Webhook / Status (Polling)
fmt.Println("\n🔄 Waiting for processing...")
err = waitForProcessing(token, trackID)
if err != nil {
fmt.Printf("⚠️ Processing warning: %v (might be expected if workers are not running)\n", err)
} else {
fmt.Println("✅ Track processed")
}
// 5. Playback Verification (Metadata check)
fmt.Println("\n▶ Verifying playback accessibility...")
err = verifyPlayback(token, trackID)
if err != nil {
fatal("Playback verification failed: %v", err)
}
fmt.Println("✅ Playback metadata accessible")
fmt.Println("\n🎉 All smoke tests passed!")
}
func getEnv(key, fallback string) string {
if value, ok := os.LookupEnv(key);
ok {
return value
}
return fallback
}
func fatal(format string, args ...interface{}) {
fmt.Printf("❌ "+format+"\n", args...)
os.Exit(1)
}
type LoginResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
func authenticate() (string, error) {
jsonData := map[string]interface{}{
"email": testEmail,
"password": testPassword,
"remember_me": false,
}
jsonBytes, _ := json.Marshal(jsonData)
req, err := http.NewRequest("POST", baseURL+"/auth/login", bytes.NewBuffer(jsonBytes))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", fmt.Errorf("login failed (status %d): %s", resp.StatusCode, string(body))
}
var result LoginResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", err
}
if result.AccessToken == "" {
return "", fmt.Errorf("no access token received")
}
return result.AccessToken, nil
}
func createDummyAudioFile() (string, error) {
// Create a 1-second silent wav file or just random bytes pretending to be mp3
// For smoke test validation, random bytes might be rejected if validation is strict (magic numbers).
// Let's create a file with MP3 magic number if possible, or just use text and hope validation is loose enough for this test,
// OR actually generate a valid minimal mp3 header.
// MP3 magic number: ID3 or FF FB.
// Minimal fake MP3
filename := "smoke_test_track.mp3"
f, err := os.Create(filename)
if err != nil {
return "", err
}
defer f.Close()
// ID3 header + junk
f.Write([]byte("ID3\x03\x00\x00\x00\x00\x00\x0a")) // Fake ID3 tag
f.Write(make([]byte, 1024*10)) // 10KB of junk
return filename, nil
}
type UploadResponse struct {
Message string `json:"message"`
Track struct {
ID string `json:"id"`
} `json:"track"`
}
func uploadTrack(token, filePath string) (string, error) {
file, err := os.Open(filePath)
if err != nil {
return "", err
}
defer file.Close()
body := &bytes.Buffer{}
writer := multipart.NewWriter(body)
part, err := writer.CreateFormFile("file", filepath.Base(filePath))
if err != nil {
return 0, err
}
io.Copy(part, file)
writer.Close()
req, err := http.NewRequest("POST", baseURL+"/tracks/upload", body)
if err != nil {
return "", err
}
req.Header.Set("Content-Type", writer.FormDataContentType())
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{Timeout: 30 * time.Second}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
respBody, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusCreated {
return "", fmt.Errorf("upload failed (status %d): %s", resp.StatusCode, string(respBody))
}
var result UploadResponse
if err := json.Unmarshal(respBody, &result); err != nil {
return "", fmt.Errorf("failed to parse upload response: %v. Body: %s", err, string(respBody))
}
if result.Track.ID == "" {
return "", fmt.Errorf("no track ID returned")
}
return result.Track.ID, nil
}
type TrackStatusResponse struct {
Status string `json:"status"`
}
func waitForProcessing(token string, trackID string) error {
client := &http.Client{Timeout: 5 * time.Second}
maxRetries := 5 // Short wait
for i := 0; i < maxRetries; i++ {
req, err := http.NewRequest("GET", fmt.Sprintf("%s/tracks/%s", baseURL, trackID), nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
resp, err := client.Do(req)
if err != nil {
fmt.Printf(" Attempt %d: Error %v\n", i+1, err)
time.Sleep(1 * time.Second)
continue
}
defer resp.Body.Close()
if resp.StatusCode == 200 {
// Parsing status
// Assuming GET /tracks/:id returns the track object with a 'status' field
var track map[string]interface{}
json.NewDecoder(resp.Body).Decode(&track)
status, _ := track["status"].(string)
fmt.Printf(" Attempt %d: Status = %s\n", i+1, status)
if status == "ready" || status == "processing" || status == "uploading" {
// processing/uploading is acceptable for "Upload successful" smoke test
// ready means transcoding finished
return nil
}
} else {
fmt.Printf(" Attempt %d: Status Code %d\n", i+1, resp.StatusCode)
}
time.Sleep(1 * time.Second)
}
return fmt.Errorf("track did not reach stable state")
}
func verifyPlayback(token string, trackID string) error {
// Check if we can get the track details.
// The actual stream URL might be in the track details or a separate endpoint.
// Based on API Spec: GET /tracks/:id
req, err := http.NewRequest("GET", fmt.Sprintf("%s/tracks/%s", baseURL, trackID), nil)
if err != nil {
return err
}
req.Header.Set("Authorization", "Bearer "+token)
client := &http.Client{Timeout: 5 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
return fmt.Errorf("failed to get track details (status %d)", resp.StatusCode)
}
return nil
}