Skip to content

Commit 4dd4f48

Browse files
feat: add fake low level file upload client
1 parent fcbc306 commit 4dd4f48

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
package lowlevel
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
8+
"github.com/google/uuid"
9+
)
10+
11+
type LoadedFile struct {
12+
Path string
13+
Content string
14+
}
15+
16+
// revisionState holds the in-memory state for a single revision.
17+
type revisionState struct {
18+
orgID OrgID
19+
sealed bool
20+
files []LoadedFile
21+
}
22+
23+
// FakeSealableClient is a mock implementation of the SealableClient for testing.
24+
// It tracks revisions in memory and enforces the revision lifecycle (create -> upload -> seal).
25+
type FakeSealableClient struct {
26+
cfg FakeClientConfig
27+
revisions map[RevisionID]*revisionState
28+
}
29+
30+
type FakeClientConfig struct {
31+
Limits
32+
}
33+
34+
// NewFakeSealableClient creates a new instance of the fake client.
35+
func NewFakeSealableClient(cfg FakeClientConfig) *FakeSealableClient {
36+
return &FakeSealableClient{
37+
cfg: cfg,
38+
revisions: make(map[RevisionID]*revisionState),
39+
}
40+
}
41+
42+
func (f *FakeSealableClient) CreateRevision(ctx context.Context, orgID OrgID) (*UploadRevisionResponseBody, error) {
43+
newRevisionID := uuid.New()
44+
f.revisions[newRevisionID] = &revisionState{
45+
orgID: orgID,
46+
sealed: false,
47+
}
48+
49+
return &UploadRevisionResponseBody{
50+
Data: UploadRevisionResponseData{
51+
ID: newRevisionID,
52+
},
53+
}, nil
54+
}
55+
56+
func (f *FakeSealableClient) UploadFiles(ctx context.Context, orgID OrgID, revisionID RevisionID, files []UploadFile) error {
57+
rev, ok := f.revisions[revisionID]
58+
if !ok {
59+
return fmt.Errorf("revision %s not found", revisionID)
60+
}
61+
62+
if rev.orgID != orgID {
63+
return fmt.Errorf("orgID mismatch for revision %s", revisionID)
64+
}
65+
66+
if rev.sealed {
67+
return fmt.Errorf("revision %s is sealed and cannot be modified", revisionID)
68+
}
69+
70+
if len(files) > f.cfg.FileCountLimit {
71+
return NewFileCountLimitError(len(files), f.cfg.FileCountLimit)
72+
}
73+
74+
if len(files) == 0 {
75+
return ErrNoFilesProvided
76+
}
77+
78+
for _, file := range files {
79+
fileInfo, err := file.File.Stat()
80+
if err != nil {
81+
return NewFileAccessError(file.Path, err)
82+
}
83+
84+
if fileInfo.IsDir() {
85+
return NewDirectoryError(file.Path)
86+
}
87+
88+
if fileInfo.Size() > f.cfg.FileSizeLimit {
89+
return NewFileSizeLimitError(file.Path, fileInfo.Size(), f.cfg.FileSizeLimit)
90+
}
91+
}
92+
93+
for _, file := range files {
94+
bts, err := io.ReadAll(file.File)
95+
if err != nil {
96+
return err
97+
}
98+
rev.files = append(rev.files, LoadedFile{
99+
Path: file.Path,
100+
Content: string(bts),
101+
})
102+
}
103+
return nil
104+
}
105+
106+
func (f *FakeSealableClient) SealRevision(ctx context.Context, orgID OrgID, revisionID RevisionID) (*SealUploadRevisionResponseBody, error) {
107+
rev, ok := f.revisions[revisionID]
108+
if !ok {
109+
return nil, fmt.Errorf("revision %s not found", revisionID)
110+
}
111+
112+
if rev.orgID != orgID {
113+
return nil, fmt.Errorf("orgID mismatch for revision %s", revisionID)
114+
}
115+
116+
rev.sealed = true
117+
return &SealUploadRevisionResponseBody{}, nil
118+
}
119+
120+
// GetSealedRevisionFiles is a test helper to retrieve files for a sealed revision.
121+
// It is not part of the SealableClient interface.
122+
func (f *FakeSealableClient) GetSealedRevisionFiles(revisionID RevisionID) ([]LoadedFile, error) {
123+
rev, ok := f.revisions[revisionID]
124+
if !ok {
125+
return nil, fmt.Errorf("revision %s not found", revisionID)
126+
}
127+
128+
if !rev.sealed {
129+
return nil, fmt.Errorf("revision %s is not sealed", revisionID)
130+
}
131+
132+
return rev.files, nil
133+
}
134+
135+
func (f *FakeSealableClient) GetLimits() Limits {
136+
return f.cfg.Limits
137+
}

0 commit comments

Comments
 (0)