1// Copyright (C) MongoDB, Inc. 2014-present.
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may
4// not use this file except in compliance with the License. You may obtain
5// a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
6
7package mongostat
8
9import (
10	"io/ioutil"
11	"strings"
12	"testing"
13	"time"
14
15	"github.com/mongodb/mongo-tools-common/testtype"
16	"github.com/mongodb/mongo-tools/mongostat/stat_consumer/line"
17	"github.com/mongodb/mongo-tools/mongostat/status"
18	. "github.com/smartystreets/goconvey/convey"
19	"gopkg.in/mgo.v2/bson"
20)
21
22func readBSONFile(file string, t *testing.T) (stat *status.ServerStatus) {
23	stat = &status.ServerStatus{}
24	ssBSON, err := ioutil.ReadFile(file)
25	if err == nil {
26		err = bson.Unmarshal(ssBSON, stat)
27	}
28	if err != nil {
29		t.Logf("Could not load new ServerStatus BSON: %s", err)
30		t.FailNow()
31	}
32	return
33}
34
35func TestStatLine(t *testing.T) {
36	testtype.SkipUnlessTestType(t, testtype.UnitTestType)
37
38	defaultHeaders := make([]string, len(line.CondHeaders))
39	for i, h := range line.CondHeaders {
40		defaultHeaders[i] = h.Key
41	}
42	defaultConfig := &status.ReaderConfig{
43		HumanReadable: true,
44	}
45
46	serverStatusOld := readBSONFile("test_data/server_status_old.bson", t)
47	serverStatusNew := readBSONFile("test_data/server_status_new.bson", t)
48	serverStatusNew.ShardCursorType = nil
49	serverStatusOld.ShardCursorType = nil
50
51	Convey("StatsLine should accurately calculate opcounter diffs", t, func() {
52		statsLine := line.NewStatLine(serverStatusOld, serverStatusNew, defaultHeaders, defaultConfig)
53		So(statsLine.Fields["insert"], ShouldEqual, "10")
54		So(statsLine.Fields["query"], ShouldEqual, "5")
55		So(statsLine.Fields["update"], ShouldEqual, "7")
56		So(statsLine.Fields["delete"], ShouldEqual, "2")
57		So(statsLine.Fields["getmore"], ShouldEqual, "3")
58		command := strings.Split(statsLine.Fields["command"], "|")[0]
59		So(command, ShouldEqual, "669")
60		So(statsLine.Fields["faults"], ShouldEqual, "5")
61
62		locked := strings.Split(statsLine.Fields["locked_db"], ":")
63		So(locked[0], ShouldEqual, "test")
64		So(locked[1], ShouldEqual, "50.0%")
65		qrw := strings.Split(statsLine.Fields["qrw"], "|")
66		So(qrw[0], ShouldEqual, "3")
67		So(qrw[1], ShouldEqual, "2")
68		arw := strings.Split(statsLine.Fields["arw"], "|")
69		So(arw[0], ShouldEqual, "4")
70		So(arw[1], ShouldEqual, "6")
71		So(statsLine.Fields["net_in"], ShouldEqual, "2.00k")
72		So(statsLine.Fields["net_out"], ShouldEqual, "3.00k")
73		So(statsLine.Fields["conn"], ShouldEqual, "5")
74	})
75
76	serverStatusNew.SampleTime, _ = time.Parse("2006 Jan 02 15:04:05", "2015 Nov 30 4:25:33")
77	Convey("StatsLine with non-default interval should calculate average diffs", t, func() {
78		statsLine := line.NewStatLine(serverStatusOld, serverStatusNew, defaultHeaders, defaultConfig)
79		// Opcounters and faults are averaged over sample period
80		So(statsLine.Fields["insert"], ShouldEqual, "3")
81		So(statsLine.Fields["query"], ShouldEqual, "1")
82		So(statsLine.Fields["update"], ShouldEqual, "2")
83		delete := strings.TrimPrefix(statsLine.Fields["delete"], "*")
84		So(delete, ShouldEqual, "0")
85		So(statsLine.Fields["getmore"], ShouldEqual, "1")
86		command := strings.Split(statsLine.Fields["command"], "|")[0]
87		So(command, ShouldEqual, "223")
88		So(statsLine.Fields["faults"], ShouldEqual, "1")
89
90		locked := strings.Split(statsLine.Fields["locked_db"], ":")
91		So(locked[0], ShouldEqual, "test")
92		So(locked[1], ShouldEqual, "50.0%")
93		qrw := strings.Split(statsLine.Fields["qrw"], "|")
94		So(qrw[0], ShouldEqual, "3")
95		So(qrw[1], ShouldEqual, "2")
96		arw := strings.Split(statsLine.Fields["arw"], "|")
97		So(arw[0], ShouldEqual, "4")
98		So(arw[1], ShouldEqual, "6")
99		So(statsLine.Fields["net_in"], ShouldEqual, "666b")
100		So(statsLine.Fields["net_out"], ShouldEqual, "1.00k")
101		So(statsLine.Fields["conn"], ShouldEqual, "5")
102	})
103}
104
105func TestIsMongos(t *testing.T) {
106	testtype.SkipUnlessTestType(t, testtype.UnitTestType)
107
108	runCheck := func(process string) bool {
109		return status.IsMongos(&status.ServerStatus{
110			Process: process,
111		})
112	}
113
114	Convey("should accept reasonable process names", t, func() {
115		So(runCheck("/mongos-prod.exe"), ShouldBeTrue)
116		So(runCheck("/mongos.exe"), ShouldBeTrue)
117		So(runCheck("mongos"), ShouldBeTrue)
118		So(runCheck("mongodb/bin/mongos"), ShouldBeTrue)
119		So(runCheck(`C:\data\mci\48de1dc1ec3c2be5dcd6a53739578de4\src\mongos.exe`), ShouldBeTrue)
120	})
121	Convey("should accept reasonable process names", t, func() {
122		So(runCheck("mongosx/mongod"), ShouldBeFalse)
123		So(runCheck("mongostat"), ShouldBeFalse)
124		So(runCheck("mongos_stuff/mongod"), ShouldBeFalse)
125		So(runCheck("mongos.stuff/mongod"), ShouldBeFalse)
126		So(runCheck("mongodb/bin/mongod"), ShouldBeFalse)
127	})
128}
129