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 }