import { IntegrationConfigService } from './integration-config.service';

describe('IntegrationConfigService.testConnection', () => {
  const originalFetch = global.fetch;

  afterEach(() => {
    global.fetch = originalFetch;
    jest.restoreAllMocks();
  });

  it('uses the saved AppSecret when testing an existing configuration with a blank secret', async () => {
    const configRepo = {
      findOne: jest.fn().mockResolvedValue({ id: 1, type: 'THIRD' }),
    };
    const detailRepo = {
      findOne: jest.fn().mockResolvedValue({
        configId: 1,
        type: 'DINGTALK_SYNC',
        content: JSON.stringify({
          config: {
            corpId: 'ding-corp',
            agentId: 'saved-key',
            appSecret: 'saved-secret',
          },
        }),
      }),
    };
    global.fetch = jest.fn().mockResolvedValue({
      ok: true,
      json: jest.fn().mockResolvedValue({ accessToken: 'token' }),
    }) as never;

    const service = new IntegrationConfigService(configRepo as never, detailRepo as never);

    await expect(
      service.testConnection('DINGTALK', {
        corpId: 'ding-corp',
        agentId: 'saved-key',
        appSecret: '',
      }),
    ).resolves.toBe(true);

    expect(global.fetch).toHaveBeenCalledWith(
      'https://api.dingtalk.com/v1.0/oauth2/accessToken',
      expect.objectContaining({
        body: JSON.stringify({ appKey: 'saved-key', appSecret: 'saved-secret' }),
      }),
    );
  });
});

describe('IntegrationConfigService security boundaries', () => {
  const originalFetch = global.fetch;

  afterEach(() => {
    global.fetch = originalFetch;
    jest.restoreAllMocks();
  });

  it('masks AppSecret without mutating the parsed source object', async () => {
    const content = JSON.stringify({
      config: { corpId: 'corp', agentId: 'agent', appSecret: 'top-secret' },
    });
    const configRepo = {
      findOne: jest.fn().mockResolvedValue({ id: 1, type: 'THIRD' }),
    };
    const detailRepo = {
      find: jest.fn().mockResolvedValue([{ type: 'DINGTALK_SYNC', enable: true, content }]),
    };
    const service = new IntegrationConfigService(configRepo as never, detailRepo as never);

    await expect(service.getThirdConfig()).resolves.toEqual([
      {
        type: 'DINGTALK',
        verify: true,
        config: { corpId: 'corp', agentId: 'agent' },
      },
    ]);
    expect(JSON.parse(content).config.appSecret).toBe('top-secret');
  });

  it('treats a non-2xx DingTalk token response as a failed connection even if it contains a token field', async () => {
    const configRepo = { findOne: jest.fn() };
    const detailRepo = { findOne: jest.fn() };
    global.fetch = jest.fn().mockResolvedValue({
      ok: false,
      json: jest.fn().mockResolvedValue({ accessToken: 'must-not-be-used' }),
    }) as never;
    const service = new IntegrationConfigService(configRepo as never, detailRepo as never);

    await expect(
      service.testConnection('DINGTALK' as never, {
        corpId: 'corp',
        agentId: 'agent',
        appSecret: 'secret',
      }),
    ).resolves.toBe(false);
  });
});
