Files
SpotiFLAC-Mobile/go_backend/log_progress_timeout_supplement_test.go
T

198 lines
5.9 KiB
Go

package gobackend
import (
"bytes"
"context"
"encoding/json"
"errors"
"strings"
"testing"
"time"
"github.com/dop251/goja"
)
func TestLogBufferExportedHelpersAndRedaction(t *testing.T) {
ClearLogs()
SetLoggingEnabled(false)
LogInfo("test", "ignored access_token=secret")
LogError("test", "Authorization: Bearer secret-token api_key=value")
if GetLogCount() != 1 {
t.Fatalf("disabled logging should keep errors only, got %d", GetLogCount())
}
SetLoggingEnabled(true)
defer SetLoggingEnabled(false)
LogDebug("debug", "client_secret=secret")
LogWarn("warn", "warning password=secret")
GoLog("[GoTag] success token=abc")
var entries []LogEntry
if err := json.Unmarshal([]byte(GetLogs()), &entries); err != nil {
t.Fatalf("GetLogs JSON: %v", err)
}
if len(entries) < 4 {
t.Fatalf("expected log entries, got %#v", entries)
}
for _, entry := range entries {
if strings.Contains(entry.Message, "secret-token") || strings.Contains(entry.Message, "api_key=value") || strings.Contains(entry.Message, "password=secret") {
t.Fatalf("log was not redacted: %#v", entry)
}
}
sinceJSON := GetLogsSince(1)
if !strings.Contains(sinceJSON, `"next_index"`) || !strings.Contains(sinceJSON, `"logs"`) {
t.Fatalf("GetLogsSince = %q", sinceJSON)
}
if emptyJSON := GetLogsSince(999); !strings.Contains(emptyJSON, `"logs":[]`) {
t.Fatalf("GetLogsSince empty = %q", emptyJSON)
}
if negativeJSON := GetLogsSince(-5); !strings.Contains(negativeJSON, `"logs"`) {
t.Fatalf("GetLogsSince negative = %q", negativeJSON)
}
ClearLogs()
if GetLogCount() != 0 || GetLogs() != "[]" {
t.Fatalf("logs were not cleared: count=%d logs=%s", GetLogCount(), GetLogs())
}
}
func TestLogBufferCursorSurvivesRollover(t *testing.T) {
lb := &LogBuffer{
entries: make([]LogEntry, 3),
maxSize: 3,
loggingEnabled: true,
}
for _, message := range []string{"one", "two", "three"} {
lb.Add("INFO", "Test", message)
}
initial, cursor := lb.getSince(0)
if cursor != 3 || len(initial) != 3 || initial[0].Message != "one" {
t.Fatalf("initial logs/cursor = %#v/%d", initial, cursor)
}
lb.Add("INFO", "Test", "four")
newLogs, cursor := lb.getSince(cursor)
if cursor != 4 || len(newLogs) != 1 || newLogs[0].Message != "four" {
t.Fatalf("rollover logs/cursor = %#v/%d", newLogs, cursor)
}
lb.Add("INFO", "Test", "five")
lb.Add("INFO", "Test", "six")
retained, cursor := lb.getSince(1)
if cursor != 6 || len(retained) != 3 || retained[0].Message != "four" || retained[2].Message != "six" {
t.Fatalf("retained logs/cursor = %#v/%d", retained, cursor)
}
lb.Clear()
lb.Add("INFO", "Test", "seven")
afterClear, cursor := lb.getSince(6)
if cursor != 7 || len(afterClear) != 1 || afterClear[0].Message != "seven" {
t.Fatalf("after clear logs/cursor = %#v/%d", afterClear, cursor)
}
}
func TestProgressItemHelpersAndWriter(t *testing.T) {
ClearAllItemProgress()
itemID := "progress-writer"
StartItemProgress(itemID)
SetItemBytesTotal(itemID, int64(progressUpdateThreshold*2))
SetItemBytesReceived(itemID, int64(progressUpdateThreshold))
progressJSON := GetItemProgress(itemID)
if !strings.Contains(progressJSON, `"bytes_received":131072`) || !strings.Contains(progressJSON, `"progress":0.5`) {
t.Fatalf("GetItemProgress = %q", progressJSON)
}
if missing := GetItemProgress("missing"); missing != "{}" {
t.Fatalf("missing progress = %q", missing)
}
var out bytes.Buffer
writer := NewItemProgressWriter(&out, itemID)
payload := bytes.Repeat([]byte("x"), progressUpdateThreshold+1)
n, err := writer.Write(payload)
if err != nil || n != len(payload) {
t.Fatalf("progress writer = %d/%v", n, err)
}
if out.Len() != len(payload) {
t.Fatalf("writer output length = %d", out.Len())
}
if progressJSON = GetItemProgress(itemID); !strings.Contains(progressJSON, `"bytes_received":131073`) {
t.Fatalf("progress after writer = %q", progressJSON)
}
cancelDownload(itemID)
defer clearDownloadCancel(itemID)
n, err = writer.Write([]byte("cancelled"))
if n != 0 || !errors.Is(err, ErrDownloadCancelled) {
t.Fatalf("cancelled writer = %d/%v", n, err)
}
ClearAllItemProgress()
}
func TestRunWithTimeoutBranches(t *testing.T) {
timeoutVM := goja.New()
_, err := RunWithTimeoutAndRecover(timeoutVM, "for (;;) {}", 10*time.Millisecond)
if err == nil {
t.Fatal("expected timeout error")
}
if !IsTimeoutError(&JSExecutionError{Message: "timeout", IsTimeout: true}) {
t.Fatal("JSExecutionError should be recognized as timeout")
}
if IsTimeoutError(errors.New("plain")) {
t.Fatal("plain error should not be timeout")
}
if (&JSExecutionError{Message: "boom"}).Error() != "boom" {
t.Fatal("JSExecutionError Error mismatch")
}
}
func TestRunWithTimeoutQuarantinesUnresponsiveRuntime(t *testing.T) {
previousGrace := jsInterruptGracePeriod
jsInterruptGracePeriod = 10 * time.Millisecond
defer func() { jsInterruptGracePeriod = previousGrace }()
vm := goja.New()
release := make(chan struct{})
if err := vm.Set("block", func() { <-release }); err != nil {
t.Fatal(err)
}
_, err := RunWithTimeoutAndRecover(vm, "block()", 10*time.Millisecond)
if !IsRuntimeUnsafeError(err) {
close(release)
t.Fatalf("expected unsafe runtime error, got %v", err)
}
close(release)
}
func TestRunWithTimeoutQuarantinesUnresponsiveCancelledRuntime(t *testing.T) {
previousGrace := jsInterruptGracePeriod
jsInterruptGracePeriod = 10 * time.Millisecond
defer func() { jsInterruptGracePeriod = previousGrace }()
vm := goja.New()
entered := make(chan struct{})
release := make(chan struct{})
if err := vm.Set("block", func() {
close(entered)
<-release
}); err != nil {
t.Fatal(err)
}
ctx, cancel := context.WithCancel(context.Background())
result := make(chan error, 1)
go func() {
_, err := RunWithTimeoutContextAndRecover(ctx, vm, "block()", time.Second)
result <- err
}()
<-entered
cancel()
err := <-result
if !IsRuntimeUnsafeError(err) || !errors.Is(err, ErrExtensionRequestCancelled) {
close(release)
t.Fatalf("expected unsafe cancellation error, got %v", err)
}
close(release)
}