From 18bb006d58b16cbe75b7d8b6326ad6bde6e5a534 Mon Sep 17 00:00:00 2001 From: Tanmay Sharma Date: Tue, 12 Aug 2025 12:54:54 +0530 Subject: [PATCH] fix the rspec --- enterprise/app/models/captain/document.rb | 2 - .../llm/pdf_processing_service_spec.rb | 122 +++++++++++ .../captain/pdf_processing_service_spec.rb | 206 ------------------ 3 files changed, 122 insertions(+), 208 deletions(-) create mode 100644 spec/enterprise/services/captain/llm/pdf_processing_service_spec.rb delete mode 100644 spec/enterprise/services/captain/pdf_processing_service_spec.rb diff --git a/enterprise/app/models/captain/document.rb b/enterprise/app/models/captain/document.rb index 95e7e7350..9d40fc73e 100644 --- a/enterprise/app/models/captain/document.rb +++ b/enterprise/app/models/captain/document.rb @@ -4,9 +4,7 @@ # # id :bigint not null, primary key # content :text -# content_type :string # external_link :string not null -# file_size :bigint # metadata :jsonb # name :string # status :integer default("in_progress"), not null diff --git a/spec/enterprise/services/captain/llm/pdf_processing_service_spec.rb b/spec/enterprise/services/captain/llm/pdf_processing_service_spec.rb new file mode 100644 index 000000000..39f2491e1 --- /dev/null +++ b/spec/enterprise/services/captain/llm/pdf_processing_service_spec.rb @@ -0,0 +1,122 @@ +require 'rails_helper' + +RSpec.describe Captain::Llm::PdfProcessingService do + let(:account) { create(:account) } + let(:assistant) { create(:captain_assistant, account: account) } + let(:document) { create(:captain_document, account: account, assistant: assistant) } + let(:openai_client) { instance_double(OpenAI::Client) } + let(:service) do + # Mock the InstallationConfig for base class initialization + allow(InstallationConfig).to receive(:find_by!).with(name: 'CAPTAIN_OPEN_AI_API_KEY') + .and_return(instance_double(InstallationConfig, value: 'test-api-key')) + allow(OpenAI::Client).to receive(:new).and_return(openai_client) + described_class.new(document) + end + + describe '#initialize' do + it 'initializes with document' do + expect(service.instance_variable_get(:@document)).to eq(document) + end + end + + describe '#process' do + let(:files_api) { instance_double(OpenAI::Files) } + let(:temp_file) { instance_double(Tempfile) } + let(:pdf_attachment) { instance_double(ActiveStorage::Attachment) } + + before do + allow(openai_client).to receive(:files).and_return(files_api) + allow(document).to receive(:pdf_file).and_return(pdf_attachment) + allow(pdf_attachment).to receive(:download).and_return('pdf content') + allow(Tempfile).to receive(:new).and_return(temp_file) + allow(temp_file).to receive(:binmode) + allow(temp_file).to receive(:write) + allow(temp_file).to receive(:close) + allow(temp_file).to receive(:path).and_return('/tmp/test.pdf') + allow(temp_file).to receive(:unlink) + allow(File).to receive(:open).with('/tmp/test.pdf', 'rb').and_yield(StringIO.new('pdf content')) + end + + context 'when document already has openai_file_id' do + before do + allow(document).to receive(:openai_file_id).and_return('existing-file-id') + end + + it 'returns success message without uploading' do + expect(files_api).not_to receive(:upload) + result = service.process + expect(result).to eq('PDF ready for paginated processing') + end + end + + context 'when upload is successful' do + let(:upload_response) do + { + 'id' => 'file-abc123', + 'object' => 'file', + 'bytes' => 120_000, + 'created_at' => 1_677_610_602, + 'filename' => 'sample.pdf', + 'purpose' => 'assistants' + } + end + + before do + allow(document).to receive(:openai_file_id).and_return(nil) + allow(files_api).to receive(:upload).and_return(upload_response) + allow(document).to receive(:store_openai_file_id) + end + + it 'uploads the PDF file to OpenAI' do + expect(files_api).to receive(:upload).with( + parameters: { + file: anything, + purpose: 'assistants' + } + ) + + service.process + end + + it 'returns success message with file ID' do + result = service.process + expect(result).to include('file-abc123') + expect(result).to include('PDF ready for paginated processing') + end + + it 'stores the file ID in document' do + expect(document).to receive(:store_openai_file_id).with('file-abc123') + service.process + end + end + + context 'when upload fails' do + before do + allow(document).to receive(:openai_file_id).and_return(nil) + allow(files_api).to receive(:upload).and_raise(StandardError, 'Upload failed') + end + + it 'raises the error' do + expect { service.process }.to raise_error(StandardError, 'Upload failed') + end + end + + context 'when file_id is missing from response' do + let(:invalid_response) do + { + 'object' => 'file', + 'bytes' => 120_000 + } + end + + before do + allow(document).to receive(:openai_file_id).and_return(nil) + allow(files_api).to receive(:upload).and_return(invalid_response) + end + + it 'raises an error' do + expect { service.process }.to raise_error('Failed to upload PDF to OpenAI') + end + end + end +end diff --git a/spec/enterprise/services/captain/pdf_processing_service_spec.rb b/spec/enterprise/services/captain/pdf_processing_service_spec.rb deleted file mode 100644 index 3b369d4ca..000000000 --- a/spec/enterprise/services/captain/pdf_processing_service_spec.rb +++ /dev/null @@ -1,206 +0,0 @@ -require 'rails_helper' - -RSpec.describe Captain::PdfProcessingService do - let(:account) { create(:account) } - let(:assistant) { create(:captain_assistant, account: account) } - let(:pdf_file) do - ActiveStorage::Blob.create_and_upload!( - io: File.open(Rails.root.join('spec/fixtures/files/sample.pdf')), - filename: 'sample.pdf', - content_type: 'application/pdf' - ) - end - let(:service) { described_class.new(assistant: assistant, pdf_file: pdf_file) } - - describe '#initialize' do - it 'initializes with assistant and pdf_file' do - expect(service.instance_variable_get(:@assistant)).to eq(assistant) - expect(service.instance_variable_get(:@pdf_file)).to eq(pdf_file) - end - - it 'raises error when assistant is nil' do - expect { described_class.new(assistant: nil, pdf_file: pdf_file) } - .to raise_error(ArgumentError, 'Assistant is required') - end - - it 'raises error when pdf_file is nil' do - expect { described_class.new(assistant: assistant, pdf_file: nil) } - .to raise_error(ArgumentError, 'PDF file is required') - end - end - - describe '#perform' do - let(:openai_client) { instance_double(OpenAI::Client) } - let(:files_api) { instance_double(OpenAI::Files) } - - before do - allow(service).to receive(:openai_client).and_return(openai_client) - allow(openai_client).to receive(:files).and_return(files_api) - end - - context 'when upload is successful' do - let(:upload_response) do - { - 'id' => 'file-abc123', - 'object' => 'file', - 'bytes' => 120_000, - 'created_at' => 1_677_610_602, - 'filename' => 'sample.pdf', - 'purpose' => 'assistants' - } - end - - before do - allow(files_api).to receive(:upload).and_return(upload_response) - end - - it 'uploads the PDF file to OpenAI' do - expect(files_api).to receive(:upload).with( - parameters: { - file: anything, - purpose: 'assistants' - } - ) - - service.perform - end - - it 'returns the file ID' do - result = service.perform - expect(result).to eq('file-abc123') - end - - it 'attaches the file to the assistant' do - allow(service).to receive(:attach_file_to_assistant) - - expect(service).to receive(:attach_file_to_assistant).with('file-abc123') - - service.perform - end - end - - context 'when upload fails' do - before do - allow(files_api).to receive(:upload).and_raise(StandardError, 'Upload failed') - end - - it 'raises the error' do - expect { service.perform }.to raise_error(StandardError, 'Upload failed') - end - - it 'logs the error' do - expect(Rails.logger).to receive(:error).with(/Failed to upload PDF/) - - expect { service.perform }.to raise_error(StandardError) - end - end - end - - describe '#attach_file_to_assistant' do - let(:openai_client) { instance_double(OpenAI::Client) } - let(:assistants_api) { instance_double(OpenAI::Assistants) } - let(:file_id) { 'file-abc123' } - - before do - allow(service).to receive(:openai_client).and_return(openai_client) - allow(openai_client).to receive(:assistants).and_return(assistants_api) - end - - context 'when attachment is successful' do - let(:assistant_response) do - { - 'id' => assistant.openai_id, - 'file_ids' => %w[file-abc123 file-xyz789] - } - end - - before do - allow(assistants_api).to receive(:modify).and_return(assistant_response) - end - - it 'attaches the file to the assistant' do - expect(assistants_api).to receive(:modify).with( - id: assistant.openai_id, - parameters: { - file_ids: array_including(file_id) - } - ) - - service.send(:attach_file_to_assistant, file_id) - end - - it 'preserves existing file IDs' do - assistant.update!(file_ids: ['file-existing']) - - expect(assistants_api).to receive(:modify).with( - id: assistant.openai_id, - parameters: { - file_ids: ['file-existing', file_id] - } - ) - - service.send(:attach_file_to_assistant, file_id) - end - end - - context 'when attachment fails' do - before do - allow(assistants_api).to receive(:modify).and_raise(StandardError, 'Attachment failed') - end - - it 'raises the error' do - expect { service.send(:attach_file_to_assistant, file_id) } - .to raise_error(StandardError, 'Attachment failed') - end - - it 'logs the error' do - expect(Rails.logger).to receive(:error).with(/Failed to attach file to assistant/) - - expect { service.send(:attach_file_to_assistant, file_id) } - .to raise_error(StandardError) - end - end - end - - describe '#download_pdf_content' do - it 'downloads the PDF file content' do - pdf_file.open do |file| - content = service.send(:download_pdf_content) - expect(content).to eq(file.read) - end - end - - it 'returns binary content' do - content = service.send(:download_pdf_content) - expect(content.encoding).to eq(Encoding::ASCII_8BIT) - end - end - - describe 'integration with OpenAI' do - context 'when OpenAI credentials are configured' do - before do - allow(ENV).to receive(:[]).with('OPENAI_API_KEY').and_return('test-api-key') - end - - it 'creates OpenAI client with correct configuration' do - expect(OpenAI::Client).to receive(:new).with( - access_token: 'test-api-key', - request_timeout: 240 - ) - - service.send(:openai_client) - end - end - - context 'when OpenAI credentials are not configured' do - before do - allow(ENV).to receive(:[]).with('OPENAI_API_KEY').and_return(nil) - end - - it 'raises an error' do - expect { service.send(:openai_client) } - .to raise_error(StandardError, 'OpenAI API key not configured') - end - end - end -end