Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 965e8af

Browse files
committedFeb 13, 2025··
enhance: add ability to get revision ID when opening file in a workspace
Signed-off-by: Donnie Adams <[email protected]>
1 parent 834896a commit 965e8af

File tree

5 files changed

+195
-90
lines changed

5 files changed

+195
-90
lines changed
 

‎go.mod

+7-6
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,19 @@ module github.com/gptscript-ai/go-gptscript
33
go 1.23.0
44

55
require (
6-
github.com/getkin/kin-openapi v0.124.0
7-
github.com/stretchr/testify v1.8.4
6+
github.com/getkin/kin-openapi v0.129.0
7+
github.com/stretchr/testify v1.10.0
88
)
99

1010
require (
1111
github.com/davecgh/go-spew v1.1.1 // indirect
12-
github.com/go-openapi/jsonpointer v0.20.2 // indirect
13-
github.com/go-openapi/swag v0.22.8 // indirect
14-
github.com/invopop/yaml v0.2.0 // indirect
12+
github.com/go-openapi/jsonpointer v0.21.0 // indirect
13+
github.com/go-openapi/swag v0.23.0 // indirect
1514
github.com/josharian/intern v1.0.0 // indirect
16-
github.com/mailru/easyjson v0.7.7 // indirect
15+
github.com/mailru/easyjson v0.9.0 // indirect
1716
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
17+
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 // indirect
18+
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 // indirect
1819
github.com/perimeterx/marshmallow v1.1.5 // indirect
1920
github.com/pmezard/go-difflib v1.0.0 // indirect
2021
gopkg.in/yaml.v3 v3.0.1 // indirect

‎go.sum

+14-13
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,39 @@
11
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/getkin/kin-openapi v0.124.0 h1:VSFNMB9C9rTKBnQ/fpyDU8ytMTr4dWI9QovSKj9kz/M=
4-
github.com/getkin/kin-openapi v0.124.0/go.mod h1:wb1aSZA/iWmorQP9KTAS/phLj/t17B5jT7+fS8ed9NM=
5-
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
6-
github.com/go-openapi/jsonpointer v0.20.2/go.mod h1:bHen+N0u1KEO3YlmqOjTT9Adn1RfD91Ar825/PuiRVs=
7-
github.com/go-openapi/swag v0.22.8 h1:/9RjDSQ0vbFR+NyjGMkFTsA1IA0fmhKSThmfGZjicbw=
8-
github.com/go-openapi/swag v0.22.8/go.mod h1:6QT22icPLEqAM/z/TChgb4WAveCHF92+2gF0CNjHpPI=
3+
github.com/getkin/kin-openapi v0.129.0 h1:QGYTNcmyP5X0AtFQ2Dkou9DGBJsUETeLH9rFrJXZh30=
4+
github.com/getkin/kin-openapi v0.129.0/go.mod h1:gmWI+b/J45xqpyK5wJmRRZse5wefA5H0RDMK46kLUtI=
5+
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
6+
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
7+
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
8+
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
99
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
1010
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
11-
github.com/invopop/yaml v0.2.0 h1:7zky/qH+O0DwAyoobXUqvVBwgBFRxKoQ/3FjcVpjTMY=
12-
github.com/invopop/yaml v0.2.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
1311
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
1412
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
1513
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
1614
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
1715
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
1816
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
19-
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
20-
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
17+
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
18+
github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
2119
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
2220
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
21+
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80 h1:nZspmSkneBbtxU9TopEAE0CY+SBJLxO8LPUlw2vG4pU=
22+
github.com/oasdiff/yaml v0.0.0-20241210131133-6b86fb107d80/go.mod h1:7tFDb+Y51LcDpn26GccuUgQXUk6t0CXZsivKjyimYX8=
23+
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349 h1:t05Ww3DxZutOqbMN+7OIuqDwXbhl32HiZGpLy26BAPc=
24+
github.com/oasdiff/yaml3 v0.0.0-20241210130736-a94c01f36349/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
2325
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
2426
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
2527
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2628
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2729
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
2830
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
29-
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
30-
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
31+
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
32+
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
3133
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
3234
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
3335
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3436
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
3537
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
36-
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
3738
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
3839
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

‎gptscript_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func TestMain(m *testing.M) {
3030
panic(fmt.Sprintf("error creating gptscript: %s", err))
3131
}
3232

33-
g, err = NewGPTScript(GlobalOptions{OpenAIAPIKey: os.Getenv("OPENAI_API_KEY")})
33+
g, err = NewGPTScript(GlobalOptions{OpenAIAPIKey: os.Getenv("OPENAI_API_KEY"), WorkspaceTool: "/Users/thedadams/code/workspace-provider"})
3434
if err != nil {
3535
gFirst.Close()
3636
panic(fmt.Sprintf("error creating gptscript: %s", err))

‎workspace.go

+44-31
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,9 @@ func (g *GPTScript) RemoveAll(ctx context.Context, opts ...RemoveAllOptions) err
147147
}
148148

149149
type WriteFileInWorkspaceOptions struct {
150-
WorkspaceID string
151-
CreateRevision *bool
152-
LatestRevision string
150+
WorkspaceID string
151+
CreateRevision *bool
152+
LatestRevisionID string
153153
}
154154

155155
func (g *GPTScript) WriteFileInWorkspace(ctx context.Context, filePath string, contents []byte, opts ...WriteFileInWorkspaceOptions) error {
@@ -161,8 +161,8 @@ func (g *GPTScript) WriteFileInWorkspace(ctx context.Context, filePath string, c
161161
if o.CreateRevision != nil {
162162
opt.CreateRevision = o.CreateRevision
163163
}
164-
if o.LatestRevision != "" {
165-
opt.LatestRevision = o.LatestRevision
164+
if o.LatestRevisionID != "" {
165+
opt.LatestRevisionID = o.LatestRevisionID
166166
}
167167
}
168168

@@ -171,13 +171,13 @@ func (g *GPTScript) WriteFileInWorkspace(ctx context.Context, filePath string, c
171171
}
172172

173173
_, err := g.runBasicCommand(ctx, "workspaces/write-file", map[string]any{
174-
"id": opt.WorkspaceID,
175-
"contents": base64.StdEncoding.EncodeToString(contents),
176-
"filePath": filePath,
177-
"createRevision": opt.CreateRevision,
178-
"latestRevision": opt.LatestRevision,
179-
"workspaceTool": g.globalOpts.WorkspaceTool,
180-
"env": g.globalOpts.Env,
174+
"id": opt.WorkspaceID,
175+
"contents": base64.StdEncoding.EncodeToString(contents),
176+
"filePath": filePath,
177+
"createRevision": opt.CreateRevision,
178+
"latestRevisionID": opt.LatestRevisionID,
179+
"workspaceTool": g.globalOpts.WorkspaceTool,
180+
"env": g.globalOpts.Env,
181181
})
182182

183183
return parsePossibleConflictInWorkspaceError(err)
@@ -214,26 +214,34 @@ func (g *GPTScript) DeleteFileInWorkspace(ctx context.Context, filePath string,
214214
}
215215

216216
type ReadFileInWorkspaceOptions struct {
217-
WorkspaceID string
217+
WorkspaceID string
218+
WithLatestRevisionID bool
218219
}
219220

220-
func (g *GPTScript) ReadFileInWorkspace(ctx context.Context, filePath string, opts ...ReadFileInWorkspaceOptions) ([]byte, error) {
221+
type ReadFileInWorkspaceResponse struct {
222+
Content []byte `json:"content"`
223+
RevisionID string `json:"revisionID"`
224+
}
225+
226+
func (g *GPTScript) ReadFileInWorkspace(ctx context.Context, filePath string, opts ...ReadFileInWorkspaceOptions) (*ReadFileInWorkspaceResponse, error) {
221227
var opt ReadFileInWorkspaceOptions
222228
for _, o := range opts {
223229
if o.WorkspaceID != "" {
224230
opt.WorkspaceID = o.WorkspaceID
225231
}
232+
opt.WithLatestRevisionID = opt.WithLatestRevisionID || o.WithLatestRevisionID
226233
}
227234

228235
if opt.WorkspaceID == "" {
229236
opt.WorkspaceID = os.Getenv("GPTSCRIPT_WORKSPACE_ID")
230237
}
231238

232239
out, err := g.runBasicCommand(ctx, "workspaces/read-file", map[string]any{
233-
"id": opt.WorkspaceID,
234-
"filePath": filePath,
235-
"workspaceTool": g.globalOpts.WorkspaceTool,
236-
"env": g.globalOpts.Env,
240+
"id": opt.WorkspaceID,
241+
"filePath": filePath,
242+
"withLatestRevisionID": opt.WithLatestRevisionID,
243+
"workspaceTool": g.globalOpts.WorkspaceTool,
244+
"env": g.globalOpts.Env,
237245
})
238246
if err != nil {
239247
if strings.HasSuffix(err.Error(), fmt.Sprintf("not found: %s/%s", opt.WorkspaceID, filePath)) {
@@ -242,7 +250,13 @@ func (g *GPTScript) ReadFileInWorkspace(ctx context.Context, filePath string, op
242250
return nil, err
243251
}
244252

245-
return base64.StdEncoding.DecodeString(out)
253+
var resp ReadFileInWorkspaceResponse
254+
err = json.Unmarshal([]byte(out), &resp)
255+
if err != nil {
256+
return nil, err
257+
}
258+
259+
return &resp, nil
246260
}
247261

248262
type FileInfo struct {
@@ -251,10 +265,12 @@ type FileInfo struct {
251265
Size int64
252266
ModTime time.Time
253267
MimeType string
268+
RevisionID string
254269
}
255270

256271
type StatFileInWorkspaceOptions struct {
257-
WorkspaceID string
272+
WorkspaceID string
273+
WithLatestRevisionID bool
258274
}
259275

260276
func (g *GPTScript) StatFileInWorkspace(ctx context.Context, filePath string, opts ...StatFileInWorkspaceOptions) (FileInfo, error) {
@@ -263,17 +279,19 @@ func (g *GPTScript) StatFileInWorkspace(ctx context.Context, filePath string, op
263279
if o.WorkspaceID != "" {
264280
opt.WorkspaceID = o.WorkspaceID
265281
}
282+
opt.WithLatestRevisionID = opt.WithLatestRevisionID || o.WithLatestRevisionID
266283
}
267284

268285
if opt.WorkspaceID == "" {
269286
opt.WorkspaceID = os.Getenv("GPTSCRIPT_WORKSPACE_ID")
270287
}
271288

272289
out, err := g.runBasicCommand(ctx, "workspaces/stat-file", map[string]any{
273-
"id": opt.WorkspaceID,
274-
"filePath": filePath,
275-
"workspaceTool": g.globalOpts.WorkspaceTool,
276-
"env": g.globalOpts.Env,
290+
"id": opt.WorkspaceID,
291+
"filePath": filePath,
292+
"withLatestRevisionID": opt.WithLatestRevisionID,
293+
"workspaceTool": g.globalOpts.WorkspaceTool,
294+
"env": g.globalOpts.Env,
277295
})
278296
if err != nil {
279297
if strings.HasSuffix(err.Error(), fmt.Sprintf("not found: %s/%s", opt.WorkspaceID, filePath)) {
@@ -291,16 +309,11 @@ func (g *GPTScript) StatFileInWorkspace(ctx context.Context, filePath string, op
291309
return info, nil
292310
}
293311

294-
type RevisionInfo struct {
295-
FileInfo
296-
RevisionID string
297-
}
298-
299312
type ListRevisionsForFileInWorkspaceOptions struct {
300313
WorkspaceID string
301314
}
302315

303-
func (g *GPTScript) ListRevisionsForFileInWorkspace(ctx context.Context, filePath string, opts ...ListRevisionsForFileInWorkspaceOptions) ([]RevisionInfo, error) {
316+
func (g *GPTScript) ListRevisionsForFileInWorkspace(ctx context.Context, filePath string, opts ...ListRevisionsForFileInWorkspaceOptions) ([]FileInfo, error) {
304317
var opt ListRevisionsForFileInWorkspaceOptions
305318
for _, o := range opts {
306319
if o.WorkspaceID != "" {
@@ -325,7 +338,7 @@ func (g *GPTScript) ListRevisionsForFileInWorkspace(ctx context.Context, filePat
325338
return nil, err
326339
}
327340

328-
var info []RevisionInfo
341+
var info []FileInfo
329342
err = json.Unmarshal([]byte(out), &info)
330343
if err != nil {
331344
return nil, err

‎workspace_test.go

+129-39
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@ func TestCreateAndDeleteWorkspaceFromWorkspace(t *testing.T) {
3333
t.Fatalf("Error creating workspace: %v", err)
3434
}
3535

36+
t.Cleanup(func() {
37+
err = g.DeleteWorkspace(context.Background(), id)
38+
if err != nil {
39+
t.Errorf("Error deleting workspace: %v", err)
40+
}
41+
})
42+
3643
err = g.WriteFileInWorkspace(context.Background(), "file.txt", []byte("hello world"), WriteFileInWorkspaceOptions{
3744
WorkspaceID: id,
3845
})
@@ -45,26 +52,21 @@ func TestCreateAndDeleteWorkspaceFromWorkspace(t *testing.T) {
4552
t.Errorf("Error creating workspace from workspace: %v", err)
4653
}
4754

48-
data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
55+
content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
4956
WorkspaceID: newID,
5057
})
5158
if err != nil {
52-
t.Errorf("Error reading file: %v", err)
59+
t.Fatalf("Error reading file: %v", err)
5360
}
5461

55-
if !bytes.Equal(data, []byte("hello world")) {
56-
t.Errorf("Unexpected content: %s", data)
62+
if !bytes.Equal(content.Content, []byte("hello world")) {
63+
t.Errorf("Unexpected content: %s", content.Content)
5764
}
5865

5966
err = g.DeleteWorkspace(context.Background(), id)
6067
if err != nil {
6168
t.Errorf("Error deleting workspace: %v", err)
6269
}
63-
64-
err = g.DeleteWorkspace(context.Background(), newID)
65-
if err != nil {
66-
t.Errorf("Error deleting new workspace: %v", err)
67-
}
6870
}
6971

7072
func TestWriteReadAndDeleteFileFromWorkspace(t *testing.T) {
@@ -90,8 +92,26 @@ func TestWriteReadAndDeleteFileFromWorkspace(t *testing.T) {
9092
t.Errorf("Error reading file: %v", err)
9193
}
9294

93-
if !bytes.Equal(content, []byte("test")) {
94-
t.Errorf("Unexpected content: %s", content)
95+
if !bytes.Equal(content.Content, []byte("test")) {
96+
t.Errorf("Unexpected content: %s", content.Content)
97+
}
98+
99+
if content.RevisionID != "" {
100+
t.Errorf("Unexpected file revision ID when not requesting it: %s", content.RevisionID)
101+
}
102+
103+
// Read the file and request the revision ID
104+
content, err = g.ReadFileInWorkspace(context.Background(), "test.txt", ReadFileInWorkspaceOptions{WorkspaceID: id, WithLatestRevisionID: true})
105+
if err != nil {
106+
t.Errorf("Error reading file: %v", err)
107+
}
108+
109+
if !bytes.Equal(content.Content, []byte("test")) {
110+
t.Errorf("Unexpected content: %s", content.Content)
111+
}
112+
113+
if content.RevisionID == "" {
114+
t.Errorf("Expected file revision ID when requesting it: %s", content.RevisionID)
95115
}
96116

97117
// Stat the file to ensure it exists
@@ -120,6 +140,24 @@ func TestWriteReadAndDeleteFileFromWorkspace(t *testing.T) {
120140
t.Errorf("Unexpected file mime type: %s", fileInfo.MimeType)
121141
}
122142

143+
if fileInfo.RevisionID != "" {
144+
t.Errorf("Unexpected file revision ID when not requesting it: %s", fileInfo.RevisionID)
145+
}
146+
147+
// Stat file and request the revision ID
148+
fileInfo, err = g.StatFileInWorkspace(context.Background(), "test.txt", StatFileInWorkspaceOptions{WorkspaceID: id, WithLatestRevisionID: true})
149+
if err != nil {
150+
t.Errorf("Error statting file: %v", err)
151+
}
152+
153+
if fileInfo.WorkspaceID != id {
154+
t.Errorf("Unexpected file workspace ID: %v", fileInfo.WorkspaceID)
155+
}
156+
157+
if fileInfo.RevisionID == "" {
158+
t.Errorf("Expected file revision ID when requesting it: %s", fileInfo.RevisionID)
159+
}
160+
123161
// Ensure we get the error we expect when trying to read a non-existent file
124162
_, err = g.ReadFileInWorkspace(context.Background(), "test1.txt", ReadFileInWorkspaceOptions{WorkspaceID: id})
125163
if nf := (*NotFoundInWorkspaceError)(nil); !errors.As(err, &nf) {
@@ -322,7 +360,7 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
322360

323361
ce := (*ConflictInWorkspaceError)(nil)
324362
// Writing a new file with a non-zero latest revision should fail
325-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: "1"})
363+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: "1"})
326364
if err == nil || !errors.As(err, &ce) {
327365
t.Errorf("Expected error writing file with non-zero latest revision: %v", err)
328366
}
@@ -347,7 +385,7 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
347385
}
348386

349387
// Writing to the file with the latest revision should succeed
350-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
388+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
351389
if err != nil {
352390
t.Fatalf("Error creating file: %v", err)
353391
}
@@ -362,12 +400,13 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
362400
}
363401

364402
// Writing to the file with the same revision should fail
365-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
403+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
366404
if err == nil || !errors.As(err, &ce) {
367405
t.Errorf("Expected error writing file with same revision: %v", err)
368406
}
369407

370-
err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", revisions[1].RevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
408+
latestRevisionID := revisions[1].RevisionID
409+
err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", latestRevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
371410
if err != nil {
372411
t.Errorf("Error deleting revision for file: %v", err)
373412
}
@@ -381,10 +420,16 @@ func TestConflictsForFileInWorkspace(t *testing.T) {
381420
t.Errorf("Unexpected number of revisions: %d", len(revisions))
382421
}
383422

423+
// Ensure we cannot write a new file with the zero-th revision ID
424+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
425+
if err == nil || !errors.As(err, &ce) {
426+
t.Errorf("Unexpected error writing to file: %v", err)
427+
}
428+
384429
// Ensure we can write a new file after deleting the latest revision
385-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
430+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: latestRevisionID})
386431
if err != nil {
387-
t.Fatalf("Error creating file: %v", err)
432+
t.Errorf("Error writing file: %v", err)
388433
}
389434

390435
err = g.DeleteFileInWorkspace(context.Background(), "test.txt", DeleteFileInWorkspaceOptions{WorkspaceID: id})
@@ -501,15 +546,15 @@ func TestCreateAndDeleteWorkspaceFromWorkspaceS3(t *testing.T) {
501546
t.Errorf("Error creating workspace from workspace: %v", err)
502547
}
503548

504-
data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
549+
content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
505550
WorkspaceID: newID,
506551
})
507552
if err != nil {
508553
t.Errorf("Error reading file: %v", err)
509554
}
510555

511-
if !bytes.Equal(data, []byte("hello world")) {
512-
t.Errorf("Unexpected content: %s", data)
556+
if !bytes.Equal(content.Content, []byte("hello world")) {
557+
t.Errorf("Unexpected content: %s", content.Content)
513558
}
514559

515560
err = g.DeleteWorkspace(context.Background(), id)
@@ -545,15 +590,15 @@ func TestCreateAndDeleteDirectoryWorkspaceFromWorkspaceS3(t *testing.T) {
545590
t.Errorf("Error creating workspace from workspace: %v", err)
546591
}
547592

548-
data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
593+
content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
549594
WorkspaceID: newID,
550595
})
551596
if err != nil {
552597
t.Errorf("Error reading file: %v", err)
553598
}
554599

555-
if !bytes.Equal(data, []byte("hello world")) {
556-
t.Errorf("Unexpected content: %s", data)
600+
if !bytes.Equal(content.Content, []byte("hello world")) {
601+
t.Errorf("Unexpected content: %s", content.Content)
557602
}
558603

559604
err = g.DeleteWorkspace(context.Background(), id)
@@ -577,6 +622,13 @@ func TestCreateAndDeleteS3WorkspaceFromWorkspaceDirectory(t *testing.T) {
577622
t.Fatalf("Error creating workspace: %v", err)
578623
}
579624

625+
t.Cleanup(func() {
626+
err = g.DeleteWorkspace(context.Background(), id)
627+
if err != nil {
628+
t.Errorf("Error deleting workspace: %v", err)
629+
}
630+
})
631+
580632
err = g.WriteFileInWorkspace(context.Background(), "file.txt", []byte("hello world"), WriteFileInWorkspaceOptions{
581633
WorkspaceID: id,
582634
})
@@ -589,26 +641,21 @@ func TestCreateAndDeleteS3WorkspaceFromWorkspaceDirectory(t *testing.T) {
589641
t.Errorf("Error creating workspace from workspace: %v", err)
590642
}
591643

592-
data, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
644+
content, err := g.ReadFileInWorkspace(context.Background(), "file.txt", ReadFileInWorkspaceOptions{
593645
WorkspaceID: newID,
594646
})
595647
if err != nil {
596-
t.Errorf("Error reading file: %v", err)
648+
t.Fatalf("Error reading file: %v", err)
597649
}
598650

599-
if !bytes.Equal(data, []byte("hello world")) {
600-
t.Errorf("Unexpected content: %s", data)
651+
if !bytes.Equal(content.Content, []byte("hello world")) {
652+
t.Errorf("Unexpected content: %s", content.Content)
601653
}
602654

603655
err = g.DeleteWorkspace(context.Background(), id)
604656
if err != nil {
605657
t.Errorf("Error deleting workspace: %v", err)
606658
}
607-
608-
err = g.DeleteWorkspace(context.Background(), newID)
609-
if err != nil {
610-
t.Errorf("Error deleting new workspace: %v", err)
611-
}
612659
}
613660

614661
func TestWriteReadAndDeleteFileFromWorkspaceS3(t *testing.T) {
@@ -638,8 +685,26 @@ func TestWriteReadAndDeleteFileFromWorkspaceS3(t *testing.T) {
638685
t.Errorf("Error reading file: %v", err)
639686
}
640687

641-
if !bytes.Equal(content, []byte("test")) {
642-
t.Errorf("Unexpected content: %s", content)
688+
if !bytes.Equal(content.Content, []byte("test")) {
689+
t.Errorf("Unexpected content: %s", content.Content)
690+
}
691+
692+
if content.RevisionID != "" {
693+
t.Errorf("Unexpected file revision ID when not requesting it: %s", content.RevisionID)
694+
}
695+
696+
// Read the file and request the revision ID
697+
content, err = g.ReadFileInWorkspace(context.Background(), "test.txt", ReadFileInWorkspaceOptions{WorkspaceID: id, WithLatestRevisionID: true})
698+
if err != nil {
699+
t.Errorf("Error reading file: %v", err)
700+
}
701+
702+
if !bytes.Equal(content.Content, []byte("test")) {
703+
t.Errorf("Unexpected content: %s", content.Content)
704+
}
705+
706+
if content.RevisionID == "" {
707+
t.Errorf("Expected file revision ID when requesting it: %s", content.RevisionID)
643708
}
644709

645710
// Stat the file to ensure it exists
@@ -668,6 +733,24 @@ func TestWriteReadAndDeleteFileFromWorkspaceS3(t *testing.T) {
668733
t.Errorf("Unexpected file mime type: %s", fileInfo.MimeType)
669734
}
670735

736+
if fileInfo.RevisionID != "" {
737+
t.Errorf("Unexpected file revision ID when not requesting it: %s", fileInfo.RevisionID)
738+
}
739+
740+
// Stat file and request the revision ID
741+
fileInfo, err = g.StatFileInWorkspace(context.Background(), "test.txt", StatFileInWorkspaceOptions{WorkspaceID: id, WithLatestRevisionID: true})
742+
if err != nil {
743+
t.Errorf("Error statting file: %v", err)
744+
}
745+
746+
if fileInfo.WorkspaceID != id {
747+
t.Errorf("Unexpected file workspace ID: %v", fileInfo.WorkspaceID)
748+
}
749+
750+
if fileInfo.RevisionID == "" {
751+
t.Errorf("Expected file revision ID when requesting it: %s", fileInfo.RevisionID)
752+
}
753+
671754
// Ensure we get the error we expect when trying to read a non-existent file
672755
_, err = g.ReadFileInWorkspace(context.Background(), "test1.txt", ReadFileInWorkspaceOptions{WorkspaceID: id})
673756
if nf := (*NotFoundInWorkspaceError)(nil); !errors.As(err, &nf) {
@@ -795,7 +878,7 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
795878

796879
ce := (*ConflictInWorkspaceError)(nil)
797880
// Writing a new file with a non-zero latest revision should fail
798-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: "1"})
881+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test0"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: "1"})
799882
if err == nil || !errors.As(err, &ce) {
800883
t.Errorf("Expected error writing file with non-zero latest revision: %v", err)
801884
}
@@ -820,7 +903,7 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
820903
}
821904

822905
// Writing to the file with the latest revision should succeed
823-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
906+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test2"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
824907
if err != nil {
825908
t.Fatalf("Error creating file: %v", err)
826909
}
@@ -835,12 +918,13 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
835918
}
836919

837920
// Writing to the file with the same revision should fail
838-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
921+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test3"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
839922
if err == nil || !errors.As(err, &ce) {
840923
t.Errorf("Expected error writing file with same revision: %v", err)
841924
}
842925

843-
err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", revisions[1].RevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
926+
latestRevisionID := revisions[1].RevisionID
927+
err = g.DeleteRevisionForFileInWorkspace(context.Background(), "test.txt", latestRevisionID, DeleteRevisionForFileInWorkspaceOptions{WorkspaceID: id})
844928
if err != nil {
845929
t.Errorf("Error deleting revision for file: %v", err)
846930
}
@@ -854,8 +938,14 @@ func TestConflictsForFileInWorkspaceS3(t *testing.T) {
854938
t.Errorf("Unexpected number of revisions: %d", len(revisions))
855939
}
856940

941+
// Ensure we cannot write a new file with the zero-th revision ID
942+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: revisions[0].RevisionID})
943+
if err == nil || !errors.As(err, &ce) {
944+
t.Fatalf("Error creating file: %v", err)
945+
}
946+
857947
// Ensure we can write a new file after deleting the latest revision
858-
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevision: revisions[0].RevisionID})
948+
err = g.WriteFileInWorkspace(context.Background(), "test.txt", []byte("test4"), WriteFileInWorkspaceOptions{WorkspaceID: id, LatestRevisionID: latestRevisionID})
859949
if err != nil {
860950
t.Fatalf("Error creating file: %v", err)
861951
}

0 commit comments

Comments
 (0)
Please sign in to comment.