Skip to content

Commit 4ec22d8

Browse files
committed
fixup test
1 parent 1a8c658 commit 4ec22d8

File tree

1 file changed

+196
-0
lines changed

1 file changed

+196
-0
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
package provisionerdserver_test
2+
3+
import (
4+
"context"
5+
crand "crypto/rand"
6+
"fmt"
7+
"testing"
8+
9+
"github.com/google/uuid"
10+
"github.com/stretchr/testify/require"
11+
"golang.org/x/xerrors"
12+
"storj.io/drpc"
13+
14+
"github.com/coder/coder/v2/coderd/database"
15+
"github.com/coder/coder/v2/coderd/database/dbmem"
16+
"github.com/coder/coder/v2/coderd/externalauth"
17+
"github.com/coder/coder/v2/codersdk/drpcsdk"
18+
proto "github.com/coder/coder/v2/provisionerd/proto"
19+
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
20+
"github.com/coder/coder/v2/testutil"
21+
)
22+
23+
// TestUploadFileLargeModuleFiles tests the UploadFile RPC with large module files
24+
func TestUploadFileLargeModuleFiles(t *testing.T) {
25+
t.Parallel()
26+
27+
ctx := testutil.Context(t, testutil.WaitMedium)
28+
db := dbmem.New()
29+
30+
// Create server
31+
server, db, _, _ := setup(t, false, &overrides{
32+
externalAuthConfigs: []*externalauth.Config{{}},
33+
})
34+
35+
testSizes := []int{
36+
512, // A small file
37+
drpcsdk.MaxMessageSize + 1024, // Just over the limit
38+
drpcsdk.MaxMessageSize * 2, // 2x the limit
39+
sdkproto.ChunkSize*3 + 512, // Multiple chunks with partial last
40+
}
41+
42+
for _, size := range testSizes {
43+
t.Run(fmt.Sprintf("size_%d_bytes", size), func(t *testing.T) {
44+
// Generate test module files data
45+
moduleData := make([]byte, size)
46+
_, err := crand.Read(moduleData)
47+
require.NoError(t, err)
48+
49+
// Convert to upload format
50+
upload, chunks := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleData)
51+
52+
stream := newMockUploadStream(upload, chunks...)
53+
54+
// Execute upload
55+
err = server.UploadFile(stream)
56+
require.NoError(t, err)
57+
58+
// Upload should be done
59+
require.True(t, stream.isDone(), "stream should be done after upload")
60+
61+
// Verify file was stored in database
62+
hashString := fmt.Sprintf("%x", upload.DataHash)
63+
file, err := db.GetFileByHashAndCreator(ctx, database.GetFileByHashAndCreatorParams{
64+
Hash: hashString,
65+
CreatedBy: uuid.Nil, // Provisionerd creates with Nil UUID
66+
})
67+
require.NoError(t, err)
68+
require.Equal(t, hashString, file.Hash)
69+
require.Equal(t, moduleData, file.Data)
70+
require.Equal(t, "application/x-tar", file.Mimetype)
71+
72+
// Try to upload it again, and it should still be successful
73+
stream = newMockUploadStream(upload, chunks...)
74+
err = server.UploadFile(stream)
75+
require.NoError(t, err, "re-upload should succeed without error")
76+
require.True(t, stream.isDone(), "stream should be done after re-upload")
77+
})
78+
}
79+
}
80+
81+
// TestUploadFileErrorScenarios tests various error conditions in file upload
82+
func TestUploadFileErrorScenarios(t *testing.T) {
83+
t.Parallel()
84+
85+
server, _, _, _ := setup(t, false, &overrides{
86+
externalAuthConfigs: []*externalauth.Config{{}},
87+
})
88+
89+
// Generate test data
90+
moduleData := make([]byte, sdkproto.ChunkSize*2)
91+
_, err := crand.Read(moduleData)
92+
require.NoError(t, err)
93+
94+
upload, chunks := sdkproto.BytesToDataUpload(sdkproto.DataUploadType_UPLOAD_TYPE_MODULE_FILES, moduleData)
95+
var _ = upload
96+
97+
t.Run("chunk_before_upload", func(t *testing.T) {
98+
stream := newMockUploadStream(nil, chunks[0])
99+
100+
err := server.UploadFile(stream)
101+
require.ErrorContains(t, err, "unexpected chunk piece while waiting for file upload")
102+
require.True(t, stream.isDone(), "stream should be done after error")
103+
})
104+
105+
t.Run("duplicate_upload", func(t *testing.T) {
106+
stream := &mockUploadStream{
107+
done: make(chan struct{}),
108+
messages: make(chan *proto.UploadFileRequest, 2),
109+
}
110+
111+
up := &proto.UploadFileRequest{Type: &proto.UploadFileRequest_DataUpload{&proto.DataUpload{
112+
UploadType: proto.DataUploadType(upload.UploadType),
113+
DataHash: upload.DataHash,
114+
FileSize: upload.FileSize,
115+
Chunks: upload.Chunks,
116+
}}}
117+
118+
// Send it twice
119+
stream.messages <- up
120+
stream.messages <- up
121+
122+
err := server.UploadFile(stream)
123+
require.ErrorContains(t, err, "unexpected file upload while waiting for file completion")
124+
require.True(t, stream.isDone(), "stream should be done after error")
125+
})
126+
127+
t.Run("unsupported_upload_type", func(t *testing.T) {
128+
cpy := *upload
129+
cpy.UploadType = sdkproto.DataUploadType_UPLOAD_TYPE_UNKNOWN // Set to an unsupported type
130+
stream := newMockUploadStream(&cpy, chunks...)
131+
132+
err := server.UploadFile(stream)
133+
require.ErrorContains(t, err, "unsupported file upload type")
134+
require.True(t, stream.isDone(), "stream should be done after error")
135+
})
136+
}
137+
138+
type mockUploadStream struct {
139+
done chan struct{}
140+
messages chan *proto.UploadFileRequest
141+
}
142+
143+
func (m mockUploadStream) SendAndClose(empty *proto.Empty) error {
144+
close(m.done)
145+
return nil
146+
}
147+
148+
func (m mockUploadStream) Recv() (*proto.UploadFileRequest, error) {
149+
msg, ok := <-m.messages
150+
if !ok {
151+
return nil, xerrors.New("no more messages to receive")
152+
}
153+
return msg, nil
154+
}
155+
func (m *mockUploadStream) Context() context.Context { panic(errUnimplemented) }
156+
func (m *mockUploadStream) MsgSend(msg drpc.Message, enc drpc.Encoding) error {
157+
panic(errUnimplemented)
158+
}
159+
func (m *mockUploadStream) MsgRecv(msg drpc.Message, enc drpc.Encoding) error {
160+
panic(errUnimplemented)
161+
}
162+
func (m *mockUploadStream) CloseSend() error { panic(errUnimplemented) }
163+
func (m *mockUploadStream) Close() error { panic(errUnimplemented) }
164+
func (m *mockUploadStream) isDone() bool {
165+
select {
166+
case <-m.done:
167+
return true
168+
default:
169+
return false
170+
}
171+
}
172+
173+
func newMockUploadStream(up *sdkproto.DataUpload, chunks ...*sdkproto.ChunkPiece) *mockUploadStream {
174+
stream := &mockUploadStream{
175+
done: make(chan struct{}),
176+
messages: make(chan *proto.UploadFileRequest, 1+len(chunks)),
177+
}
178+
if up != nil {
179+
stream.messages <- &proto.UploadFileRequest{Type: &proto.UploadFileRequest_DataUpload{&proto.DataUpload{
180+
UploadType: proto.DataUploadType(up.UploadType),
181+
DataHash: up.DataHash,
182+
FileSize: up.FileSize,
183+
Chunks: up.Chunks,
184+
}}}
185+
}
186+
187+
for _, chunk := range chunks {
188+
stream.messages <- &proto.UploadFileRequest{Type: &proto.UploadFileRequest_ChunkPiece{&proto.ChunkPiece{
189+
Data: chunk.Data,
190+
FullDataHash: chunk.FullDataHash,
191+
PieceIndex: chunk.PieceIndex,
192+
}}}
193+
}
194+
close(stream.messages)
195+
return stream
196+
}

0 commit comments

Comments
 (0)