/* Vuls - Vulnerability Scanner
Copyright (C) 2016  Future Architect, Inc. Japan.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package commands

import (
	"reflect"
	"testing"
	"time"

	"github.com/future-architect/vuls/models"
	"github.com/k0kubun/pp"
)

func TestIsCveInfoUpdated(t *testing.T) {
	f := "2006-01-02"
	old, _ := time.Parse(f, "2015-12-15")
	new, _ := time.Parse(f, "2015-12-16")

	type In struct {
		cveID string
		cur   models.ScanResult
		prev  models.ScanResult
	}
	var tests = []struct {
		in       In
		expected bool
	}{
		// NVD compare non-initialized times
		{
			in: In{
				cveID: "CVE-2017-0001",
				cur: models.ScanResult{
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2017-0001",
							CveContents: models.NewCveContents(
								models.CveContent{
									Type:         models.NVD,
									CveID:        "CVE-2017-0001",
									LastModified: time.Time{},
								},
							),
						},
					},
				},
				prev: models.ScanResult{
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2017-0001",
							CveContents: models.NewCveContents(
								models.CveContent{
									Type:         models.NVD,
									CveID:        "CVE-2017-0001",
									LastModified: time.Time{},
								},
							),
						},
					},
				},
			},
			expected: false,
		},
		// JVN not updated
		{
			in: In{
				cveID: "CVE-2017-0002",
				cur: models.ScanResult{
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2017-0002",
							CveContents: models.NewCveContents(
								models.CveContent{
									Type:         models.NVD,
									CveID:        "CVE-2017-0002",
									LastModified: old,
								},
							),
						},
					},
				},
				prev: models.ScanResult{
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2017-0002",
							CveContents: models.NewCveContents(
								models.CveContent{
									Type:         models.NVD,
									CveID:        "CVE-2017-0002",
									LastModified: old,
								},
							),
						},
					},
				},
			},
			expected: false,
		},
		// OVAL updated
		{
			in: In{
				cveID: "CVE-2017-0003",
				cur: models.ScanResult{
					Family: "ubuntu",
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2017-0003",
							CveContents: models.NewCveContents(
								models.CveContent{
									Type:         models.NVD,
									CveID:        "CVE-2017-0002",
									LastModified: new,
								},
							),
						},
					},
				},
				prev: models.ScanResult{
					Family: "ubuntu",
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2017-0003",
							CveContents: models.NewCveContents(
								models.CveContent{
									Type:         models.NVD,
									CveID:        "CVE-2017-0002",
									LastModified: old,
								},
							),
						},
					},
				},
			},
			expected: true,
		},
		// OVAL newly detected
		{
			in: In{
				cveID: "CVE-2017-0004",
				cur: models.ScanResult{
					Family: "redhat",
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2017-0004",
							CveContents: models.NewCveContents(
								models.CveContent{
									Type:         models.NVD,
									CveID:        "CVE-2017-0002",
									LastModified: old,
								},
							),
						},
					},
				},
				prev: models.ScanResult{
					Family:      "redhat",
					ScannedCves: []models.VulnInfo{},
				},
			},
			expected: true,
		},
	}
	for i, tt := range tests {
		actual := isCveInfoUpdated(tt.in.cveID, tt.in.prev, tt.in.cur)
		if actual != tt.expected {
			t.Errorf("[%d] actual: %t, expected: %t", i, actual, tt.expected)
		}
	}
}

func TestDiff(t *testing.T) {
	atCurrent, _ := time.Parse("2006-01-02", "2014-12-31")
	atPrevious, _ := time.Parse("2006-01-02", "2014-11-31")
	var tests = []struct {
		inCurrent  models.ScanResults
		inPrevious models.ScanResults
		out        models.ScanResult
	}{
		{
			inCurrent: models.ScanResults{
				{
					ScannedAt:  atCurrent,
					ServerName: "u16",
					Family:     "ubuntu",
					Release:    "16.04",
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2012-6702",
							Packages: models.Packages{
								{
									Name:       "libexpat1",
									Version:    "2.1.0-7",
									Release:    "",
									NewVersion: "2.1.0-7ubuntu0.16.04.2",
									NewRelease: "",
									Repository: "",
								},
							},
							DistroAdvisories: []models.DistroAdvisory{},
							CpeNames:         []string{},
						},
						{
							CveID: "CVE-2014-9761",
							Packages: models.Packages{
								{
									Name:       "libc-bin",
									Version:    "2.21-0ubuntu5",
									Release:    "",
									NewVersion: "2.23-0ubuntu5",
									NewRelease: "",
									Repository: "",
								},
							},
							DistroAdvisories: []models.DistroAdvisory{},
							CpeNames:         []string{},
						},
					},
					Packages: []models.Package{},
					Errors:   []string{},
					Optional: [][]interface{}{},
				},
			},
			inPrevious: models.ScanResults{
				{
					ScannedAt:  atPrevious,
					ServerName: "u16",
					Family:     "ubuntu",
					Release:    "16.04",
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2012-6702",
							Packages: models.Packages{
								{
									Name:       "libexpat1",
									Version:    "2.1.0-7",
									Release:    "",
									NewVersion: "2.1.0-7ubuntu0.16.04.2",
									NewRelease: "",
									Repository: "",
								},
							},
							DistroAdvisories: []models.DistroAdvisory{},
							CpeNames:         []string{},
						},
						{
							CveID: "CVE-2014-9761",
							Packages: models.Packages{
								{
									Name:       "libc-bin",
									Version:    "2.21-0ubuntu5",
									Release:    "",
									NewVersion: "2.23-0ubuntu5",
									NewRelease: "",
									Repository: "",
								},
							},
							DistroAdvisories: []models.DistroAdvisory{},
							CpeNames:         []string{},
						},
					},
					Packages: []models.Package{},
					Errors:   []string{},
					Optional: [][]interface{}{},
				},
			},
			out: models.ScanResult{
				ScannedAt:  atCurrent,
				ServerName: "u16",
				Family:     "ubuntu",
				Release:    "16.04",
				Packages:   []models.Package{},
				Errors:     []string{},
				Optional:   [][]interface{}{},
			},
		},
		{
			inCurrent: models.ScanResults{
				{
					ScannedAt:  atCurrent,
					ServerName: "u16",
					Family:     "ubuntu",
					Release:    "16.04",
					ScannedCves: []models.VulnInfo{
						{
							CveID: "CVE-2016-6662",
							Packages: models.Packages{
								{
									Name:       "mysql-libs",
									Version:    "5.1.73",
									Release:    "7.el6",
									NewVersion: "5.1.73",
									NewRelease: "8.el6_8",
									Repository: "",
								},
							},
							DistroAdvisories: []models.DistroAdvisory{},
							CpeNames:         []string{},
						},
					},
				},
			},
			inPrevious: models.ScanResults{
				{
					ScannedAt:   atPrevious,
					ServerName:  "u16",
					Family:      "ubuntu",
					Release:     "16.04",
					ScannedCves: []models.VulnInfo{},
				},
			},
			out: models.ScanResult{
				ScannedAt:  atCurrent,
				ServerName: "u16",
				Family:     "ubuntu",
				Release:    "16.04",
				ScannedCves: []models.VulnInfo{
					{
						CveID: "CVE-2016-6662",
						Packages: models.Packages{
							{
								Name:       "mysql-libs",
								Version:    "5.1.73",
								Release:    "7.el6",
								NewVersion: "5.1.73",
								NewRelease: "8.el6_8",
								Repository: "",
							},
						},
						DistroAdvisories: []models.DistroAdvisory{},
						CpeNames:         []string{},
					},
				},
				Packages: models.Packages{
					models.Package{
						Name:       "mysql-libs",
						Version:    "5.1.73",
						Release:    "7.el6",
						NewVersion: "5.1.73",
						NewRelease: "8.el6_8",
						Repository: "",
						Changelog: models.Changelog{
							Contents: "",
							Method:   "",
						},
					},
				},
			},
		},
	}

	for i, tt := range tests {
		diff, _ := diff(tt.inCurrent, tt.inPrevious)
		for _, actual := range diff {
			if !reflect.DeepEqual(actual.ScannedCves, tt.out.ScannedCves) {
				h := pp.Sprint(actual.ScannedCves)
				x := pp.Sprint(tt.out.ScannedCves)
				t.Errorf("[%d] actual: \n %s \n expected: \n %s", i, h, x)
			}

			for j := range tt.out.Packages {
				if !reflect.DeepEqual(tt.out.Packages[j], actual.Packages[j]) {
					h := pp.Sprint(tt.out.Packages[j])
					x := pp.Sprint(actual.Packages[j])
					t.Errorf("[%d] actual: \n %s \n expected: \n %s", i, x, h)
				}
			}
		}
	}
}
