package interceptors import ( "errors" "testing" packagev1 "buf.build/gen/go/safedep/api/protocolbuffers/go/safedep/messages/package/v1" "github.com/safedep/pmg/analyzer" "github.com/safedep/pmg/packagemanager" "github.com/stretchr/testify/assert" ) func mockPackageVersion(name, version string) *packagev1.PackageVersion { return &packagev1.PackageVersion{ Package: &packagev1.Package{Name: name}, Version: version, } } func mockAnalysisResult() *analyzer.PackageVersionAnalysisResult { return &analyzer.PackageVersionAnalysisResult{ Action: analyzer.ActionConfirm, Summary: "Test suspicious package", } } func TestHandleConfirmationRequests(t *testing.T) { tests := []struct { name string confirmationResponse bool confirmationError error beforeInteractionErr error afterInteractionErr error useNilHooks bool expectedResponse bool verifyHooksCalled bool }{ { name: "user confirms installation", confirmationResponse: true, confirmationError: nil, useNilHooks: false, expectedResponse: true, verifyHooksCalled: true, }, { name: "user denies installation", confirmationResponse: false, confirmationError: nil, useNilHooks: false, expectedResponse: false, verifyHooksCalled: true, }, { name: "confirmation error returns false", confirmationResponse: false, confirmationError: errors.New("confirmation failed"), useNilHooks: false, expectedResponse: false, verifyHooksCalled: true, }, { name: "before interaction hook error is non-fatal", confirmationResponse: true, confirmationError: nil, beforeInteractionErr: errors.New("before hook failed"), useNilHooks: false, expectedResponse: true, verifyHooksCalled: true, }, { name: "after interaction hook error is non-fatal", confirmationResponse: true, confirmationError: nil, afterInteractionErr: errors.New("after hook failed"), useNilHooks: false, expectedResponse: true, verifyHooksCalled: true, }, { name: "nil hooks does not panic", confirmationResponse: true, confirmationError: nil, useNilHooks: true, expectedResponse: true, verifyHooksCalled: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { beforeCalled := false afterCalled := false var afterConfirmedParam bool interaction := packagemanager.PackageManagerInteraction{ GetConfirmationOnMalware: func(results []*analyzer.PackageVersionAnalysisResult) (bool, error) { assert.Len(t, results, 1) return tt.confirmationResponse, tt.confirmationError }, } var hooks *ConfirmationHook if !tt.useNilHooks { hooks = &ConfirmationHook{ BeforeInteraction: func(results []*analyzer.PackageVersionAnalysisResult) error { beforeCalled = true assert.Len(t, results, 1) return tt.beforeInteractionErr }, AfterInteraction: func(results []*analyzer.PackageVersionAnalysisResult, confirmed bool) error { afterCalled = true afterConfirmedParam = confirmed assert.Len(t, results, 1) return tt.afterInteractionErr }, } } confirmationChan := make(chan *ConfirmationRequest, 1) go HandleConfirmationRequests(confirmationChan, &interaction, hooks) pkgVersion := mockPackageVersion("test-package", "1.0.0") analysisResult := mockAnalysisResult() analysisResult.PackageVersion = pkgVersion req := NewConfirmationRequest(pkgVersion, analysisResult) confirmationChan <- req response := <-req.ResponseChan assert.Equal(t, tt.expectedResponse, response) if tt.verifyHooksCalled { assert.True(t, beforeCalled, "BeforeInteraction hook should be called") assert.True(t, afterCalled, "AfterInteraction hook should be called") if tt.confirmationError == nil { assert.Equal(t, tt.confirmationResponse, afterConfirmedParam, "AfterInteraction should receive correct confirmation status") } } else { assert.False(t, beforeCalled, "BeforeInteraction hook should not be called when hooks are nil") assert.False(t, afterCalled, "AfterInteraction hook should not be called when hooks are nil") } close(confirmationChan) }) } } func TestHandleConfirmationRequests_MultipleSequential(t *testing.T) { processedPackages := []string{} interaction := packagemanager.PackageManagerInteraction{ GetConfirmationOnMalware: func(results []*analyzer.PackageVersionAnalysisResult) (bool, error) { pkgName := results[0].PackageVersion.GetPackage().GetName() processedPackages = append(processedPackages, pkgName) return true, nil }, } confirmationChan := make(chan *ConfirmationRequest, 3) go HandleConfirmationRequests(confirmationChan, &interaction, nil) pkgVersion1 := mockPackageVersion("package-1", "1.0.0") analysisResult1 := mockAnalysisResult() analysisResult1.PackageVersion = pkgVersion1 req1 := NewConfirmationRequest(pkgVersion1, analysisResult1) pkgVersion2 := mockPackageVersion("package-2", "1.0.0") analysisResult2 := mockAnalysisResult() analysisResult2.PackageVersion = pkgVersion2 req2 := NewConfirmationRequest(pkgVersion2, analysisResult2) pkgVersion3 := mockPackageVersion("package-3", "1.0.0") analysisResult3 := mockAnalysisResult() analysisResult3.PackageVersion = pkgVersion3 req3 := NewConfirmationRequest(pkgVersion3, analysisResult3) confirmationChan <- req1 confirmationChan <- req2 confirmationChan <- req3 response1 := <-req1.ResponseChan response2 := <-req2.ResponseChan response3 := <-req3.ResponseChan assert.True(t, response1) assert.True(t, response2) assert.True(t, response3) assert.Equal(t, []string{"package-1", "package-2", "package-3"}, processedPackages) close(confirmationChan) } func TestNewConfirmationRequest(t *testing.T) { pkgVersion := mockPackageVersion("test-package", "1.0.0") analysisResult := mockAnalysisResult() analysisResult.PackageVersion = pkgVersion req := NewConfirmationRequest(pkgVersion, analysisResult) assert.NotNil(t, req) assert.Equal(t, pkgVersion, req.PackageVersion) assert.Equal(t, analysisResult, req.AnalysisResult) assert.NotNil(t, req.ResponseChan) assert.Equal(t, 1, cap(req.ResponseChan), "ResponseChan should have buffer size of 1") }