veza/scripts/smoke_test.go

277 lines
6.9 KiB
Go
Raw Normal View History

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
}