github.com/quay/claircore@v1.5.28/updater/updater_test.go (about) 1 package updater 2 3 import ( 4 "archive/zip" 5 "bytes" 6 "context" 7 "crypto/sha256" 8 "encoding/hex" 9 "encoding/json" 10 "io" 11 "io/fs" 12 "net/http" 13 "path" 14 "reflect" 15 "testing" 16 17 "github.com/golang/mock/gomock" 18 "github.com/google/uuid" 19 "github.com/quay/zlog" 20 21 mock_updater "github.com/quay/claircore/test/mock/updater" 22 mock_driver "github.com/quay/claircore/test/mock/updater/driver/v1" 23 "github.com/quay/claircore/updater/driver/v1" 24 ) 25 26 func TestNew(t *testing.T) { 27 ctx := zlog.Test(context.Background(), t) 28 t.Run("MissingStore", func(t *testing.T) { 29 ctx := zlog.Test(ctx, t) 30 u, err := New(ctx, &Options{ 31 Client: &http.Client{}, 32 }) 33 t.Log(err) 34 if err == nil { 35 t.Error("unexpected success") 36 if err := u.Close(); err != nil { 37 t.Error(err) 38 } 39 } 40 }) 41 t.Run("MissingClient", func(t *testing.T) { 42 ctx := zlog.Test(ctx, t) 43 u, err := New(ctx, &Options{ 44 Store: mock_updater.NewMockStore(nil), 45 }) 46 t.Log(err) 47 if err == nil { 48 t.Error("unexpected success") 49 if err := u.Close(); err != nil { 50 t.Error(err) 51 } 52 } 53 }) 54 } 55 56 type mockparser struct { 57 driver.Updater 58 driver.VulnerabilityParser 59 driver.EnrichmentParser 60 } 61 62 const ( 63 vulnerabilityFile = `vulnerability.json` 64 enrichmentFile = `enrichment.json` 65 ) 66 67 var ( 68 matchCtx = gomock.AssignableToTypeOf(reflect.TypeOf((*context.Context)(nil)).Elem()) 69 matchFp = gomock.AssignableToTypeOf(reflect.TypeOf(driver.Fingerprint(""))) 70 matchZip = gomock.AssignableToTypeOf(reflect.TypeOf((*zip.Writer)(nil))) 71 matchClient = gomock.AssignableToTypeOf(reflect.TypeOf((*http.Client)(nil))) 72 matchFS = gomock.AssignableToTypeOf(reflect.TypeOf((*fs.FS)(nil)).Elem()) 73 matchUUID = gomock.AssignableToTypeOf(reflect.TypeOf(uuid.Nil)) 74 ) 75 76 func fetchFunc(t *testing.T, es []driver.EnrichmentRecord, vs *driver.ParsedVulnerabilities) func(context.Context, *zip.Writer, driver.Fingerprint, *http.Client) (driver.Fingerprint, error) { 77 return func(_ context.Context, z *zip.Writer, fp driver.Fingerprint, _ *http.Client) (driver.Fingerprint, error) { 78 h := sha256.New() 79 var vb, eb bytes.Buffer 80 if err := json.NewEncoder(io.MultiWriter(h, &vb)).Encode(vs); err != nil { 81 return fp, err 82 } 83 if err := json.NewEncoder(io.MultiWriter(h, &eb)).Encode(es); err != nil { 84 return fp, err 85 } 86 cfp := driver.Fingerprint(hex.EncodeToString(h.Sum(nil))) 87 t.Logf("prev fp: %q", fp) 88 t.Logf("calc fp: %q", cfp) 89 if fp == cfp { 90 return fp, driver.ErrUnchanged 91 } 92 93 w, err := z.Create(vulnerabilityFile) 94 if err != nil { 95 return fp, err 96 } 97 if _, err := io.Copy(w, &vb); err != nil { 98 return fp, err 99 } 100 w, err = z.Create(enrichmentFile) 101 if err != nil { 102 return fp, err 103 } 104 if _, err := io.Copy(w, &eb); err != nil { 105 return fp, err 106 } 107 return cfp, nil 108 } 109 } 110 111 func parseVuln(_ context.Context, in fs.FS) (*driver.ParsedVulnerabilities, error) { 112 var r driver.ParsedVulnerabilities 113 f, err := in.Open(vulnerabilityFile) 114 if err != nil { 115 return nil, err 116 } 117 if err := json.NewDecoder(f).Decode(&r); err != nil { 118 return nil, err 119 } 120 return &r, f.Close() 121 } 122 123 func parseEnrich(_ context.Context, in fs.FS) (r []driver.EnrichmentRecord, err error) { 124 f, err := in.Open(enrichmentFile) 125 if err != nil { 126 return nil, err 127 } 128 if err := json.NewDecoder(f).Decode(&r); err != nil { 129 return nil, err 130 } 131 return r, f.Close() 132 } 133 134 func TestRun(t *testing.T) { 135 ctx := zlog.Test(context.Background(), t) 136 ctl := gomock.NewController(t) 137 n := path.Base(t.Name()) 138 vs := &driver.ParsedVulnerabilities{ 139 Updater: t.Name(), 140 Vulnerability: []driver.Vulnerability{{}}, 141 } 142 es := []driver.EnrichmentRecord{ 143 {Enrichment: json.RawMessage("null")}, 144 } 145 146 upd := mock_driver.NewMockUpdater(ctl) 147 upd.EXPECT().Name(). 148 MinTimes(2). 149 Return(n) 150 upd.EXPECT().Fetch(matchCtx, matchZip, matchFp, matchClient). 151 Times(2). 152 DoAndReturn(fetchFunc(t, es, vs)) 153 vp := mock_driver.NewMockVulnerabilityParser(ctl) 154 vp.EXPECT().ParseVulnerability(matchCtx, matchFS). 155 Times(1). 156 DoAndReturn(parseVuln) 157 ep := mock_driver.NewMockEnrichmentParser(ctl) 158 ep.EXPECT().ParseEnrichment(matchCtx, matchFS). 159 Times(1). 160 DoAndReturn(parseEnrich) 161 fac := mock_driver.NewMockUpdaterFactory(ctl) 162 fac.EXPECT().Name(). 163 MinTimes(2). 164 Return(n) 165 fac.EXPECT().Create(matchCtx, gomock.Nil()). 166 Times(2). 167 Return([]driver.Updater{&mockparser{ 168 Updater: upd, 169 VulnerabilityParser: vp, 170 EnrichmentParser: ep, 171 }}, nil) 172 store := mock_updater.NewMockStore(ctl) 173 var ops []driver.UpdateOperation 174 store.EXPECT().UpdateVulnerabilities(matchCtx, matchUUID, gomock.Eq(n), matchFp, gomock.Eq(vs)). 175 Times(1). 176 DoAndReturn(func(_ context.Context, ref uuid.UUID, name string, fp driver.Fingerprint, _ *driver.ParsedVulnerabilities) error { 177 t.Log(fp) 178 ops = append(ops, driver.UpdateOperation{ 179 Fingerprint: fp, 180 Updater: name, 181 Kind: driver.VulnerabilityKind, 182 Ref: ref, 183 }) 184 return nil 185 }) 186 store.EXPECT().UpdateEnrichments(matchCtx, matchUUID, gomock.Eq(n), matchFp, gomock.Eq(es)). 187 Times(1). 188 DoAndReturn(func(_ context.Context, ref uuid.UUID, name string, fp driver.Fingerprint, _ []driver.EnrichmentRecord) error { 189 t.Log(fp) 190 ops = append(ops, driver.UpdateOperation{ 191 Fingerprint: fp, 192 Updater: name, 193 Kind: driver.EnrichmentKind, 194 Ref: ref, 195 }) 196 return nil 197 }) 198 store.EXPECT().GetLatestUpdateOperations(matchCtx). 199 Times(2). 200 DoAndReturn(func(context.Context) ([]driver.UpdateOperation, error) { 201 t.Logf("%#+v", ops) 202 return ops, nil 203 }) 204 205 u, err := New(ctx, &Options{ 206 Store: store, 207 Client: &http.Client{}, 208 Factories: []driver.UpdaterFactory{fac}, 209 }) 210 if err != nil { 211 t.Fatal(err) 212 } 213 defer func() { 214 if err := u.Close(); err != nil { 215 t.Error(err) 216 } 217 }() 218 219 if err := u.Run(ctx, false); err != nil { 220 t.Error(err) 221 } 222 223 t.Run("Unchanged", func(t *testing.T) { 224 if err := u.Run(ctx, false); err != nil { 225 t.Error(err) 226 } 227 }) 228 }