package s3 import ( "bytes" "context" "crypto/md5" "fmt" "io" "io/ioutil" "strings" "time" "git.kapelle.org/niklas/s3browser/internal/types" ) type mockS3 struct { buckets []string objects map[types.ID]mockObject } type mockObject struct { content []byte contentType string lastMod time.Time } type mockObjectReader struct { *bytes.Reader } func (r mockObjectReader) Close() error { // NOOP return nil } func NewMockS3(buckets []string) (S3Service, error) { return &mockS3{ buckets: buckets, objects: map[types.ID]mockObject{}, }, nil } func (m *mockS3) ListBuckets(ctx context.Context) ([]string, error) { return m.buckets, nil } func (m *mockS3) ListObjects(ctx context.Context, id types.ID) ([]Object, error) { var results []Object dirs := make(map[string]bool) depth := len(strings.Split(id.Key, "/")) for k, v := range m.objects { if k.Bucket == id.Bucket { if k.Parent().Key == id.Key { results = append(results, *mockObjToObject(v, k)) } else if strings.HasPrefix(k.Key, id.Key) { s := strings.Join(strings.Split(k.Key, "/")[:depth], "/") + "/" dirs[s] = true } } } for k := range dirs { results = append(results, Object{ ID: types.ID{ Bucket: id.Bucket, Key: k, }, }) } return results, nil } func (m *mockS3) ListObjectsRecursive(ctx context.Context, id types.ID) ([]Object, error) { var results []Object for k, v := range m.objects { if k.Bucket == id.Bucket { if strings.HasPrefix(k.Key, id.Key) { results = append(results, *mockObjToObject(v, k)) } } } return results, nil } func (m *mockS3) GetObject(ctx context.Context, id types.ID) (ObjectReader, error) { mockObj, exist := m.objects[id] if !exist { return nil, fmt.Errorf("Object not found") } reader := bytes.NewReader(mockObj.content) return mockObjectReader{reader}, nil } func (m *mockS3) PutObject(ctx context.Context, id types.ID, reader io.Reader, objectSize int64) error { content, err := ioutil.ReadAll(reader) if err != nil { return err } m.objects[id] = mockObject{ content: content, lastMod: time.Now(), contentType: "application/octet-stream", // TODO: detect MIME type or dont its just a mock after all } return nil } func (m *mockS3) CopyObject(ctx context.Context, src types.ID, dest types.ID) error { srcObj, exist := m.objects[src] if !exist { return fmt.Errorf("Object not found") } m.objects[dest] = srcObj return nil } func (m *mockS3) StatObject(ctx context.Context, id types.ID) (*Object, error) { mockObj, exist := m.objects[id] if !exist { return nil, fmt.Errorf("Object not found") } return mockObjToObject(mockObj, id), nil } func (m *mockS3) RemoveObject(ctx context.Context, id types.ID) error { delete(m.objects, id) return nil } func mockObjToObject(mockObj mockObject, id types.ID) *Object { return &Object{ ID: id, Size: int64(len(mockObj.content)), ContentType: mockObj.contentType, LastModified: mockObj.lastMod, ETag: fmt.Sprintf("%x", md5.Sum(mockObj.content)), } }