blob: 73aa397d1a3367e98d3f092eebeb61a0c6547931 [file] [log] [blame]
Serge Bazanski424e2012023-02-15 23:31:49 +01001package bmdb
2
3import (
4 "context"
5 "fmt"
6 "strings"
7 "testing"
8 "time"
9
10 "github.com/google/uuid"
11
12 "source.monogon.dev/cloud/bmaas/bmdb/model"
13 "source.monogon.dev/cloud/bmaas/bmdb/reflection"
14)
15
16// TestReflection exercises the BMDB reflection schema reflection and data
17// retrieval code. Ideally this code would live in //cloud/bmaas/bmdb/reflection,
18// but due to namespacing issues it lives here.
19func TestReflection(t *testing.T) {
20 b := dut()
21 conn, err := b.Open(true)
22 if err != nil {
23 t.Fatalf("Open failed: %v", err)
24 }
25
26 ctx, ctxC := context.WithCancel(context.Background())
27 defer ctxC()
28
29 sess, err := conn.StartSession(ctx)
30 if err != nil {
31 t.Fatalf("StartSession: %v", err)
32 }
33
34 // Create 10 test machines.
35 var mids []uuid.UUID
36 sess.Transact(ctx, func(q *model.Queries) error {
37 for i := 0; i < 10; i += 1 {
38 mach, err := q.NewMachine(ctx)
39 if err != nil {
40 return err
41 }
42 err = q.MachineAddProvided(ctx, model.MachineAddProvidedParams{
43 MachineID: mach.MachineID,
44 Provider: model.ProviderEquinix,
45 ProviderID: fmt.Sprintf("test-%d", i),
46 })
47 if err != nil {
48 return err
49 }
50 mids = append(mids, mach.MachineID)
51 }
52 return nil
53 })
54 if err != nil {
55 t.Fatal(err)
56 }
57 // Start and fail work on one of the machines with an hour long backoff.
58 w, err := sess.Work(ctx, model.ProcessUnitTest1, func(q *model.Queries) ([]uuid.UUID, error) {
59 return mids[0:1], nil
60 })
61 if err != nil {
62 t.Fatal(err)
63 }
64 to := time.Hour
65 w.Fail(ctx, &to, "failure test")
66
67 // On another machine, create a failure with a 1 second backoff.
68 w, err = sess.Work(ctx, model.ProcessUnitTest1, func(q *model.Queries) ([]uuid.UUID, error) {
69 return mids[1:2], nil
70 })
71 if err != nil {
72 t.Fatal(err)
73 }
74 to = time.Second
75 w.Fail(ctx, &to, "failure test")
76 // Later on in the test we must wait for this backoff to actually elapse. Start
77 // counting now.
78 elapsed := time.NewTicker(to * 1)
79 defer elapsed.Stop()
80
81 // On another machine, create work and don't finish it yet.
82 _, err = sess.Work(ctx, model.ProcessUnitTest1, func(q *model.Queries) ([]uuid.UUID, error) {
83 return mids[2:3], nil
84 })
85 if err != nil {
86 t.Fatal(err)
87 }
88
89 schema, err := conn.Reflect(ctx)
90 if err != nil {
91 t.Fatalf("ReflectTagTypes: %v", err)
92 }
93
94 // Dump all in strict mode.
95 opts := &reflection.GetMachinesOpts{
96 Strict: true,
97 }
98 res, err := schema.GetMachines(ctx, opts)
99 if err != nil {
100 t.Fatalf("Dump failed: %v", err)
101 }
102 if res.Query == "" {
103 t.Errorf("Query not set on result")
104 }
105 machines := res.Data
106 if want, got := 10, len(machines); want != got {
107 t.Fatalf("Expected %d machines in dump, got %d", want, got)
108 }
109
110 // Expect Provided tag on all machines. Do a detailed check on fields, too.
111 for _, machine := range machines {
112 tag, ok := machine.Tags["Provided"]
113 if !ok {
114 t.Errorf("No Provided tag on machine.")
115 continue
116 }
117 if want, got := "Provided", tag.Type.Name(); want != got {
118 t.Errorf("Provided tag should have type %q, got %q", want, got)
119 }
120 if provider := tag.Field("provider"); provider != nil {
121 if want, got := provider.HumanValue(), "Equinix"; want != got {
122 t.Errorf("Wanted Provided.provider value %q, got %q", want, got)
123 }
124 } else {
125 t.Errorf("Provider tag has no provider field")
126 }
127 if providerId := tag.Field("provider_id"); providerId != nil {
128 if !strings.HasPrefix(providerId.HumanValue(), "test-") {
129 t.Errorf("Unexpected provider_id value %q", providerId.HumanValue())
130 }
131 } else {
132 t.Errorf("Provider tag has no provider_id field")
133 }
134 }
135
136 // Now just dump one machine.
137 opts.FilterMachine = &mids[0]
138 res, err = schema.GetMachines(ctx, opts)
139 if err != nil {
140 t.Fatalf("Dump failed: %v", err)
141 }
142 machines = res.Data
143 if want, got := 1, len(machines); want != got {
144 t.Fatalf("Expected %d machines in dump, got %d", want, got)
145 }
146 if want, got := mids[0].String(), machines[0].ID.String(); want != got {
147 t.Fatalf("Expected machine %s, got %s", want, got)
148 }
149
150 // Now dump a machine that doesn't exist. That should just return an empty list.
151 fakeMid := uuid.New()
152 opts.FilterMachine = &fakeMid
153 res, err = schema.GetMachines(ctx, opts)
154 if err != nil {
155 t.Fatalf("Dump failed: %v", err)
156 }
157 machines = res.Data
158 if want, got := 0, len(machines); want != got {
159 t.Fatalf("Expected %d machines in dump, got %d", want, got)
160 }
161
162 // Finally, check the special case machines. The first one should have an active
163 // backoff.
164 opts.FilterMachine = &mids[0]
165 res, err = schema.GetMachines(ctx, opts)
166 if err != nil {
167 t.Errorf("Dump failed: %v", err)
168 } else {
169 machine := res.Data[0]
170 if _, ok := machine.Backoffs["UnitTest1"]; !ok {
171 t.Errorf("Expected UnitTest1 backoff on machine")
172 }
173 }
174 // The second one should have an expired backoff that shouldn't be reported in a
175 // normal call..
176 <-elapsed.C
177 opts.FilterMachine = &mids[1]
178 res, err = schema.GetMachines(ctx, opts)
179 if err != nil {
180 t.Errorf("Dump failed: %v", err)
181 } else {
182 machine := res.Data[0]
183 if _, ok := machine.Backoffs["UnitTest1"]; ok {
184 t.Errorf("Expected no UnitTest1 backoff on machine")
185 }
186 }
187 // But if we ask for expired backoffs, we should get it.
188 opts.ExpiredBackoffs = true
189 res, err = schema.GetMachines(ctx, opts)
190 if err != nil {
191 t.Errorf("Dump failed: %v", err)
192 } else {
193 machine := res.Data[0]
194 if _, ok := machine.Backoffs["UnitTest1"]; !ok {
195 t.Errorf("Expected UnitTest1 backoff on machine")
196 }
197 }
198 // Finally, the third machine should have an active Work item.
199 opts.FilterMachine = &mids[2]
200 res, err = schema.GetMachines(ctx, opts)
201 if err != nil {
202 t.Errorf("Dump failed: %v", err)
203 } else {
204 machine := res.Data[0]
205 if _, ok := machine.Work["UnitTest1"]; !ok {
206 t.Errorf("Expected UnitTest1 work item on machine")
207 }
208 }
209}