Skip to content

Commit feebf2b

Browse files
committed
add unit test to verify subset of fields
1 parent 63ae42d commit feebf2b

File tree

6 files changed

+160
-62
lines changed

6 files changed

+160
-62
lines changed

coderd/database/dbgen/dbgen.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ func WorkspaceAgentScriptTiming(t testing.TB, db database.Store, orig database.W
232232
return timing
233233
}
234234

235-
func Workspace(t testing.TB, db database.Store, orig database.Workspace) database.Workspace {
235+
func Workspace(t testing.TB, db database.Store, orig database.WorkspaceTable) database.WorkspaceTable {
236236
t.Helper()
237237

238238
workspace, err := db.InsertWorkspace(genCtx, database.InsertWorkspaceParams{

coderd/database/gentest/models_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ func TestViewSubsetWorkspaceBuild(t *testing.T) {
6565
}
6666
}
6767

68+
// TestViewSubsetWorkspace ensures WorkspaceTable is a subset of Workspace
69+
func TestViewSubsetWorkspace(t *testing.T) {
70+
t.Parallel()
71+
table := reflect.TypeOf(database.WorkspaceTable{})
72+
joined := reflect.TypeOf(database.Workspace{})
73+
74+
tableFields := allFields(table)
75+
joinedFields := allFields(joined)
76+
if !assert.Subset(t, fieldNames(joinedFields), fieldNames(tableFields), "table is not subset") {
77+
t.Log("Some fields were added to the Workspace Table without updating the 'workspaces_expanded' view.")
78+
t.Log("See migration 000262_workspace_with_names.up.sql to create the view.")
79+
}
80+
}
81+
6882
func fieldNames(fields []reflect.StructField) []string {
6983
names := make([]string, len(fields))
7084
for i, field := range fields {

coderd/database/modelqueries_internal_test.go

Lines changed: 6 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package database
22

33
import (
44
"fmt"
5-
"reflect"
65
"testing"
76

87
"github.com/stretchr/testify/require"
8+
9+
"github.com/coder/coder/v2/testutil"
910
)
1011

1112
func TestIsAuthorizedQuery(t *testing.T) {
@@ -22,68 +23,12 @@ func TestWorkspaceTableConvert(t *testing.T) {
2223
t.Parallel()
2324

2425
var workspace Workspace
25-
err := populateStruct(&workspace)
26+
err := testutil.PopulateStruct(&workspace, nil)
2627
require.NoError(t, err)
2728

28-
}
29-
30-
func populateStruct(s interface{}) error {
31-
v := reflect.ValueOf(s)
32-
if v.Kind() != reflect.Ptr || v.IsNil() {
33-
return fmt.Errorf("s must be a non-nil pointer")
34-
}
35-
36-
v = v.Elem()
37-
if v.Kind() != reflect.Struct {
38-
return fmt.Errorf("s must be a pointer to a struct")
39-
}
40-
41-
t := v.Type()
42-
for i := 0; i < t.NumField(); i++ {
43-
field := t.Field(i)
44-
fieldName := field.Name
45-
46-
fieldValue := v.Field(i)
47-
if !fieldValue.CanSet() {
48-
continue // Skip if field is unexported
49-
}
29+
workspace.WorkspaceTable()
30+
require.JSONEq(t)
5031

51-
switch fieldValue.Kind() {
52-
case reflect.Struct:
53-
if err := populateStruct(fieldValue.Addr().Interface()); err != nil {
54-
return fmt.Errorf("%s : %w", fieldName, err)
55-
}
56-
case reflect.String:
57-
fieldValue.SetString("foo")
58-
case reflect.Invalid:
59-
case reflect.Bool:
60-
case reflect.Int:
61-
case reflect.Int8:
62-
case reflect.Int16:
63-
case reflect.Int32:
64-
case reflect.Int64:
65-
case reflect.Uint:
66-
case reflect.Uint8:
67-
case reflect.Uint16:
68-
case reflect.Uint32:
69-
case reflect.Uint64:
70-
case reflect.Uintptr:
71-
case reflect.Float32:
72-
case reflect.Float64:
73-
case reflect.Complex64:
74-
case reflect.Complex128:
75-
case reflect.Array:
76-
case reflect.Chan:
77-
case reflect.Func:
78-
case reflect.Interface:
79-
case reflect.Map:
80-
case reflect.Pointer:
81-
case reflect.Slice:
82-
case reflect.UnsafePointer:
83-
default:
84-
return fmt.Errorf("unsupported kind %s", fieldValue.Kind())
85-
}
86-
}
32+
fmt.Println(workspace)
8733

88-
return nil
8934
}

testutil/reflect.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package testutil
2+
3+
import (
4+
"fmt"
5+
"reflect"
6+
"time"
7+
)
8+
9+
type Random struct {
10+
String func() string
11+
Bool func() bool
12+
Int func() int64
13+
Uint func() uint64
14+
Float func() float64
15+
Complex func() complex128
16+
}
17+
18+
func NewRandom() *Random {
19+
// Guaranteed to be random...
20+
return &Random{
21+
String: func() string { return "foo" },
22+
Bool: func() bool { return true },
23+
Int: func() int64 { return 500 },
24+
Uint: func() uint64 { return 126 },
25+
Float: func() float64 { return 3.14 },
26+
Complex: func() complex128 { return 6.24 },
27+
}
28+
}
29+
30+
// PopulateStruct does a best effort to populate a struct with random values.
31+
func PopulateStruct(s interface{}, r *Random) error {
32+
if r == nil {
33+
r = NewRandom()
34+
}
35+
36+
v := reflect.ValueOf(s)
37+
if v.Kind() != reflect.Ptr || v.IsNil() {
38+
return fmt.Errorf("s must be a non-nil pointer")
39+
}
40+
41+
v = v.Elem()
42+
if v.Kind() != reflect.Struct {
43+
return fmt.Errorf("s must be a pointer to a struct")
44+
}
45+
46+
t := v.Type()
47+
for i := 0; i < t.NumField(); i++ {
48+
field := t.Field(i)
49+
fieldName := field.Name
50+
51+
fieldValue := v.Field(i)
52+
if !fieldValue.CanSet() {
53+
continue // Skip if field is unexported
54+
}
55+
56+
nv, err := populateValue(fieldValue, r)
57+
if err != nil {
58+
return fmt.Errorf("%s : %w", fieldName, err)
59+
}
60+
v.Field(i).Set(nv)
61+
}
62+
63+
return nil
64+
}
65+
66+
func populateValue(v reflect.Value, r *Random) (reflect.Value, error) {
67+
var err error
68+
69+
// Handle some special cases
70+
switch v.Type() {
71+
case reflect.TypeOf(time.Time{}):
72+
v.Set(reflect.ValueOf(time.Date(2020, 5, 2, 5, 19, 21, 30, time.UTC)))
73+
return v, nil
74+
}
75+
76+
switch v.Kind() {
77+
case reflect.Struct:
78+
if err := PopulateStruct(v.Addr().Interface(), r); err != nil {
79+
return v, err
80+
}
81+
case reflect.String:
82+
v.SetString(r.String())
83+
case reflect.Bool:
84+
v.SetBool(true)
85+
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
86+
v.SetInt(r.Int())
87+
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
88+
v.SetUint(r.Uint())
89+
case reflect.Float32, reflect.Float64:
90+
v.SetFloat(r.Float())
91+
case reflect.Complex64, reflect.Complex128:
92+
v.SetComplex(r.Complex())
93+
case reflect.Array:
94+
for i := 0; i < v.Len(); i++ {
95+
nv, err := populateValue(v.Index(i), r)
96+
if err != nil {
97+
return v, fmt.Errorf("array index %d : %w", i, err)
98+
}
99+
v.Index(i).Set(nv)
100+
}
101+
case reflect.Map:
102+
m := reflect.MakeMap(v.Type())
103+
104+
// Set a value in the map
105+
k := reflect.New(v.Type().Key())
106+
kv := reflect.New(v.Type().Elem())
107+
k, err = populateValue(k, r)
108+
if err != nil {
109+
return v, fmt.Errorf("map key : %w", err)
110+
}
111+
kv, err = populateValue(kv, r)
112+
if err != nil {
113+
return v, fmt.Errorf("map value : %w", err)
114+
}
115+
116+
m.SetMapIndex(k, kv)
117+
return m, nil
118+
case reflect.Pointer:
119+
return populateValue(v.Elem(), r)
120+
case reflect.Slice:
121+
s := reflect.MakeSlice(v.Type(), 2, 2)
122+
sv, err := populateValue(reflect.New(v.Type().Elem()), r)
123+
if err != nil {
124+
return v, fmt.Errorf("slice value : %w", err)
125+
}
126+
127+
s.Index(0).Set(sv)
128+
s.Index(1).Set(sv)
129+
//reflect.AppendSlice(s, sv)
130+
131+
return s, nil
132+
case reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func, reflect.Interface:
133+
// Unsupported
134+
return v, fmt.Errorf("%s is not supported", v.Kind())
135+
default:
136+
return v, fmt.Errorf("unsupported kind %s", v.Kind())
137+
}
138+
return v, nil
139+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy