Skip to content

Commit 19f2ad4

Browse files
CalamarBicefalopaulrosca-snyk
authored andcommitted
wip
1 parent d4c1727 commit 19f2ad4

File tree

9 files changed

+262
-66
lines changed

9 files changed

+262
-66
lines changed

internal/fileupload/client_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
//go:generate go run github.com/golang/mock/mockgen -package=mocks -destination=./mocks/mock_http_client.go github.com/snyk/cli-extension-os-flows/internal/fileupload/lowlevel SealableClient
2+
package fileupload
3+
4+
import (
5+
"fmt"
6+
"os"
7+
"testing"
8+
9+
"github.com/golang/mock/gomock"
10+
"github.com/google/uuid"
11+
"github.com/snyk/cli-extension-os-flows/internal/fileupload/lowlevel"
12+
"github.com/snyk/cli-extension-os-flows/internal/fileupload/mocks"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
type File struct {
17+
filename string
18+
content string
19+
}
20+
21+
type FileUploadClient struct {
22+
}
23+
24+
func (c FileUploadClient) UploadDir(dir os.File) (RevisionID, error) {
25+
return uuid.Nil, nil
26+
}
27+
28+
func NewFileUploadClient(sealableClientMock lowlevel.SealableClient) *FileUploadClient {
29+
return &FileUploadClient{}
30+
}
31+
32+
func Test_uploadingADirectory(t *testing.T) {
33+
ctrl := gomock.NewController(t)
34+
defer ctrl.Finish()
35+
sealableClientMock := mocks.NewMockSealableClient(ctrl)
36+
client := NewFileUploadClient(sealableClientMock)
37+
orgID := uuid.New()
38+
dir, files, cleanup := createFlatDirWithFiles([]File{{
39+
filename: "file1",
40+
content: "content1",
41+
}})
42+
defer cleanup()
43+
44+
expectedRevID := expectRevisionUploaded(sealableClientMock, orgID, files)
45+
46+
revID, err := client.UploadDir(*dir)
47+
48+
require.NoError(t, err)
49+
require.Equal(t, expectedRevID, revID)
50+
}
51+
52+
func expectRevisionUploaded(sealableClientMock *mocks.MockSealableClient, orgID uuid.UUID, files []lowlevel.UploadFile) RevisionID {
53+
revID := uuid.New()
54+
crResp := lowlevel.UploadRevisionResponseBody{
55+
Data: lowlevel.UploadRevisionResponseData{
56+
ID: revID,
57+
},
58+
}
59+
sealableClientMock.EXPECT().CreateRevision(gomock.Any(), gomock.Eq(orgID)).Return(
60+
&crResp,
61+
nil,
62+
)
63+
sealableClientMock.EXPECT().UploadFiles(gomock.Any(), gomock.Eq(orgID), gomock.Eq(revID), gomock.Eq(files))
64+
sealableClientMock.EXPECT().SealRevision(gomock.Any(), gomock.Eq(orgID), gomock.Eq(revID))
65+
66+
return revID
67+
}
68+
69+
func createFlatDirWithFiles(filesContent []File) (dir *os.File, files []lowlevel.UploadFile, cleanup func()) {
70+
tempDir, err := os.MkdirTemp("", "cliuploadtest*")
71+
if err != nil {
72+
panic(err)
73+
}
74+
75+
dir, err = os.Open(tempDir)
76+
if err != nil {
77+
panic(err)
78+
}
79+
for _, file := range filesContent {
80+
f, err := os.Create(tempDir + "/" + file.filename)
81+
if err != nil {
82+
panic(err)
83+
}
84+
files = append(files, lowlevel.UploadFile{
85+
Path: file.filename,
86+
File: f,
87+
})
88+
}
89+
90+
cleanup = func() {
91+
// Close all opened files first
92+
if dir != nil {
93+
dir.Close()
94+
}
95+
for _, file := range files {
96+
if file.File != nil {
97+
file.File.Close()
98+
}
99+
}
100+
// Remove the temporary directory and all its contents
101+
if err := os.RemoveAll(tempDir); err != nil {
102+
// Log error but don't panic in cleanup
103+
fmt.Printf("failed to cleanup temp directory: %s\n", err.Error())
104+
}
105+
}
106+
107+
return dir, files, cleanup
108+
}

internal/fileupload/lowlevel/client.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package lowlevel_fileupload //nolint:revive // underscore naming is intentional for this internal package
1+
package lowlevel
22

33
import (
44
"bytes"
@@ -14,23 +14,23 @@ import (
1414
"github.com/snyk/error-catalog-golang-public/snyk_errors"
1515
)
1616

17-
// Client defines the interface for file upload API operations.
18-
type Client interface {
17+
// SealableClient defines the interface for file upload API operations.
18+
type SealableClient interface {
1919
CreateRevision(ctx context.Context, orgID OrgID) (*UploadRevisionResponseBody, error)
2020
UploadFiles(ctx context.Context, orgID OrgID, revisionID RevisionID, files []UploadFile) error
2121
SealRevision(ctx context.Context, orgID OrgID, revisionID RevisionID) (*SealUploadRevisionResponseBody, error)
2222
}
2323

2424
// This will force go to complain if the type doesn't satisfy the interface.
25-
var _ Client = (*HTTPClient)(nil)
25+
var _ SealableClient = (*HTTPSealableClient)(nil)
2626

2727
// Config contains configuration for the file upload client.
2828
type Config struct {
2929
BaseURL string
3030
}
3131

32-
// HTTPClient implements the Client interface for file upload operations via HTTP API.
33-
type HTTPClient struct {
32+
// HTTPSealableClient implements the SealableClient interface for file upload operations via HTTP API.
33+
type HTTPSealableClient struct {
3434
cfg Config
3535
httpClient *http.Client
3636
}
@@ -48,8 +48,8 @@ const FileCountLimit = 100 // arbitrary number, will need to be re-evaluated
4848
const ContentType = "Content-Type"
4949

5050
// NewClient creates a new file upload client with the given configuration and options.
51-
func NewClient(cfg Config, opts ...Opt) *HTTPClient {
52-
c := HTTPClient{cfg, http.DefaultClient}
51+
func NewClient(cfg Config, opts ...Opt) *HTTPSealableClient {
52+
c := HTTPSealableClient{cfg, http.DefaultClient}
5353

5454
for _, opt := range opts {
5555
opt(&c)
@@ -59,7 +59,7 @@ func NewClient(cfg Config, opts ...Opt) *HTTPClient {
5959
}
6060

6161
// CreateRevision creates a new upload revision for the specified organization.
62-
func (c *HTTPClient) CreateRevision(ctx context.Context, orgID OrgID) (*UploadRevisionResponseBody, error) {
62+
func (c *HTTPSealableClient) CreateRevision(ctx context.Context, orgID OrgID) (*UploadRevisionResponseBody, error) {
6363
if orgID == uuid.Nil {
6464
return nil, ErrEmptyOrgID
6565
}
@@ -103,7 +103,7 @@ func (c *HTTPClient) CreateRevision(ctx context.Context, orgID OrgID) (*UploadRe
103103
}
104104

105105
// UploadFiles uploads the provided files to the specified revision. It will not close the file descriptors.
106-
func (c *HTTPClient) UploadFiles(ctx context.Context, orgID OrgID, revisionID RevisionID, files []UploadFile) error {
106+
func (c *HTTPSealableClient) UploadFiles(ctx context.Context, orgID OrgID, revisionID RevisionID, files []UploadFile) error {
107107
if orgID == uuid.Nil {
108108
return ErrEmptyOrgID
109109
}
@@ -165,7 +165,7 @@ func (c *HTTPClient) UploadFiles(ctx context.Context, orgID OrgID, revisionID Re
165165
return nil
166166
}
167167

168-
func (c *HTTPClient) streamFilesToPipe(pWriter *io.PipeWriter, mpartWriter *multipart.Writer, files []UploadFile) {
168+
func (c *HTTPSealableClient) streamFilesToPipe(pWriter *io.PipeWriter, mpartWriter *multipart.Writer, files []UploadFile) {
169169
var streamError error
170170
defer func() {
171171
pWriter.CloseWithError(streamError)
@@ -189,7 +189,7 @@ func (c *HTTPClient) streamFilesToPipe(pWriter *io.PipeWriter, mpartWriter *mult
189189
}
190190

191191
// SealRevision seals the specified upload revision, marking it as complete.
192-
func (c *HTTPClient) SealRevision(ctx context.Context, orgID OrgID, revisionID RevisionID) (*SealUploadRevisionResponseBody, error) {
192+
func (c *HTTPSealableClient) SealRevision(ctx context.Context, orgID OrgID, revisionID RevisionID) (*SealUploadRevisionResponseBody, error) {
193193
if orgID == uuid.Nil {
194194
return nil, ErrEmptyOrgID
195195
}
@@ -237,7 +237,7 @@ func (c *HTTPClient) SealRevision(ctx context.Context, orgID OrgID, revisionID R
237237
return &respBody, nil
238238
}
239239

240-
func (c *HTTPClient) handleUnexpectedStatusCodes(body io.ReadCloser, statusCode int, status, operation string) error {
240+
func (c *HTTPSealableClient) handleUnexpectedStatusCodes(body io.ReadCloser, statusCode int, status, operation string) error {
241241
bts, err := io.ReadAll(body)
242242
if err != nil {
243243
return fmt.Errorf("failed to read response body: %w", err)

0 commit comments

Comments
 (0)