2026-05-02 00:22:12 +00:00
|
|
|
package upload_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
|
|
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
2026-05-02 01:47:13 +00:00
|
|
|
"lmika.dev/actions/wails-release/internal/upload"
|
2026-05-02 00:22:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func TestRenderKey(t *testing.T) {
|
|
|
|
|
cases := []struct {
|
|
|
|
|
template string
|
|
|
|
|
version string
|
|
|
|
|
filename string
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{"releases/{version}/{filename}", "1.2.3", "App.app.zip", "releases/1.2.3/App.app.zip"},
|
|
|
|
|
{"{filename}", "1.0.0", "App.app.zip", "App.app.zip"},
|
|
|
|
|
{"app/{version}-{filename}", "1.2.3", "App.app.zip", "app/1.2.3-App.app.zip"},
|
|
|
|
|
}
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
got := upload.RenderKey(c.template, c.version, c.filename)
|
|
|
|
|
if got != c.want {
|
|
|
|
|
t.Fatalf("template %q: got %q want %q", c.template, got, c.want)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 03:54:57 +00:00
|
|
|
func TestHTTPSURL(t *testing.T) {
|
|
|
|
|
cases := []struct {
|
|
|
|
|
name string
|
|
|
|
|
bucket, key string
|
|
|
|
|
region string
|
|
|
|
|
endpoint string
|
|
|
|
|
want string
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "aws regional virtual-hosted",
|
|
|
|
|
bucket: "my-bucket", key: "releases/1.2.3/App.app.zip",
|
|
|
|
|
region: "ap-southeast-2",
|
|
|
|
|
want: "https://my-bucket.s3.ap-southeast-2.amazonaws.com/releases/1.2.3/App.app.zip",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "aws us-east-1 still uses regional form",
|
|
|
|
|
bucket: "my-bucket", key: "k",
|
|
|
|
|
region: "us-east-1",
|
|
|
|
|
want: "https://my-bucket.s3.us-east-1.amazonaws.com/k",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "custom endpoint path-style",
|
|
|
|
|
bucket: "releases", key: "myapp/1.0.0/App.app.zip",
|
|
|
|
|
region: "auto", endpoint: "https://my-minio.example.com",
|
|
|
|
|
want: "https://my-minio.example.com/releases/myapp/1.0.0/App.app.zip",
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "custom endpoint with trailing slash is normalised",
|
|
|
|
|
bucket: "b", key: "k",
|
|
|
|
|
region: "auto", endpoint: "https://example.com/",
|
|
|
|
|
want: "https://example.com/b/k",
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
for _, c := range cases {
|
|
|
|
|
t.Run(c.name, func(t *testing.T) {
|
|
|
|
|
got := upload.HTTPSURL(c.bucket, c.key, c.region, c.endpoint)
|
|
|
|
|
if got != c.want {
|
|
|
|
|
t.Fatalf("got %q\nwant %q", got, c.want)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 00:22:12 +00:00
|
|
|
type fakePut struct {
|
|
|
|
|
calls []*s3.PutObjectInput
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (f *fakePut) PutObject(ctx context.Context, in *s3.PutObjectInput, _ ...func(*s3.Options)) (*s3.PutObjectOutput, error) {
|
|
|
|
|
f.calls = append(f.calls, in)
|
|
|
|
|
return &s3.PutObjectOutput{}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUpload_CallsPutObjectWithCorrectInputs(t *testing.T) {
|
|
|
|
|
tmp := writeTempFile(t, "hello")
|
|
|
|
|
c := &fakePut{}
|
|
|
|
|
|
|
|
|
|
url, err := upload.Upload(context.Background(), c, upload.Opts{
|
|
|
|
|
Bucket: "my-bucket",
|
|
|
|
|
Key: "releases/1.2.3/App.app.zip",
|
|
|
|
|
FilePath: tmp,
|
|
|
|
|
})
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if url != "s3://my-bucket/releases/1.2.3/App.app.zip" {
|
|
|
|
|
t.Fatalf("url got %q", url)
|
|
|
|
|
}
|
|
|
|
|
if len(c.calls) != 1 {
|
|
|
|
|
t.Fatalf("expected 1 call, got %d", len(c.calls))
|
|
|
|
|
}
|
|
|
|
|
if aws.ToString(c.calls[0].Bucket) != "my-bucket" || aws.ToString(c.calls[0].Key) != "releases/1.2.3/App.app.zip" {
|
|
|
|
|
t.Fatalf("bucket/key wrong: %+v", c.calls[0])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 03:59:49 +00:00
|
|
|
func TestUpload_ForwardsACLWhenSet(t *testing.T) {
|
|
|
|
|
tmp := writeTempFile(t, "hello")
|
|
|
|
|
c := &fakePut{}
|
|
|
|
|
|
|
|
|
|
if _, err := upload.Upload(context.Background(), c, upload.Opts{
|
|
|
|
|
Bucket: "b", Key: "k", FilePath: tmp, ACL: "public-read",
|
|
|
|
|
}); err != nil {
|
|
|
|
|
t.Fatalf("unexpected: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if string(c.calls[0].ACL) != "public-read" {
|
|
|
|
|
t.Fatalf("ACL got %q want public-read", c.calls[0].ACL)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestUpload_OmitsACLWhenEmpty(t *testing.T) {
|
|
|
|
|
tmp := writeTempFile(t, "hello")
|
|
|
|
|
c := &fakePut{}
|
|
|
|
|
|
|
|
|
|
if _, err := upload.Upload(context.Background(), c, upload.Opts{
|
|
|
|
|
Bucket: "b", Key: "k", FilePath: tmp,
|
|
|
|
|
}); err != nil {
|
|
|
|
|
t.Fatalf("unexpected: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if c.calls[0].ACL != "" {
|
|
|
|
|
t.Fatalf("ACL got %q want empty (no ACL sent)", c.calls[0].ACL)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-02 00:22:12 +00:00
|
|
|
func TestUpload_FailsWhenFileMissing(t *testing.T) {
|
|
|
|
|
_, err := upload.Upload(context.Background(), &fakePut{}, upload.Opts{
|
|
|
|
|
Bucket: "b",
|
|
|
|
|
Key: "k",
|
|
|
|
|
FilePath: "/does/not/exist",
|
|
|
|
|
})
|
|
|
|
|
if err == nil || !strings.Contains(err.Error(), "open") {
|
|
|
|
|
t.Fatalf("expected open error, got %v", err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// writeTempFile creates a small temp file and returns its path.
|
|
|
|
|
func writeTempFile(t *testing.T, contents string) string {
|
|
|
|
|
t.Helper()
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
path := dir + "/blob"
|
|
|
|
|
if err := writeAll(path, contents); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
return path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func writeAll(path, s string) error {
|
|
|
|
|
return upload.WriteFileForTest(path, []byte(s))
|
|
|
|
|
}
|