github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/internal/pkgtest/observing_resolver.go (about)

     1  package pkgtest
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"sort"
     7  
     8  	"github.com/nextlinux/gosbom/gosbom/file"
     9  	"github.com/scylladb/go-set/strset"
    10  )
    11  
    12  var _ file.Resolver = (*ObservingResolver)(nil)
    13  
    14  type ObservingResolver struct {
    15  	decorated          file.Resolver
    16  	pathQueries        map[string][]string
    17  	pathResponses      []file.Location
    18  	contentQueries     []file.Location
    19  	emptyPathResponses map[string][]string
    20  }
    21  
    22  func NewObservingResolver(resolver file.Resolver) *ObservingResolver {
    23  	return &ObservingResolver{
    24  		decorated:          resolver,
    25  		pathResponses:      make([]file.Location, 0),
    26  		emptyPathResponses: make(map[string][]string),
    27  		pathQueries:        make(map[string][]string),
    28  	}
    29  }
    30  
    31  // testing helpers...
    32  
    33  func (r *ObservingResolver) ObservedPathQuery(input string) bool {
    34  	for _, vs := range r.pathQueries {
    35  		for _, v := range vs {
    36  			if v == input {
    37  				return true
    38  			}
    39  		}
    40  	}
    41  	return false
    42  }
    43  
    44  func (r *ObservingResolver) ObservedPathResponses(path string) bool {
    45  	for _, loc := range r.pathResponses {
    46  		if loc.RealPath == path {
    47  			return true
    48  		}
    49  	}
    50  	return false
    51  }
    52  
    53  func (r *ObservingResolver) ObservedContentQueries(path string) bool {
    54  	for _, loc := range r.contentQueries {
    55  		if loc.RealPath == path {
    56  			return true
    57  		}
    58  	}
    59  	return false
    60  }
    61  
    62  func (r *ObservingResolver) AllContentQueries() []string {
    63  	observed := strset.New()
    64  	for _, loc := range r.contentQueries {
    65  		observed.Add(loc.RealPath)
    66  	}
    67  	return observed.List()
    68  }
    69  
    70  func (r *ObservingResolver) AllPathQueries() map[string][]string {
    71  	return r.pathQueries
    72  }
    73  
    74  func (r *ObservingResolver) PruneUnfulfilledPathResponses(ignore map[string][]string, ignorePaths ...string) {
    75  	if ignore == nil {
    76  		return
    77  	}
    78  	// remove any paths that were ignored for specific calls
    79  	for k, v := range ignore {
    80  		results := r.emptyPathResponses[k]
    81  		for _, ig := range v {
    82  			for i, result := range results {
    83  				if result == ig {
    84  					results = append(results[:i], results[i+1:]...)
    85  					break
    86  				}
    87  			}
    88  		}
    89  		if len(results) > 0 {
    90  			r.emptyPathResponses[k] = results
    91  		} else {
    92  			delete(r.emptyPathResponses, k)
    93  		}
    94  	}
    95  
    96  	// remove any paths that were ignored for all calls
    97  	for _, ig := range ignorePaths {
    98  		for k, v := range r.emptyPathResponses {
    99  			for i, result := range v {
   100  				if result == ig {
   101  					v = append(v[:i], v[i+1:]...)
   102  					break
   103  				}
   104  			}
   105  			if len(v) > 0 {
   106  				r.emptyPathResponses[k] = v
   107  			} else {
   108  				delete(r.emptyPathResponses, k)
   109  			}
   110  		}
   111  	}
   112  }
   113  
   114  func (r *ObservingResolver) HasUnfulfilledPathRequests() bool {
   115  	return len(r.emptyPathResponses) > 0
   116  }
   117  
   118  func (r *ObservingResolver) PrettyUnfulfilledPathRequests() string {
   119  	var res string
   120  	var keys []string
   121  
   122  	for k := range r.emptyPathResponses {
   123  		keys = append(keys, k)
   124  	}
   125  
   126  	sort.Strings(keys)
   127  
   128  	for _, k := range keys {
   129  		res += fmt.Sprintf("   %s: %+v\n", k, r.emptyPathResponses[k])
   130  	}
   131  	return res
   132  }
   133  
   134  // For the file path resolver...
   135  
   136  func (r *ObservingResolver) addPathQuery(name string, input ...string) {
   137  	r.pathQueries[name] = append(r.pathQueries[name], input...)
   138  }
   139  
   140  func (r *ObservingResolver) addPathResponse(locs ...file.Location) {
   141  	r.pathResponses = append(r.pathResponses, locs...)
   142  }
   143  
   144  func (r *ObservingResolver) addEmptyPathResponse(name string, locs []file.Location, paths ...string) {
   145  	if len(locs) == 0 {
   146  		results := r.emptyPathResponses[name]
   147  		results = append(results, paths...)
   148  		r.emptyPathResponses[name] = results
   149  	}
   150  }
   151  
   152  func (r *ObservingResolver) FilesByPath(paths ...string) ([]file.Location, error) {
   153  	name := "FilesByPath"
   154  	r.addPathQuery(name, paths...)
   155  
   156  	locs, err := r.decorated.FilesByPath(paths...)
   157  
   158  	r.addPathResponse(locs...)
   159  	r.addEmptyPathResponse(name, locs, paths...)
   160  	return locs, err
   161  }
   162  
   163  func (r *ObservingResolver) FilesByGlob(patterns ...string) ([]file.Location, error) {
   164  	name := "FilesByGlob"
   165  	r.addPathQuery(name, patterns...)
   166  
   167  	locs, err := r.decorated.FilesByGlob(patterns...)
   168  
   169  	r.addPathResponse(locs...)
   170  	r.addEmptyPathResponse(name, locs, patterns...)
   171  	return locs, err
   172  }
   173  
   174  func (r *ObservingResolver) FilesByMIMEType(types ...string) ([]file.Location, error) {
   175  	name := "FilesByMIMEType"
   176  	r.addPathQuery(name, types...)
   177  
   178  	locs, err := r.decorated.FilesByMIMEType(types...)
   179  
   180  	r.addPathResponse(locs...)
   181  	r.addEmptyPathResponse(name, locs, types...)
   182  	return locs, err
   183  }
   184  
   185  func (r *ObservingResolver) RelativeFileByPath(l file.Location, path string) *file.Location {
   186  	name := "RelativeFileByPath"
   187  	r.addPathQuery(name, path)
   188  
   189  	loc := r.decorated.RelativeFileByPath(l, path)
   190  
   191  	if loc != nil {
   192  		r.addPathResponse(*loc)
   193  	} else {
   194  		results := r.emptyPathResponses[name]
   195  		results = append(results, path)
   196  		r.emptyPathResponses[name] = results
   197  	}
   198  	return loc
   199  }
   200  
   201  // For the content resolver methods...
   202  
   203  func (r *ObservingResolver) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
   204  	r.contentQueries = append(r.contentQueries, location)
   205  	reader, err := r.decorated.FileContentsByLocation(location)
   206  	return reader, err
   207  }
   208  
   209  // For the remaining resolver methods...
   210  
   211  func (r *ObservingResolver) AllLocations() <-chan file.Location {
   212  	return r.decorated.AllLocations()
   213  }
   214  
   215  func (r *ObservingResolver) HasPath(s string) bool {
   216  	return r.decorated.HasPath(s)
   217  }
   218  
   219  func (r *ObservingResolver) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
   220  	return r.decorated.FileMetadataByLocation(location)
   221  }