Merge pull request #17 from sadnub/checks-rework

Checks rework
This commit is contained in:
wh1te909 2020-05-30 11:46:09 -07:00 committed by GitHub
commit d80dd23742
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 717 additions and 243 deletions

View File

@ -3,6 +3,7 @@ from rest_framework import serializers
from .models import Policy
from autotasks.models import AutomatedTask
from autotasks.serializers import TaskSerializer
from checks.serializers import CheckSerializer
class PolicySerializer(serializers.ModelSerializer):
@ -29,3 +30,4 @@ class AutoTaskPolicySerializer(serializers.ModelSerializer):
"name",
"autotasks",
)

View File

@ -6,6 +6,7 @@ urlpatterns = [
path("policies/<int:pk>/related/", views.GetRelated.as_view()),
path("policies/overview/", views.OverviewPolicy.as_view()),
path("policies/<pk>/", views.GetUpdateDeletePolicy.as_view()),
path("<int:pk>/policychecks/", views.PolicyCheck.as_view()),
path("<int:pk>/policyautomatedtasks/", views.PolicyAutoTask.as_view()),
path("runwintask/<int:pk>/", views.RunPolicyTask.as_view()),
]

View File

@ -13,6 +13,7 @@ from .models import Policy
from agents.models import Agent
from scripts.models import Script
from clients.models import Client, Site
from checks.models import Check
from clients.serializers import (
ClientSerializer,
@ -27,6 +28,8 @@ from .serializers import (
AutoTaskPolicySerializer,
)
from checks.serializers import CheckSerializer
class GetAddPolicies(APIView):
def get(self, request):
@ -112,6 +115,11 @@ class RunPolicyTask(APIView):
# TODO: Run tasks for all Agents under policy
return Response("ok")
class PolicyCheck(APIView):
def get(self, request, pk):
checks = Check.objects.filter(policy__pk=pk)
return Response(CheckSerializer(checks, many=True).data)
class OverviewPolicy(APIView):
def get(self, request):

View File

@ -57,29 +57,3 @@ class CheckSerializer(serializers.ModelSerializer):
)
return val
""" class PolicyChecksSerializer(serializers.ModelSerializer):
diskchecks = DiskCheckSerializer(many=True, read_only=True)
cpuloadchecks = CpuLoadCheckSerializer(many=True, read_only=True)
memchecks = MemCheckSerializer(many=True, read_only=True)
pingchecks = PingCheckSerializer(many=True, read_only=True)
winservicechecks = WinServiceCheckSerializer(many=True, read_only=True)
scriptchecks = ScriptCheckSerializer(many=True, read_only=True)
eventlogchecks = EventLogCheckSerializer(many=True, read_only=True)
class Meta:
model = Policy
fields = (
"id",
"name",
"active",
"diskchecks",
"cpuloadchecks",
"memchecks",
"pingchecks",
"winservicechecks",
"scriptchecks",
"eventlogchecks",
)
"""

View File

@ -4,7 +4,7 @@ from .serializers import CheckSerializer # , PolicyChecksSerializer
class TestCheckViews(BaseTestCase):
def test_add_disk_check(self):
url = "/checks/addstandardcheck/"
url = "/checks/"
disk_data = {
"pk": self.agent.pk,
"check_type": "diskspace",
@ -22,7 +22,7 @@ class TestCheckViews(BaseTestCase):
self.check_not_authenticated("post", url)
def test_add_policy_disk_check(self):
url = "/checks/addstandardcheck/"
url = "/checks/"
policy_disk_data = {
"policy": self.policy.pk,
"check_type": "diskspace",
@ -32,7 +32,7 @@ class TestCheckViews(BaseTestCase):
}
resp = self.client.post(url, policy_disk_data, format="json")
self.assertEqual(resp.status_code, 200)
data = PolicyChecksSerializer(self.policy).data
data = CheckSerializer(self.policy).data
self.assertEqual(data["diskchecks"][0]["threshold"], 87)
self.assertEqual(data["diskchecks"][0]["failures"], 1)
self.assertEqual(data["diskchecks"][0]["disk"], "M:")

View File

@ -5,7 +5,6 @@ urlpatterns = [
path("checks/", views.GetAddCheck.as_view()),
path("<int:pk>/check/", views.GetUpdateDeleteCheck.as_view()),
path("<pk>/loadchecks/", views.load_checks),
path("<pk>/loadpolicychecks/", views.load_policy_checks),
path("checkrunner/", views.check_runner),
path("getalldisks/", views.get_disks_for_policies),
path("runchecks/<pk>/", views.run_checks),

View File

@ -157,12 +157,6 @@ def load_checks(request, pk):
return Response(CheckSerializer(checks, many=True).data)
@api_view()
def load_policy_checks(request, pk):
policy = get_object_or_404(Policy, pk=pk)
return Response(PolicyChecksSerializer(policy).data)
@api_view()
def get_disks_for_policies(request):
return Response(DiskCheck.all_disks())
return Response(Check.all_disks())

View File

@ -247,7 +247,7 @@ export default {
},
clearRow() {
this.$store.commit("automation/setSelectedPolicy", null);
this.$store.commit("automation/setPolicyChecks", {});
this.$store.commit("automation/setPolicyChecks", []);
this.$store.commit("automation/setPolicyAutomatedTasks", {});
},
refresh() {

View File

@ -1,5 +1,5 @@
<template>
<div v-if="Object.keys(checks).length === 0">No Policy Selected</div>
<div v-if="selectedPolicy === null">No Policy Selected</div>
<div class="row" v-else>
<div class="col-12">
<q-btn
@ -8,46 +8,47 @@
icon="fas fa-plus"
label="Add Check"
text-color="black"
ref="add"
>
<q-menu>
<q-list dense style="min-width: 200px">
<q-item clickable v-close-popup @click="showAddDialog('AddDiskSpaceCheck')">
<q-item clickable v-close-popup @click="showAddDialog('DiskSpaceCheck')">
<q-item-section side>
<q-icon size="xs" name="far fa-hdd" />
</q-item-section>
<q-item-section>Disk Space Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddDialog('AddPingCheck')">
<q-item clickable v-close-popup @click="showAddDialog('PingCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-network-wired" />
</q-item-section>
<q-item-section>Ping Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddDialog('AddCpuLoadCheck')">
<q-item clickable v-close-popup @click="showAddDialog('CpuLoadCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-microchip" />
</q-item-section>
<q-item-section>CPU Load Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddDialog('AddMemCheck')">
<q-item clickable v-close-popup @click="showAddDialog('MemCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-memory" />
</q-item-section>
<q-item-section>Memory Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddDialog('AddWinSvcCheck')">
<q-item clickable v-close-popup @click="showAddDialog('WinSvcCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-cogs" />
</q-item-section>
<q-item-section>Windows Service Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddDialog('AddScriptCheck')">
<q-item clickable v-close-popup @click="showAddDialog('ScriptCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-terminal" />
</q-item-section>
<q-item-section>Script Check</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="showAddDialog('AddEventLogCheck')">
<q-item clickable v-close-popup @click="showAddDialog('EventLogCheck')">
<q-item-section side>
<q-icon size="xs" name="fas fa-clipboard-list" />
</q-item-section>
@ -60,17 +61,18 @@
dense
flat
push
@click="onRefresh(checks.id)"
@click="onRefresh(selectedPolicy)"
icon="refresh"
ref="refresh"
/>
<template v-if="allChecks === undefined || allChecks.length === 0">
<template v-if="checks.length === 0">
<p>No Checks</p>
</template>
<template v-else>
<q-table
dense
class="tabs-tbl-sticky"
:data="allChecks"
:data="checks"
:columns="columns"
:row-key="row => row.id + row.check_type"
binary-state-sort
@ -97,14 +99,15 @@
</template>
<!-- body slots -->
<template v-slot:body="props">
<q-tr @contextmenu="editCheckPK = props.row.id" :props="props">
<q-tr :props="props">
<!-- context menu -->
<q-menu context-menu>
<q-list dense style="min-width: 200px">
<q-item
clickable
v-close-popup
@click="showEditDialog(props.row.check_type)"
@click="showEditDialog(props.row)"
id="context-edit"
>
<q-item-section side>
<q-icon name="edit" />
@ -114,7 +117,8 @@
<q-item
clickable
v-close-popup
@click="deleteCheck(props.row.id, props.row.check_type)"
@click="deleteCheck(props.row)"
id="context-delete"
>
<q-item-section side>
<q-icon name="delete" />
@ -124,7 +128,12 @@
<q-separator></q-separator>
<q-item clickable v-close-popup @click="showPolicyCheckStatusModal(props.row)">
<q-item
clickable
v-close-popup
@click="showPolicyCheckStatusModal(props.row)"
id="context-status"
>
<q-item-section side>
<q-icon name="sync" />
</q-item-section>
@ -158,6 +167,7 @@
<span
style="cursor:pointer;color:blue;text-decoration:underline"
@click="showPolicyCheckStatusModal(props.row)"
class="status-cell"
>
See Status
</span>
@ -178,12 +188,16 @@
</q-dialog>
<!-- add/edit modals -->
<q-dialog v-model="showDialog">
<q-dialog
v-model="showDialog"
@hide="hideDialog">
<component
v-if="dialogComponent !== null"
:is="dialogComponent"
@close="hideDialog"
:policypk="checks.id"
:editCheckPK="editCheckPK"
:policypk="selectedPolicy"
:checkpk="editCheckPK"
:mode="!!editCheckPK ? 'edit' : 'add'"
/>
</q-dialog>
</div>
@ -193,11 +207,25 @@
import { mapState, mapGetters } from "vuex";
import mixins, { notifySuccessConfig, notifyErrorConfig } from "@/mixins/mixins";
import PolicyStatus from "@/components/automation/modals/PolicyStatus";
import DiskSpaceCheck from "@/components/modals/checks/DiskSpaceCheck";
import PingCheck from "@/components/modals/checks/PingCheck";
import CpuLoadCheck from "@/components/modals/checks/CpuLoadCheck";
import MemCheck from "@/components/modals/checks/MemCheck";
import WinSvcCheck from "@/components/modals/checks/WinSvcCheck";
import ScriptCheck from "@/components/modals/checks/ScriptCheck";
import EventLogCheck from "@/components/modals/checks/EventLogCheck";
export default {
name: "PolicyChecksTab",
components: {
PolicyStatus
PolicyStatus,
DiskSpaceCheck,
PingCheck,
CpuLoadCheck,
MemCheck,
WinSvcCheck,
ScriptCheck,
EventLogCheck,
},
mixins: [mixins],
data() {
@ -242,62 +270,59 @@ export default {
this.$store.dispatch("automation/loadPolicyChecks", id);
},
showAddDialog(component) {
this.dialogComponent = () => import(`@/components/modals/checks/${component}`);
this.dialogComponent = component;
this.showDialog = true;
},
showEditDialog(category) {
let component = null;
switch (category) {
showEditDialog(check) {
switch (check.check_type) {
case "diskspace":
component = "EditDiskSpaceCheck";
this.dialogComponent = "DiskSpaceCheck";
break;
case "ping":
component = "EditPingCheck";
this.dialogComponent = "PingCheck";
break;
case "cpuload":
tcomponent = "EditCpuLoadCheck";
this.dialogComponent = "CpuLoadCheck";
break;
case "memory":
component = "EditMemCheck";
this.dialogComponent = "MemCheck";
break;
case "winsvc":
component = "EditWinSvcCheck";
this.dialogComponent = "WinSvcCheck";
break;
case "script":
component = "EditScriptCheck";
this.dialogComponent = "ScriptCheck";
break;
case "eventlog":
component = "EditEventLogCheck";
this.dialogComponent = "EventLogCheck";
break;
default:
return null;
}
this.dialogComponent = () => import(`@/components/modals/checks/${component}`);
this.editCheckPK = check.id
this.showDialog = true;
},
hideDialog(component) {
this.showDialog = false;
this.dialogComponent = null;
},
deleteCheck(pk, check_type) {
deleteCheck(check) {
this.$q
.dialog({
title: "Are you sure?",
message: `Delete ${check_type} check`,
title: `Delete ${check.check_type} check`,
ok: { label: "Delete", color: "negative" },
cancel: true,
persistent: true
})
.onOk(() => {
const data = { pk: pk, checktype: check_type };
const data = { pk: check.id, checktype: check.check_type };
this.$store
.dispatch("deleteCheck", data)
.then(r => {
this.$store.dispatch("automation/loadPolicyChecks", this.checks.id);
this.$q.notify(notifySuccessConfig);
this.$store.dispatch("automation/loadPolicyChecks", check.id);
this.$q.notify(notifySuccessConfig("Check Deleted!"));
})
.catch(e => this.$q.notify(notifyErrorConfig));
.catch(e => this.$q.notify(notifyErrorConfig("An Error Occurred while deleting")));
});
},
showPolicyCheckStatusModal(check) {
@ -326,12 +351,10 @@ export default {
}
},
computed: {
...mapState({
checks: state => state.automation.checks
}),
...mapGetters({
allChecks: "automation/allChecks"
})
checks: "automation/checks",
selectedPolicy: "automation/selectedPolicyPk"
})
}
};
</script>

View File

@ -4,22 +4,14 @@ export default {
namespaced: true,
state: {
selectedPolicy: null,
checks: {},
checks: [],
automatedTasks: {},
policies: [],
},
getters: {
allChecks(state) {
return [
...state.checks.diskchecks,
...state.checks.cpuloadchecks,
...state.checks.memchecks,
...state.checks.scriptchecks,
...state.checks.winservicechecks,
...state.checks.pingchecks,
...state.checks.eventlogchecks
];
checks(state) {
return state.checks;
},
selectedPolicyPk(state) {
return state.selectedPolicy;
@ -56,7 +48,7 @@ export default {
});
},
loadPolicyChecks(context, pk) {
axios.get(`/checks/${pk}/loadpolicychecks/`).then(r => {
axios.get(`/automation/${pk}/policychecks/`).then(r => {
context.commit("setPolicyChecks", r.data);
});
},

View File

@ -153,6 +153,12 @@ export const store = new Vuex.Store({
context.commit("setChecks", r.data);
});
},
loadDefaultServices(context) {
return axios.get("/services/getdefaultservices/");
},
loadAgentServices(context, agentpk) {
return axios.get(`/services/${agentpk}/services/`);
},
editCheckAlertAction(context, data) {
return axios.patch("/checks/checkalert/", data);
},
@ -196,7 +202,7 @@ export const store = new Vuex.Store({
child_single.push({
label: sites_arr[i].split("|")[0],
id: sites_arr[i].split("|")[1],
raw: sites_arr[i],
raw: `Site|${sites_arr[i]}`,
header: "generic",
icon: "apartment",
iconColor: sites_arr[i].split("|")[2]
@ -205,7 +211,7 @@ export const store = new Vuex.Store({
output.push({
label: prop.split("|")[0],
id: prop.split("|")[1],
raw: prop,
raw: `Client|${prop}`,
header: "root",
icon: "business",
iconColor: prop.split("|")[2],

View File

@ -6,8 +6,18 @@ import "@/quasar.js"
const localVue = createLocalVue();
localVue.use(Vuex);
describe("AutomationManager.vue", () => {
const bodyWrapper = createWrapper(document.body);
// This is needed to remove q-dialogs since body doesn't rerender
afterEach(() => {
const dialogs = document.querySelectorAll(".q-dialog");
const menus = document.querySelectorAll(".q-menu");
dialogs.forEach(x => x.remove());
menus.forEach(x => x.remove());
});
describe("AutomationManager.vue", () => {
const policiesData = [
{
id: 1,
@ -29,7 +39,6 @@ describe("AutomationManager.vue", () => {
}
];
const bodyWrapper = createWrapper(document.body);
let wrapper;
let state, mutations, actions, store;
@ -83,23 +92,12 @@ describe("AutomationManager.vue", () => {
});
// Runs after every test
// This is needed to remove q-dialogs since body doesn't rerender
afterEach(() => {
const dialogs = document.querySelectorAll(".q-dialog");
const menus = document.querySelectorAll(".q-menu");
dialogs.forEach(x => x.remove());
menus.forEach(x => x.remove());
});
// The Tests
it("calls vuex loadPolicies action on mount", () => {
expect(actions.loadPolicies).toHaveBeenCalled();
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), {});
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), []);
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});
@ -208,7 +206,7 @@ describe("AutomationManager.vue", () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), {});
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), []);
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});
@ -220,7 +218,7 @@ describe("AutomationManager.vue", () => {
button.trigger("click");
expect(actions.loadPolicies).toHaveBeenCalled();
expect(mutations.setSelectedPolicy).toHaveBeenCalledWith(expect.anything(), null);
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), {});
expect(mutations.setPolicyChecks).toHaveBeenCalledWith(expect.anything(), []);
expect(mutations.setPolicyAutomatedTasks).toHaveBeenCalledWith(expect.anything(), {});
});

View File

@ -0,0 +1,102 @@
const common = {
email_alert: false,
failure_count: 0,
failures: 5,
history: [],
last_run: null,
more_info: null,
status: "pending",
task_on_failure: null,
text_alert: false,
agent: null,
policy: 1
};
const diskcheck = {
id: 1,
check_type: "diskspace",
disk: "C:",
threshold: 25,
readable_desc: "Disk space check: Drive C",
...common
};
const cpuloadcheck = {
id: 2,
check_type: "cpuload",
cpuload: 85,
readable_desc: "CPU Load check: > 85%",
...common
};
const memcheck = {
id: 3,
check_type: "memory",
threshold: 75,
readable_desc: "Memory checks: > 85%",
...common
};
const scriptcheck = {
id: 4,
check_type: "script",
execution_time: "0.0000",
retcode: 0,
script: {
description: "Test",
filename: "local_admin_group.bat",
filepath: "salt://scripts//userdefined//local_admin_group.bat",
id: 1,
name: "Test Script",
shell: "cmd"
},
stderr: null,
stdout: null,
timeout: 120,
readable_desc: "Script check: Test Script",
...common
};
const winservicecheck = {
id: 5,
check_type: "winsvc",
pass_if_start_pending: false,
restart_if_stopped: false,
svc_display_name: "Agent Activation Runtime_1232as",
svc_name: "AarSvc_1232as",
readable_desc: "Service check: Agent Activation Runtime_1232as",
...common
};
const pingcheck = {
id: 6,
name: "fghfgh",
check_type: "ping",
ip: "10.10.10.10",
readable_desc: "Ping Check: Test Ping Check",
...common
};
const eventlogcheck = {
id: 7,
desc: "asasasa",
check_type: "eventlog",
log_name: "Application",
event_id: 1456,
event_type: "ERROR",
fail_when: "contains",
search_last_days: 1,
readable_desc: "Event log check: asdsasa",
...common,
};
export {
diskcheck,
cpuloadcheck,
memcheck,
scriptcheck,
winservicecheck,
pingcheck,
eventlogcheck
}

View File

@ -7,80 +7,126 @@ import "@/quasar.js";
const localVue = createLocalVue();
localVue.use(Vuex);
describe("PolicyForm.vue", () => {
/*** TEST DATA ***/
const clients = [
{
id: 1,
client: "Test Client"
},
{
id: 2,
client: "Test Client2"
},
{
id: 3,
client: "Test Client3"
}
];
const sites = [
{
id: 1,
site: "Site Name",
client_name: "Test Client"
},
{
id: 2,
site: "Site Name2",
client_name: "Test Client2"
}
];
const clients = [
{
id: 1,
client: "Test Client"
},
{
id: 2,
client: "Test Client2"
},
{
id: 3,
client: "Test Client3"
}
];
const sites = [
{
id: 1,
site: "Site Name",
client_name: "Test Client"
},
{
id: 2,
site: "Site Name2",
client_name: "Test Client2"
}
];
const policy = {
id: 1,
name: "Test Policy",
desc: "Test Desc",
active: true,
clients: [],
sites: []
};
const policy = {
id: 1,
name: "Test Policy",
desc: "Test Desc",
active: true,
clients: [],
sites: []
let actions, rootActions, store;
beforeEach(() => {
rootActions = {
loadClients: jest.fn(() => new Promise(res => res({ data: clients }))),
loadSites: jest.fn(() => new Promise(res => res({ data: sites }))),
};
let actions, rootActions, store;
actions = {
loadPolicy: jest.fn(() => new Promise(res => res({ data: policy }))),
addPolicy: jest.fn(() => new Promise(res => res())),
editPolicy: jest.fn(() => new Promise(res => res())),
};
// Runs before every test
store = new Vuex.Store({
actions: rootActions,
modules: {
automation: {
namespaced: true,
actions,
}
}
});
})
/*** TEST SUITES ***/
describe("PolicyForm.vue when editting", () => {
let wrapper;
beforeEach(() => {
rootActions = {
loadClients: jest.fn(() => new Promise(res => res({ data: clients }))),
loadSites: jest.fn(() => new Promise(res => res({ data: sites }))),
};
actions = {
loadPolicy: jest.fn(() => new Promise(res => res({ data: policy }))),
addPolicy: jest.fn(() => new Promise(res => res())),
editPolicy: jest.fn(() => new Promise(res => res())),
};
store = new Vuex.Store({
actions: rootActions,
modules: {
automation: {
namespaced: true,
actions,
}
wrapper = mount(PolicyForm, {
localVue,
store,
propsData: {
pk: 1
}
});
});
// The Tests
it("calls vuex actions on mount", () => {
/*** TESTS ***/
it("calls vuex actions on mount with pk prop set", () => {
const wrapper = mount(PolicyForm, {
expect(rootActions.loadClients).toHaveBeenCalled();
expect(rootActions.loadSites).toHaveBeenCalled();
expect(actions.loadPolicy).toHaveBeenCalledWith(expect.anything(), 1);
});
it("sends the correct edit action on submit", async () => {
await flushPromises();
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
expect(actions.addPolicy).not.toHaveBeenCalled();
expect(actions.editPolicy).toHaveBeenCalledWith(expect.anything(), policy);
});
it("Renders correct title on edit", () => {
expect(wrapper.vm.title).toBe("Edit Policy");
});
});
describe("PolicyForm.vue when adding", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(PolicyForm, {
localVue,
store
});
});
/*** TESTS ***/
it("calls vuex actions on mount", () => {
expect(rootActions.loadClients).toHaveBeenCalled();
expect(rootActions.loadSites).toHaveBeenCalled();
@ -89,29 +135,8 @@ describe("PolicyForm.vue", () => {
});
it("calls vuex actions on mount with pk prop set", () => {
mount(PolicyForm, {
localVue,
store,
propsData: {
pk: 1
}
});
expect(rootActions.loadClients).toHaveBeenCalled();
expect(rootActions.loadSites).toHaveBeenCalled();
expect(actions.loadPolicy).toHaveBeenCalledWith(expect.anything(), 1);
});
it("Sets client and site options correctly", async () => {
const wrapper = mount(PolicyForm, {
localVue,
store
});
// Make sure the promises are resolved
await flushPromises();
@ -119,13 +144,8 @@ describe("PolicyForm.vue", () => {
expect(wrapper.vm.siteOptions).toHaveLength(2);
});
it("sends the correct add action on submit", async () => {
const wrapper = mount(PolicyForm, {
localVue,
store
});
it("sends the correct add action on submit", async () => {
wrapper.setData({name: "Test Policy"});
const form = wrapper.findComponent({ ref: "form" });
@ -137,33 +157,8 @@ describe("PolicyForm.vue", () => {
});
it("sends the correct edit action on submit", async () => {
const wrapper = mount(PolicyForm, {
localVue,
store,
propsData: {
pk: 1
}
});
await flushPromises();
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
expect(actions.addPolicy).not.toHaveBeenCalled();
expect(actions.editPolicy).toHaveBeenCalledWith(expect.anything(), policy);
});
it("sends error when name isn't set on submit", async () => {
const wrapper = mount(PolicyForm, {
localVue,
store
});
const form = wrapper.findComponent({ ref: "form" });
form.vm.$emit("submit");
await wrapper.vm.$nextTick();
@ -172,28 +167,9 @@ describe("PolicyForm.vue", () => {
expect(actions.editPolicy).not.toHaveBeenCalled();
});
it("Renders correct title on edit", () => {
const wrapper = mount(PolicyForm, {
localVue,
store,
propsData: {
pk: 1
}
});
expect(wrapper.vm.title).toBe("Edit Policy");
});
it("Renders correct title on add", () => {
const wrapper = mount(PolicyForm, {
localVue,
store,
});
expect(wrapper.vm.title).toBe("Add Policy");
});
});
});

View File

@ -0,0 +1,391 @@
import { mount, shallowMount, createLocalVue, createWrapper } from "@vue/test-utils";
import PolicyChecksTab from "@/components/automation/PolicyChecksTab";
import Vuex from "vuex";
import "@/quasar.js";
// Import Test Data
import {
diskcheck,
cpuloadcheck,
memcheck,
scriptcheck,
winservicecheck,
pingcheck,
eventlogcheck
} from "./checksData.js";
const localVue = createLocalVue();
localVue.use(Vuex);
const bodyWrapper = createWrapper(document.body);
// This is needed to remove q-dialogs since body doesn't rerender
afterEach(() => {
const dialogs = document.querySelectorAll(".q-dialog");
const menus = document.querySelectorAll(".q-menu");
dialogs.forEach(x => x.remove());
menus.forEach(x => x.remove());
});
/*** TEST SUITES ***/
describe("PolicyChecksTab.vue with no policy selected", () => {
let wrapper, state, getters, store;
// Runs before every test
beforeEach(() => {
// Create the Test store
// Create the Test store
state = {
checks: [],
selectedPolicy: null
};
getters = {
checks(state) {
return state.checks
},
selectedPolicyPk(state) {
return state.selectedPolicy
}
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
getters
}
}
});
wrapper = shallowMount(PolicyChecksTab, {
store,
localVue
});
});
/*** TESTS ***/
it("renders text when policy is selected with no checks", () => {
expect(wrapper.html()).toContain("No Policy Selected");
});
});
describe("PolicyChecksTab.vue with policy selected and no checks", () => {
// Used for the add check test loop
const addChecksMenu = [
{ name: "DiskSpaceCheck", index: 0 },
{ name: "PingCheck", index: 1},
{ name: "CpuLoadCheck", index: 2},
{ name: "MemCheck", index: 3},
{ name: "WinSvcCheck", index: 4},
{ name: "ScriptCheck", index: 5},
{ name: "EventLogCheck", index: 6}
];
let wrapper, store, state, actions, getters;
// Runs before every test
beforeEach(() => {
// Create the Test store
state = {
checks: [],
selectedPolicy: 1
};
getters = {
checks(state) {
return state.checks
},
selectedPolicyPk(state) {
return state.selectedPolicy
}
};
actions = {
loadPolicyChecks: jest.fn()
};
store = new Vuex.Store({
modules: {
automation: {
namespaced: true,
state,
getters,
actions
}
}
});
// Mount all sub components except the ones specified
wrapper = mount(PolicyChecksTab, {
store,
localVue,
stubs: [
"DiskSpaceCheck",
"PingCheck",
"CpuLoadCheck",
"MemCheck",
"WinSvcCheck",
"ScriptCheck",
"EventLogCheck"
]
});
});
it("renders text when policy is selected with no checks", () => {
expect(wrapper.html()).toContain("No Checks");
});
it("sends vuex actions on refresh button click", () => {
wrapper.findComponent({ ref: "refresh" }).trigger("click");
expect(actions.loadPolicyChecks).toHaveBeenCalledWith(expect.anything(), 1);
});
// Create a test for each Add modal
addChecksMenu.forEach(item => {
it(`opens ${item.name} Dialog`, async () => {
const addButton = wrapper.findComponent({ ref: "add" });
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
await addButton.trigger("click");
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
// Selects correct menu item
await bodyWrapper.findAll(".q-item").wrappers[item.index].trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.showDialog).toBe(true);
expect(wrapper.vm.dialogComponent).toBe(item.name);
});
});
});
describe("PolicyChecksTab.vue with policy selected and checks", () => {
// Used for the edit check test loop
const editChecksModals = [
{name: "DiskSpaceCheck", index: 0, id: 1},
{name: "CpuLoadCheck", index: 1, id: 2},
{name: "MemCheck", index: 2, id: 3},
{name: "ScriptCheck", index: 3, id: 4},
{name: "WinSvcCheck", index: 4, id: 5},
{name: "PingCheck", index: 5, id: 6},
{name: "EventLogCheck", index: 6, id: 7}
];
let state, rootActions, actions, getters, store, wrapper;
// Runs before every test
beforeEach(() => {
// Create the Test store
// Create the Test store
state = {
checks: [
diskcheck,
cpuloadcheck,
memcheck,
scriptcheck,
winservicecheck,
pingcheck,
eventlogcheck
],
selectedPolicy: 1
};
getters = {
checks(state) {
return state.checks
},
selectedPolicyPk(state) {
return state.selectedPolicy
}
};
actions = {
loadPolicyChecks: jest.fn()
};
rootActions = {
editCheckAlertAction: jest.fn(),
deleteCheck: jest.fn()
};
store = new Vuex.Store({
actions: rootActions,
modules: {
automation: {
namespaced: true,
state,
getters,
actions
}
}
});
// Mount all sub components except the ones specified
wrapper = mount(PolicyChecksTab, {
store,
localVue,
stubs: [
"DiskSpaceCheck",
"PingCheck",
"CpuLoadCheck",
"MemCheck",
"WinSvcCheck",
"ScriptCheck",
"EventLogCheck",
"PolicyStatus"
]
});
});
/*** TESTS ***/
it("renders the correct number of rows based on checks", () => {
const rows = wrapper.findAll(".q-table > tbody > .q-tr").wrappers;
expect(rows).toHaveLength(7);
});
// Create a test for each Edit modal
editChecksModals.forEach(item => {
it(`show ${item.name} Dialog`, async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[item.index];
await row.trigger("contextmenu");
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-edit").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.showDialog).toBe(true);
expect(wrapper.vm.dialogComponent).toBe(item.name);
expect(wrapper.vm.editCheckPK).toBe(item.id);
});
});
it("shows policy status modal on cell click", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
const row = wrapper.findAll(".status-cell").wrappers[0];
await row.trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.statusCheck).toEqual(diskcheck);
});
it("shows policy status modal on context menu item click", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[0];
await row.trigger("contextmenu");
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-status").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
expect(wrapper.vm.statusCheck).toEqual(diskcheck);
});
it("renders correct description for checks", () => {
expect(wrapper.find(".q-table").html()).toContain("Disk Space Drive C: &gt; 25%");
expect(wrapper.find(".q-table").html()).toContain("Avg CPU Load &gt; 85%");
expect(wrapper.find(".q-table").html()).toContain("Avg memory usage &gt; 75%");
expect(wrapper.find(".q-table").html()).toContain("Script check: Test Script");
expect(wrapper.find(".q-table").html()).toContain("Service Check - Agent Activation Runtime_1232as");
expect(wrapper.find(".q-table").html()).toContain("Ping fghfgh (10.10.10.10)");
expect(wrapper.find(".q-table").html()).toContain("Event Log Check - asasasa");
});
it("deletes check", async () => {
expect(bodyWrapper.find(".q-dialog").exists()).toBe(false);
expect(bodyWrapper.find(".q-menu").exists()).toBe(false);
const row = wrapper.findAll(".q-table > tbody > .q-tr").wrappers[0];
await row.trigger("contextmenu");
expect(bodyWrapper.find(".q-menu").exists()).toBe(true);
await bodyWrapper.find("#context-delete").trigger("click");
expect(bodyWrapper.find(".q-dialog").exists()).toBe(true);
//Get OK button on confirmation dialog and click it
await bodyWrapper.findAll(".q-btn").wrappers[1].trigger("click");
expect(rootActions.deleteCheck).toHaveBeenCalledWith(expect.anything(), {pk: 1, checktype:"diskspace"});
expect(actions.loadPolicyChecks).toHaveBeenCalled();
});
it("enables and disables text alerts for check", async () => {
//Get first checkbox in first row
const row = wrapper.findAll(".q-checkbox").wrappers[0];
//Enable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlertAction).toHaveBeenCalledWith(expect.anything(), {
alertType: "text",
checkid: 1,
category: "diskspace",
action: "enabled"
});
//Disable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlertAction).toHaveBeenCalledWith(expect.anything(), {
alertType: "text",
checkid: 1,
category: "diskspace",
action: "disabled"
});
});
it("enables and disables email alerts for check", async () => {
//Get second checkbox in first row
const row = wrapper.findAll(".q-checkbox").wrappers[1];
//Enable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlertAction).toHaveBeenCalledWith(expect.anything(), {
alertType: "email",
checkid: 1,
category: "diskspace",
action: "enabled"
});
//Disable Text Alert
await row.trigger("click");
expect(rootActions.editCheckAlertAction).toHaveBeenCalledWith(expect.anything(), {
alertType: "email",
checkid: 1,
category: "diskspace",
action: "disabled"
});
});
/* TODO: test @close and @hide events */
});

View File

@ -0,0 +1,8 @@
import { mount, shallowMount, createLocalVue, createWrapper } from "@vue/test-utils";
import PolicyAutomatedTasksTab from "@/components/automation/PolicyAutomatedTasksTab";
import Vuex from "vuex";
import "@/quasar.js";
describe.skip("PolicyAutomatedTasks.vue", () => {
// TODO after checks rework
});