Skip to content

Commit a8f60f3

Browse files
committed
add fake time utility
1 parent 3d859e7 commit a8f60f3

File tree

6 files changed

+123
-67
lines changed

6 files changed

+123
-67
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ lint: install-build-deps
1010
golangci-lint run
1111

1212
test:
13-
go test ./...
13+
go test ./... -count=1
1414

1515
generate:
1616
go generate ./...

api/workspace.go

Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,8 @@ func (c *Client) SetEnvVarOnWorkspace(workspaceId int, envVars map[string]string
4646
// Waits for a given workspace to be running.
4747
//
4848
// Returns [TimedOut] error if the workspace does not become running in time.
49-
func (client *Client) WaitForWorkspaceRunning(workspace *Workspace, opts WaitForWorkspaceRunningOptions) error {
50-
timeout := opts.Timeout
51-
if timeout == 0 {
52-
timeout = 20 * time.Minute
53-
}
54-
delay := opts.Delay
55-
if delay == 0 {
56-
delay = 5 * time.Second
57-
}
49+
func (client *Client) WaitForWorkspaceRunning(workspace *Workspace, timeout time.Duration) error {
50+
delay := 5 * time.Second
5851

5952
maxWaitTime := time.Now().Add(timeout)
6053
for {
@@ -83,15 +76,15 @@ type DeployWorkspaceArgs struct {
8376
PlanId int
8477
Name string
8578
EnvVars map[string]string
86-
VpnConfigName *string
79+
VpnConfigName string
8780

8881
Timeout time.Duration
8982
}
9083

9184
// Deploys a workspace with the given configuration.
9285
//
9386
// Returns [TimedOut] error if the timeout is reached
94-
func (client Client) DeployWorkspace(args DeployWorkspaceArgs) error {
87+
func (client Client) DeployWorkspace(args DeployWorkspaceArgs) (*Workspace, error) {
9588
workspace, err := client.CreateWorkspace(CreateWorkspaceArgs{
9689
TeamId: args.TeamId,
9790
Name: args.Name,
@@ -102,19 +95,19 @@ func (client Client) DeployWorkspace(args DeployWorkspaceArgs) error {
10295
SourceWorkspaceId: nil,
10396
WelcomeMessage: nil,
10497
Replicas: 1,
105-
VpnConfig: args.VpnConfigName,
98+
VpnConfig: &args.VpnConfigName,
10699
})
107100
if err != nil {
108-
return err
101+
return nil, err
109102
}
110-
if err := client.WaitForWorkspaceRunning(workspace, WaitForWorkspaceRunningOptions{Timeout: args.Timeout}); err != nil {
111-
return err
103+
if err := client.WaitForWorkspaceRunning(workspace, args.Timeout); err != nil {
104+
return workspace, err
112105
}
113106

114107
if len(args.EnvVars) != 0 {
115108
if err := client.SetEnvVarOnWorkspace(workspace.Id, args.EnvVars); err != nil {
116-
return err
109+
return workspace, err
117110
}
118111
}
119-
return nil
112+
return workspace, nil
120113
}

api/workspace_test.go

Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
package api_test
22

33
import (
4+
"context"
45
"testing"
56
"time"
67

7-
"context"
8-
98
"github.com/codesphere-cloud/cs-go/api"
109
"github.com/codesphere-cloud/cs-go/api/errors"
1110
"github.com/codesphere-cloud/cs-go/api/openapi_client"
11+
tu "github.com/codesphere-cloud/cs-go/pkg/testutils"
1212
"github.com/stretchr/testify/assert"
1313
"github.com/stretchr/testify/mock"
1414
)
@@ -18,9 +18,27 @@ func getTestingClient(t *testing.T) (*api.Client, *openapi_client.MockWorkspaces
1818
apis := openapi_client.APIClient{
1919
WorkspacesAPI: wsApiMock,
2020
}
21+
tu.PatchTimeFuncs(t)
2122
return api.NewClientWithCustomApi(context.TODO(), api.Configuration{}, &apis), wsApiMock
2223
}
2324

25+
func mockWorkspaceStatus(wsApiMock *openapi_client.MockWorkspacesAPI, workspaceId int, isRunning ...bool) {
26+
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatus(mock.Anything, float32(workspaceId)).
27+
Return(openapi_client.ApiWorkspacesGetWorkspaceStatusRequest{ApiService: wsApiMock})
28+
for _, running := range isRunning {
29+
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatusExecute(mock.Anything).Once().Return(&api.WorkspaceStatus{
30+
IsRunning: running,
31+
}, nil, nil)
32+
}
33+
mock.InOrder(wsApiMock.ExpectedCalls...)
34+
}
35+
36+
func mockCreateWorkspace(wsApiMock *openapi_client.MockWorkspacesAPI, ws api.Workspace) {
37+
wsApiMock.EXPECT().WorkspacesCreateWorkspace(mock.Anything).
38+
Return(openapi_client.ApiWorkspacesCreateWorkspaceRequest{ApiService: wsApiMock})
39+
wsApiMock.EXPECT().WorkspacesCreateWorkspaceExecute(mock.Anything).Return(&ws, nil, nil)
40+
}
41+
2442
func TestListWorkspaces(t *testing.T) {
2543
client, wsApiMock := getTestingClient(t)
2644

@@ -45,16 +63,9 @@ func TestWaitForWorkspaceRunningSuccess(t *testing.T) {
4563
Id: 0, Name: "fakeWorkspace",
4664
}
4765

48-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatus(mock.Anything, float32(0)).
49-
Return(openapi_client.ApiWorkspacesGetWorkspaceStatusRequest{ApiService: wsApiMock})
50-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatusExecute(mock.Anything).Return(&api.WorkspaceStatus{
51-
IsRunning: true,
52-
}, nil, nil)
66+
mockWorkspaceStatus(wsApiMock, ws.Id, true)
5367

54-
err := client.WaitForWorkspaceRunning(
55-
&ws,
56-
api.WaitForWorkspaceRunningOptions{Timeout: 1 * time.Millisecond, Delay: 1 * time.Millisecond},
57-
)
68+
err := client.WaitForWorkspaceRunning(&ws, 1*time.Millisecond)
5869

5970
assert.Nil(t, err, "should be nil")
6071
}
@@ -66,16 +77,9 @@ func TestWaitForWorkspaceRunningTimeout(t *testing.T) {
6677
Id: 0, Name: "fakeWorkspace",
6778
}
6879

69-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatus(mock.Anything, float32(0)).
70-
Return(openapi_client.ApiWorkspacesGetWorkspaceStatusRequest{ApiService: wsApiMock})
71-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatusExecute(mock.Anything).Return(&api.WorkspaceStatus{
72-
IsRunning: false,
73-
}, nil, nil)
80+
mockWorkspaceStatus(wsApiMock, ws.Id, false, false)
7481

75-
err := client.WaitForWorkspaceRunning(
76-
&ws,
77-
api.WaitForWorkspaceRunningOptions{Timeout: 1 * time.Millisecond, Delay: 1 * time.Millisecond},
78-
)
82+
err := client.WaitForWorkspaceRunning(&ws, 1*time.Second)
7983

8084
assert.IsType(t, err, &errors.TimedOutError{}, "expected timeout error")
8185
}
@@ -87,19 +91,8 @@ func TestWaitForWorkspaceRunningOnRetry(t *testing.T) {
8791
Id: 42, Name: "fakeWorkspace",
8892
}
8993

90-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatus(mock.Anything, float32(42)).
91-
Return(openapi_client.ApiWorkspacesGetWorkspaceStatusRequest{ApiService: wsApiMock})
92-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatusExecute(mock.Anything).Return(&api.WorkspaceStatus{
93-
IsRunning: false,
94-
}, nil, nil).Once()
95-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatusExecute(mock.Anything).Return(&api.WorkspaceStatus{
96-
IsRunning: true,
97-
}, nil, nil).Once()
98-
99-
err := client.WaitForWorkspaceRunning(
100-
&ws,
101-
api.WaitForWorkspaceRunningOptions{Timeout: 10 * time.Millisecond, Delay: 1 * time.Millisecond},
102-
)
94+
mockWorkspaceStatus(wsApiMock, ws.Id, false, true)
95+
err := client.WaitForWorkspaceRunning(&ws, 1*time.Millisecond)
10396

10497
assert.Nil(t, err, "should be nil")
10598
}
@@ -115,17 +108,14 @@ func TestDeployWorkspace(t *testing.T) {
115108
Return(openapi_client.ApiWorkspacesCreateWorkspaceRequest{ApiService: wsApiMock})
116109
wsApiMock.EXPECT().WorkspacesCreateWorkspaceExecute(mock.Anything).Return(&ws, nil, nil)
117110

118-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatus(mock.Anything, float32(42)).
119-
Return(openapi_client.ApiWorkspacesGetWorkspaceStatusRequest{ApiService: wsApiMock})
120-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatusExecute(mock.Anything).Return(&api.WorkspaceStatus{
121-
IsRunning: true,
122-
}, nil, nil)
111+
mockWorkspaceStatus(wsApiMock, ws.Id, true)
123112

124-
err := client.DeployWorkspace(
113+
newWs, err := client.DeployWorkspace(
125114
api.DeployWorkspaceArgs{Timeout: 1 * time.Millisecond},
126115
)
127116

128117
assert.Nil(t, err, "should be nil")
118+
assert.Equal(t, newWs.Name, ws.Name, "Should have the same name")
129119
}
130120

131121
func TestDeployWorkspaceWithEnvVars(t *testing.T) {
@@ -142,21 +132,15 @@ func TestDeployWorkspaceWithEnvVars(t *testing.T) {
142132
},
143133
}
144134

145-
wsApiMock.EXPECT().WorkspacesCreateWorkspace(mock.Anything).
146-
Return(openapi_client.ApiWorkspacesCreateWorkspaceRequest{ApiService: wsApiMock})
147-
wsApiMock.EXPECT().WorkspacesCreateWorkspaceExecute(mock.Anything).Return(&ws, nil, nil)
148-
149-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatus(mock.Anything, float32(42)).
150-
Return(openapi_client.ApiWorkspacesGetWorkspaceStatusRequest{ApiService: wsApiMock})
151-
wsApiMock.EXPECT().WorkspacesGetWorkspaceStatusExecute(mock.Anything).Return(&api.WorkspaceStatus{
152-
IsRunning: true,
153-
}, nil, nil)
135+
mockCreateWorkspace(wsApiMock, ws)
136+
mockWorkspaceStatus(wsApiMock, ws.Id, true)
154137

155138
wsApiMock.EXPECT().WorkspacesSetEnvVar(mock.Anything, float32(42)).
156139
Return(openapi_client.ApiWorkspacesSetEnvVarRequest{ApiService: wsApiMock})
157140
wsApiMock.EXPECT().WorkspacesSetEnvVarExecute(mock.Anything).Return(nil, nil).Once()
158141

159-
err := client.DeployWorkspace(args)
142+
newWs, err := client.DeployWorkspace(args)
160143

161144
assert.Nil(t, err, "should be nil")
145+
assert.Equal(t, newWs.Name, ws.Name, "Should have the same name")
162146
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/codesphere-cloud/cs-go
33
go 1.24.2
44

55
require (
6+
github.com/agiledragon/gomonkey/v2 v2.13.0
67
github.com/jedib0t/go-pretty/v6 v6.6.7
78
github.com/spf13/cobra v1.9.1
89
github.com/stretchr/testify v1.10.0

go.sum

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
1+
github.com/agiledragon/gomonkey/v2 v2.13.0 h1:B24Jg6wBI1iB8EFR1c+/aoTg7QN/Cum7YffG8KMIyYo=
2+
github.com/agiledragon/gomonkey/v2 v2.13.0/go.mod h1:ap1AmDzcVOAz1YpeJ3TCzIgstoaWLA6jbbgxfB4w2iY=
13
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
24
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
35
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
46
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
7+
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
58
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
69
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
710
github.com/jedib0t/go-pretty/v6 v6.6.7 h1:m+LbHpm0aIAPLzLbMfn8dc3Ht8MW7lsSO4MPItz/Uuo=
811
github.com/jedib0t/go-pretty/v6 v6.6.7/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
12+
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
913
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
1014
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
1115
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -18,6 +22,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
1822
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
1923
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
2024
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
25+
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
26+
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
2127
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
2228
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
2329
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
@@ -26,10 +32,15 @@ github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
2632
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
2733
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
2834
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
35+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
36+
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
37+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
2938
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
3039
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
40+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
3141
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
3242
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
43+
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
3344
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
3445
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
3546
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

pkg/testutils/time.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package testutils
2+
3+
import (
4+
"github.com/agiledragon/gomonkey/v2"
5+
"regexp"
6+
"runtime"
7+
"strings"
8+
"sync"
9+
"testing"
10+
"time"
11+
)
12+
13+
type TestTimer struct {
14+
Timestamp map[string]time.Time
15+
Patches gomonkey.Patches
16+
mu sync.Mutex
17+
}
18+
19+
func (t *TestTimer) GetTestFunc() string {
20+
for i := range 100 {
21+
pc, _, _, _ := runtime.Caller(i)
22+
fn := runtime.FuncForPC(pc)
23+
if fn == nil {
24+
break
25+
}
26+
name := fn.Name()
27+
re := regexp.MustCompile(`^.*\.`)
28+
name = re.ReplaceAllString(name, "")
29+
30+
if strings.HasPrefix(name, "Test") {
31+
return name
32+
}
33+
}
34+
return "main"
35+
}
36+
37+
func (t *TestTimer) Now() time.Time {
38+
t.mu.Lock()
39+
defer t.mu.Unlock()
40+
return t.Timestamp[t.GetTestFunc()]
41+
}
42+
43+
func (t *TestTimer) Sleep(delay time.Duration) {
44+
t.mu.Lock()
45+
f := t.GetTestFunc()
46+
t.Timestamp[f] = t.Timestamp[f].Add(delay)
47+
time.Tick(1 * time.Millisecond)
48+
t.mu.Unlock()
49+
}
50+
51+
func (t *TestTimer) Patch() {
52+
patches := gomonkey.ApplyFunc(time.Now, t.Now)
53+
patches.ApplyFunc(time.Sleep, t.Sleep)
54+
}
55+
56+
var testTimer TestTimer
57+
58+
func PatchTimeFuncs(t *testing.T) {
59+
testTimer.mu.Lock()
60+
if testTimer.Timestamp == nil {
61+
testTimer.Timestamp = map[string]time.Time{}
62+
}
63+
testTimer.Timestamp[testTimer.GetTestFunc()] = time.Unix(1745900000, 0)
64+
testTimer.mu.Unlock()
65+
testTimer.Patch()
66+
t.Cleanup(testTimer.Patches.Reset)
67+
}

0 commit comments

Comments
 (0)