agiledragon / gomonkey

gomonkey is a library to make monkey patching in unit tests easy
MIT License
2k stars 179 forks source link

panic: permission denied; While adding export GOARCH=amd64 debugger is not working. Apple M1 Pro #112

Open avinashsingh1152 opened 2 years ago

avinashsingh1152 commented 2 years ago
Screen Shot 2022-10-04 at 12 16 26 PM

While running a test case of monkey patching I am getting the below error (go test ./...) : --- FAIL: TestInquirySuite (0.01s) --- FAIL: TestInquirySuite/TestCore_CreateInquiryController (0.00s) suite.go:77: test panicked: permission denied goroutine 13 [running]: runtime/debug.Stack() /usr/local/go/src/runtime/debug/stack.go:24 +0x68 github.com/stretchr/testify/suite.failOnPanic(0x14000682ea0, {0x1037f8d00, 0x103e5b3a8}) /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/stretchr/testify/suite/suite.go:77 +0x38 github.com/stretchr/testify/suite.Run.func1.1() /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/stretchr/testify/suite/suite.go:161 +0x1f0 panic({0x1037f8d00, 0x103e5b3a8}) /usr/local/go/src/runtime/panic.go:838 +0x204 github.com/agiledragon/gomonkey/v2.modifyBinary(0x1034d5650, {0x140001dabd0, 0x18, 0xd695af4ba0afdf5f?}) /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/agiledragon/gomonkey/v2/modify_binary_darwin.go:9 +0xa4 github.com/agiledragon/gomonkey/v2.replace(0x1034d5650, 0x140004049f0?) /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/agiledragon/gomonkey/v2/patch.go:256 +0x8c github.com/agiledragon/gomonkey/v2.(*Patches).ApplyCore(0x14000404530, {0x1037cfe80?, 0x1400019cf38?, 0x0?}, {0x1037cfe80?, 0x1038d9d18?, 0x0?}) /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/agiledragon/gomonkey/v2/patch.go:218 +0xe8 github.com/agiledragon/gomonkey/v2.(*Patches).ApplyMethod(0x0?, {0x1038bd180, 0x10388c720}, {0x10351ae49, 0x26}, {0x1037cfe80?, 0x1038d9d18?}) /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/agiledragon/gomonkey/v2/patch.go:92 +0x1c4 github.com/agiledragon/gomonkey/v2.ApplyMethod(...) /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/agiledragon/gomonkey/v2/patch.go:29 quotation-service/inquiry/channel/delphius/core.(*InquiryTestSuite).TestCore_CreateInquiryController(0x140004b9440) /Users/avinash/Desktop/Backend/quotation-service/inquiry/channel/delphius/core/core_test.go:389 +0x1ec reflect.Value.call({0x1400019e9c0?, 0x1400019c560?, 0x13?}, {0x1034f12f3, 0x4}, {0x140000dde68, 0x1, 0x1?}) /usr/local/go/src/reflect/value.go:556 +0x5e4 reflect.Value.Call({0x1400019e9c0?, 0x1400019c560?, 0x140004b9440?}, {0x1400058b668, 0x1, 0x1}) /usr/local/go/src/reflect/value.go:339 +0x98 github.com/stretchr/testify/suite.Run.func1(0x14000682ea0) /Users/avinash/Desktop/Backend/quotation-service/vendor/github.com/stretchr/testify/suite/suite.go:175 +0x3e8 testing.tRunner(0x14000682ea0, 0x140004df170) /usr/local/go/src/testing/testing.go:1439 +0x110 created by testing.(*T).Run /usr/local/go/src/testing/testing.go:1486 +0x300

While Adding export GOARCH=amd64 Test cases are working fine but the debugger stopped working of GoLand I have tried two ways.

  1. Adding GOARCH=amd64 in the Environment Variable (RUN/DEBUG Configuration).
  2. Add GOARCH=amd64 to the path variable in the zsh file.
Marakai commented 1 year ago

Did you add -gcflags=all=-l to the Go tools argument in the configuration? That made it work for me.

Package path: github.com/agiledragon/gomonkey/v2/test

Working directory: .../test

Goland: 2022.2.3

on MacOS 12.6 on M1

Instead, the issue I'm having is that a can't actually debug: the tests run fine, but in Debug mode, it doesn't stop at my breakpoints.

I should clarify, though, that I only managed to get the tests in the gomonkey package to work. Despite copy-pasting the tests and adapting them to my own code in what I can only assume is correct - because the author says that they should suffice as documentation, which is sadly and blatantly false - I cannot get my own to work.

There seem to be limitations on using pointer receivers - only methods with value receivers are discovered as far as I could tell (by single-stepping into MethodByName and seeing what ut looks like).

Patching a private method simply is not happening. I've tried every variation of a closure I could think of. I tried to move the return patch into a separate func. My methods take multiple paramaters (as pointers) and return multiple values. All "examples" in the test for private methods use far too simple case - no passed-in parameters for the methods and single return values.

This package could be a god-send and the ability to do proper monkey patching would be fantastic, but after wasting hours on this, I give up: at this time, the package is not complete enough to support this in all situations.

If you have a different experience, by all means post it here, I'd love to see it!

avinashsingh1152 commented 1 year ago

@Marakai I tried to run the multiple test case after adding -gcflags=all=-l to the Go tools argument in the configuration, but it still doesn't stop at any breakpoint.

I think your package is github.com/agiledragon/gomonkey/v2/test whereas I have used github.com/agiledragon/gomonkey/v2. I have attached my code below for more clarity.

Package version: github.com/agiledragon/gomonkey/v2 v2.8.0

package core

import (
    "bytes"
    "context"
    "errors"
    "github.com/stretchr/testify/assert"
    "gopkg.in/guregu/null.v3"
    "net/http"
    "net/http/httptest"
    internalModel "quotation-service/internal/model/v1"
    mockQuotationRepo "quotation-service/internal/repository/postgres/v1/mocks"
    mockDataMapper "quotation-service/quotation/channel/b2b/dataMapper/mocks"
    quotationModel "quotation-service/quotation/channel/b2b/model"
    "strings"
)
func (quotationSuite *QuotationTestSuite) TestCore_GetQuotationDetailsController() {
    ctx := context.Background()

    type args struct {
        context     context.Context
        userId      int
        quotationId int
        httpWriter  http.ResponseWriter
    }

    testCases := []struct {
        name           string
        args           args
        beforeTest     func(ctx context.Context, mockQuotationRepo *mockQuotationRepo.IQuotationDBRepository, mockDataMapper *mockDataMapper.IQuotationDataMapper)
        wantStatusCode int
        wantBody       string
    }{
        {
            name: "error while getting Quotation details",
            args: args{
                context:     ctx,
                userId:      1,
                quotationId: 100,
                httpWriter:  httptest.NewRecorder(),
            },
            beforeTest: func(ctx context.Context, mockQuotationRepo *mockQuotationRepo.IQuotationDBRepository, mockDataMapper *mockDataMapper.IQuotationDataMapper) {
                mockQuotationRepo.On("GetQuotationDetails", ctx, 100).Return(internalModel.B2BQuotationDetails{}, errors.New("error while getting quotations details")).Once()
            },
            wantStatusCode: http.StatusInternalServerError,
            wantBody:       `{"success":false,"error_message":"Internal Server Error"}`,
        },
        {
            name: "QuotationDetails data not found",
            args: args{
                context:     ctx,
                userId:      1,
                quotationId: 101,
                httpWriter:  httptest.NewRecorder(),
            },
            beforeTest: func(ctx context.Context, mockQuotationRepo *mockQuotationRepo.IQuotationDBRepository, mockDataMapper *mockDataMapper.IQuotationDataMapper) {
                mockQuotationRepo.On("GetQuotationDetails", ctx, 101).Return(internalModel.B2BQuotationDetails{}, nil).Once()
            },
            wantStatusCode: http.StatusNotFound,
            wantBody:       `{"success":false,"error_message":"Not Found"}`,
        },
        {
            name: "Error while mappingQuotationDetails from QuotationAndProducts",
            args: args{
                context:     ctx,
                userId:      1,
                quotationId: 102,
                httpWriter:  httptest.NewRecorder(),
            },
            beforeTest: func(ctx context.Context, mockQuotationRepo *mockQuotationRepo.IQuotationDBRepository, mockDataMapper *mockDataMapper.IQuotationDataMapper) {
                quotationDetails := internalModel.B2BQuotationDetails{
                    Quotation: internalModel.Quotation{Id: 1},
                    Products:  nil,
                    Deal:      internalModel.QuotationDealMapping{},
                }
                mockQuotationRepo.On("GetQuotationDetails", ctx, 102).Return(quotationDetails, nil).Once()
                mockDataMapper.On("MapQuotationDetailsForHttpResponse", quotationDetails).Return(quotationModel.QuotationsDetailsResponseV3{}, errors.New("error while mappingQuotationDetails from QuotationAndProducts")).Once()
            },
            wantStatusCode: http.StatusInternalServerError,
            wantBody:       `{"success":false,"error_message":"Internal Server Error"}`,
        },
        {
            name: "successfully for get Quotation details",
            args: args{
                context:     ctx,
                userId:      1,
                quotationId: 103,
                httpWriter:  httptest.NewRecorder(),
            },
            beforeTest: func(ctx context.Context, mockQuotationRepo *mockQuotationRepo.IQuotationDBRepository, mockDataMapper *mockDataMapper.IQuotationDataMapper) {
                quotationDetails := internalModel.B2BQuotationDetails{
                    Quotation: internalModel.Quotation{Id: 103},
                    Products:  nil,
                    Deal:      internalModel.QuotationDealMapping{},
                }
                response := quotationModel.QuotationsDetailsResponseV3{
                    QuotationId:      103,
                    DealId:           2,
                    Name:             "test",
                    State:            "test",
                    Version:          0,
                    CustomerId:       2,
                    CustomerName:     "test customer",
                    CustomerSiteId:   23,
                    CustomerSiteName: "customer site name test",
                    CustomerRepName:  "rep name test",
                    PaymentType:      "cash",
                    SecurityType:     "letter_of_credit",
                    CreditDays:       "60_days",
                    Products:         nil,
                    ProductGroupName: "Concrete",
                    Info:             nil,
                    TotalSaleRate:    null.Float{},
                    TotalBaseRate:    null.Float{},
                    IsOpen:           false,
                    IsLut:            false,
                    IsAutoGenerated:  false,
                }
                mockQuotationRepo.On("GetQuotationDetails", ctx, 103).Return(quotationDetails, nil).Once()
                mockDataMapper.On("MapQuotationDetailsForHttpResponse", quotationDetails).Return(response, nil).Once()
            },
            wantStatusCode: http.StatusOK,
            wantBody:       `{"data":{"credit_days":"60_days","customer_alias_id":"CUST2","customer_name":"test customer","customer_rep_name":"rep name test","customer_site_alias_id":"SITE23","customer_site_name":"customer site name test","deal_alias_id":"DEAL2","info":null,"is_auto_generated":false,"is_lut":false,"is_open":false,"name":"test","payment_type":"cash","product_group_name":"Concrete","products":null,"quotation_alias_id":"QUOT103","security_type":"letter_of_credit","state":"test","total_base_rate":null,"total_sale_rate":null,"version":0},"success":true}`,
        },
    }

    for _, test := range testCases {
        if test.beforeTest != nil {
            test.beforeTest(ctx, quotationSuite.mockInternalRepo, quotationSuite.mockDataMapper)
        }

        quotationSuite.quotationCore.GetQuotationDetailsController(test.args.context, test.args.quotationId, test.args.userId, test.args.httpWriter)
        httpWriterRecorder := test.args.httpWriter.(*httptest.ResponseRecorder)
        resp := httpWriterRecorder.Result()

        assert.Equal(quotationSuite.T(), resp.StatusCode, test.wantStatusCode)

        resBodyBuffer := new(bytes.Buffer)
        resBodyBuffer.ReadFrom(resp.Body)
        respBody := strings.TrimSpace(resBodyBuffer.String())

        assert.Equal(quotationSuite.T(), respBody, test.wantBody)
    }
}