package upload_test import ( "context" "strings" "testing" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/s3" "lmika.dev/actions/wails-release/internal/upload" ) 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) } } } 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) } }) } } 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]) } } 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) } } 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)) }