github.com/prebid/prebid-server/v2@v2.18.0/modules/modules_test.go (about)

     1  package modules
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  	"net/http"
    10  	"testing"
    11  
    12  	"github.com/prebid/prebid-server/v2/config"
    13  	"github.com/prebid/prebid-server/v2/hooks"
    14  	"github.com/prebid/prebid-server/v2/hooks/hookstage"
    15  	"github.com/prebid/prebid-server/v2/modules/moduledeps"
    16  	"github.com/stretchr/testify/assert"
    17  )
    18  
    19  func TestModuleBuilderBuild(t *testing.T) {
    20  	vendor := "acme"
    21  	moduleName := "foobar"
    22  	defaultModulesConfig := map[string]map[string]interface{}{vendor: {moduleName: map[string]interface{}{"enabled": true}}}
    23  	defaultHookRepository, err := hooks.NewHookRepository(map[string]interface{}{vendor + "." + moduleName: module{}})
    24  	if err != nil {
    25  		t.Fatalf("Failed to init default hook repository: %s", err)
    26  	}
    27  	emptyHookRepository, err := hooks.NewHookRepository(map[string]interface{}{})
    28  	if err != nil {
    29  		t.Fatalf("Failed to init empty hook repository: %s", err)
    30  	}
    31  
    32  	testCases := map[string]struct {
    33  		givenModule           interface{}
    34  		givenConfig           config.Modules
    35  		givenHookBuilderErr   error
    36  		expectedHookRepo      hooks.HookRepository
    37  		expectedModulesStages map[string][]string
    38  		expectedErr           error
    39  	}{
    40  		"Can build module with config": {
    41  			givenModule:           module{},
    42  			givenConfig:           defaultModulesConfig,
    43  			expectedModulesStages: map[string][]string{vendor + "_" + moduleName: {hooks.StageEntrypoint.String(), hooks.StageAuctionResponse.String()}},
    44  			expectedHookRepo:      defaultHookRepository,
    45  			expectedErr:           nil,
    46  		},
    47  		"Module is not added to hook repository if it's disabled": {
    48  			givenModule:           module{},
    49  			givenConfig:           map[string]map[string]interface{}{vendor: {moduleName: map[string]interface{}{"enabled": false, "attr": "val"}}},
    50  			expectedModulesStages: map[string][]string{},
    51  			expectedHookRepo:      emptyHookRepository,
    52  			expectedErr:           nil,
    53  		},
    54  		"Module considered disabled if status property not defined in module config": {
    55  			givenModule:           module{},
    56  			givenConfig:           map[string]map[string]interface{}{vendor: {moduleName: map[string]interface{}{"foo": "bar"}}},
    57  			expectedHookRepo:      emptyHookRepository,
    58  			expectedModulesStages: map[string][]string{},
    59  			expectedErr:           nil,
    60  		},
    61  		"Module considered disabled if its config not provided and as a result skipped from execution": {
    62  			givenModule:           module{},
    63  			givenConfig:           nil,
    64  			expectedHookRepo:      emptyHookRepository,
    65  			expectedModulesStages: map[string][]string{},
    66  			expectedErr:           nil,
    67  		},
    68  		"Fails if module does not implement any hook interface": {
    69  			givenModule:           struct{}{},
    70  			givenConfig:           defaultModulesConfig,
    71  			expectedHookRepo:      nil,
    72  			expectedModulesStages: nil,
    73  			expectedErr:           fmt.Errorf(`hook "%s.%s" does not implement any supported hook interface`, vendor, moduleName),
    74  		},
    75  		"Fails if module builder function returns error": {
    76  			givenModule:           module{},
    77  			givenConfig:           defaultModulesConfig,
    78  			givenHookBuilderErr:   errors.New("failed to build module"),
    79  			expectedHookRepo:      nil,
    80  			expectedModulesStages: nil,
    81  			expectedErr:           fmt.Errorf(`failed to init "%s.%s" module: %s`, vendor, moduleName, "failed to build module"),
    82  		},
    83  		"Fails if config marshaling returns error": {
    84  			givenModule:           module{},
    85  			givenConfig:           map[string]map[string]interface{}{vendor: {moduleName: math.Inf(1)}},
    86  			expectedHookRepo:      nil,
    87  			expectedModulesStages: nil,
    88  			expectedErr:           fmt.Errorf(`failed to marshal "%s.%s" module config: unsupported value: +Inf`, vendor, moduleName),
    89  		},
    90  	}
    91  
    92  	for name, test := range testCases {
    93  		t.Run(name, func(t *testing.T) {
    94  			builder := &builder{
    95  				builders: ModuleBuilders{
    96  					vendor: {
    97  						moduleName: func(cfg json.RawMessage, deps moduledeps.ModuleDeps) (interface{}, error) {
    98  							return test.givenModule, test.givenHookBuilderErr
    99  						},
   100  					},
   101  				},
   102  			}
   103  
   104  			repo, modulesStages, err := builder.Build(test.givenConfig, moduledeps.ModuleDeps{HTTPClient: http.DefaultClient})
   105  			assert.Equal(t, test.expectedErr, err)
   106  			assert.Equal(t, test.expectedModulesStages, modulesStages)
   107  			assert.Equal(t, test.expectedHookRepo, repo)
   108  		})
   109  	}
   110  }
   111  
   112  type module struct{}
   113  
   114  func (h module) HandleEntrypointHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.EntrypointPayload) (hookstage.HookResult[hookstage.EntrypointPayload], error) {
   115  	return hookstage.HookResult[hookstage.EntrypointPayload]{}, nil
   116  }
   117  
   118  func (h module) HandleAuctionResponseHook(_ context.Context, _ hookstage.ModuleInvocationContext, _ hookstage.AuctionResponsePayload) (hookstage.HookResult[hookstage.AuctionResponsePayload], error) {
   119  	return hookstage.HookResult[hookstage.AuctionResponsePayload]{}, nil
   120  }