HEX
Server: LiteSpeed
System: Linux houston.panomity.com 6.8.0-100-generic #100-Ubuntu SMP PREEMPT_DYNAMIC Tue Jan 13 16:40:06 UTC 2026 x86_64
User: nudepix (1011)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //opt/LC/api/server/services/Threads/processMessages.spec.js
const { retrieveAndProcessFile } = require('~/server/services/Files/process');
const { processMessages } = require('./manage');

jest.mock('~/server/services/Files/process', () => ({
  retrieveAndProcessFile: jest.fn(),
}));

describe('processMessages', () => {
  let openai, client;

  beforeEach(() => {
    openai = {};
    client = {
      processedFileIds: new Set(),
    };
    jest.clearAllMocks();
    retrieveAndProcessFile.mockReset();
  });

  test('handles normal case with single source', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'This is a test ^1^ and another^1^',
              annotations: [
                {
                  type: 'file_citation',
                  start_index: 15,
                  end_index: 18,
                  file_citation: { file_id: 'file1' },
                },
                {
                  type: 'file_citation',
                  start_index: 30,
                  end_index: 33,
                  file_citation: { file_id: 'file1' },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockResolvedValue({ filename: 'test.txt' });

    const result = await processMessages({ openai, client, messages });

    expect(result.text).toBe('This is a test ^1^ and another^1^\n\n^1.^ test.txt');
    expect(result.edited).toBe(true);
  });

  test('handles multiple different sources', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'This is a test ^1^ and another^2^',
              annotations: [
                {
                  type: 'file_citation',
                  start_index: 15,
                  end_index: 18,
                  file_citation: { file_id: 'file1' },
                },
                {
                  type: 'file_citation',
                  start_index: 30,
                  end_index: 33,
                  file_citation: { file_id: 'file2' },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile
      .mockResolvedValueOnce({ filename: 'test1.txt' })
      .mockResolvedValueOnce({ filename: 'test2.txt' });

    const result = await processMessages({ openai, client, messages });

    expect(result.text).toBe('This is a test ^1^ and another^2^\n\n^1.^ test1.txt\n^2.^ test2.txt');
    expect(result.edited).toBe(true);
  });

  test('handles file retrieval failure', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'This is a test ^1^',
              annotations: [
                {
                  type: 'file_citation',
                  start_index: 15,
                  end_index: 18,
                  file_citation: { file_id: 'file1' },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockRejectedValue(new Error('File not found'));

    const result = await processMessages({ openai, client, messages });

    expect(result.text).toBe('This is a test ^1^');
    expect(result.edited).toBe(false);
  });

  test('handles citations without file ids', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'This is a test ^1^',
              annotations: [{ type: 'file_citation', start_index: 15, end_index: 18 }],
            },
          },
        ],
        created_at: 1,
      },
    ];

    const result = await processMessages({ openai, client, messages });

    expect(result.text).toBe('This is a test ^1^');
    expect(result.edited).toBe(false);
  });

  test('handles mixed valid and invalid citations', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'This is a test ^1^ and ^2^ and ^3^',
              annotations: [
                {
                  type: 'file_citation',
                  start_index: 15,
                  end_index: 18,
                  file_citation: { file_id: 'file1' },
                },
                { type: 'file_citation', start_index: 23, end_index: 26 },
                {
                  type: 'file_citation',
                  start_index: 31,
                  end_index: 34,
                  file_citation: { file_id: 'file3' },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile
      .mockResolvedValueOnce({ filename: 'test1.txt' })
      .mockResolvedValueOnce({ filename: 'test3.txt' });

    const result = await processMessages({ openai, client, messages });

    expect(result.text).toBe(
      'This is a test ^1^ and ^2^ and ^2^\n\n^1.^ test1.txt\n^2.^ test3.txt',
    );
    expect(result.edited).toBe(true);
  });

  test('handles adjacent identical citations', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'This is a test ^1^^1^ and ^1^ ^1^',
              annotations: [
                {
                  type: 'file_citation',
                  start_index: 15,
                  end_index: 18,
                  file_citation: { file_id: 'file1' },
                },
                {
                  type: 'file_citation',
                  start_index: 18,
                  end_index: 21,
                  file_citation: { file_id: 'file1' },
                },
                {
                  type: 'file_citation',
                  start_index: 26,
                  end_index: 29,
                  file_citation: { file_id: 'file1' },
                },
                {
                  type: 'file_citation',
                  start_index: 30,
                  end_index: 33,
                  file_citation: { file_id: 'file1' },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockResolvedValue({ filename: 'test.txt' });

    const result = await processMessages({ openai, client, messages });

    expect(result.text).toBe('This is a test ^1^ and ^1^\n\n^1.^ test.txt');
    expect(result.edited).toBe(true);
  });
  test('handles real data with multiple adjacent citations', async () => {
    const messages = [
      {
        id: 'msg_XXXXXXXXXXXXXXXXXXXX',
        object: 'thread.message',
        created_at: 1722980324,
        assistant_id: 'asst_XXXXXXXXXXXXXXXXXXXX',
        thread_id: 'thread_XXXXXXXXXXXXXXXXXXXX',
        run_id: 'run_XXXXXXXXXXXXXXXXXXXX',
        status: 'completed',
        incomplete_details: null,
        incomplete_at: null,
        completed_at: 1722980331,
        role: 'assistant',
        content: [
          {
            type: 'text',
            text: {
              value:
                'The text you have uploaded is from the book "Harry Potter and the Philosopher\'s Stone" by J.K. Rowling. It follows the story of a young boy named Harry Potter who discovers that he is a wizard on his eleventh birthday. Here are some key points of the narrative:\n\n1. **Discovery and Invitation to Hogwarts**: Harry learns that he is a wizard and receives an invitation to attend Hogwarts School of Witchcraft and Wizardry【11:2†source】【11:4†source】.\n\n2. **Shopping for Supplies**: Hagrid takes Harry to Diagon Alley to buy his school supplies, including his wand from Ollivander\'s【11:9†source】【11:14†source】.\n\n3. **Introduction to Hogwarts**: Harry is introduced to Hogwarts, the magical school where he will learn about magic and discover more about his own background【11:12†source】【11:18†source】.\n\n4. **Meeting Friends and Enemies**: At Hogwarts, Harry makes friends like Ron Weasley and Hermione Granger, and enemies like Draco Malfoy【11:16†source】.\n\n5. **Uncovering the Mystery**: Harry, along with Ron and Hermione, uncovers the mystery of the Philosopher\'s Stone and its connection to the dark wizard Voldemort【11:1†source】【11:10†source】【11:7†source】.\n\nThese points highlight Harry\'s initial experiences in the magical world and set the stage for his adventures at Hogwarts.',
              annotations: [
                {
                  type: 'file_citation',
                  text: '【11:2†source】',
                  start_index: 420,
                  end_index: 433,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:4†source】',
                  start_index: 433,
                  end_index: 446,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:9†source】',
                  start_index: 578,
                  end_index: 591,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:14†source】',
                  start_index: 591,
                  end_index: 605,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:12†source】',
                  start_index: 767,
                  end_index: 781,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:18†source】',
                  start_index: 781,
                  end_index: 795,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:16†source】',
                  start_index: 935,
                  end_index: 949,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:1†source】',
                  start_index: 1114,
                  end_index: 1127,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:10†source】',
                  start_index: 1127,
                  end_index: 1141,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:7†source】',
                  start_index: 1141,
                  end_index: 1154,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
              ],
            },
          },
        ],
        attachments: [],
        metadata: {},
        files: [
          {
            object: 'file',
            id: 'file-XXXXXXXXXXXXXXXXXXXX',
            purpose: 'assistants',
            filename: 'hp1.txt',
            bytes: 439742,
            created_at: 1722962139,
            status: 'processed',
            status_details: null,
            type: 'text/plain',
            file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
            filepath:
              'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-XXXXXXXXXXXXXXXXXXXX/hp1.txt',
            usage: 1,
            user: 'XXXXXXXXXXXXXXXXXXXX',
            context: 'assistants',
            source: 'openai',
            model: 'gpt-4o',
          },
        ],
      },
    ];

    retrieveAndProcessFile.mockResolvedValue({ filename: 'hp1.txt' });

    const result = await processMessages({
      openai: {},
      client: { processedFileIds: new Set() },
      messages,
    });

    const expectedText = `The text you have uploaded is from the book "Harry Potter and the Philosopher's Stone" by J.K. Rowling. It follows the story of a young boy named Harry Potter who discovers that he is a wizard on his eleventh birthday. Here are some key points of the narrative:

1. **Discovery and Invitation to Hogwarts**: Harry learns that he is a wizard and receives an invitation to attend Hogwarts School of Witchcraft and Wizardry^1^.

2. **Shopping for Supplies**: Hagrid takes Harry to Diagon Alley to buy his school supplies, including his wand from Ollivander's^1^.

3. **Introduction to Hogwarts**: Harry is introduced to Hogwarts, the magical school where he will learn about magic and discover more about his own background^1^.

4. **Meeting Friends and Enemies**: At Hogwarts, Harry makes friends like Ron Weasley and Hermione Granger, and enemies like Draco Malfoy^1^.

5. **Uncovering the Mystery**: Harry, along with Ron and Hermione, uncovers the mystery of the Philosopher's Stone and its connection to the dark wizard Voldemort^1^.

These points highlight Harry's initial experiences in the magical world and set the stage for his adventures at Hogwarts.

^1.^ hp1.txt`;

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(true);
  });

  test('handles real data with multiple adjacent citations with multiple sources', async () => {
    const messages = [
      {
        id: 'msg_XXXXXXXXXXXXXXXXXXXX',
        object: 'thread.message',
        created_at: 1722980324,
        assistant_id: 'asst_XXXXXXXXXXXXXXXXXXXX',
        thread_id: 'thread_XXXXXXXXXXXXXXXXXXXX',
        run_id: 'run_XXXXXXXXXXXXXXXXXXXX',
        status: 'completed',
        incomplete_details: null,
        incomplete_at: null,
        completed_at: 1722980331,
        role: 'assistant',
        content: [
          {
            type: 'text',
            text: {
              value:
                'The text you have uploaded is from the book "Harry Potter and the Philosopher\'s Stone" by J.K. Rowling. It follows the story of a young boy named Harry Potter who discovers that he is a wizard on his eleventh birthday. Here are some key points of the narrative:\n\n1. **Discovery and Invitation to Hogwarts**: Harry learns that he is a wizard and receives an invitation to attend Hogwarts School of Witchcraft and Wizardry【11:2†source】【11:4†source】.\n\n2. **Shopping for Supplies**: Hagrid takes Harry to Diagon Alley to buy his school supplies, including his wand from Ollivander\'s【11:9†source】【11:14†source】.\n\n3. **Introduction to Hogwarts**: Harry is introduced to Hogwarts, the magical school where he will learn about magic and discover more about his own background【11:12†source】【11:18†source】.\n\n4. **Meeting Friends and Enemies**: At Hogwarts, Harry makes friends like Ron Weasley and Hermione Granger, and enemies like Draco Malfoy【11:16†source】.\n\n5. **Uncovering the Mystery**: Harry, along with Ron and Hermione, uncovers the mystery of the Philosopher\'s Stone and its connection to the dark wizard Voldemort【11:1†source】【11:10†source】【11:7†source】.\n\nThese points highlight Harry\'s initial experiences in the magical world and set the stage for his adventures at Hogwarts.',
              annotations: [
                {
                  type: 'file_citation',
                  text: '【11:2†source】',
                  start_index: 420,
                  end_index: 433,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:4†source】',
                  start_index: 433,
                  end_index: 446,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:9†source】',
                  start_index: 578,
                  end_index: 591,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:14†source】',
                  start_index: 591,
                  end_index: 605,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:12†source】',
                  start_index: 767,
                  end_index: 781,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:18†source】',
                  start_index: 781,
                  end_index: 795,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:16†source】',
                  start_index: 935,
                  end_index: 949,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:1†source】',
                  start_index: 1114,
                  end_index: 1127,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:10†source】',
                  start_index: 1127,
                  end_index: 1141,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_citation',
                  text: '【11:7†source】',
                  start_index: 1141,
                  end_index: 1154,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
              ],
            },
          },
        ],
        attachments: [],
        metadata: {},
        files: [
          {
            object: 'file',
            id: 'file-XXXXXXXXXXXXXXXXXXXX',
            purpose: 'assistants',
            filename: 'hp1.txt',
            bytes: 439742,
            created_at: 1722962139,
            status: 'processed',
            status_details: null,
            type: 'text/plain',
            file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
            filepath:
              'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-XXXXXXXXXXXXXXXXXXXX/hp1.txt',
            usage: 1,
            user: 'XXXXXXXXXXXXXXXXXXXX',
            context: 'assistants',
            source: 'openai',
            model: 'gpt-4o',
          },
        ],
      },
    ];

    retrieveAndProcessFile.mockResolvedValue({ filename: 'hp1.txt' });

    const result = await processMessages({
      openai: {},
      client: { processedFileIds: new Set() },
      messages,
    });

    const expectedText = `The text you have uploaded is from the book "Harry Potter and the Philosopher's Stone" by J.K. Rowling. It follows the story of a young boy named Harry Potter who discovers that he is a wizard on his eleventh birthday. Here are some key points of the narrative:

1. **Discovery and Invitation to Hogwarts**: Harry learns that he is a wizard and receives an invitation to attend Hogwarts School of Witchcraft and Wizardry^1^.

2. **Shopping for Supplies**: Hagrid takes Harry to Diagon Alley to buy his school supplies, including his wand from Ollivander's^1^.

3. **Introduction to Hogwarts**: Harry is introduced to Hogwarts, the magical school where he will learn about magic and discover more about his own background^1^.

4. **Meeting Friends and Enemies**: At Hogwarts, Harry makes friends like Ron Weasley and Hermione Granger, and enemies like Draco Malfoy^1^.

5. **Uncovering the Mystery**: Harry, along with Ron and Hermione, uncovers the mystery of the Philosopher's Stone and its connection to the dark wizard Voldemort^1^.

These points highlight Harry's initial experiences in the magical world and set the stage for his adventures at Hogwarts.

^1.^ hp1.txt`;

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(true);
  });

  test('handles edge case with pre-existing citation-like text', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value:
                'This is a test ^1^ with pre-existing citation-like text. Here\'s a real citation【11:2†source】.',
              annotations: [
                {
                  type: 'file_citation',
                  text: '【11:2†source】',
                  start_index: 79,
                  end_index: 92,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockResolvedValue({ filename: 'test.txt' });

    const result = await processMessages({
      openai: {},
      client: { processedFileIds: new Set() },
      messages,
    });

    const expectedText =
      'This is a test ^1^ with pre-existing citation-like text. Here\'s a real citation^1^.\n\n^1.^ test.txt';

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(true);
  });

  test('handles FILE_PATH annotation type', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'Here is a file path: [file_path]',
              annotations: [
                {
                  type: 'file_path',
                  text: '[file_path]',
                  start_index: 21,
                  end_index: 32,
                  file_path: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockResolvedValue({
      filename: 'test.txt',
      filepath: '/path/to/test.txt',
    });

    const result = await processMessages({
      openai: {},
      client: { processedFileIds: new Set() },
      messages,
    });

    const expectedText = 'Here is a file path: /path/to/test.txt';

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(true);
  });

  test('handles FILE_CITATION annotation type', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'Here is a citation: [citation]',
              annotations: [
                {
                  type: 'file_citation',
                  text: '[citation]',
                  start_index: 20,
                  end_index: 30,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockResolvedValue({ filename: 'test.txt' });

    const result = await processMessages({
      openai: {},
      client: { processedFileIds: new Set() },
      messages,
    });

    const expectedText = 'Here is a citation: ^1^\n\n^1.^ test.txt';

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(true);
  });

  test('handles multiple annotation types in a single message', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value:
                'File path: [file_path]. Citation: [citation1]. Another citation: [citation2].',
              annotations: [
                {
                  type: 'file_path',
                  text: '[file_path]',
                  start_index: 11,
                  end_index: 22,
                  file_path: {
                    file_id: 'file-XXXXXXXXXXXXXXXX1',
                  },
                },
                {
                  type: 'file_citation',
                  text: '[citation1]',
                  start_index: 34,
                  end_index: 45,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXX2',
                  },
                },
                {
                  type: 'file_citation',
                  text: '[citation2]',
                  start_index: 65,
                  end_index: 76,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXX3',
                  },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockResolvedValueOnce({
      filename: 'file1.txt',
      filepath: '/path/to/file1.txt',
    });
    retrieveAndProcessFile.mockResolvedValueOnce({ filename: 'file2.txt' });
    retrieveAndProcessFile.mockResolvedValueOnce({ filename: 'file3.txt' });

    const result = await processMessages({
      openai: {},
      client: { processedFileIds: new Set() },
      messages,
    });

    const expectedText =
      'File path: /path/to/file1.txt. Citation: ^1^. Another citation: ^2^.\n\n^1.^ file2.txt\n^2.^ file3.txt';

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(true);
  });

  test('handles annotation processing failure', async () => {
    const messages = [
      {
        content: [
          {
            type: 'text',
            text: {
              value: 'This citation will fail: [citation]',
              annotations: [
                {
                  type: 'file_citation',
                  text: '[citation]',
                  start_index: 25,
                  end_index: 35,
                  file_citation: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
              ],
            },
          },
        ],
        created_at: 1,
      },
    ];

    retrieveAndProcessFile.mockRejectedValue(new Error('File not found'));

    const result = await processMessages({
      openai: {},
      client: { processedFileIds: new Set() },
      messages,
    });

    const expectedText = 'This citation will fail: [citation]';

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(false);
  });

  test('handles multiple FILE_PATH annotations with sandbox links', async () => {
    const messages = [
      {
        id: 'msg_XXXXXXXXXXXXXXXXXXXX',
        object: 'thread.message',
        created_at: 1722983745,
        assistant_id: 'asst_XXXXXXXXXXXXXXXXXXXX',
        thread_id: 'thread_XXXXXXXXXXXXXXXXXXXX',
        run_id: 'run_XXXXXXXXXXXXXXXXXXXX',
        status: 'completed',
        incomplete_details: null,
        incomplete_at: null,
        completed_at: 1722983747,
        role: 'assistant',
        content: [
          {
            type: 'text',
            text: {
              value:
                'I have generated three dummy CSV files for you. You can download them using the links below:\n\n1. [Download Dummy Data 1](sandbox:/mnt/data/dummy_data1.csv)\n2. [Download Dummy Data 2](sandbox:/mnt/data/dummy_data2.csv)\n3. [Download Dummy Data 3](sandbox:/mnt/data/dummy_data3.csv)',
              annotations: [
                {
                  type: 'file_path',
                  text: 'sandbox:/mnt/data/dummy_data1.csv',
                  start_index: 121,
                  end_index: 154,
                  file_path: {
                    file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
                  },
                },
                {
                  type: 'file_path',
                  text: 'sandbox:/mnt/data/dummy_data2.csv',
                  start_index: 183,
                  end_index: 216,
                  file_path: {
                    file_id: 'file-YYYYYYYYYYYYYYYYYYYY',
                  },
                },
                {
                  type: 'file_path',
                  text: 'sandbox:/mnt/data/dummy_data3.csv',
                  start_index: 245,
                  end_index: 278,
                  file_path: {
                    file_id: 'file-ZZZZZZZZZZZZZZZZZZZZ',
                  },
                },
              ],
            },
          },
        ],
        attachments: [
          {
            file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
            tools: [
              {
                type: 'code_interpreter',
              },
            ],
          },
          {
            file_id: 'file-YYYYYYYYYYYYYYYYYYYY',
            tools: [
              {
                type: 'code_interpreter',
              },
            ],
          },
          {
            file_id: 'file-ZZZZZZZZZZZZZZZZZZZZ',
            tools: [
              {
                type: 'code_interpreter',
              },
            ],
          },
        ],
        metadata: {},
        files: [
          {
            object: 'file',
            id: 'file-XXXXXXXXXXXXXXXXXXXX',
            purpose: 'assistants_output',
            filename: 'dummy_data1.csv',
            bytes: 1925,
            created_at: 1722983746,
            status: 'processed',
            status_details: null,
            type: 'text/csv',
            file_id: 'file-XXXXXXXXXXXXXXXXXXXX',
            filepath:
              'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-XXXXXXXXXXXXXXXXXXXX/dummy_data1.csv',
            usage: 1,
            user: 'XXXXXXXXXXXXXXXXXXXX',
            context: 'assistants_output',
            source: 'openai',
            model: 'gpt-4o-mini',
          },
          {
            object: 'file',
            id: 'file-YYYYYYYYYYYYYYYYYYYY',
            purpose: 'assistants_output',
            filename: 'dummy_data2.csv',
            bytes: 4221,
            created_at: 1722983746,
            status: 'processed',
            status_details: null,
            type: 'text/csv',
            file_id: 'file-YYYYYYYYYYYYYYYYYYYY',
            filepath:
              'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-YYYYYYYYYYYYYYYYYYYY/dummy_data2.csv',
            usage: 1,
            user: 'XXXXXXXXXXXXXXXXXXXX',
            context: 'assistants_output',
            source: 'openai',
            model: 'gpt-4o-mini',
          },
          {
            object: 'file',
            id: 'file-ZZZZZZZZZZZZZZZZZZZZ',
            purpose: 'assistants_output',
            filename: 'dummy_data3.csv',
            bytes: 3534,
            created_at: 1722983747,
            status: 'processed',
            status_details: null,
            type: 'text/csv',
            file_id: 'file-ZZZZZZZZZZZZZZZZZZZZ',
            filepath:
              'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-ZZZZZZZZZZZZZZZZZZZZ/dummy_data3.csv',
            usage: 1,
            user: 'XXXXXXXXXXXXXXXXXXXX',
            context: 'assistants_output',
            source: 'openai',
            model: 'gpt-4o-mini',
          },
        ],
      },
    ];

    const mockClient = {
      processedFileIds: new Set(),
    };

    // Mock the retrieveAndProcessFile function for each file
    retrieveAndProcessFile.mockImplementation(({ file_id }) => {
      const fileMap = {
        'file-XXXXXXXXXXXXXXXXXXXX': {
          filename: 'dummy_data1.csv',
          filepath:
            'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-XXXXXXXXXXXXXXXXXXXX/dummy_data1.csv',
        },
        'file-YYYYYYYYYYYYYYYYYYYY': {
          filename: 'dummy_data2.csv',
          filepath:
            'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-YYYYYYYYYYYYYYYYYYYY/dummy_data2.csv',
        },
        'file-ZZZZZZZZZZZZZZZZZZZZ': {
          filename: 'dummy_data3.csv',
          filepath:
            'https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-ZZZZZZZZZZZZZZZZZZZZ/dummy_data3.csv',
        },
      };

      return Promise.resolve(fileMap[file_id]);
    });

    const result = await processMessages({ openai: {}, client: mockClient, messages });

    const expectedText =
      'I have generated three dummy CSV files for you. You can download them using the links below:\n\n1. [Download Dummy Data 1](https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-XXXXXXXXXXXXXXXXXXXX/dummy_data1.csv)\n2. [Download Dummy Data 2](https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-YYYYYYYYYYYYYYYYYYYY/dummy_data2.csv)\n3. [Download Dummy Data 3](https://api.openai.com/v1/files/XXXXXXXXXXXXXXXXXXXX/file-ZZZZZZZZZZZZZZZZZZZZ/dummy_data3.csv)';

    expect(result.text).toBe(expectedText);
    expect(result.edited).toBe(true);
  });
});