mattermost-mobile/app/managers/apps_manager.test.ts
2025-02-27 12:46:17 +01:00

289 lines
11 KiB
TypeScript

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {of as of$} from 'rxjs';
import DatabaseManager from '@database/manager';
import NetworkManager from '@managers/network_manager';
import {getChannelById} from '@queries/servers/channel';
import {getConfig} from '@queries/servers/system';
import {logDebug} from '@utils/log';
import {AppsManagerSingleton as AppsManagerClass} from './apps_manager';
import type ChannelModel from '@typings/database/models/servers/channel';
jest.mock('@database/manager', () => ({
default: {
getServerDatabaseAndOperator: jest.fn(),
},
}));
jest.mock('@managers/network_manager', () => ({
default: {
getClient: jest.fn(),
},
}));
jest.mock('@queries/servers/channel', () => ({
getChannelById: jest.fn(() => ({id: 'channel_id', teamId: 'team_id'})),
}));
jest.mock('@queries/servers/system', () => ({
getConfig: jest.fn(),
getCurrentUserId: jest.fn(() => 'user_id'),
getCurrentTeamId: jest.fn(() => 'team_id'),
getCurrentChannelId: jest.fn(() => 'channel_id'),
}));
jest.mock('@utils/apps', () => ({
validateBindings: jest.fn(() => []),
}));
jest.mock('@utils/log');
describe('AppsManager', () => {
const mockServerUrl = 'https://example.com';
const mockChannelId = 'channel_id';
const mockBindings: AppBinding[] = [
{
location: 'test_location',
bindings: [{
app_id: 'test_app_id',
location: 'sub_location',
label: 'Test Binding',
}],
app_id: 'test_app_id',
label: 'test_label',
},
];
let AppsManager: AppsManagerClass;
beforeEach(() => {
jest.clearAllMocks();
const mockDatabase = {
database: {},
operator: {},
};
DatabaseManager.getServerDatabaseAndOperator = jest.fn();
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockReturnValue(mockDatabase);
const mockClient = {
getAppsBindings: jest.fn().mockResolvedValue(mockBindings),
};
NetworkManager.getClient = jest.fn();
(NetworkManager.getClient as jest.Mock).mockReturnValue(mockClient);
AppsManager = new AppsManagerClass();
});
it('initializes with empty bindings', () => {
const bindings = AppsManager.getBindings(mockServerUrl);
expect(bindings).toEqual([]);
});
describe('apps enabled state', () => {
beforeEach(() => {
const mockConfig = {
FeatureFlagAppsEnabled: 'true',
} as ClientConfig;
jest.mocked(getConfig).mockResolvedValue(mockConfig);
});
it('should return true when apps are enabled', async () => {
const enabled = await AppsManager.isAppsEnabled(mockServerUrl);
expect(enabled).toBe(true);
});
it('should return false when database error occurs', async () => {
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockImplementation(() => {
throw new Error('Database error');
});
const enabled = await AppsManager.isAppsEnabled(mockServerUrl);
expect(enabled).toBe(false);
});
it('should observe apps enabled state', async () => {
const mockObservable = of$(false);
jest.spyOn(AppsManager, 'observeIsAppsEnabled').mockReturnValue(mockObservable);
const result = await new Promise((resolve) => {
AppsManager.observeIsAppsEnabled(mockServerUrl).subscribe((enabled) => {
resolve(enabled);
});
});
expect(result).toBe(false);
});
it('should handle database error in observeIsAppsEnabled', async () => {
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockImplementation(() => {
throw new Error('Database error');
});
const result = await new Promise((resolve) => {
AppsManager.observeIsAppsEnabled(mockServerUrl).subscribe((enabled) => {
resolve(enabled);
});
});
expect(result).toBe(false);
});
});
describe('bindings management', () => {
it('should fetch bindings successfully', async () => {
await AppsManager.fetchBindings(mockServerUrl, mockChannelId);
expect(NetworkManager.getClient).toHaveBeenCalledWith(mockServerUrl);
expect(NetworkManager.getClient(mockServerUrl).getAppsBindings).toHaveBeenCalled();
});
it('should handle fetch bindings error', async () => {
const error = new Error('Network error');
jest.mocked(NetworkManager.getClient(mockServerUrl).getAppsBindings).mockRejectedValue(error);
await AppsManager.fetchBindings(mockServerUrl, mockChannelId);
expect(logDebug).toHaveBeenCalledWith('error on fetchBindings', expect.any(String));
});
it('should fetch bindings successfully for DM channel and thread', async () => {
jest.mocked(getChannelById).mockResolvedValue({id: 'channel_id', teamId: ''} as ChannelModel);
await AppsManager.fetchBindings(mockServerUrl, mockChannelId, true);
expect(NetworkManager.getClient).toHaveBeenCalledWith(mockServerUrl);
expect(NetworkManager.getClient(mockServerUrl).getAppsBindings).toHaveBeenCalled();
});
it('should clear bindings', async () => {
await AppsManager.clearBindings(mockServerUrl);
const bindings = AppsManager.getBindings(mockServerUrl);
expect(bindings).toEqual([]);
});
it('should copy main bindings to thread', async () => {
const channelId = 'test_channel';
await AppsManager.copyMainBindingsToThread(mockServerUrl, channelId);
const threadBindings = AppsManager.getBindings(mockServerUrl, undefined, true);
expect(threadBindings).toEqual([]);
});
it('should observe bindings changes', async () => {
const mockObservable = of$([]);
jest.spyOn(AppsManager, 'observeBindings').mockReturnValue(mockObservable);
const result = await new Promise((resolve) => {
AppsManager.observeBindings(mockServerUrl).subscribe((bindings) => {
resolve(bindings);
});
});
expect(result).toEqual([]);
});
it('should observe bindings with location filter', async () => {
const location = 'test_location';
const result = await new Promise((resolve) => {
AppsManager.observeBindings(mockServerUrl, location).subscribe((bindings) => {
resolve(bindings);
});
});
expect(result).toEqual([]);
});
it('should refresh app bindings successfully', async () => {
const mockConfig = {FeatureFlagAppsEnabled: 'true'} as ClientConfig;
jest.mocked(getConfig).mockResolvedValue(mockConfig);
const mockClient = {
getAppsBindings: jest.fn().mockResolvedValue(mockBindings),
};
(NetworkManager.getClient as jest.Mock).mockReturnValue(mockClient);
await AppsManager.refreshAppBindings(mockServerUrl);
expect(NetworkManager.getClient).toHaveBeenCalledWith(mockServerUrl);
expect(mockClient.getAppsBindings).toHaveBeenCalledWith('user_id', mockChannelId, 'team_id', undefined);
});
it('should handle error in refreshAppBindings', async () => {
const error = new Error('Refresh error');
(DatabaseManager.getServerDatabaseAndOperator as jest.Mock).mockImplementation(() => {
throw error;
});
await AppsManager.refreshAppBindings(mockServerUrl);
expect(logDebug).toHaveBeenCalledWith('Error refreshing apps', error);
});
it('should clear bindings when apps are disabled', async () => {
const mockConfig = {FeatureFlagAppsEnabled: 'false'} as ClientConfig;
jest.mocked(getConfig).mockResolvedValue(mockConfig);
await AppsManager.refreshAppBindings(mockServerUrl);
const bindings = AppsManager.getBindings(mockServerUrl);
expect(bindings).toEqual([]);
});
});
describe('command forms', () => {
beforeEach(() => {
// Clear any existing forms before each test
AppsManager.removeServer(mockServerUrl);
});
const mockForm: AppForm = {
title: 'Test Form',
fields: [],
};
it('should set and get command form', () => {
const key = 'test_key';
AppsManager.setCommandForm(mockServerUrl, key, mockForm);
const form = AppsManager.getCommandForm(mockServerUrl, key);
const nonExistentForm = AppsManager.getCommandForm(mockServerUrl, 'non_existent');
expect(form).toEqual(mockForm);
expect(nonExistentForm).toBeUndefined();
});
it('should handle thread command forms separately', () => {
// Clear any existing forms
AppsManager.removeServer(mockServerUrl);
// Set form for thread context only
AppsManager.setCommandForm(mockServerUrl, 'test_key', mockForm, true);
// Get forms for both contexts
const threadForm = AppsManager.getCommandForm(mockServerUrl, 'test_key', true);
const regularForm = AppsManager.getCommandForm(mockServerUrl, 'test_key', false);
// Verify thread form exists and regular form doesn't
expect(threadForm).toEqual(mockForm);
expect(regularForm).toBeUndefined();
});
});
describe('server management', () => {
it('should remove server data', () => {
AppsManager.setCommandForm(mockServerUrl, 'test_key', {title: 'Test'});
AppsManager.removeServer(mockServerUrl);
const form = AppsManager.getCommandForm(mockServerUrl, 'test_key');
expect(form).toBeUndefined();
});
it('should clear server data', async () => {
AppsManager.setCommandForm(mockServerUrl, 'test_key', {title: 'Test'});
await AppsManager.clearServer(mockServerUrl);
const form = AppsManager.getCommandForm(mockServerUrl, 'test_key');
const bindings = AppsManager.getBindings(mockServerUrl);
expect(form).toBeUndefined();
expect(bindings).toEqual([]);
});
});
});