Skip to content

Commit 022e74a

Browse files
Use pointer storage for fake-server owners, projects, and environments (#450)
We have a fake server for render REST API that we can write tests against. We have factory-like methods to seed data for the server. Across resources, we were storing a mix of struct literals and references. For this use case, references are preferred, because then you can mutate the references in a test as needed to change the server state and simulate different situations. In real-life, the server state can change between CLI actions! ## Summary Update `renderapi/fakes/server.go` to store references to structs for all of our seeded data. This is PR 1 of 6 for PG resume / suspend. See [GROW-2559: pg resume](https://linear.app/render-com/issue/GROW-2559/pg-resume). This particular change is setting up better test helpers. I noticed that tests were getting long and hard to read, because so much of them was creating resources and wiring them together via foreign keys. This change will let me set up better factory methods in the next PR that can produce projects and environments already wired together. GitOrigin-RevId: 6941912d3d9207ee46b0b3fc491b58f9e3ed820b
1 parent 510581e commit 022e74a

2 files changed

Lines changed: 33 additions & 31 deletions

File tree

internal/fakes/renderapi/server.go

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ func (pg *PostgresResource) nextError() (int, bool) {
111111
}
112112

113113
// NewOwner returns an Owner with sensible defaults for any zero-value fields.
114-
func NewOwner(o client.Owner) client.Owner {
114+
func NewOwner(o client.Owner) *client.Owner {
115115
if o.Id == "" {
116116
o.Id = testids.RandomWorkspaceID()
117117
}
@@ -121,7 +121,7 @@ func NewOwner(o client.Owner) client.Owner {
121121
if o.Email == "" {
122122
o.Email = "team@example.com"
123123
}
124-
return o
124+
return &o
125125
}
126126

127127
// ProjectAttrs defines the fields callers can specify when creating a fake project.
@@ -132,29 +132,29 @@ type ProjectAttrs struct {
132132
}
133133

134134
// NewProject returns a Project with sensible defaults for any zero-value fields.
135-
func NewProject(attrs ProjectAttrs) client.Project {
135+
func NewProject(attrs ProjectAttrs) *client.Project {
136136
if attrs.Id == "" {
137137
attrs.Id = testids.RandomProjectID()
138138
}
139139
if attrs.Name == "" {
140140
attrs.Name = "My Project"
141141
}
142-
return client.Project{
142+
return &client.Project{
143143
Id: attrs.Id,
144144
Name: attrs.Name,
145145
Owner: client.Owner{Id: attrs.OwnerId},
146146
}
147147
}
148148

149149
// NewEnvironment returns an Environment with sensible defaults for any zero-value fields.
150-
func NewEnvironment(e client.Environment) client.Environment {
150+
func NewEnvironment(e client.Environment) *client.Environment {
151151
if e.Id == "" {
152152
e.Id = testids.RandomEnvironmentID()
153153
}
154154
if e.Name == "" {
155155
e.Name = "My Environment"
156156
}
157-
return e
157+
return &e
158158
}
159159

160160
// NewKV returns a KeyValueDetail with sensible defaults for any zero-value fields.
@@ -282,9 +282,9 @@ type Server struct {
282282
server *httptest.Server
283283
Requests []RecordedRequest
284284
CurrentUser *client.User
285-
Owners *Resource[client.Owner]
286-
Projects *Resource[client.Project]
287-
Environments *Resource[client.Environment]
285+
Owners *Resource[*client.Owner]
286+
Projects *Resource[*client.Project]
287+
Environments *Resource[*client.Environment]
288288
KV *KVResource
289289
Postgres *PostgresResource
290290
}
@@ -295,7 +295,7 @@ type Server struct {
295295
func (s *Server) ownerByID(id string) (client.Owner, bool) {
296296
for _, o := range s.Owners.Instances {
297297
if o.Id == id {
298-
return o, true
298+
return *o, true
299299
}
300300
}
301301
return client.Owner{}, false
@@ -328,9 +328,9 @@ func NewServer(t *testing.T) *Server {
328328
t.Helper()
329329

330330
s := &Server{
331-
Owners: &Resource[client.Owner]{},
332-
Projects: &Resource[client.Project]{},
333-
Environments: &Resource[client.Environment]{},
331+
Owners: &Resource[*client.Owner]{},
332+
Projects: &Resource[*client.Project]{},
333+
Environments: &Resource[*client.Environment]{},
334334
KV: &KVResource{},
335335
Postgres: &PostgresResource{},
336336
}
@@ -361,7 +361,7 @@ func NewServer(t *testing.T) *Server {
361361
record(r)
362362
result := s.Owners.Instances
363363
if name := r.URL.Query().Get("name"); name != "" {
364-
var filtered []client.Owner
364+
var filtered []*client.Owner
365365
for _, o := range s.Owners.Instances {
366366
if o.Name == name {
367367
filtered = append(filtered, o)
@@ -371,8 +371,7 @@ func NewServer(t *testing.T) *Server {
371371
}
372372
wrapped := make([]client.OwnerWithCursor, len(result))
373373
for i := range result {
374-
o := result[i]
375-
wrapped[i] = client.OwnerWithCursor{Owner: &o}
374+
wrapped[i] = client.OwnerWithCursor{Owner: result[i]}
376375
}
377376
writeJSON(w, http.StatusOK, wrapped)
378377
})
@@ -395,7 +394,7 @@ func NewServer(t *testing.T) *Server {
395394
record(r)
396395
result := s.Projects.Instances
397396
if ownerIDs := queryListValues(r, "ownerId"); len(ownerIDs) > 0 {
398-
var filtered []client.Project
397+
var filtered []*client.Project
399398
for _, p := range result {
400399
if slices.Contains(ownerIDs, p.Owner.Id) {
401400
filtered = append(filtered, p)
@@ -404,7 +403,7 @@ func NewServer(t *testing.T) *Server {
404403
result = filtered
405404
}
406405
if name := r.URL.Query().Get("name"); name != "" {
407-
var filtered []client.Project
406+
var filtered []*client.Project
408407
for _, p := range result {
409408
if p.Name == name {
410409
filtered = append(filtered, p)
@@ -414,7 +413,8 @@ func NewServer(t *testing.T) *Server {
414413
}
415414
wrapped := make([]client.ProjectWithCursor, len(result))
416415
for i := range result {
417-
p := result[i]
416+
// Copy before setting Owner so the response doesn't mutate stored state.
417+
p := *result[i]
418418
owner, ok := s.ownerByID(p.Owner.Id)
419419
if !ok {
420420
http.Error(w, "fake server: project owner not seeded: "+p.Owner.Id, http.StatusInternalServerError)
@@ -430,8 +430,10 @@ func NewServer(t *testing.T) *Server {
430430
mux.HandleFunc("GET /projects/{id}", func(w http.ResponseWriter, r *http.Request) {
431431
record(r)
432432
id := r.PathValue("id")
433-
for _, p := range s.Projects.Instances {
434-
if p.Id == id {
433+
for _, stored := range s.Projects.Instances {
434+
if stored.Id == id {
435+
// Copy before setting Owner so the response doesn't mutate stored state.
436+
p := *stored
435437
owner, ok := s.ownerByID(p.Owner.Id)
436438
if !ok {
437439
http.Error(w, "fake server: project owner not seeded: "+p.Owner.Id, http.StatusInternalServerError)
@@ -450,7 +452,7 @@ func NewServer(t *testing.T) *Server {
450452
record(r)
451453
result := s.Environments.Instances
452454
if projectIDs := queryListValues(r, "projectId"); len(projectIDs) > 0 {
453-
var filtered []client.Environment
455+
var filtered []*client.Environment
454456
for _, e := range result {
455457
if slices.Contains(projectIDs, e.ProjectId) {
456458
filtered = append(filtered, e)
@@ -459,7 +461,7 @@ func NewServer(t *testing.T) *Server {
459461
result = filtered
460462
}
461463
if name := r.URL.Query().Get("name"); name != "" {
462-
var filtered []client.Environment
464+
var filtered []*client.Environment
463465
for _, e := range result {
464466
if e.Name == name {
465467
filtered = append(filtered, e)
@@ -469,7 +471,7 @@ func NewServer(t *testing.T) *Server {
469471
}
470472
wrapped := make([]client.EnvironmentWithCursor, len(result))
471473
for i := range result {
472-
wrapped[i] = client.EnvironmentWithCursor{Environment: result[i]}
474+
wrapped[i] = client.EnvironmentWithCursor{Environment: *result[i]}
473475
}
474476
writeJSON(w, http.StatusOK, wrapped)
475477
})
@@ -530,7 +532,7 @@ func NewServer(t *testing.T) *Server {
530532
var owner client.Owner
531533
for _, o := range s.Owners.Instances {
532534
if o.Id == body.OwnerId {
533-
owner = o
535+
owner = *o
534536
break
535537
}
536538
}

pkg/postgres/service_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -98,13 +98,13 @@ func (h testHarness) addProjectAndEnvironment(workspaceID string, projectName st
9898
project.EnvironmentIds = []string{env.Id}
9999
h.server.Projects.Add(project)
100100
h.server.Environments.Add(env)
101-
return env
101+
return *env
102102
}
103103

104104
func (h testHarness) requireOwner(workspaceID string) {
105105
h.t.Helper()
106106

107-
idx := slices.IndexFunc(h.server.Owners.Instances, func(owner client.Owner) bool {
107+
idx := slices.IndexFunc(h.server.Owners.Instances, func(owner *client.Owner) bool {
108108
return owner.Id == workspaceID
109109
})
110110
require.NotEqual(h.t, -1, idx, "test setup: owner %q must be registered before use", workspaceID)
@@ -113,21 +113,21 @@ func (h testHarness) requireOwner(workspaceID string) {
113113
func (h testHarness) requireEnvironment(environmentID string) client.Environment {
114114
h.t.Helper()
115115

116-
idx := slices.IndexFunc(h.server.Environments.Instances, func(env client.Environment) bool {
116+
idx := slices.IndexFunc(h.server.Environments.Instances, func(env *client.Environment) bool {
117117
return env.Id == environmentID
118118
})
119119
require.NotEqual(h.t, -1, idx, "test setup: environment %q must be registered before use", environmentID)
120-
return h.server.Environments.Instances[idx]
120+
return *h.server.Environments.Instances[idx]
121121
}
122122

123123
func (h testHarness) requireProject(projectID string) client.Project {
124124
h.t.Helper()
125125

126-
idx := slices.IndexFunc(h.server.Projects.Instances, func(project client.Project) bool {
126+
idx := slices.IndexFunc(h.server.Projects.Instances, func(project *client.Project) bool {
127127
return project.Id == projectID
128128
})
129129
require.NotEqual(h.t, -1, idx, "test setup: project %q must be registered before use", projectID)
130-
return h.server.Projects.Instances[idx]
130+
return *h.server.Projects.Instances[idx]
131131
}
132132

133133
func TestServiceCreate_UsesResolvedWorkspaceAndDefaults(t *testing.T) {

0 commit comments

Comments
 (0)