github.com/google/osv-scalibr@v0.4.1/testing/fakeenricher/fake_enricher.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package fakeenricher provides an Enricher implementation to be used in tests. 16 // 17 //nolint:plugger // This package contains test only mocks 18 package fakeenricher 19 20 import ( 21 "context" 22 "fmt" 23 "testing" 24 25 "github.com/gohugoio/hashstructure" 26 "github.com/google/osv-scalibr/enricher" 27 "github.com/google/osv-scalibr/inventory" 28 "github.com/google/osv-scalibr/plugin" 29 ) 30 31 // Enricher is a fake enricher implementation to be used in tests. 32 type Enricher struct { 33 name string 34 version int 35 capabilities *plugin.Capabilities 36 requiredPlugins []string 37 wantEnrich map[uint64]InventoryAndErr 38 } 39 40 // Config for creating a fake enricher. 41 type Config struct { 42 Name string 43 Version int 44 Capabilities *plugin.Capabilities 45 RequiredPlugins []string 46 WantEnrich map[uint64]InventoryAndErr 47 } 48 49 // InventoryAndErr is the expected enrichment response for a given input and inventory. 50 type InventoryAndErr struct { 51 Inventory *inventory.Inventory 52 Err error 53 } 54 55 type inputAndInventory struct { 56 Input *enricher.ScanInput 57 Inventory *inventory.Inventory 58 } 59 60 // New creates a new fake enricher. 61 // 62 // The expected usage in tests is to create a fake enricher with the expected input and inventory 63 // to enrichment response mapping. 64 // Example: 65 // 66 // key, err := fakeenricher.Hash(tc.scanInput, tc.inventory) 67 // if err != nil { 68 // t.Fatalf("failed to hash input and inventory: %v", err) 69 // } 70 // wantEnrich := map[uint64]fakeenricher.InventoryAndErr{ 71 // key: fakeenricher.InventoryAndErr{ 72 // Inventory: tc.wantInventory, 73 // Err: tc.wantErr, 74 // }, 75 // } 76 // e := fakeenricher.New(&fakeenricher.Config{ 77 // Name: "FakeEnricher", 78 // Version: 1, 79 // Capabilities: &plugin.Capabilities{}, 80 // RequiredPlugins: []string{}, 81 // WantEnrich: wantEnrich, 82 // }) 83 // err := e.Enrich(ctx, tc.scanInput, tc.inventory) 84 // if !cmp.Equal(err, tc.wantErr, cmpopts.EquateErrors()) { 85 // t.Fatalf("e.Enrich(%+v, %+v, %+v) error: got %v, want %v\n", ctx, tc.scanInput, tc.inventory, err, tc.wantErr) 86 // } 87 // if diff := cmp.Diff(tc.wantInventory, tc.inventory); diff != "" { 88 // t.Fatalf("e.Enrich(%+v, %+v, %+v) returned unexpected diff (-want +got):\n%s", ctx, tc.scanInput, tc.inventory, diff) 89 // } 90 // 91 // For convenience, the MustNew and MustHash functions can be used to create a fake enricher and set 92 // behavior which will fail the test if an error is returned. 93 func New(cfg *Config) (*Enricher, error) { 94 return &Enricher{ 95 name: cfg.Name, 96 version: cfg.Version, 97 capabilities: cfg.Capabilities, 98 requiredPlugins: cfg.RequiredPlugins, 99 wantEnrich: cfg.WantEnrich, 100 }, nil 101 } 102 103 // MustNew creates a new fake enricher and fails the test if an error is returned. 104 func MustNew(t *testing.T, cfg *Config) *Enricher { 105 t.Helper() 106 e, err := New(cfg) 107 if err != nil { 108 t.Fatalf("failed to create fake enricher: %v", err) 109 } 110 return e 111 } 112 113 // Name returns the enricher's name. 114 func (e *Enricher) Name() string { return e.name } 115 116 // Version returns the enricher's version. 117 func (e *Enricher) Version() int { return e.version } 118 119 // Requirements about the scanning environment, e.g. "needs to have network access". 120 func (e *Enricher) Requirements() *plugin.Capabilities { return e.capabilities } 121 122 // RequiredPlugins returns a list of Plugins that need to be enabled for this Enricher to run. 123 func (e *Enricher) RequiredPlugins() []string { return e.requiredPlugins } 124 125 // Enrich enriches the scan results with additional information. 126 func (e *Enricher) Enrich(ctx context.Context, input *enricher.ScanInput, inv *inventory.Inventory) error { 127 key, err := Hash(input, inv) 128 if err != nil { 129 return err 130 } 131 invAndErr, ok := e.wantEnrich[key] 132 if !ok { 133 return fmt.Errorf("no enrichment response for key %d, input: %v, inventory: %v", key, input, inv) 134 } 135 newInv := invAndErr.Inventory 136 inv.Packages = newInv.Packages 137 inv.PackageVulns = newInv.PackageVulns 138 inv.GenericFindings = newInv.GenericFindings 139 return invAndErr.Err 140 } 141 142 // Hash returns a hash of the input and inventory. This is used to match the input and inventory 143 // to the expected enrichment response. 144 func Hash(input *enricher.ScanInput, inventory *inventory.Inventory) (uint64, error) { 145 ii := &inputAndInventory{ 146 Input: input, 147 Inventory: inventory, 148 } 149 return hashstructure.Hash(ii, nil) 150 } 151 152 // MustHash returns a hash of the input and inventory. This is used to match the input and inventory 153 // to the expected enrichment response. 154 func MustHash(t *testing.T, input *enricher.ScanInput, inventory *inventory.Inventory) uint64 { 155 t.Helper() 156 hash, err := Hash(input, inventory) 157 if err != nil { 158 t.Fatalf("failed to hash input and inventory: %v", err) 159 } 160 return hash 161 }