Files
huly-platform/services/gmail/pod-gmail/src/__tests__/tokens.test.ts
T
Artyom SavchenkoandGitHub 7e92c12073 UBERF-11414: Integrations (#9610)
* UBERF-11414: Use common integration kind

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Allow multiple gmail

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Merge with develop

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Refactor integration kinds

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Add integrations package

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Integrations

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Integration state and config

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Add integration events

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Refresh integration states

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Rename to integration-client

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Add filter button

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Add translations for filters

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Synced channels count

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Actions

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Add isLoading for connect

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Create connection and integration

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Use social id from hulygram

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Remove old integrations

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Gmail state

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Gmail state and disconnect all handler

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Add tests

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Formatting

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Gmail sync state

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix rush check

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix gmail tests

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix calendar actions and config

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Calendar state

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Access config

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Github integration state

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix svelte errors

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix formatting

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix typos

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix validate

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Fix translations

Signed-off-by: Artem Savchenko <armisav@gmail.com>

* UBERF-11414: Retry on network issues

Signed-off-by: Artem Savchenko <armisav@gmail.com>

---------

Signed-off-by: Artem Savchenko <armisav@gmail.com>
2025-08-04 15:14:06 +07:00

141 lines
4.7 KiB
TypeScript

import { AccountUuid, PersonId, WorkspaceUuid, type MeasureContext } from '@hcengineering/core'
import { AccountClient, IntegrationSecret } from '@hcengineering/account-client'
import { getAccountClient } from '@hcengineering/server-client'
import { gmailIntegrationKind } from '@hcengineering/gmail'
import { TokenStorage } from '../tokens'
import { SecretType, Token } from '../types'
// Mock the getAccountClient function
jest.mock('@hcengineering/server-client', () => ({
getAccountClient: jest.fn()
}))
describe('TokenStorage', () => {
const mockSocialId = 'test-social-id' as PersonId
const mockWorkspace = 'test-workspace' as WorkspaceUuid
const mockPlatformToken = 'test-platform-token'
const mockUserId = 'test-user' as AccountUuid
const mockToken: Token = {
userId: mockUserId,
socialId: { _id: '123' } as any,
workspace: mockWorkspace,
token: 'test-token',
access_token: 'test-access-token',
refresh_token: 'test-refresh-token',
scope: 'test-scope',
token_type: 'Bearer',
expiry_date: 123456789
}
let tokenStorage: TokenStorage
let mockAccountClient: jest.Mocked<AccountClient>
// Add mock MeasureContext
const mockMeasureContext: MeasureContext = {
info: jest.fn(),
error: jest.fn()
} as any
beforeEach(() => {
// Create mock AccountClient
mockAccountClient = {
getIntegrationSecret: jest.fn(),
addIntegrationSecret: jest.fn(),
updateIntegrationSecret: jest.fn(),
deleteIntegrationSecret: jest.fn()
} as any
// Set up getAccountClient mock
;(getAccountClient as jest.Mock).mockReturnValue(mockAccountClient)
// Create TokenStorage instance with MeasureContext
tokenStorage = new TokenStorage(mockMeasureContext, mockWorkspace, mockPlatformToken)
})
afterEach(() => {
jest.clearAllMocks()
})
describe('getToken', () => {
it('should return null when no token exists', async () => {
mockAccountClient.getIntegrationSecret.mockResolvedValue(null)
const result = await tokenStorage.getToken(mockSocialId)
expect(result).toBeNull()
expect(mockAccountClient.getIntegrationSecret).toHaveBeenCalledWith({
key: SecretType.TOKEN,
kind: gmailIntegrationKind,
socialId: mockSocialId,
workspaceUuid: mockWorkspace
})
})
it('should return parsed token when token exists', async () => {
const mockSecret: IntegrationSecret = {
socialId: mockSocialId,
kind: gmailIntegrationKind,
workspaceUuid: mockWorkspace,
key: SecretType.TOKEN,
secret: JSON.stringify(mockToken)
}
mockAccountClient.getIntegrationSecret.mockResolvedValue(mockSecret)
const result = await tokenStorage.getToken(mockSocialId)
expect(result).toEqual(mockToken)
})
})
describe('saveToken', () => {
it('should update token when it exists', async () => {
const existingToken = { existing: 'token' }
const mockSecret: IntegrationSecret = {
socialId: mockSocialId,
kind: gmailIntegrationKind,
workspaceUuid: mockWorkspace,
key: SecretType.TOKEN,
secret: JSON.stringify(existingToken)
}
mockAccountClient.getIntegrationSecret.mockResolvedValue(mockSecret)
await tokenStorage.saveToken(mockSocialId, mockToken)
// Expect merged token (existing token properties + new token properties)
const expectedMergedToken = {
...existingToken,
...mockToken
}
expect(mockAccountClient.updateIntegrationSecret).toHaveBeenCalledWith({
key: SecretType.TOKEN,
kind: gmailIntegrationKind,
socialId: mockSocialId,
secret: JSON.stringify(expectedMergedToken),
workspaceUuid: mockWorkspace
})
expect(mockAccountClient.addIntegrationSecret).not.toHaveBeenCalled()
})
it('should add new token when it does not exist', async () => {
mockAccountClient.getIntegrationSecret.mockResolvedValue(null)
await tokenStorage.saveToken(mockSocialId, mockToken)
expect(mockAccountClient.addIntegrationSecret).toHaveBeenCalledWith({
key: SecretType.TOKEN,
kind: gmailIntegrationKind,
socialId: mockSocialId,
secret: JSON.stringify(mockToken),
workspaceUuid: mockWorkspace
})
expect(mockAccountClient.updateIntegrationSecret).not.toHaveBeenCalled()
})
})
describe('deleteToken', () => {
it('should delete the token', async () => {
await tokenStorage.deleteToken(mockSocialId)
expect(mockAccountClient.deleteIntegrationSecret).toHaveBeenCalledWith({
key: SecretType.TOKEN,
kind: gmailIntegrationKind,
socialId: mockSocialId,
workspaceUuid: mockWorkspace
})
})
})
})