feat: add comprehensive tests and samples for host and user rights endpoints
- Added GraphQL sample queries and mutations for host and user rights endpoints in the 'docs' directory. - Implemented unit tests for all remaining GraphQL endpoints, including hosts, devices, host groups, locations, and user permissions. - Created dedicated integration tests for host and user rights workflows, utilizing the new sample files. - Fixed a bug in 'HostImporter.getHostGroupHierarchyNames' to correctly process and sort nested host group hierarchies. - Refined Zabbix API mocking in tests to improve reliability and support path-based routing. - Verified all 38 tests across 11 suites pass successfully.
This commit is contained in:
parent
a3ed4886a3
commit
fdfd5f1e0e
14 changed files with 729 additions and 6 deletions
21
docs/sample_all_devices_query.graphql
Normal file
21
docs/sample_all_devices_query.graphql
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
### Query
|
||||||
|
Use this query to list all devices (hosts with device type and state).
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query AllDevices($name_pattern: String, $with_items: Boolean) {
|
||||||
|
allDevices(name_pattern: $name_pattern, with_items: $with_items) {
|
||||||
|
hostid
|
||||||
|
host
|
||||||
|
name
|
||||||
|
deviceType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name_pattern": "%",
|
||||||
|
"with_items": true
|
||||||
|
}
|
||||||
|
```
|
||||||
18
docs/sample_all_host_groups_query.graphql
Normal file
18
docs/sample_all_host_groups_query.graphql
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
### Query
|
||||||
|
# Use this query to list all host groups.
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query AllHostGroups($search_name: String) {
|
||||||
|
allHostGroups(search_name: $search_name) {
|
||||||
|
groupid
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"search_name": "Baustellen-Devices/*"
|
||||||
|
}
|
||||||
|
```
|
||||||
25
docs/sample_all_hosts_query.graphql
Normal file
25
docs/sample_all_hosts_query.graphql
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
### Query
|
||||||
|
Use this query to list all hosts, filtered by name pattern and/or device type.
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query AllHosts($name_pattern: String, $tag_deviceType: [String]) {
|
||||||
|
allHosts(name_pattern: $name_pattern, tag_deviceType: $tag_deviceType) {
|
||||||
|
hostid
|
||||||
|
host
|
||||||
|
name
|
||||||
|
deviceType
|
||||||
|
hostgroups {
|
||||||
|
groupid
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name_pattern": "BT_%",
|
||||||
|
"tag_deviceType": ["bt_device_tracker_generic"]
|
||||||
|
}
|
||||||
|
```
|
||||||
28
docs/sample_export_user_rights_query.graphql
Normal file
28
docs/sample_export_user_rights_query.graphql
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
### Query
|
||||||
|
Use this query to export all user rights (roles and groups).
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
query ExportUserRights($name_pattern: String) {
|
||||||
|
exportUserRights(name_pattern: $name_pattern) {
|
||||||
|
userGroups {
|
||||||
|
usrgrpid
|
||||||
|
name
|
||||||
|
hostgroup_rights {
|
||||||
|
name
|
||||||
|
permission
|
||||||
|
}
|
||||||
|
}
|
||||||
|
userRoles {
|
||||||
|
roleid
|
||||||
|
name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name_pattern": "Admin%"
|
||||||
|
}
|
||||||
|
```
|
||||||
28
docs/sample_import_host_groups_mutation.graphql
Normal file
28
docs/sample_import_host_groups_mutation.graphql
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
### Mutation
|
||||||
|
Use this mutation to import host groups.
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
mutation ImportHostGroups($hostGroups: [CreateHostGroup!]!) {
|
||||||
|
importHostGroups(hostGroups: $hostGroups) {
|
||||||
|
groupName
|
||||||
|
groupid
|
||||||
|
message
|
||||||
|
error {
|
||||||
|
message
|
||||||
|
code
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hostGroups": [
|
||||||
|
{
|
||||||
|
"groupName": "ConstructionSite/Test"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
36
docs/sample_import_hosts_mutation.graphql
Normal file
36
docs/sample_import_hosts_mutation.graphql
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
### Mutation
|
||||||
|
Use this mutation to import hosts and assign them to host groups.
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
mutation ImportHosts($hosts: [CreateHost!]!) {
|
||||||
|
importHosts(hosts: $hosts) {
|
||||||
|
deviceKey
|
||||||
|
hostid
|
||||||
|
message
|
||||||
|
error {
|
||||||
|
message
|
||||||
|
code
|
||||||
|
data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"hosts": [
|
||||||
|
{
|
||||||
|
"deviceKey": "TEST_DEVICE_001",
|
||||||
|
"name": "Test Device 001",
|
||||||
|
"deviceType": "bt_device_tracker_generic",
|
||||||
|
"groupNames": ["ConstructionSite/Test"],
|
||||||
|
"location": {
|
||||||
|
"name": "Test Location",
|
||||||
|
"location_lat": "52.5200",
|
||||||
|
"location_lon": "13.4050"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
50
docs/sample_import_user_rights_mutation.graphql
Normal file
50
docs/sample_import_user_rights_mutation.graphql
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
### Mutation
|
||||||
|
Use this mutation to import user rights (roles and groups).
|
||||||
|
|
||||||
|
```graphql
|
||||||
|
mutation ImportUserRights($input: UserRightsInput!, $dryRun: Boolean) {
|
||||||
|
importUserRights(input: $input, dryRun: $dryRun) {
|
||||||
|
userRoles {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
message
|
||||||
|
}
|
||||||
|
userGroups {
|
||||||
|
id
|
||||||
|
name
|
||||||
|
message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variables
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"input": {
|
||||||
|
"userRoles": [
|
||||||
|
{
|
||||||
|
"name": "Test Role",
|
||||||
|
"type": 1,
|
||||||
|
"rules": {
|
||||||
|
"api_access": 1,
|
||||||
|
"api": ["host.get", "item.get"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"userGroups": [
|
||||||
|
{
|
||||||
|
"name": "Test Group",
|
||||||
|
"gui_access": 0,
|
||||||
|
"hostgroup_rights": [
|
||||||
|
{
|
||||||
|
"name": "ConstructionSite/Test",
|
||||||
|
"permission": "READ_WRITE"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"dryRun": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
@ -13,16 +13,21 @@ import {ZABBIX_EDGE_DEVICE_BASE_GROUP, zabbixAPI} from "../datasources/zabbix-ap
|
||||||
|
|
||||||
export class HostImporter {
|
export class HostImporter {
|
||||||
public static getHostGroupHierarchyNames(hostGroups: Array<CreateHostGroup>) {
|
public static getHostGroupHierarchyNames(hostGroups: Array<CreateHostGroup>) {
|
||||||
let resultSet: Set<CreateHostGroup> = new Set<CreateHostGroup>(hostGroups)
|
let nameToGroup = new Map<string, CreateHostGroup>()
|
||||||
for (let group of hostGroups || []) {
|
for (let group of hostGroups || []) {
|
||||||
let levelNames = group.groupName.split("/", hostGroups?.length - 1)
|
let levelNames = group.groupName.split("/")
|
||||||
let leafName = ""
|
let leafName = ""
|
||||||
for (let level of levelNames) {
|
for (let i = 0; i < levelNames.length; i++) {
|
||||||
leafName += (leafName ? "/" + level : level)
|
leafName += (leafName ? "/" + levelNames[i] : levelNames[i])
|
||||||
resultSet.add({groupName: leafName})
|
if (!nameToGroup.has(leafName)) {
|
||||||
|
// Use original group object if it matches the name (to keep UUID), else create new
|
||||||
|
let original = hostGroups.find(g => g.groupName === leafName)
|
||||||
|
nameToGroup.set(leafName, original ? original : {groupName: leafName})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return resultSet
|
}
|
||||||
|
// Sort alphabetically to process parents before children
|
||||||
|
return Array.from(nameToGroup.values()).sort((a, b) => a.groupName.localeCompare(b.groupName))
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async importHostGroups(hostGroups: InputMaybe<Array<CreateHostGroup>> | undefined, zabbixAuthToken?: string, cookie?: string) {
|
public static async importHostGroups(hostGroups: InputMaybe<Array<CreateHostGroup>> | undefined, zabbixAuthToken?: string, cookie?: string) {
|
||||||
|
|
|
||||||
84
src/test/host_importer.test.ts
Normal file
84
src/test/host_importer.test.ts
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
|
||||||
|
import {HostImporter} from "../execution/host_importer.js";
|
||||||
|
import {zabbixAPI, ZABBIX_EDGE_DEVICE_BASE_GROUP} from "../datasources/zabbix-api.js";
|
||||||
|
import {ZabbixRequestWithPermissions} from "../datasources/zabbix-permissions.js";
|
||||||
|
|
||||||
|
// Mocking ZabbixAPI
|
||||||
|
jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
|
zabbixAPI: {
|
||||||
|
executeRequest: jest.fn(),
|
||||||
|
post: jest.fn(),
|
||||||
|
requestByPath: jest.fn()
|
||||||
|
},
|
||||||
|
ZABBIX_EDGE_DEVICE_BASE_GROUP: "Baustellen-Devices"
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock ZabbixRequestWithPermissions to skip permission checks
|
||||||
|
jest.mock("../datasources/zabbix-permissions.js", () => ({
|
||||||
|
ZabbixRequestWithPermissions: class {
|
||||||
|
constructor(public path: string) {}
|
||||||
|
async prepare() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
async executeRequestReturnError(api: any, args: any) {
|
||||||
|
// @ts-ignore
|
||||||
|
return api.post(this.path, { body: { method: this.path, params: args?.zabbix_params } });
|
||||||
|
}
|
||||||
|
async executeRequestThrowError(api: any, args: any) {
|
||||||
|
return this.executeRequestReturnError(api, args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
ZabbixPermissionsHelper: {
|
||||||
|
hasUserPermissions: jest.fn().mockResolvedValue(true)
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("HostImporter", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("importHostGroups - create new hierarchy", async () => {
|
||||||
|
const hostGroups = [{ groupName: "Parent/Child" }];
|
||||||
|
|
||||||
|
// Parent lookup (from importHostGroups loop)
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce([]); // findHostGroupIdsByName (Parent)
|
||||||
|
// Parent creation
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce({ groupids: ["101"] }); // hostgroup.create (Parent)
|
||||||
|
|
||||||
|
// Parent/Child lookup
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce([]); // findHostGroupIdsByName (Parent/Child)
|
||||||
|
// Parent/Child creation
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce({ groupids: ["102"] }); // hostgroup.create (Parent/Child)
|
||||||
|
|
||||||
|
const result = await HostImporter.importHostGroups(hostGroups, "token");
|
||||||
|
|
||||||
|
expect(result).toHaveLength(2);
|
||||||
|
expect(result!.find(r => r.groupName === "Parent")?.groupid).toBe(101);
|
||||||
|
expect(result!.find(r => r.groupName === "Parent/Child")?.groupid).toBe(102);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("importHosts - basic host", async () => {
|
||||||
|
const hosts = [{
|
||||||
|
deviceKey: "Device1",
|
||||||
|
deviceType: "Type1",
|
||||||
|
groupNames: ["Group1"]
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Mocking group lookup for Base group and Group1
|
||||||
|
// findHostGroupIdsByName for [Base, Group1]
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce([{ groupid: "201", name: ZABBIX_EDGE_DEVICE_BASE_GROUP }]); // Base
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce([{ groupid: "202", name: ZABBIX_EDGE_DEVICE_BASE_GROUP + "/Group1" }]); // Group1
|
||||||
|
|
||||||
|
// Mocking template lookup for deviceType
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce([{ templateid: "301" }]);
|
||||||
|
|
||||||
|
// Mocking host.create via requestByPath
|
||||||
|
(zabbixAPI.requestByPath as jest.Mock).mockResolvedValueOnce({ hostids: ["401"] });
|
||||||
|
|
||||||
|
const result = await HostImporter.importHosts(hosts, "token");
|
||||||
|
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result![0].hostid).toBe("401");
|
||||||
|
});
|
||||||
|
});
|
||||||
83
src/test/host_integration.test.ts
Normal file
83
src/test/host_integration.test.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
import { ApolloServer } from '@apollo/server';
|
||||||
|
import { schema_loader } from '../api/schema.js';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { zabbixAPI, ZABBIX_EDGE_DEVICE_BASE_GROUP } from '../datasources/zabbix-api.js';
|
||||||
|
|
||||||
|
// Mocking ZabbixAPI.post
|
||||||
|
jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
|
zabbixAPI: {
|
||||||
|
post: jest.fn(),
|
||||||
|
executeRequest: jest.fn(),
|
||||||
|
baseURL: 'http://localhost/zabbix',
|
||||||
|
getLocations: jest.fn(),
|
||||||
|
requestByPath: jest.fn()
|
||||||
|
},
|
||||||
|
ZABBIX_EDGE_DEVICE_BASE_GROUP: "Baustellen-Devices"
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("Host Integration Tests", () => {
|
||||||
|
let server: ApolloServer;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const schema = await schema_loader();
|
||||||
|
server = new ApolloServer({
|
||||||
|
schema,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Query allHosts using sample", async () => {
|
||||||
|
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_all_hosts_query.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
|
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
||||||
|
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
||||||
|
|
||||||
|
const query = mutationMatch![1];
|
||||||
|
const variables = JSON.parse(variablesMatch![1]);
|
||||||
|
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce([{ hostid: "1", host: "BT_DEVICE_1", name: "BT_DEVICE_1" }]);
|
||||||
|
|
||||||
|
const response = await server.executeOperation({
|
||||||
|
query: query,
|
||||||
|
variables: variables,
|
||||||
|
}, {
|
||||||
|
contextValue: { zabbixAuthToken: 'test-token', dataSources: { zabbixAPI: zabbixAPI } }
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.body.kind).toBe('single');
|
||||||
|
// @ts-ignore
|
||||||
|
const result = response.body.singleResult;
|
||||||
|
expect(result.errors).toBeUndefined();
|
||||||
|
expect(result.data.allHosts).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Import hosts using sample", async () => {
|
||||||
|
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_hosts_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
|
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
||||||
|
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
||||||
|
|
||||||
|
const mutation = mutationMatch![1];
|
||||||
|
const variables = JSON.parse(variablesMatch![1]);
|
||||||
|
|
||||||
|
// Mocking for importHosts
|
||||||
|
(zabbixAPI.post as jest.Mock)
|
||||||
|
.mockResolvedValueOnce([{ groupid: "201", name: ZABBIX_EDGE_DEVICE_BASE_GROUP }]) // Base group
|
||||||
|
.mockResolvedValueOnce([{ groupid: "202", name: ZABBIX_EDGE_DEVICE_BASE_GROUP + "/ConstructionSite/Test" }]) // Specific group
|
||||||
|
.mockResolvedValueOnce([{ templateid: "301" }]); // Template lookup
|
||||||
|
|
||||||
|
(zabbixAPI.requestByPath as jest.Mock).mockResolvedValueOnce({ hostids: ["401"] });
|
||||||
|
|
||||||
|
const response = await server.executeOperation({
|
||||||
|
query: mutation,
|
||||||
|
variables: variables,
|
||||||
|
}, {
|
||||||
|
contextValue: { zabbixAuthToken: 'test-token', dataSources: { zabbixAPI: zabbixAPI } }
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.body.kind).toBe('single');
|
||||||
|
// @ts-ignore
|
||||||
|
const result = response.body.singleResult;
|
||||||
|
expect(result.errors).toBeUndefined();
|
||||||
|
expect(result.data.importHosts).toHaveLength(1);
|
||||||
|
expect(result.data.importHosts[0].hostid).toBe("401");
|
||||||
|
});
|
||||||
|
});
|
||||||
112
src/test/host_query.test.ts
Normal file
112
src/test/host_query.test.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
|
||||||
|
import {createResolvers} from "../api/resolvers.js";
|
||||||
|
import {zabbixAPI, ZABBIX_EDGE_DEVICE_BASE_GROUP} from "../datasources/zabbix-api.js";
|
||||||
|
import {QueryAllHostsArgs, QueryAllDevicesArgs, QueryAllHostGroupsArgs} from "../schema/generated/graphql.js";
|
||||||
|
|
||||||
|
// Mocking ZabbixAPI
|
||||||
|
jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
|
zabbixAPI: {
|
||||||
|
executeRequest: jest.fn(),
|
||||||
|
post: jest.fn(),
|
||||||
|
baseURL: "http://mock-zabbix",
|
||||||
|
getLocations: jest.fn()
|
||||||
|
},
|
||||||
|
ZABBIX_EDGE_DEVICE_BASE_GROUP: "Baustellen-Devices"
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("Host and HostGroup Resolvers", () => {
|
||||||
|
let resolvers: any;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
resolvers = createResolvers();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("allHosts query", async () => {
|
||||||
|
const mockHosts = [{ hostid: "1", host: "Host 1" }];
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce(mockHosts);
|
||||||
|
|
||||||
|
const args: QueryAllHostsArgs = { name_pattern: "Test" };
|
||||||
|
const context = {
|
||||||
|
zabbixAuthToken: "test-token",
|
||||||
|
dataSources: { zabbixAPI: zabbixAPI }
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await resolvers.Query.allHosts(null, args, context);
|
||||||
|
|
||||||
|
expect(result).toEqual(mockHosts);
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("host.get.with_items", expect.objectContaining({
|
||||||
|
body: expect.objectContaining({
|
||||||
|
method: "host.get",
|
||||||
|
params: expect.objectContaining({
|
||||||
|
search: { name: "Test" },
|
||||||
|
tags: expect.arrayContaining([{
|
||||||
|
tag: "hostType",
|
||||||
|
operator: 1,
|
||||||
|
value: ZABBIX_EDGE_DEVICE_BASE_GROUP
|
||||||
|
}])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("allDevices query", async () => {
|
||||||
|
const mockDevices = [{ hostid: "2", host: "Device 1" }];
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce(mockDevices);
|
||||||
|
|
||||||
|
const args: QueryAllDevicesArgs = { hostids: 2 };
|
||||||
|
const context = {
|
||||||
|
zabbixAuthToken: "test-token",
|
||||||
|
dataSources: { zabbixAPI: zabbixAPI }
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await resolvers.Query.allDevices(null, args, context);
|
||||||
|
|
||||||
|
expect(result).toEqual(mockDevices);
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("host.get.with_items", expect.objectContaining({
|
||||||
|
body: expect.objectContaining({
|
||||||
|
method: "host.get",
|
||||||
|
params: expect.objectContaining({
|
||||||
|
hostids: 2
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("allHostGroups query", async () => {
|
||||||
|
const mockGroups = [{ groupid: "10", name: ZABBIX_EDGE_DEVICE_BASE_GROUP + "/Group 1" }];
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce(mockGroups);
|
||||||
|
|
||||||
|
const args: QueryAllHostGroupsArgs = { search_name: "Group 1" };
|
||||||
|
const context = {
|
||||||
|
zabbixAuthToken: "test-token"
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await resolvers.Query.allHostGroups(null, args, context);
|
||||||
|
|
||||||
|
expect(result).toEqual(mockGroups);
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("hostgroup.get", expect.objectContaining({
|
||||||
|
body: expect.objectContaining({
|
||||||
|
params: expect.objectContaining({
|
||||||
|
search: { name: ["Group 1"] }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("locations query", async () => {
|
||||||
|
const mockLocations = [{ name: "Loc 1", latitude: 1.0, longitude: 2.0 }];
|
||||||
|
(zabbixAPI.getLocations as jest.Mock).mockResolvedValueOnce(mockLocations);
|
||||||
|
|
||||||
|
const args = { name_pattern: "Loc" };
|
||||||
|
const context = {
|
||||||
|
zabbixAuthToken: "test-token",
|
||||||
|
dataSources: { zabbixAPI: zabbixAPI }
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = await resolvers.Query.locations(null, args, context);
|
||||||
|
|
||||||
|
expect(result).toEqual(mockLocations);
|
||||||
|
expect(zabbixAPI.getLocations).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
49
src/test/misc_resolvers.test.ts
Normal file
49
src/test/misc_resolvers.test.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
|
||||||
|
import {createResolvers} from "../api/resolvers.js";
|
||||||
|
import {zabbixAPI} from "../datasources/zabbix-api.js";
|
||||||
|
|
||||||
|
// Mocking ZabbixAPI
|
||||||
|
jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
|
zabbixAPI: {
|
||||||
|
executeRequest: jest.fn(),
|
||||||
|
post: jest.fn()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("Miscellaneous Resolvers", () => {
|
||||||
|
let resolvers: any;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
resolvers = createResolvers();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("apiVersion query", async () => {
|
||||||
|
const result = await resolvers.Query.apiVersion();
|
||||||
|
expect(typeof result).toBe("string");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("zabbixVersion query", async () => {
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce({ result: "7.0.0" });
|
||||||
|
const result = await resolvers.Query.zabbixVersion();
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("apiinfo.version", expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
test("login query", async () => {
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce("mock-token");
|
||||||
|
const result = await resolvers.Query.login(null, { username: "admin", password: "password" });
|
||||||
|
expect(result).toBe("mock-token");
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("user.login", expect.objectContaining({
|
||||||
|
body: expect.objectContaining({
|
||||||
|
params: { username: "admin", password: "password" }
|
||||||
|
})
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("logout query", async () => {
|
||||||
|
(zabbixAPI.post as jest.Mock).mockResolvedValueOnce(true);
|
||||||
|
const result = await resolvers.Query.logout(null, null, { zabbixAuthToken: "token" });
|
||||||
|
expect(result).toBe(true);
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("user.logout", expect.anything());
|
||||||
|
});
|
||||||
|
});
|
||||||
123
src/test/user_rights.test.ts
Normal file
123
src/test/user_rights.test.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
|
||||||
|
import {createResolvers} from "../api/resolvers.js";
|
||||||
|
import {zabbixAPI} from "../datasources/zabbix-api.js";
|
||||||
|
|
||||||
|
// Mocking ZabbixAPI
|
||||||
|
jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
|
zabbixAPI: {
|
||||||
|
executeRequest: jest.fn(),
|
||||||
|
post: jest.fn(),
|
||||||
|
baseURL: "http://mock-zabbix"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("User Rights and Permissions Resolvers", () => {
|
||||||
|
let resolvers: any;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
resolvers = createResolvers();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("exportUserRights query", async () => {
|
||||||
|
// Mocks for exportUserRights
|
||||||
|
(zabbixAPI.post as jest.Mock).mockImplementation((path: string) => {
|
||||||
|
if (path === "templategroup.get") return Promise.resolve([]);
|
||||||
|
if (path === "hostgroup.get") return Promise.resolve([]);
|
||||||
|
if (path === "usergroup.get.withuuids") return Promise.resolve([{ usrgrpid: "1", name: "UserGroup1", hostgroup_rights: [], templategroup_rights: [] }]);
|
||||||
|
if (path === "module.get") return Promise.resolve([{ moduleid: "10", id: "mod1" }]);
|
||||||
|
if (path === "role.get") return Promise.resolve([{ roleid: "2", name: "UserRole1" }]);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = { name_pattern: "User" };
|
||||||
|
const context = { zabbixAuthToken: "test-token" };
|
||||||
|
|
||||||
|
const result = await resolvers.Query.exportUserRights(null, args, context);
|
||||||
|
|
||||||
|
expect(result.userGroups).toBeDefined();
|
||||||
|
expect(result.userRoles).toBeDefined();
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("usergroup.get.withuuids", expect.anything());
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("module.get", expect.anything());
|
||||||
|
expect(zabbixAPI.post).toHaveBeenCalledWith("role.get", expect.anything());
|
||||||
|
});
|
||||||
|
|
||||||
|
test("userPermissions query", async () => {
|
||||||
|
// Mock for userPermissions
|
||||||
|
(zabbixAPI.post as jest.Mock).mockImplementation((path: string) => {
|
||||||
|
if (path === "usergroup.get.permissions") return Promise.resolve([
|
||||||
|
{
|
||||||
|
usrgrpid: "1",
|
||||||
|
name: "Group 1",
|
||||||
|
templategroup_rights: [{ id: "1001", permission: "3" }]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
if (path === "templategroup.get.permissions") return Promise.resolve([
|
||||||
|
{ groupid: "1001", name: "Permissions/Hostgroup/1001" }
|
||||||
|
]);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = { objectNames: ["Hostgroup/1001"] };
|
||||||
|
const context = { zabbixAuthToken: "test-token" };
|
||||||
|
|
||||||
|
const result = await resolvers.Query.userPermissions(null, args, context);
|
||||||
|
|
||||||
|
expect(result).toBeDefined();
|
||||||
|
expect(result).toHaveLength(1);
|
||||||
|
expect(result[0].objectName).toBe("Hostgroup/1001");
|
||||||
|
expect(result[0].permission).toBe("3"); // Zabbix value "3" (READ_WRITE)
|
||||||
|
});
|
||||||
|
|
||||||
|
test("hasPermissions query", async () => {
|
||||||
|
// Mock for hasPermissions
|
||||||
|
(zabbixAPI.post as jest.Mock).mockImplementation((path: string) => {
|
||||||
|
if (path === "usergroup.get.permissions") return Promise.resolve([
|
||||||
|
{
|
||||||
|
usrgrpid: "1",
|
||||||
|
templategroup_rights: [{ id: "1002", permission: "3" }]
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
if (path === "templategroup.get.permissions") return Promise.resolve([
|
||||||
|
{ groupid: "1002", name: "Permissions/Hostgroup/1002" }
|
||||||
|
]);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = { permissions: [{ objectName: "Hostgroup/1002", permission: "READ" }] };
|
||||||
|
const context = { zabbixAuthToken: "test-token" };
|
||||||
|
|
||||||
|
const result = await resolvers.Query.hasPermissions(null, args, context);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("importUserRights mutation", async () => {
|
||||||
|
// Mocks for importUserRights
|
||||||
|
(zabbixAPI.post as jest.Mock).mockImplementation((path: string) => {
|
||||||
|
if (path === "module.get") return Promise.resolve([{ moduleid: "10", id: "mod1" }]);
|
||||||
|
if (path === "role.get") return Promise.resolve([{ roleid: "2", name: "NewRole" }]);
|
||||||
|
if (path === "role.update") return Promise.resolve({ roleids: ["2"] });
|
||||||
|
if (path === "templategroup.get") return Promise.resolve([{ groupid: "101", name: "Group1", uuid: "uuid1" }]);
|
||||||
|
if (path === "hostgroup.get") return Promise.resolve([{ groupid: "201", name: "HostGroup1", uuid: "uuid2" }]);
|
||||||
|
if (path === "usergroup.get") return Promise.resolve([{ usrgrpid: "1", name: "NewGroup" }]);
|
||||||
|
if (path === "usergroup.update") return Promise.resolve({ usrgrpids: ["1"] });
|
||||||
|
if (path === "hostgroup.propagate") return Promise.resolve(true);
|
||||||
|
return Promise.resolve([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
input: {
|
||||||
|
userRoles: [{ name: "NewRole", type: 1 }],
|
||||||
|
userGroups: [{ name: "NewGroup" }]
|
||||||
|
},
|
||||||
|
dryRun: false
|
||||||
|
};
|
||||||
|
const context = { zabbixAuthToken: "test-token" };
|
||||||
|
|
||||||
|
const result = await resolvers.Mutation.importUserRights(null, args, context);
|
||||||
|
|
||||||
|
expect(result.userRoles).toHaveLength(1);
|
||||||
|
expect(result.userGroups).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
61
src/test/user_rights_integration.test.ts
Normal file
61
src/test/user_rights_integration.test.ts
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
import { ApolloServer } from '@apollo/server';
|
||||||
|
import { schema_loader } from '../api/schema.js';
|
||||||
|
import { readFileSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { zabbixAPI } from '../datasources/zabbix-api.js';
|
||||||
|
|
||||||
|
// Mocking ZabbixAPI.post
|
||||||
|
jest.mock("../datasources/zabbix-api.js", () => ({
|
||||||
|
zabbixAPI: {
|
||||||
|
post: jest.fn(),
|
||||||
|
executeRequest: jest.fn(),
|
||||||
|
baseURL: 'http://localhost/zabbix',
|
||||||
|
getLocations: jest.fn(),
|
||||||
|
requestByPath: jest.fn()
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe("User Rights Integration Tests", () => {
|
||||||
|
let server: ApolloServer;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const schema = await schema_loader();
|
||||||
|
server = new ApolloServer({
|
||||||
|
schema,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Import user rights using sample", async () => {
|
||||||
|
const sampleFile = readFileSync(join(process.cwd(), 'docs', 'sample_import_user_rights_mutation.graphql'), 'utf-8').replace(/\r\n/g, '\n');
|
||||||
|
const mutationMatch = sampleFile.match(/```graphql\n([\s\S]*?)\n```/);
|
||||||
|
const variablesMatch = sampleFile.match(/```json\n([\s\S]*?)\n```/);
|
||||||
|
|
||||||
|
const mutation = mutationMatch![1];
|
||||||
|
const variables = JSON.parse(variablesMatch![1]);
|
||||||
|
|
||||||
|
// Mocks for importUserRights
|
||||||
|
(zabbixAPI.post as jest.Mock)
|
||||||
|
.mockResolvedValueOnce([{ groupid: "101", name: "Group1", uuid: "uuid1" }]) // templategroup.get for roles (in prepare)
|
||||||
|
.mockResolvedValueOnce([{ groupid: "201", name: "ConstructionSite/Test", uuid: "uuid2" }]) // hostgroup.get for roles (in prepare)
|
||||||
|
.mockResolvedValueOnce([{ moduleid: "10", id: "mod1" }]) // module.get for roles
|
||||||
|
.mockResolvedValueOnce([{ roleid: "2", name: "Test Role" }]) // role.get
|
||||||
|
.mockResolvedValueOnce({ roleids: ["2"] }) // role.update
|
||||||
|
.mockResolvedValueOnce([{ groupid: "101", name: "Group1", uuid: "uuid1" }]) // templategroup.get for groups (in prepare)
|
||||||
|
.mockResolvedValueOnce([{ groupid: "201", name: "ConstructionSite/Test", uuid: "uuid2" }]) // hostgroup.get for groups (in prepare)
|
||||||
|
.mockResolvedValueOnce([{ usrgrpid: "1", name: "Test Group" }]) // usergroup.get
|
||||||
|
.mockResolvedValueOnce({ usrgrpids: ["1"] }); // usergroup.update
|
||||||
|
|
||||||
|
const response = await server.executeOperation({
|
||||||
|
query: mutation,
|
||||||
|
variables: variables,
|
||||||
|
}, {
|
||||||
|
contextValue: { zabbixAuthToken: 'test-token', dataSources: { zabbixAPI: zabbixAPI } }
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.body.kind).toBe('single');
|
||||||
|
// @ts-ignore
|
||||||
|
const result = response.body.singleResult;
|
||||||
|
expect(result.errors).toBeUndefined();
|
||||||
|
expect(result.data.importUserRights.userRoles).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue