package library

import (
	"testing"

	library2 "github.com/aquasecurity/trivy/pkg/detector/library"

	"golang.org/x/xerrors"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"

	"github.com/aquasecurity/fanal/extractor"
	ptypes "github.com/aquasecurity/go-dep-parser/pkg/types"
	"github.com/aquasecurity/trivy/pkg/types"
)

func TestScanner_Scan(t *testing.T) {
	type detectInput struct {
		filePath string
		libs     []ptypes.Library
	}
	type detectOutput struct {
		vulns []types.DetectedVulnerability
		err   error
	}
	type detect struct {
		input  detectInput
		output detectOutput
	}
	type args struct {
		files extractor.FileMap
	}
	tests := []struct {
		name    string
		args    args
		detect  []detect
		want    map[string][]types.DetectedVulnerability
		wantErr string
	}{
		{
			name: "happy",
			args: args{
				files: extractor.FileMap{
					"app/Pipfile.lock": []byte(`{
    "_meta": {
        "hash": {
            "sha256": "ad1805ab0e16cf08032c3fe45eeaa29b79e9c196650411977af14e31b12ff0cd"
        },
        "pipfile-spec": 6,
        "requires": {
            "python_version": "3.7"
        },
        "sources": [
            {
                "name": "pypi",
                "url": "https://pypi.python.org/simple",
                "verify_ssl": true
            }
        ]
    },
    "default": {
        "django": {
            "hashes": [
                "sha256:665457d4146bbd34ae9d2970fa3b37082d7b225b0671bfd24c337458f229db78",
                "sha256:bde46d4dbc410678e89bc95ea5d312dd6eb4c37d0fa0e19c9415cad94addf22f"
            ],
            "index": "pypi",
            "version": "==3.0.0"
        }
    }
}
`),
					"app/package-lock.json": []byte(`{
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "react": {
      "version": "16.8.6",
      "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
      "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
      "requires": {
        "loose-envify": "^1.1.0",
        "object-assign": "^4.1.1",
        "prop-types": "^15.6.2",
        "scheduler": "^0.13.6"
      }
    }
  }
}`),
				},
			},
			detect: []detect{
				{
					input: detectInput{
						filePath: "app/Pipfile.lock",
						libs: []ptypes.Library{
							{Name: "django", Version: "3.0.0"},
						},
					},
					output: detectOutput{
						vulns: []types.DetectedVulnerability{
							{VulnerabilityID: "CVE-2019-0001"},
						},
					},
				},
				{
					input: detectInput{
						filePath: "app/package-lock.json",
						libs: []ptypes.Library{
							{Name: "react", Version: "16.8.6"},
						},
					},
					output: detectOutput{
						vulns: []types.DetectedVulnerability{
							{VulnerabilityID: "CVE-2019-0002"},
							{VulnerabilityID: "CVE-2019-0003"},
						},
					},
				},
			},
			want: map[string][]types.DetectedVulnerability{
				"app/Pipfile.lock": {{VulnerabilityID: "CVE-2019-0001"}},
				"app/package-lock.json": {
					{VulnerabilityID: "CVE-2019-0002"},
					{VulnerabilityID: "CVE-2019-0003"},
				},
			},
		},
		{
			name: "broken lock file",
			args: args{
				files: extractor.FileMap{
					"app/Pipfile.lock": []byte(`{broken}`),
				},
			},
			wantErr: "failed to analyze libraries",
		},
		{
			name: "Detect returns an error",
			args: args{
				files: extractor.FileMap{
					"app/package-lock.json": []byte(`{
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "react": {
      "version": "16.8.6",
      "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
      "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
      "requires": {
        "loose-envify": "^1.1.0",
        "object-assign": "^4.1.1",
        "prop-types": "^15.6.2",
        "scheduler": "^0.13.6"
      }
    }
  }
}`),
				},
			},
			detect: []detect{
				{
					input: detectInput{
						filePath: "app/package-lock.json",
						libs: []ptypes.Library{
							{Name: "react", Version: "16.8.6"},
						},
					},
					output: detectOutput{err: xerrors.New("error")},
				},
			},
			wantErr: "failed library scan",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			mockDetector := new(library2.MockDetector)
			for _, d := range tt.detect {
				mockDetector.On("Detect", d.input.filePath, d.input.libs).Return(
					d.output.vulns, d.output.err)
			}

			s := Scanner{
				detector: mockDetector,
			}
			got, err := s.Scan(tt.args.files)
			if tt.wantErr != "" {
				require.NotNil(t, err, tt.name)
				assert.Contains(t, err.Error(), tt.wantErr, tt.name)
				return
			} else {
				assert.NoError(t, err, tt.name)
			}
			assert.Equal(t, tt.want, got, tt.name)
			mockDetector.AssertExpectations(t)
		})
	}
}
