mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-28 03:06:38 +08:00
249 lines
6.9 KiB
Go
249 lines
6.9 KiB
Go
package service
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
|
"github.com/ShukeBta/MediaStationGo/internal/repository"
|
|
)
|
|
|
|
func TestDownloadClientCreateNormalizesHostAndClearsDefault(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.DownloadClient{}, &model.Setting{})
|
|
repos := repository.New(db)
|
|
svc := NewDownloadClientService(zap.NewNop(), repos)
|
|
|
|
first, err := svc.Create(t.Context(), DownloadClientInput{
|
|
Name: "qB old",
|
|
Type: "qbittorrent",
|
|
Host: "http://127.0.0.1:8080/",
|
|
IsDefault: true,
|
|
Enabled: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
second, err := svc.Create(t.Context(), DownloadClientInput{
|
|
Name: "qB NAS",
|
|
Type: "qbittorrent",
|
|
Host: "172.17.0.1:8085",
|
|
IsDefault: true,
|
|
Enabled: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if second.Host != "http://172.17.0.1:8085" {
|
|
t.Fatalf("host = %q, want normalized http URL", second.Host)
|
|
}
|
|
refreshedFirst, err := repos.DownloadClient.FindByID(t.Context(), first.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if refreshedFirst == nil || refreshedFirst.IsDefault {
|
|
t.Fatalf("old default should be cleared, got %#v", refreshedFirst)
|
|
}
|
|
refreshedSecond, err := repos.DownloadClient.FindByID(t.Context(), second.ID)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if refreshedSecond == nil || !refreshedSecond.IsDefault {
|
|
t.Fatalf("new default should be active, got %#v", refreshedSecond)
|
|
}
|
|
}
|
|
|
|
func TestDownloadClientCreateMakesFirstEnabledClientDefault(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.DownloadClient{}, &model.Setting{})
|
|
repos := repository.New(db)
|
|
svc := NewDownloadClientService(zap.NewNop(), repos)
|
|
|
|
client, err := svc.Create(t.Context(), DownloadClientInput{
|
|
Name: "qB",
|
|
Type: "qbittorrent",
|
|
Host: "127.0.0.1:8080",
|
|
Enabled: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !client.IsDefault {
|
|
t.Fatalf("first enabled client should become default: %#v", client)
|
|
}
|
|
}
|
|
|
|
func TestDownloadClientRejectsUnsupportedHostScheme(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.DownloadClient{}, &model.Setting{})
|
|
svc := NewDownloadClientService(zap.NewNop(), repository.New(db))
|
|
|
|
if _, err := svc.Create(t.Context(), DownloadClientInput{
|
|
Name: "bad",
|
|
Type: "qbittorrent",
|
|
Host: "ftp://127.0.0.1:8080",
|
|
Enabled: true,
|
|
}); err == nil {
|
|
t.Fatal("expected unsupported scheme error")
|
|
}
|
|
}
|
|
|
|
func TestDownloadClientRejectsUnsafeEndpointParts(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.DownloadClient{}, &model.Setting{})
|
|
svc := NewDownloadClientService(zap.NewNop(), repository.New(db))
|
|
|
|
for _, host := range []string{
|
|
"http://user:pass@127.0.0.1:6800",
|
|
"http://127.0.0.1:6800/jsonrpc?target=http://169.254.169.254",
|
|
"http://127.0.0.1:6800/jsonrpc#fragment",
|
|
"http://127.0.0.1:70000",
|
|
"file:///etc/passwd",
|
|
} {
|
|
if _, err := svc.Create(t.Context(), DownloadClientInput{
|
|
Name: "bad",
|
|
Type: "aria2",
|
|
Host: host,
|
|
Enabled: true,
|
|
}); err == nil {
|
|
t.Fatalf("Create allowed unsafe host %q", host)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDownloadClientRPCURLAppendsExpectedPath(t *testing.T) {
|
|
cases := []struct {
|
|
clientType string
|
|
host string
|
|
want string
|
|
}{
|
|
{"aria2", "127.0.0.1:6800", "http://127.0.0.1:6800/jsonrpc"},
|
|
{"aria2", "http://nas.local:6800/rpc", "http://nas.local:6800/rpc/jsonrpc"},
|
|
{"transmission", "http://nas.local:9091", "http://nas.local:9091/transmission/rpc"},
|
|
{"transmission", "http://nas.local:9091/transmission/rpc", "http://nas.local:9091/transmission/rpc"},
|
|
}
|
|
for _, tc := range cases {
|
|
got, err := downloadClientRPCURL(tc.clientType, tc.host)
|
|
if err != nil {
|
|
t.Fatalf("downloadClientRPCURL(%q, %q) error: %v", tc.clientType, tc.host, err)
|
|
}
|
|
if got != tc.want {
|
|
t.Fatalf("downloadClientRPCURL(%q, %q) = %q, want %q", tc.clientType, tc.host, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestAria2AdapterRejectsUnsafeHostBeforeHTTPRequest(t *testing.T) {
|
|
adapter := NewAria2Adapter()
|
|
called := false
|
|
adapter.client = &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
|
|
called = true
|
|
return &http.Response{StatusCode: http.StatusOK, Body: http.NoBody}, nil
|
|
})}
|
|
|
|
if err := adapter.Initialize(t.Context(), DownloadClientConfig{
|
|
Host: "http://user:pass@127.0.0.1:6800",
|
|
Password: "secret",
|
|
}); err == nil {
|
|
t.Fatal("expected unsafe host error")
|
|
}
|
|
if called {
|
|
t.Fatal("unsafe aria2 host should be rejected before any HTTP request")
|
|
}
|
|
}
|
|
|
|
func TestAria2AdapterUsesNormalizedRPCURL(t *testing.T) {
|
|
var gotPath string
|
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
gotPath = r.URL.Path
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer server.Close()
|
|
|
|
adapter := NewAria2Adapter()
|
|
if err := adapter.Initialize(t.Context(), DownloadClientConfig{
|
|
Host: server.URL,
|
|
Password: "secret",
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if gotPath != "/jsonrpc" {
|
|
t.Fatalf("aria2 request path = %q, want /jsonrpc", gotPath)
|
|
}
|
|
}
|
|
|
|
func TestDownloadClientDeleteClearsLegacyQBitConnectionWhenNoDefault(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.DownloadClient{}, &model.Setting{})
|
|
repos := repository.New(db)
|
|
for key, value := range map[string]string{
|
|
"qbittorrent.url": "http://127.0.0.1:8080",
|
|
"qbittorrent.username": "admin",
|
|
"qbittorrent.password": "admin",
|
|
} {
|
|
if err := repos.Setting.Set(t.Context(), key, value); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
svc := NewDownloadClientService(zap.NewNop(), repos)
|
|
row, err := svc.Create(t.Context(), DownloadClientInput{
|
|
Name: "qB",
|
|
Type: "qbittorrent",
|
|
Host: "http://127.0.0.1:8080",
|
|
IsDefault: true,
|
|
Enabled: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := svc.Delete(t.Context(), row.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
for _, key := range []string{"qbittorrent.url", "qbittorrent.username", "qbittorrent.password"} {
|
|
value, err := repos.Setting.Get(t.Context(), key)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if value != "" {
|
|
t.Fatalf("%s = %q, want cleared", key, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDownloadClientUpdateClearsLegacyQBitConnectionWhenDefaultDisabled(t *testing.T) {
|
|
db := newServiceTestDB(t, &model.DownloadClient{}, &model.Setting{})
|
|
repos := repository.New(db)
|
|
if err := repos.Setting.Set(t.Context(), "qbittorrent.url", "http://127.0.0.1:8080"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
svc := NewDownloadClientService(zap.NewNop(), repos)
|
|
row, err := svc.Create(t.Context(), DownloadClientInput{
|
|
Name: "qB",
|
|
Type: "qbittorrent",
|
|
Host: "http://127.0.0.1:8080",
|
|
IsDefault: true,
|
|
Enabled: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if _, err := svc.Update(t.Context(), row.ID, DownloadClientInput{
|
|
Name: "qB",
|
|
Type: "qbittorrent",
|
|
Host: "http://127.0.0.1:8080",
|
|
IsDefault: false,
|
|
Enabled: false,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
value, err := repos.Setting.Get(t.Context(), "qbittorrent.url")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if value != "" {
|
|
t.Fatalf("qbittorrent.url = %q, want cleared", value)
|
|
}
|
|
}
|