Files
OpenList/internal/op/link_cache_test.go
T
Nostalgia 893457cd50 fix(aliyundrive): limit callback concurrency (#3071)
* fix(aliyundrive): limit callback concurrency

- Share proxy callback admission by Aliyun user identity and hold permits for complete response-body lifetimes.
- Retry only verified callback-capacity rejections while preserving direct redirects and server download limiting.
- Map exhausted temporary capacity to S3 SlowDown through the merged OpenListTeam gofakes3 module.
- Cover shared limits, lifecycle release, cancellation, retry classification, and the S3 HTTP response.

Co-authored-by: Codex <267193182+codex@users.noreply.github.com>

# Conflicts:
#	go.mod
#	go.sum
#	server/s3/pager.go

* fix(op): separate redirect and proxy link cache entries

- Include redirect mode in the link cache key for all drivers.

- Cover both redirect-to-proxy and proxy-to-redirect cache reuse.

Co-authored-by: Codex <267193182+codex@users.noreply.github.com>

* fix(proxy): close range bodies before opening next

- make ServeHTTP own each range body and preserve cleanup failures
- remove the aggregate range closer and pass range readers directly
- replace the obsolete callback transport test with focused lifecycle coverage

Co-authored-by: Codex <267193182+codex@users.noreply.github.com>

---------

Co-authored-by: nostalume <nostalucent@gmail.com>
Co-authored-by: Codex <267193182+codex@users.noreply.github.com>
2026-09-24 12:01:50 +08:00

72 lines
2.0 KiB
Go

package op
import (
"context"
"io"
"strings"
"testing"
"time"
"github.com/OpenListTeam/OpenList/v4/internal/driver"
"github.com/OpenListTeam/OpenList/v4/internal/model"
"github.com/OpenListTeam/OpenList/v4/internal/stream"
"github.com/OpenListTeam/OpenList/v4/pkg/http_range"
)
type linkModeDriver struct {
driver.Driver
storage model.Storage
calls int
}
func (d *linkModeDriver) Config() driver.Config { return driver.Config{} }
func (d *linkModeDriver) GetStorage() *model.Storage { return &d.storage }
func (d *linkModeDriver) Get(context.Context, string) (model.Obj, error) {
return &model.Object{Name: "file"}, nil
}
func (d *linkModeDriver) Link(_ context.Context, _ model.Obj, args model.LinkArgs) (*model.Link, error) {
d.calls++
expiration := time.Minute
if args.Redirect {
return &model.Link{URL: "https://example.com/file", Expiration: &expiration}, nil
}
return &model.Link{
RangeReader: stream.RangeReaderFunc(func(context.Context, http_range.Range) (io.ReadCloser, error) {
return io.NopCloser(strings.NewReader("file")), nil
}),
Expiration: &expiration,
}, nil
}
func TestLinkCacheSeparatesRedirectAndProxy(t *testing.T) {
for _, tc := range []struct {
name string
firstRedirect bool
}{
{name: "redirect then proxy", firstRedirect: true},
{name: "proxy then redirect", firstRedirect: false},
} {
t.Run(tc.name, func(t *testing.T) {
d := &linkModeDriver{storage: model.Storage{MountPath: "/" + t.Name()}}
for _, redirect := range []bool{tc.firstRedirect, !tc.firstRedirect, tc.firstRedirect, !tc.firstRedirect} {
link, _, err := Link(context.Background(), d, "/file", model.LinkArgs{Redirect: redirect})
if err != nil {
t.Fatal(err)
}
if redirect && (link.URL == "" || link.RangeReader != nil) {
t.Fatalf("redirect link has wrong shape: %+v", link)
}
if !redirect && (link.URL != "" || link.RangeReader == nil) {
t.Fatalf("proxy link has wrong shape: %+v", link)
}
}
if d.calls != 2 {
t.Fatalf("expected one driver call per mode, got %d", d.calls)
}
})
}
}