mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-09-17 07:15:33 +02:00
130 lines
5.2 KiB
Go
130 lines
5.2 KiB
Go
package gobackend
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/dop251/goja"
|
|
)
|
|
|
|
func TestAvailabilityPreservesCanonicalVerificationOnFailure(t *testing.T) {
|
|
for _, script := range []string{
|
|
`throw new Error("VERIFY_REQUIRED");`,
|
|
`return null;`,
|
|
`return undefined;`,
|
|
`return {available:false};`,
|
|
} {
|
|
t.Run(script, func(t *testing.T) {
|
|
ext := newTestLoadedExtension(t, ExtensionTypeDownloadProvider)
|
|
t.Cleanup(func() { teardownExtension(ext) })
|
|
if err := ext.ensureRuntimeReady(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ext.VM.Set("requireChallenge", func(goja.FunctionCall) goja.Value {
|
|
ext.runtime.noteVerificationRequired("https://example.test/challenge")
|
|
return goja.Undefined()
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := ext.VM.RunString(`extension.checkAvailability = function(){ requireChallenge(); ` + script + ` };`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, err := newExtensionProviderWrapper(ext).CheckAvailabilityForItemID("", "Song", "Artist", "", "", "", "", 180000, "")
|
|
if err == nil || classifyDownloadErrorType(err.Error()) != "verification_required" || !strings.Contains(err.Error(), ext.ID) {
|
|
t.Fatalf("canonical verification not preserved: %v", err)
|
|
}
|
|
if ext.runtime.consumeVerificationRequired() != "" {
|
|
t.Fatal("verification evidence leaked into the next call")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestAvailabilityDoesNotPromoteUntrustedOrStaleVerification(t *testing.T) {
|
|
ext := newTestLoadedExtension(t, ExtensionTypeDownloadProvider)
|
|
t.Cleanup(func() { teardownExtension(ext) })
|
|
if err := ext.ensureRuntimeReady(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := ext.VM.RunString(`extension.checkAvailability = function(){ throw new Error("VERIFY_REQUIRED"); };`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ext.runtime.noteVerificationRequired("https://example.test/stale-challenge")
|
|
_, err := newExtensionProviderWrapper(ext).CheckAvailabilityForItemID("", "Song", "Artist", "", "", "", "", 180000, "")
|
|
if err == nil || classifyDownloadErrorType(err.Error()) == "verification_required" {
|
|
t.Fatalf("untrusted exception inherited a previous challenge: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestExtensionVerificationErrorsRequireOwnedPendingChallenge(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
name string
|
|
message string
|
|
pendingOwner string
|
|
challengeAge time.Duration
|
|
authURL string
|
|
wantChallenge bool
|
|
}{
|
|
{"fresh", "VERIFY_REQUIRED", "coverage-ext", 0, "https://example.test/verify", true},
|
|
{"missing", "VERIFY_REQUIRED", "", 0, "", false},
|
|
{"other-provider", "VERIFY_REQUIRED", "other-provider", 0, "https://example.test/verify", false},
|
|
{"expired", "VERIFY_REQUIRED", "coverage-ext", pendingAuthRequestTTL + time.Second, "https://example.test/verify", false},
|
|
{"future", "VERIFY_REQUIRED", "coverage-ext", -time.Minute, "https://example.test/verify", false},
|
|
{"missing-url", "VERIFY_REQUIRED", "coverage-ext", 0, "", false},
|
|
{"network-error", "network timeout", "coverage-ext", 0, "https://example.test/verify", false},
|
|
{"provider-auth", "PROVIDER_AUTH_FAILED: VERIFY_REQUIRED", "coverage-ext", 0, "https://example.test/verify", false},
|
|
{"http-status", "HTTP 401: VERIFY_REQUIRED", "coverage-ext", 0, "https://example.test/verify", false},
|
|
{"cancelled", "cancelled: VERIFY_REQUIRED", "coverage-ext", 0, "https://example.test/verify", false},
|
|
{"throwing-getter", "ordinary failure", "coverage-ext", 0, "https://example.test/verify", false},
|
|
} {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
ext := newTestLoadedExtension(t, ExtensionTypeMetadataProvider, ExtensionTypeDownloadProvider)
|
|
t.Cleanup(func() {
|
|
ClearPendingAuthRequest(ext.ID)
|
|
ClearPendingAuthRequest(tc.pendingOwner)
|
|
teardownExtension(ext)
|
|
})
|
|
if err := ext.ensureRuntimeReady(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := ext.VM.Set("fixtureMessage", tc.message); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := ext.VM.RunString(`extension.searchTracks = extension.checkAvailability = function() { throw new Error(fixtureMessage); };`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if tc.name == "throwing-getter" {
|
|
if _, err := ext.VM.RunString(`extension.searchTracks = extension.checkAvailability = function() {
|
|
throw {toString: function() { return "ordinary failure"; }, get message() { throw new Error("broken getter"); }};
|
|
};`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
if tc.pendingOwner != "" {
|
|
if err := registerPendingAuthRequest(&PendingAuthRequest{
|
|
ExtensionID: tc.pendingOwner,
|
|
AuthURL: tc.authURL,
|
|
CreatedAt: time.Now().Add(-tc.challengeAge),
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
provider := newExtensionProviderWrapper(ext)
|
|
_, searchErr := provider.SearchTracks("Song Artist", 1)
|
|
_, availabilityErr := provider.CheckAvailabilityForItemID("", "Song", "Artist", "", "", "", "", 180000, "")
|
|
for _, err := range []error{searchErr, availabilityErr} {
|
|
if err == nil {
|
|
t.Fatal("extension error was lost")
|
|
}
|
|
if got := classifyDownloadErrorType(err.Error()) == "verification_required"; got != tc.wantChallenge {
|
|
t.Fatalf("verification=%v, want %v: %v", got, tc.wantChallenge, err)
|
|
}
|
|
}
|
|
if tc.pendingOwner == "" && GetPendingAuthRequest(ext.ID) != nil {
|
|
t.Fatal("error classification created a challenge")
|
|
}
|
|
})
|
|
}
|
|
}
|