• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

shogo82148 / s3cli-mini / 15931393534

27 Jun 2025 04:40PM UTC coverage: 17.361% (-47.7%) from 65.069%
15931393534

Pull #626

github

web-flow
Bump github.com/go-viper/mapstructure/v2 from 2.2.1 to 2.3.0

Bumps [github.com/go-viper/mapstructure/v2](https://github.com/go-viper/mapstructure) from 2.2.1 to 2.3.0.
- [Release notes](https://github.com/go-viper/mapstructure/releases)
- [Changelog](https://github.com/go-viper/mapstructure/blob/main/CHANGELOG.md)
- [Commits](https://github.com/go-viper/mapstructure/compare/v2.2.1...v2.3.0)

---
updated-dependencies:
- dependency-name: github.com/go-viper/mapstructure/v2
  dependency-version: 2.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #626: Bump github.com/go-viper/mapstructure/v2 from 2.2.1 to 2.3.0

250 of 1440 relevant lines covered (17.36%)

10.05 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

13.01
/cmd/internal/testutils/testutils.go
1
package testutils
2

3
import (
4
        "context"
5
        "crypto/rand"
6
        "encoding/hex"
7
        "errors"
8
        "log"
9
        "os"
10
        "sync"
11
        "testing"
12
        "time"
13

14
        "github.com/aws/aws-sdk-go-v2/aws"
15
        "github.com/aws/aws-sdk-go-v2/config"
16
        "github.com/aws/aws-sdk-go-v2/service/s3"
17
        "github.com/aws/aws-sdk-go-v2/service/s3/types"
18
        "github.com/shogo82148/s3cli-mini/cmd/internal/interfaces"
19
)
20

21
func bucketPrefix() string {
48✔
22
        return os.Getenv("S3CLI_TEST_BUCKET_PREFIX")
48✔
23
}
48✔
24

25
// SkipIfUnitTest skips join tests.
26
func SkipIfUnitTest(t *testing.T) {
48✔
27
        if bucketPrefix() == "" {
96✔
28
                t.Skip("S3CLI_TEST_BUCKET_PREFIX environment value is not set. skip this test.")
48✔
29
        }
48✔
30
}
31

32
// Bucket is an S3 bucket.
33
// It is used for integration tests.
34
type Bucket struct {
35
        // bucket name
36
        name string
37
}
38

39
func (b *Bucket) Name() string {
×
40
        return b.name
×
41
}
×
42

43
type APIClient interface {
44
        interfaces.BucketCreator
45
        interfaces.BucketHeader
46
        interfaces.ObjectListerV2
47
        interfaces.ObjectDeleter
48
        interfaces.BucketDeleter
49
}
50

51
// BucketPool is a pool of buckets.
52
type BucketPool struct {
53
        svc   APIClient
54
        input *s3.CreateBucketInput
55
        mu    sync.Mutex
56
        pool  map[*Bucket]struct{}
57
        all   map[*Bucket]struct{}
58
        sem   chan struct{}
59
}
60

61
func NewBucketPool(input *s3.CreateBucketInput, svc APIClient, size int) *BucketPool {
9✔
62
        if input == nil {
15✔
63
                input = &s3.CreateBucketInput{}
6✔
64
        }
6✔
65
        if size < 1 {
9✔
66
                size = 1
×
67
        }
×
68
        return &BucketPool{
9✔
69
                svc:   svc,
9✔
70
                input: input,
9✔
71
                pool:  make(map[*Bucket]struct{}),
9✔
72
                all:   make(map[*Bucket]struct{}),
9✔
73
                sem:   make(chan struct{}, size),
9✔
74
        }
9✔
75
}
76

77
func (pool *BucketPool) Get(ctx context.Context) (*Bucket, error) {
×
78
        pool.sem <- struct{}{}
×
79

×
80
        if bucket := pool.get(); bucket != nil {
×
81
                if err := pool.makeEmpty(ctx, bucket); err == nil {
×
82
                        return bucket, nil
×
83
                }
×
84
        }
85

86
        bucket, err := pool.createBucket(ctx, pool.input)
×
87
        if err != nil {
×
88
                return nil, err
×
89
        }
×
90
        log.Printf("🗑 bucket %q is created", bucket.name)
×
91

×
92
        // wait for the bucket is visible
×
93
        time.Sleep(5 * time.Second)
×
94
        if err := pool.waitForCreating(ctx, bucket); err != nil {
×
95
                return nil, err
×
96
        }
×
97

98
        return bucket, nil
×
99
}
100

101
func (pool *BucketPool) Put(bucket *Bucket) {
×
102
        <-pool.sem
×
103
        pool.mu.Lock()
×
104
        defer pool.mu.Unlock()
×
105

×
106
        pool.pool[bucket] = struct{}{}
×
107
}
×
108

109
func (pool *BucketPool) Cleanup(ctx context.Context) {
×
110
        pool.mu.Lock()
×
111
        defer pool.mu.Unlock()
×
112

×
113
        log.Println("🗑 cleanup buckets...")
×
114
        for bucket := range pool.all {
×
115
                if err := DeleteBucket(ctx, pool.svc, bucket.name); err != nil {
×
116
                        log.Printf("🗑 failed to delete bucket %s: %s", bucket.name, err)
×
117
                }
×
118
        }
119
}
120

121
func (pool *BucketPool) get() *Bucket {
×
122
        pool.mu.Lock()
×
123
        defer pool.mu.Unlock()
×
124

×
125
        for bucket := range pool.pool {
×
126
                delete(pool.pool, bucket)
×
127
                return bucket
×
128
        }
×
129
        return nil
×
130
}
131

132
func (pool *BucketPool) putBucket(bucket *Bucket) {
×
133
        pool.mu.Lock()
×
134
        defer pool.mu.Unlock()
×
135

×
136
        pool.all[bucket] = struct{}{}
×
137
}
×
138

139
func (pool *BucketPool) createBucket(ctx context.Context, input *s3.CreateBucketInput) (*Bucket, error) {
×
140
        var b [8]byte
×
141
        if _, err := rand.Read(b[:]); err != nil {
×
142
                return nil, err
×
143
        }
×
144
        bucketName := bucketPrefix() + hex.EncodeToString(b[:])
×
145
        bucket := &Bucket{
×
146
                name: bucketName,
×
147
        }
×
148
        pool.putBucket(bucket)
×
149

×
150
        cfg, err := config.LoadDefaultConfig(ctx)
×
151
        if err != nil {
×
152
                return nil, err
×
153
        }
×
154
        region := cfg.Region
×
155
        if region == "" {
×
156
                region = "us-east-1"
×
157
        }
×
158
        in := *input
×
159
        in.Bucket = aws.String(bucketName)
×
160
        if region != "us-east-1" {
×
161
                in.CreateBucketConfiguration = &types.CreateBucketConfiguration{
×
162
                        LocationConstraint: types.BucketLocationConstraint(region),
×
163
                }
×
164
        }
×
165
        _, err = pool.svc.CreateBucket(ctx, &in)
×
166
        if err != nil {
×
167
                return nil, err
×
168
        }
×
169
        return bucket, nil
×
170
}
171

172
func (pool *BucketPool) waitForCreating(ctx context.Context, bucket *Bucket) error {
×
173
        for i := 0; i < 60; i++ {
×
174
                _, err := pool.svc.HeadBucket(ctx, &s3.HeadBucketInput{
×
175
                        Bucket: aws.String(bucket.name),
×
176
                })
×
177
                if err == nil {
×
178
                        return nil
×
179
                }
×
180
                time.Sleep(10 * time.Second)
×
181
        }
182

183
        return errors.New("creating bucket is timeout")
×
184
}
185

186
// makeEmpty deletes all objects in the bucket.
187
func (pool *BucketPool) makeEmpty(ctx context.Context, bucket *Bucket) error {
×
188
        return makeEmpty(ctx, pool.svc, bucket)
×
189
}
×
190

191
type DeleteBucketAPI interface {
192
        interfaces.BucketDeleter
193
        interfaces.ObjectListerV2
194
        interfaces.ObjectDeleter
195
}
196

197
func makeEmpty(ctx context.Context, svc DeleteBucketAPI, bucket *Bucket) error {
×
198
        p := s3.NewListObjectsV2Paginator(svc, &s3.ListObjectsV2Input{
×
199
                Bucket: aws.String(bucket.name),
×
200
        })
×
201
        for p.HasMorePages() {
×
202
                page, err := p.NextPage(ctx)
×
203
                if err != nil {
×
204
                        return nil
×
205
                }
×
206
                for _, obj := range page.Contents {
×
207
                        svc.DeleteObject(ctx, &s3.DeleteObjectInput{
×
208
                                Bucket: aws.String(bucket.name),
×
209
                                Key:    obj.Key,
×
210
                        })
×
211
                }
×
212
        }
213
        return nil
×
214
}
215

216
// DeleteBucket deletes a S3 bucket.
217
func DeleteBucket(ctx context.Context, svc DeleteBucketAPI, bucketName string) error {
×
218
        log.Printf("🗑 deleting %q", bucketName)
×
219

×
220
        if err := makeEmpty(ctx, svc, &Bucket{
×
221
                name: bucketName,
×
222
        }); err != nil {
×
223
                return err
×
224
        }
×
225

226
        _, err := svc.DeleteBucket(ctx, &s3.DeleteBucketInput{
×
227
                Bucket: aws.String(bucketName),
×
228
        })
×
229
        return err
×
230
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc