diff --git a/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go b/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go index 47cd93c3..de532fa2 100644 --- a/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go +++ b/cmd/exchange_wrapper_standards/exchange_wrapper_standards_test.go @@ -178,6 +178,8 @@ var validWrapperParams = []reflect.Type{ latestRateRequest, } +type testCtxKey string + func executeExchangeWrapperTests(ctx context.Context, t *testing.T, exch exchange.IBotExchange, assetParams []assetPair) { t.Helper() iExchange := reflect.TypeOf(&exch).Elem() @@ -195,7 +197,7 @@ func executeExchangeWrapperTests(ctx context.Context, t *testing.T, exch exchang if slices.ContainsFunc(validWrapperParams, func(t reflect.Type) bool { return input.AssignableTo(t) }) { - assetLen = len(assetParams) - 1 + assetLen = len(assetParams) break } } @@ -207,24 +209,25 @@ func executeExchangeWrapperTests(ctx context.Context, t *testing.T, exch exchang e = time.Now() s = e.Add(-time.Minute * 3) } - for y := 0; y <= assetLen; y++ { - inputs := make([]reflect.Value, method.Type().NumIn()) - argGenerator := &MethodArgumentGenerator{ - Exchange: exch, - AssetParams: assetParams[y], - MethodName: methodName, - Start: s, - End: e, - } - for z := range method.Type().NumIn() { - argGenerator.MethodInputType = method.Type().In(z) - generatedArg := generateMethodArg(ctx, t, argGenerator) - inputs[z] = *generatedArg - } - assetY := assetParams[y].Asset.String() - pairY := assetParams[y].Pair.String() - t.Run(methodName+"-"+assetY+"-"+pairY, func(t *testing.T) { + for y := range assetLen { + ap := assetParams[y] + t.Run(methodName+"-"+ap.Asset.String()+"-"+ap.Pair.String(), func(t *testing.T) { t.Parallel() + // Create a new context for each test run to avoid race conditions + ctx := context.WithValue(ctx, testCtxKey("test"), t.Name()) //nolint:govet // Intentional shadow + inputs := make([]reflect.Value, method.Type().NumIn()) + argGenerator := &MethodArgumentGenerator{ + Exchange: exch, + AssetParams: ap, + MethodName: methodName, + Start: s, + End: e, + } + for z := range method.Type().NumIn() { + argGenerator.MethodInputType = method.Type().In(z) + generatedArg := generateMethodArg(ctx, t, argGenerator) + inputs[z] = *generatedArg + } CallExchangeMethod(t, method, inputs, methodName, exch) }) } @@ -245,12 +248,16 @@ func CallExchangeMethod(t *testing.T, methodToCall reflect.Value, methodValues [ if isUnacceptableError(t, err) != nil { literalInputs := make([]any, len(methodValues)) for j := range methodValues { - if methodValues[j].Kind() == reflect.Ptr { + switch { + case methodValues[j].Type().Implements(contextParam): + // Errorf will use reflection on ctx and cause a race, so we need to replace it + literalInputs[j] = "" + case methodValues[j].Kind() == reflect.Ptr: // dereference pointers just to add a bit more clarity literalInputs[j] = methodValues[j].Elem().Interface() - continue + default: + literalInputs[j] = methodValues[j].Interface() } - literalInputs[j] = methodValues[j].Interface() } t.Errorf("%v Func '%v' Error: '%v'. Inputs: %v.", exch.GetName(), methodName, err, literalInputs) } @@ -340,8 +347,7 @@ func generateMethodArg(ctx context.Context, t *testing.T, argGenerator *MethodAr OneTimePassword: "test", }) case argGenerator.MethodInputType.Implements(contextParam): - // Need to deploy a context.Context value as nil value is not - // checked throughout codebase. + // Need to deploy a context.Context value as nil value is not checked throughout codebase input = reflect.ValueOf(ctx) case argGenerator.MethodInputType.AssignableTo(feeBuilderParam): input = reflect.ValueOf(&exchange.FeeBuilder{