import { FollowsRepository } from './follows.repository';

describe('FollowsRepository transaction boundary', () => {
  function setup(withTransaction: jest.Mock) {
    const session = { withTransaction, endSession: jest.fn().mockResolvedValue(undefined) };
    const followModel = { db: { startSession: jest.fn().mockResolvedValue(session) } };
    const repository = new FollowsRepository(followModel as never, {} as never);
    return { repository, session };
  }

  it('returns only after the transaction callback commits and always closes the session', async () => {
    const ctx = setup(jest.fn(async (callback: () => Promise<void>) => callback()));
    const operation = jest.fn().mockResolvedValue('committed');
    await expect(ctx.repository.runInTransaction(operation, true)).resolves.toBe('committed');
    expect(operation).toHaveBeenCalledWith(ctx.session);
    expect(ctx.session.endSession).toHaveBeenCalled();
  });

  it('propagates transaction failures in microservices mode without an unsafe fallback', async () => {
    const ctx = setup(jest.fn(async (callback: () => Promise<void>) => callback()));
    const operation = jest.fn().mockRejectedValue(new Error('outbox insert failed'));
    await expect(ctx.repository.runInTransaction(operation, true)).rejects.toThrow('outbox insert failed');
    expect(operation).toHaveBeenCalledTimes(1);
    expect(ctx.session.endSession).toHaveBeenCalled();
  });

  it('allows only the documented legacy fallback when standalone Mongo rejects transactions', async () => {
    const ctx = setup(jest.fn().mockRejectedValue(new Error('Transaction numbers are only allowed on a replica set member or mongos')));
    const operation = jest.fn().mockResolvedValue('legacy');
    await expect(ctx.repository.runInTransaction(operation, false)).resolves.toBe('legacy');
    expect(operation).toHaveBeenCalledWith(undefined);
  });
});
