github.com/GoogleCloudPlatform/testgrid@v0.0.174/pkg/pubsub/pubsub_test.go (about) 1 /* 2 Copyright 2021 The TestGrid Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package pubsub 18 19 import ( 20 "context" 21 "sync" 22 "testing" 23 "time" 24 25 "cloud.google.com/go/pubsub" 26 "github.com/GoogleCloudPlatform/testgrid/util/gcs" 27 "github.com/google/go-cmp/cmp" 28 "github.com/sirupsen/logrus" 29 ) 30 31 func mustPath(t *testing.T, s string) *gcs.Path { 32 p, err := gcs.NewPath(s) 33 if err != nil { 34 t.Fatalf("gcs.NewPath(%q): %v", s, err) 35 } 36 return p 37 } 38 39 type fakeAcker struct { 40 acks []string 41 nacks []string 42 } 43 44 func (fa *fakeAcker) Ack(m *pubsub.Message) { 45 fa.acks = append(fa.acks, m.ID) 46 } 47 48 func (fa *fakeAcker) Nack(m *pubsub.Message) { 49 fa.nacks = append(fa.nacks, m.ID) 50 } 51 52 func TestSendToReceivers(t *testing.T) { 53 now := time.Now().Round(time.Second) 54 cases := []struct { 55 name string 56 ctx context.Context 57 send Sender 58 want []*Notification 59 wantAcks fakeAcker 60 err error 61 }{ 62 { 63 name: "empty", 64 ctx: context.Background(), 65 send: func(ctx context.Context, receive func(context.Context, *pubsub.Message)) error { 66 /* 67 for _, msg := range []*pubsub.Message{ 68 {}, 69 }{ 70 receive(ctx, msg) 71 } 72 */ 73 return nil 74 }, 75 }, 76 { 77 name: "basic", 78 ctx: context.Background(), 79 send: func(ctx context.Context, receive func(context.Context, *pubsub.Message)) error { 80 for _, msg := range []*pubsub.Message{ 81 { 82 ID: "good-bar", 83 Attributes: map[string]string{ 84 keyBucket: "foo", 85 keyObject: "bar", 86 keyTime: now.Format(time.RFC3339), 87 keyEvent: string(Finalize), 88 keyGeneration: "100", 89 }, 90 }, 91 { 92 ID: "bad-path", 93 Attributes: map[string]string{ 94 keyBucket: "ignor?e-bad-path", 95 keyTime: now.Add(time.Second).Format(time.RFC3339), 96 keyEvent: string(Delete), 97 keyGeneration: "50", 98 }, 99 }, 100 { 101 ID: "bad-hash-path", 102 Attributes: map[string]string{ 103 keyBucket: "random-bucket", 104 keyObject: "ignore-path#with-a-hash", 105 keyTime: now.Add(time.Second).Format(time.RFC3339), 106 keyEvent: string(Delete), 107 keyGeneration: "50", 108 }, 109 }, 110 { 111 ID: "bad-control-path", 112 Attributes: map[string]string{ 113 keyBucket: "random-bucket", 114 keyObject: "ignore-path\x00with-a-control-char", 115 keyTime: now.Add(time.Second).Format(time.RFC3339), 116 keyEvent: string(Delete), 117 keyGeneration: "50", 118 }, 119 }, 120 { 121 ID: "bad-time", 122 Attributes: map[string]string{ 123 keyBucket: "foo", 124 keyObject: "old/bar", 125 keyTime: "ignore bad time", 126 keyEvent: string(Delete), 127 keyGeneration: "50", 128 }, 129 }, 130 { 131 ID: "bad-gen", 132 Attributes: map[string]string{ 133 keyBucket: "foo", 134 keyObject: "old/bar", 135 keyTime: now.Add(time.Second).Format(time.RFC3339), 136 keyEvent: string(Delete), 137 keyGeneration: "50.0", // bad gen 138 }, 139 }, 140 { 141 ID: "good-random-event", 142 Attributes: map[string]string{ 143 keyBucket: "foo", 144 keyObject: "old/bar", 145 keyTime: now.Add(time.Second).Format(time.RFC3339), 146 keyEvent: "random event", 147 keyGeneration: "50", 148 }, 149 }, 150 { 151 ID: "good-old-bar", 152 Attributes: map[string]string{ 153 keyBucket: "foo", 154 keyObject: "old/bar", 155 keyTime: now.Add(time.Second).Format(time.RFC3339), 156 keyEvent: string(Delete), 157 keyGeneration: "50", 158 }, 159 }, 160 } { 161 receive(ctx, msg) 162 } 163 return nil 164 }, 165 wantAcks: fakeAcker{ 166 acks: []string{ 167 "good-bar", 168 "bad-path", 169 "bad-hash-path", 170 "bad-control-path", 171 "good-random-event", 172 "good-old-bar", 173 }, 174 nacks: []string{ 175 "bad-time", 176 "bad-gen", 177 }, 178 }, 179 want: []*Notification{ 180 { 181 Path: *mustPath(t, "gs://foo/bar"), 182 Event: Finalize, 183 Time: now, 184 Generation: 100, 185 }, 186 { 187 Path: *mustPath(t, "gs://foo/old/bar"), 188 Event: Event("random event"), 189 Time: now.Add(time.Second), 190 Generation: 50, 191 }, 192 { 193 Path: *mustPath(t, "gs://foo/old/bar"), 194 Event: Delete, 195 Time: now.Add(time.Second), 196 Generation: 50, 197 }, 198 }, 199 }, 200 } 201 202 for _, tc := range cases { 203 t.Run(tc.name, func(t *testing.T) { 204 ch := make(chan *Notification) 205 var got []*Notification 206 var wg sync.WaitGroup 207 wg.Add(1) 208 go func() { 209 defer wg.Done() 210 for n := range ch { 211 got = append(got, n) 212 } 213 }() 214 215 var gotAcks fakeAcker 216 err := sendToReceivers(tc.ctx, logrus.WithField("name", tc.name), tc.send, ch, &gotAcks) 217 close(ch) 218 wg.Wait() 219 switch { 220 case err != tc.err: 221 t.Errorf("sendToReceivers() wanted error %v, got %v", tc.err, err) 222 default: 223 if diff := cmp.Diff(tc.want, got, cmp.AllowUnexported(gcs.Path{})); diff != "" { 224 t.Errorf("sendToReceivers() got unexpected diff (-want +got):\n%s", diff) 225 } 226 if diff := cmp.Diff(tc.wantAcks.acks, gotAcks.acks); diff != "" { 227 t.Errorf("sendToReceivers() got unexpected ack diff (-want +got):\n%s", diff) 228 } 229 if diff := cmp.Diff(tc.wantAcks.nacks, gotAcks.nacks); diff != "" { 230 t.Errorf("sendToReceivers() got unexpected nack diff (-want +got):\n%s", diff) 231 } 232 } 233 }) 234 } 235 }