"No","テストID","対象クラス","対象メソッド","テスト観点","テストケース名","前提条件","入力値","期待結果","優先度","備考"
"1","UT-CPT-001","Captain::Assistant","belongs_to :account","正常系","アカウントとのリレーションが正しく取得できること","Assistant、Accountが存在","assistant.account","関連するAccountが返却される","中","リレーション"
"2","UT-CPT-002","Captain::Assistant","has_many :documents","正常系","documentsリレーションが正しく取得できること","Assistantに複数のDocumentが存在","assistant.documents","関連するDocument一覧が返却される","中","リレーション"
"3","UT-CPT-003","Captain::Assistant","has_many :responses","正常系","responsesリレーションが正しく取得できること","Assistantに複数のAssistantResponseが存在","assistant.responses","関連するAssistantResponse一覧が返却される","中","リレーション"
"4","UT-CPT-004","Captain::Assistant","has_many :captain_inboxes","正常系","captain_inboxesリレーションが正しく取得できること","AssistantにCaptainInboxが関連付け","assistant.captain_inboxes","関連するCaptainInbox一覧が返却される","中","リレーション"
"5","UT-CPT-005","Captain::Assistant","has_many :inboxes, through: :captain_inboxes","正常系","inboxesリレーションがthroughで正しく取得できること","AssistantにCaptainInbox経由でInboxが関連付け","assistant.inboxes","関連するInbox一覧が返却される","中","リレーション"
"6","UT-CPT-006","Captain::Assistant","has_many :messages, as: :sender","正常系","polymorphic messagesリレーションが正しく取得できること","Assistantがsenderとして複数のMessageを持つ","assistant.messages","関連するMessage一覧が返却される","中","ポリモーフィックリレーション"
"7","UT-CPT-007","Captain::Assistant","has_many :copilot_threads","正常系","copilot_threadsリレーションが正しく取得できること","Assistantに複数のCopilotThreadが存在","assistant.copilot_threads","関連するCopilotThread一覧が返却される","中","リレーション"
"8","UT-CPT-008","Captain::Assistant","has_many :scenarios","正常系","scenariosリレーションが正しく取得できること","Assistantに複数のScenarioが存在","assistant.scenarios","関連するScenario一覧が返却される","中","リレーション"
"9","UT-CPT-009","Captain::Assistant","validates :name, presence: true","異常系","名前が空の場合バリデーションエラーになること","なし","name: nil","バリデーションエラー発生","高","必須バリデーション"
"10","UT-CPT-010","Captain::Assistant","validates :description, presence: true","異常系","descriptionが空の場合バリデーションエラーになること","なし","description: nil","バリデーションエラー発生","高","必須バリデーション"
"11","UT-CPT-011","Captain::Assistant","validates :account_id, presence: true","異常系","account_idが空の場合バリデーションエラーになること","なし","account_id: nil","バリデーションエラー発生","高","必須バリデーション"
"12","UT-CPT-012","Captain::Assistant","store_accessor :config, :temperature","正常系","configからtemperatureが取得できること","config = { temperature: 0.8 }","assistant.temperature","0.8が返却される","中","store_accessor"
"13","UT-CPT-013","Captain::Assistant","store_accessor :config, :feature_faq","正常系","configからfeature_faqが取得できること","config = { feature_faq: true }","assistant.feature_faq","trueが返却される","中","store_accessor"
"14","UT-CPT-014","Captain::Assistant","store_accessor :config, :feature_memory","正常系","configからfeature_memoryが取得できること","config = { feature_memory: true }","assistant.feature_memory","trueが返却される","中","store_accessor"
"15","UT-CPT-015","Captain::Assistant","store_accessor :config, :product_name","正常系","configからproduct_nameが取得できること","config = { product_name: ""Test Product"" }","assistant.product_name","""Test Product""が返却される","中","store_accessor"
"16","UT-CPT-016","Captain::Assistant","scope :ordered","正常系","orderedスコープでcreated_at降順でソートされること","複数のAssistantが存在","Captain::Assistant.ordered","created_at降順でソートされた結果が返却される","低","スコープ"
"17","UT-CPT-017","Captain::Assistant","scope :for_account","正常系","for_accountスコープで指定アカウントのみフィルタされること","複数アカウントにAssistantが存在","Captain::Assistant.for_account(account_id)","指定アカウントのAssistantのみ返却される","中","スコープ"
"18","UT-CPT-018","Captain::Assistant","available_name","正常系","nameがそのまま返却されること","name: ""Test Assistant""","assistant.available_name","""Test Assistant""が返却される","低","アクセサ"
"19","UT-CPT-019","Captain::Assistant","available_agent_tools","正常系","built_in_agent_toolsとcustom_toolsが結合されて返却されること","custom_toolsが存在","assistant.available_agent_tools","built-inとcustomのツール一覧が返却される","高","ツール管理"
"20","UT-CPT-020","Captain::Assistant","available_agent_tools","境界値","custom_toolsが空の場合built_in_agent_toolsのみ返却されること","custom_toolsが存在しない","assistant.available_agent_tools","built-inツールのみが返却される","中","ツール管理"
"21","UT-CPT-021","Captain::Assistant","available_tool_ids","正常系","ツールIDの配列が返却されること","ツールが存在","assistant.available_tool_ids","ツールID配列が返却される","中","ツール管理"
"22","UT-CPT-022","Captain::Assistant","push_event_data","正常系","WebSocket用のイベントデータが正しく生成されること","Assistant存在","assistant.push_event_data","id, name, avatar_url, description, created_at, typeを含むHashが返却される","中","イベントデータ"
"23","UT-CPT-023","Captain::Assistant","push_event_data","境界値","avatar_urlがnilの場合デフォルトURLが使用されること","avatar_urlが設定されていない","assistant.push_event_data","default_avatar_urlが含まれる","低","イベントデータ"
"24","UT-CPT-024","Captain::Assistant","webhook_data","正常系","Webhook用のデータが正しく生成されること","Assistant存在","assistant.webhook_data","id, name, avatar_url, description, created_at, typeを含むHashが返却される","中","Webhookデータ"
"25","UT-CPT-025","Captain::Assistant","agent_name","正常系","名前がparameterize形式で返却されること","name: ""Test Assistant""","assistant.send(:agent_name)","test_assistantが返却される","低","privateメソッド"
"26","UT-CPT-026","Captain::Assistant","agent_tools","正常系","faq_lookupとhandoffツールが返却されること","Assistant存在","assistant.send(:agent_tools)","2つのツールインスタンスが返却される","中","privateメソッド"
"27","UT-CPT-027","Captain::Assistant","prompt_context","正常系","プロンプトコンテキストが正しく構築されること","Assistantにscenarios、response_guidelines、guardrailsが設定済み","assistant.send(:prompt_context)","name, description, product_name, scenarios, response_guidelines, guardrailsを含むHashが返却される","高","プロンプト生成"
"28","UT-CPT-028","Captain::Assistant","prompt_context","境界値","product_nameがnilの場合デフォルト値が使用されること","configにproduct_nameなし","assistant.send(:prompt_context)","""this product""が使用される","低","プロンプト生成"
"29","UT-CPT-029","Captain::Assistant","default_avatar_url","正常系","デフォルトアバターURLが正しく生成されること","FRONTEND_URL環境変数が設定済み","assistant.send(:default_avatar_url)","captain/logo.svgのパスが返却される","低","privateメソッド"
"30","UT-CPT-030","Captain::AssistantResponse","belongs_to :assistant","正常系","assistantリレーションが正しく取得できること","AssistantResponseとAssistantが存在","response.assistant","関連するCaptain::Assistantが返却される","中","リレーション"
"31","UT-CPT-031","Captain::AssistantResponse","belongs_to :account","正常系","accountリレーションが正しく取得できること","AssistantResponseとAccountが存在","response.account","関連するAccountが返却される","中","リレーション"
"32","UT-CPT-032","Captain::AssistantResponse","belongs_to :documentable, polymorphic: true","正常系","polymorphic documentableリレーションが正しく取得できること","documentable_type, documentable_idが設定済み","response.documentable","関連するドキュメントオブジェクトが返却される","中","ポリモーフィックリレーション"
"33","UT-CPT-033","Captain::AssistantResponse","belongs_to :documentable, polymorphic: true","境界値","documentableがnilでも有効であること","documentable未設定","response.documentable","nilが返却される（optional: true）","低","ポリモーフィックリレーション"
"34","UT-CPT-034","Captain::AssistantResponse","has_neighbors :embedding","正常系","embeddingベクトル検索が利用可能であること","embeddingが設定済み","response.nearest_neighbors(:embedding, vector, distance: 'cosine')","類似レスポンスが返却される","高","ベクトル検索"
"35","UT-CPT-035","Captain::AssistantResponse","validates :question, presence: true","異常系","questionが空の場合バリデーションエラーになること","なし","question: nil","バリデーションエラー発生","高","必須バリデーション"
"36","UT-CPT-036","Captain::AssistantResponse","validates :answer, presence: true","異常系","answerが空の場合バリデーションエラーになること","なし","answer: nil","バリデーションエラー発生","高","必須バリデーション"
"37","UT-CPT-037","Captain::AssistantResponse","scope :ordered","正常系","orderedスコープでcreated_at降順でソートされること","複数のAssistantResponseが存在","Captain::AssistantResponse.ordered","created_at降順でソートされた結果が返却される","低","スコープ"
"38","UT-CPT-038","Captain::AssistantResponse","scope :by_account","正常系","by_accountスコープで指定アカウントのみフィルタされること","複数アカウントにResponseが存在","Captain::AssistantResponse.by_account(account_id)","指定アカウントのResponseのみ返却される","中","スコープ"
"39","UT-CPT-039","Captain::AssistantResponse","scope :by_assistant","正常系","by_assistantスコープで指定アシスタントのみフィルタされること","複数アシスタントにResponseが存在","Captain::AssistantResponse.by_assistant(assistant_id)","指定アシスタントのResponseのみ返却される","中","スコープ"
"40","UT-CPT-040","Captain::AssistantResponse","enum status","正常系","pendingステータスが正しく設定されること","なし","response.status = :pending","status == 0（pending）","中","enum"
"41","UT-CPT-041","Captain::AssistantResponse","enum status","正常系","approvedステータスが正しく設定されること","なし","response.status = :approved","status == 1（approved）","中","enum"
"42","UT-CPT-042","Captain::AssistantResponse","self.search","正常系","クエリに類似したレスポンスが検索できること","embeddingが設定されたResponseが存在","Captain::AssistantResponse.search(""query"", account_id: 1)","類似度の高い5件が返却される","高","ベクトル検索"
"43","UT-CPT-043","Captain::AssistantResponse","ensure_status","正常系","statusがnilの場合approvedに設定されること","status未設定","response.valid?","statusがapprovedに設定される","中","before_validation"
"44","UT-CPT-044","Captain::AssistantResponse","ensure_account","正常系","accountがassistantのaccountに設定されること","assistantが設定済み","response.valid?","accountがassistant.accountに設定される","中","before_validation"
"45","UT-CPT-045","Captain::AssistantResponse","update_response_embedding","正常系","questionが変更された場合embeddingが更新されること","question変更","response.update(question: ""new question"")","UpdateEmbeddingJobがenqueueされる","高","after_commit"
"46","UT-CPT-046","Captain::AssistantResponse","update_response_embedding","正常系","answerが変更された場合embeddingが更新されること","answer変更","response.update(answer: ""new answer"")","UpdateEmbeddingJobがenqueueされる","高","after_commit"
"47","UT-CPT-047","Captain::AssistantResponse","update_response_embedding","境界値","question/answer以外の変更ではembeddingが更新されないこと","status変更","response.update(status: :pending)","UpdateEmbeddingJobがenqueueされない","中","after_commit"
"48","UT-CPT-048","Captain::Document","belongs_to :assistant","正常系","assistantリレーションが正しく取得できること","DocumentとAssistantが存在","document.assistant","関連するCaptain::Assistantが返却される","中","リレーション"
"49","UT-CPT-049","Captain::Document","has_many :responses","正常系","responsesリレーションが正しく取得できること","DocumentにAssistantResponseが関連付け","document.responses","関連するAssistantResponse一覧が返却される","中","ポリモーフィックリレーション"
"50","UT-CPT-050","Captain::Document","belongs_to :account","正常系","accountリレーションが正しく取得できること","DocumentとAccountが存在","document.account","関連するAccountが返却される","中","リレーション"
"51","UT-CPT-051","Captain::Document","has_one_attached :pdf_file","正常系","pdf_fileがアタッチ可能であること","PDFファイルが存在","document.pdf_file.attach(io: file, filename: 'test.pdf')","ファイルがアタッチされる","中","ActiveStorage"
"52","UT-CPT-052","Captain::Document","validates :external_link, presence: true","異常系","pdf_file未添付かつexternal_link空の場合バリデーションエラーになること","pdf_file未添付","external_link: nil","バリデーションエラー発生","高","条件付きバリデーション"
"53","UT-CPT-053","Captain::Document","validates :external_link, presence: true","正常系","pdf_file添付時はexternal_link空でも有効であること","pdf_fileが添付済み","external_link: nil","バリデーション成功","中","条件付きバリデーション"
"54","UT-CPT-054","Captain::Document","validates :external_link, uniqueness","異常系","同一assistant_idで重複するexternal_linkの場合エラーになること","同一external_linkのDocumentが存在","external_link: 既存と同じ","バリデーションエラー発生","高","一意性バリデーション"
"55","UT-CPT-055","Captain::Document","validates :content, length: { maximum: 200_000 }","異常系","contentが200,000文字を超える場合エラーになること","なし","content: 200,001文字の文字列","バリデーションエラー発生","中","長さバリデーション"
"56","UT-CPT-056","Captain::Document","validates :content, length: { maximum: 200_000 }","境界値","contentが200,000文字の場合有効であること","なし","content: 200,000文字の文字列","バリデーション成功","中","長さバリデーション境界値"
"57","UT-CPT-057","Captain::Document","validates :pdf_file, presence: true","異常系","PDFドキュメントでpdf_file未添付の場合エラーになること","external_linkが.pdfで終わる","pdf_file未添付","バリデーションエラー発生","高","条件付きバリデーション"
"58","UT-CPT-058","Captain::Document","validate_pdf_format","異常系","pdf_fileがPDF以外の場合エラーになること","jpegファイルがアタッチ","pdf_file: image/jpeg","バリデーションエラー発生","高","フォーマットバリデーション"
"59","UT-CPT-059","Captain::Document","validate_file_attachment","異常系","pdf_fileが10MBを超える場合エラーになること","11MBのPDFがアタッチ","pdf_file: 11MB","バリデーションエラー発生","高","サイズバリデーション"
"60","UT-CPT-060","Captain::Document","validate_file_attachment","境界値","pdf_fileが10MBの場合有効であること","10MBのPDFがアタッチ","pdf_file: 10MB","バリデーション成功","中","サイズバリデーション境界値"
"61","UT-CPT-061","Captain::Document","enum status","正常系","in_progressステータスが正しく設定されること","なし","document.status = :in_progress","status == 0（in_progress）","中","enum"
"62","UT-CPT-062","Captain::Document","enum status","正常系","availableステータスが正しく設定されること","なし","document.status = :available","status == 1（available）","中","enum"
"63","UT-CPT-063","Captain::Document","scope :ordered","正常系","orderedスコープでcreated_at降順でソートされること","複数のDocumentが存在","Captain::Document.ordered","created_at降順でソートされた結果が返却される","低","スコープ"
"64","UT-CPT-064","Captain::Document","scope :for_account","正常系","for_accountスコープで指定アカウントのみフィルタされること","複数アカウントにDocumentが存在","Captain::Document.for_account(account_id)","指定アカウントのDocumentのみ返却される","中","スコープ"
"65","UT-CPT-065","Captain::Document","scope :for_assistant","正常系","for_assistantスコープで指定アシスタントのみフィルタされること","複数アシスタントにDocumentが存在","Captain::Document.for_assistant(assistant_id)","指定アシスタントのDocumentのみ返却される","中","スコープ"
"66","UT-CPT-066","Captain::Document","pdf_document?","正常系","PDFファイルがアタッチされている場合trueを返却すること","application/pdfがアタッチ済み","document.pdf_document?","trueが返却される","中","ドキュメント判定"
"67","UT-CPT-067","Captain::Document","pdf_document?","正常系","external_linkが.pdfで終わる場合trueを返却すること","external_link: 'https://example.com/test.pdf'","document.pdf_document?","trueが返却される","中","ドキュメント判定"
"68","UT-CPT-068","Captain::Document","pdf_document?","正常系","非PDFドキュメントの場合falseを返却すること","external_link: 'https://example.com/page'","document.pdf_document?","falseが返却される","中","ドキュメント判定"
"69","UT-CPT-069","Captain::Document","content_type","正常系","pdf_fileのcontent_typeが返却されること","pdf_fileがアタッチ済み","document.content_type","'application/pdf'が返却される","低","アクセサ"
"70","UT-CPT-070","Captain::Document","content_type","境界値","pdf_file未アタッチの場合nilを返却すること","pdf_file未アタッチ","document.content_type","nilが返却される","低","アクセサ"
"71","UT-CPT-071","Captain::Document","file_size","正常系","pdf_fileのバイトサイズが返却されること","pdf_fileがアタッチ済み","document.file_size","ファイルサイズ(bytes)が返却される","低","アクセサ"
"72","UT-CPT-072","Captain::Document","file_size","境界値","pdf_file未アタッチの場合nilを返却すること","pdf_file未アタッチ","document.file_size","nilが返却される","低","アクセサ"
"73","UT-CPT-073","Captain::Document","openai_file_id","正常系","metadataからopenai_file_idが取得できること","metadata: { 'openai_file_id' => 'file-123' }","document.openai_file_id","'file-123'が返却される","中","メタデータアクセス"
"74","UT-CPT-074","Captain::Document","openai_file_id","境界値","metadataがnilの場合nilを返却すること","metadata: nil","document.openai_file_id","nilが返却される","低","メタデータアクセス"
"75","UT-CPT-075","Captain::Document","store_openai_file_id","正常系","openai_file_idがmetadataに保存されること","Document存在","document.store_openai_file_id('file-456')","metadata['openai_file_id'] == 'file-456'","中","メタデータ更新"
"76","UT-CPT-076","Captain::Document","store_openai_file_id","正常系","既存metadataとマージされること","metadata: { 'key' => 'value' }","document.store_openai_file_id('file-789')","既存キーも保持される","中","メタデータ更新"
"77","UT-CPT-077","Captain::Document","display_url","正常系","external_linkがそのまま返却されること","external_link: 'https://example.com'","document.display_url","'https://example.com'が返却される","低","URL生成"
"78","UT-CPT-078","Captain::Document","display_url","正常系","PDF:で始まる場合はblob_urlが返却されること","external_link: 'PDF: test_...'、pdf_file添付済み","document.display_url","rails_blob_urlが返却される","中","URL生成"
"79","UT-CPT-079","Captain::Document","ensure_account_id","正常系","account_idがassistantから自動設定されること","assistant設定済み、account_id未設定","document.valid?","account_idがassistant.account_idに設定される","中","before_validation"
"80","UT-CPT-080","Captain::Document","ensure_within_plan_limit","異常系","プラン制限を超えている場合LimitExceededErrorが発生すること","利用可能ドキュメント数が0","document.save!","LimitExceededErrorが発生","高","プラン制限"
"81","UT-CPT-081","Captain::Document","set_external_link_for_pdf","正常系","pdf_file添付時にexternal_linkが自動生成されること","pdf_file添付済み、external_link空","document.valid?","'PDF: filename_timestamp'形式のexternal_linkが設定される","中","before_validation"
"82","UT-CPT-082","Captain::Document","normalize_external_link","正常系","末尾のスラッシュが削除されること","external_link: 'https://example.com/'","document.valid?","external_link: 'https://example.com'","低","URL正規化"
"83","UT-CPT-083","Captain::Document","normalize_external_link","正常系","PDFドキュメントの場合は正規化しないこと","external_link: 'PDF: test/'","document.valid?","external_linkが変更されない","低","URL正規化"
"84","UT-CPT-084","Captain::Document","enqueue_crawl_job","正常系","作成時にstatus=in_progressならCrawlJobがenqueueされること","status: :in_progress","document作成","CrawlJobがenqueueされる","高","after_create_commit"
"85","UT-CPT-085","Captain::Document","enqueue_crawl_job","境界値","status=availableの場合CrawlJobがenqueueされないこと","status: :available","document作成","CrawlJobがenqueueされない","中","after_create_commit"
"86","UT-CPT-086","Captain::Document","enqueue_response_builder_job","正常系","availableかつstatus変更時にResponseBuilderJobがenqueueされること","status: in_progress→available","document.update(status: :available)","ResponseBuilderJobがenqueueされる","高","after_commit"
"87","UT-CPT-087","Captain::Document","enqueue_response_builder_job","正常系","content変更時にResponseBuilderJobがenqueueされること","status: available、content変更","document.update(content: 'new content')","ResponseBuilderJobがenqueueされる","高","after_commit"
"88","UT-CPT-088","Captain::Document","enqueue_response_builder_job","境界値","destroyedの場合はJobがenqueueされないこと","document削除","document.destroy","ResponseBuilderJobがenqueueされない","中","after_commit"
"89","UT-CPT-089","Captain::Document","update_document_usage","正常系","作成時にaccountのdocument_usageが更新されること","Document作成","document作成","account.update_document_usageが呼ばれる","中","after_create_commit"
"90","UT-CPT-090","Captain::Document","update_document_usage","正常系","削除時にaccountのdocument_usageが更新されること","Document存在","document.destroy","account.update_document_usageが呼ばれる","中","after_destroy"
"91","UT-CPT-091","Captain::Document::LimitExceededError","LimitExceededError","正常系","カスタム例外クラスが定義されていること","なし","raise Captain::Document::LimitExceededError","例外が発生する","低","例外クラス"
"92","UT-CPT-092","Captain::Scenario","belongs_to :assistant","正常系","assistantリレーションが正しく取得できること","ScenarioとAssistantが存在","scenario.assistant","関連するCaptain::Assistantが返却される","中","リレーション"
"93","UT-CPT-093","Captain::Scenario","belongs_to :account","正常系","accountリレーションが正しく取得できること","ScenarioとAccountが存在","scenario.account","関連するAccountが返却される","中","リレーション"
"94","UT-CPT-094","Captain::Scenario","validates :title, presence: true","異常系","titleが空の場合バリデーションエラーになること","なし","title: nil","バリデーションエラー発生","高","必須バリデーション"
"95","UT-CPT-095","Captain::Scenario","validates :description, presence: true","異常系","descriptionが空の場合バリデーションエラーになること","なし","description: nil","バリデーションエラー発生","高","必須バリデーション"
"96","UT-CPT-096","Captain::Scenario","validates :instruction, presence: true","異常系","instructionが空の場合バリデーションエラーになること","なし","instruction: nil","バリデーションエラー発生","高","必須バリデーション"
"97","UT-CPT-097","Captain::Scenario","validates :assistant_id, presence: true","異常系","assistant_idが空の場合バリデーションエラーになること","なし","assistant_id: nil","バリデーションエラー発生","高","必須バリデーション"
"98","UT-CPT-098","Captain::Scenario","validates :account_id, presence: true","異常系","account_idが空の場合バリデーションエラーになること","なし","account_id: nil","バリデーションエラー発生","高","必須バリデーション"
"99","UT-CPT-099","Captain::Scenario","validate_instruction_tools","異常系","instructionに無効なツール参照がある場合エラーになること","instructionに存在しないツールを参照","instruction: '[Invalid](tool://invalid_tool)'","バリデーションエラー発生","高","カスタムバリデーション"
"100","UT-CPT-100","Captain::Scenario","validate_instruction_tools","正常系","instructionに有効なツール参照がある場合は成功すること","有効なツールが存在","instruction: '[Valid](tool://valid_tool)'","バリデーション成功","中","カスタムバリデーション"
"101","UT-CPT-101","Captain::Scenario","validate_instruction_tools","境界値","instruction空の場合はバリデーションスキップされること","instruction: nil","scenario.valid?","ツールバリデーションがスキップされる","低","カスタムバリデーション"
"102","UT-CPT-102","Captain::Scenario","scope :enabled","正常系","enabledスコープでenabled=trueのみフィルタされること","enabled=true/falseのScenarioが存在","Captain::Scenario.enabled","enabled=trueのScenarioのみ返却される","中","スコープ"
"103","UT-CPT-103","Captain::Scenario","delegate :temperature","正常系","assistantのtemperatureが委譲されること","assistant.temperature設定済み","scenario.temperature","assistant.temperatureが返却される","中","delegate"
"104","UT-CPT-104","Captain::Scenario","delegate :feature_faq","正常系","assistantのfeature_faqが委譲されること","assistant.feature_faq設定済み","scenario.feature_faq","assistant.feature_faqが返却される","中","delegate"
"105","UT-CPT-105","Captain::Scenario","delegate :feature_memory","正常系","assistantのfeature_memoryが委譲されること","assistant.feature_memory設定済み","scenario.feature_memory","assistant.feature_memoryが返却される","中","delegate"
"106","UT-CPT-106","Captain::Scenario","delegate :product_name","正常系","assistantのproduct_nameが委譲されること","assistant.product_name設定済み","scenario.product_name","assistant.product_nameが返却される","中","delegate"
"107","UT-CPT-107","Captain::Scenario","delegate :response_guidelines","正常系","assistantのresponse_guidelinesが委譲されること","assistant.response_guidelines設定済み","scenario.response_guidelines","assistant.response_guidelinesが返却される","中","delegate"
"108","UT-CPT-108","Captain::Scenario","delegate :guardrails","正常系","assistantのguardrailsが委譲されること","assistant.guardrails設定済み","scenario.guardrails","assistant.guardrailsが返却される","中","delegate"
"109","UT-CPT-109","Captain::Scenario","prompt_context","正常系","プロンプトコンテキストが正しく構築されること","Scenario設定済み","scenario.prompt_context","title, instructions, tools, assistant_name, response_guidelines, guardrailsを含むHashが返却される","高","プロンプト生成"
"110","UT-CPT-110","Captain::Scenario","prompt_context","境界値","response_guidelinesがnilの場合空配列が使用されること","response_guidelines: nil","scenario.prompt_context","response_guidelinesが[]になる","低","プロンプト生成"
"111","UT-CPT-111","Captain::Scenario","agent_name","正常系","タイトルからagent名が生成されること","title: ""Test Scenario""","scenario.send(:agent_name)","""test_scenario_agent""が返却される","低","privateメソッド"
"112","UT-CPT-112","Captain::Scenario","agent_tools","正常系","resolved_toolsからツールインスタンスが生成されること","toolsにツールIDが設定済み","scenario.send(:agent_tools)","ツールインスタンスの配列が返却される","中","privateメソッド"
"113","UT-CPT-113","Captain::Scenario","resolved_instructions","正常系","ツール参照がテキスト形式に変換されること","instruction: '[Tool](tool://tool_id)'","scenario.send(:resolved_instructions)","'`tool_id` tool'に変換される","中","privateメソッド"
"114","UT-CPT-114","Captain::Scenario","resolved_tools","正常系","toolsのIDから利用可能なツールメタデータが取得されること","tools: ['tool_id1', 'tool_id2']","scenario.send(:resolved_tools)","ツールメタデータの配列が返却される","中","privateメソッド"
"115","UT-CPT-115","Captain::Scenario","resolved_tools","境界値","tools空の場合は空配列を返却すること","tools: nil","scenario.send(:resolved_tools)","空配列が返却される","低","privateメソッド"
"116","UT-CPT-116","Captain::Scenario","resolve_tool_instance","正常系","customツールのインスタンスが生成されること","custom: trueのツールメタデータ","scenario.send(:resolve_tool_instance, metadata)","CustomToolのtoolインスタンスが返却される","中","privateメソッド"
"117","UT-CPT-117","Captain::Scenario","resolve_tool_instance","正常系","built-inツールのインスタンスが生成されること","custom: falseのツールメタデータ","scenario.send(:resolve_tool_instance, metadata)","built-inツールのインスタンスが返却される","中","privateメソッド"
"118","UT-CPT-118","Captain::Scenario","resolve_tool_references","正常系","instructionからツール参照が抽出されtoolsに設定されること","instruction: '[Tool1](tool://tool1) [Tool2](tool://tool2)'","scenario.save!","tools: ['tool1', 'tool2']","高","before_save"
"119","UT-CPT-119","Captain::Scenario","resolve_tool_references","境界値","ツール参照がない場合toolsがnilになること","instruction: 'No tools here'","scenario.save!","tools: nil","低","before_save"
"120","UT-CPT-120","Captain::CustomTool","belongs_to :account","正常系","accountリレーションが正しく取得できること","CustomToolとAccountが存在","custom_tool.account","関連するAccountが返却される","中","リレーション"
"121","UT-CPT-121","Captain::CustomTool","enum :http_method","正常系","GETメソッドが設定できること","なし","custom_tool.http_method = 'GET'","http_method == 'GET'","中","enum"
"122","UT-CPT-122","Captain::CustomTool","enum :http_method","正常系","POSTメソッドが設定できること","なし","custom_tool.http_method = 'POST'","http_method == 'POST'","中","enum"
"123","UT-CPT-123","Captain::CustomTool","enum :http_method","異常系","不正なHTTPメソッドでエラーになること","なし","custom_tool.http_method = 'DELETE'","バリデーションエラー発生","中","enum"
"124","UT-CPT-124","Captain::CustomTool","enum :auth_type - none","正常系","auth_type noneが設定できること","なし","custom_tool.auth_type = 'none'","auth_type == 'none'","中","enum"
"125","UT-CPT-125","Captain::CustomTool","enum :auth_type - bearer","正常系","auth_type bearerが設定できること","なし","custom_tool.auth_type = 'bearer'","auth_type == 'bearer'","中","enum"
"126","UT-CPT-126","Captain::CustomTool","enum :auth_type - basic","正常系","auth_type basicが設定できること","なし","custom_tool.auth_type = 'basic'","auth_type == 'basic'","中","enum"
"127","UT-CPT-127","Captain::CustomTool","enum :auth_type - api_key","正常系","auth_type api_keyが設定できること","なし","custom_tool.auth_type = 'api_key'","auth_type == 'api_key'","中","enum"
"128","UT-CPT-128","Captain::CustomTool","validates :slug, presence: true","異常系","slugが空の場合バリデーションエラーになること（generate_slug後）","title空でslug未設定","slug: nil, title: nil","バリデーションエラー発生","高","必須バリデーション"
"129","UT-CPT-129","Captain::CustomTool","validates :slug, uniqueness: { scope: :account_id }","異常系","同一account内で重複するslugの場合エラーになること","同一slugのCustomToolが存在","slug: 既存と同じ","バリデーションエラー発生","高","一意性バリデーション"
"130","UT-CPT-130","Captain::CustomTool","validates :title, presence: true","異常系","titleが空の場合バリデーションエラーになること","なし","title: nil","バリデーションエラー発生","高","必須バリデーション"
"131","UT-CPT-131","Captain::CustomTool","validates :endpoint_url, presence: true","異常系","endpoint_urlが空の場合バリデーションエラーになること","なし","endpoint_url: nil","バリデーションエラー発生","高","必須バリデーション"
"132","UT-CPT-132","Captain::CustomTool","JsonSchemaValidator for param_schema","正常系","param_schemaが有効なJSONスキーマの場合成功すること","param_schema: [{ name: 'id', type: 'string', description: 'ID' }]","custom_tool.valid?","バリデーション成功","高","スキーマバリデーション"
"133","UT-CPT-133","Captain::CustomTool","JsonSchemaValidator for param_schema","異常系","param_schemaに必須フィールドがない場合エラーになること","param_schema: [{ name: 'id' }]","custom_tool.valid?","バリデーションエラー発生","高","スキーマバリデーション"
"134","UT-CPT-134","Captain::CustomTool","scope :enabled","正常系","enabledスコープでenabled=trueのみフィルタされること","enabled=true/falseのCustomToolが存在","Captain::CustomTool.enabled","enabled=trueのCustomToolのみ返却される","中","スコープ"
"135","UT-CPT-135","Captain::CustomTool","to_tool_metadata","正常系","ツールメタデータが正しく生成されること","CustomTool設定済み","custom_tool.to_tool_metadata","id(slug), title, description, custom: trueを含むHashが返却される","中","メタデータ生成"
"136","UT-CPT-136","Captain::CustomTool","generate_slug","正常系","titleからslugが自動生成されること","title: 'My Custom Tool', slug未設定","custom_tool.valid?","slug: 'custom_my_custom_tool'が設定される","中","before_validation"
"137","UT-CPT-137","Captain::CustomTool","generate_slug","境界値","slug既存の場合はslugが変更されないこと","slug: 'existing_slug'","custom_tool.valid?","slugが変更されない","低","before_validation"
"138","UT-CPT-138","Captain::CustomTool","generate_slug","境界値","title空の場合はslug生成がスキップされること","title: nil, slug未設定","custom_tool.valid?","slugがnilのまま","低","before_validation"
"139","UT-CPT-139","Captain::CustomTool","find_unique_slug","正常系","重複する場合はランダム文字列が付加されること","同一base_slugのCustomToolが存在","custom_tool.valid?","'custom_title_xxxxxx'形式のslugが設定される","中","privateメソッド"
"140","UT-CPT-140","Captain::CustomTool","find_unique_slug","異常系","5回試行しても一意なslugが見つからない場合エラーになること","slug重複が5回連続発生","custom_tool.valid?","RecordNotUniqueエラーが発生","低","privateメソッド"
"141","UT-CPT-141","Captain::CustomTool","slug_exists?","正常系","同一account_idでslugが存在する場合trueを返却すること","同一slugのCustomToolが存在","custom_tool.send(:slug_exists?, 'existing_slug')","trueが返却される","低","privateメソッド"
"142","UT-CPT-142","Captain::CustomTool","slug_exists?","正常系","slugが存在しない場合falseを返却すること","slugが存在しない","custom_tool.send(:slug_exists?, 'new_slug')","falseが返却される","低","privateメソッド"
"143","UT-CPT-143","Captain::CustomTool (Concerns::Toolable)","tool","正常系","動的にツールクラスが生成されること","param_schema設定済み","custom_tool.tool(assistant)","Captain::Tools::HttpToolのサブクラスインスタンスが返却される","高","ツール生成"
"144","UT-CPT-144","Captain::CustomTool (Concerns::Toolable)","build_request_url","正常系","テンプレート変数が置換されてURLが構築されること","endpoint_url: 'https://api.example.com/{{id}}'","custom_tool.build_request_url({ 'id' => '123' })","'https://api.example.com/123'が返却される","高","URL構築"
"145","UT-CPT-145","Captain::CustomTool (Concerns::Toolable)","build_request_url","境界値","テンプレート変数がない場合はそのまま返却されること","endpoint_url: 'https://api.example.com/data'","custom_tool.build_request_url({})","endpoint_urlがそのまま返却される","中","URL構築"
"146","UT-CPT-146","Captain::CustomTool (Concerns::Toolable)","build_request_body","正常系","テンプレートからリクエストボディが構築されること","request_template: '{ ""id"": ""{{id}}"" }'","custom_tool.build_request_body({ 'id' => '123' })","'{ ""id"": ""123"" }'が返却される","高","ボディ構築"
"147","UT-CPT-147","Captain::CustomTool (Concerns::Toolable)","build_request_body","境界値","request_template空の場合nilを返却すること","request_template: nil","custom_tool.build_request_body({})","nilが返却される","中","ボディ構築"
"148","UT-CPT-148","Captain::CustomTool (Concerns::Toolable)","build_auth_headers - none","正常系","auth_type=noneの場合空ハッシュを返却すること","auth_type: 'none'","custom_tool.build_auth_headers","{}が返却される","中","認証ヘッダー"
"149","UT-CPT-149","Captain::CustomTool (Concerns::Toolable)","build_auth_headers - bearer","正常系","auth_type=bearerの場合Authorizationヘッダーが返却されること","auth_type: 'bearer', auth_config: { 'token' => 'xxx' }","custom_tool.build_auth_headers","{ 'Authorization' => 'Bearer xxx' }が返却される","高","認証ヘッダー"
"150","UT-CPT-150","Captain::CustomTool (Concerns::Toolable)","build_auth_headers - api_key header","正常系","auth_type=api_key(header)の場合カスタムヘッダーが返却されること","auth_type: 'api_key', auth_config: { 'location' => 'header', 'name' => 'X-API-Key', 'key' => 'xxx' }","custom_tool.build_auth_headers","{ 'X-API-Key' => 'xxx' }が返却される","高","認証ヘッダー"
"151","UT-CPT-151","Captain::CustomTool (Concerns::Toolable)","build_auth_headers - api_key query","正常系","auth_type=api_key(query)の場合空ハッシュが返却されること","auth_type: 'api_key', auth_config: { 'location' => 'query' }","custom_tool.build_auth_headers","{}が返却される","中","認証ヘッダー"
"152","UT-CPT-152","Captain::CustomTool (Concerns::Toolable)","build_basic_auth_credentials","正常系","auth_type=basicの場合資格情報が返却されること","auth_type: 'basic', auth_config: { 'username' => 'user', 'password' => 'pass' }","custom_tool.build_basic_auth_credentials","['user', 'pass']が返却される","高","Basic認証"
"153","UT-CPT-153","Captain::CustomTool (Concerns::Toolable)","build_basic_auth_credentials","正常系","auth_type!=basicの場合nilを返却すること","auth_type: 'bearer'","custom_tool.build_basic_auth_credentials","nilが返却される","中","Basic認証"
"154","UT-CPT-154","Captain::CustomTool (Concerns::Toolable)","build_metadata_headers","正常系","メタデータヘッダーが構築されること","state: { account_id: 1, assistant_id: 2 }","custom_tool.build_metadata_headers(state)","X-Chatwoot-Account-Id, X-Chatwoot-Assistant-Id等が含まれる","中","メタデータヘッダー"
"155","UT-CPT-155","Captain::CustomTool (Concerns::Toolable)","add_conversation_headers","正常系","会話関連ヘッダーが追加されること","conversation: { id: 1, display_id: 10 }","custom_tool.add_conversation_headers({}, conversation)","X-Chatwoot-Conversation-Id, Display-Idが含まれる","低","メタデータヘッダー"
"156","UT-CPT-156","Captain::CustomTool (Concerns::Toolable)","add_contact_headers","正常系","連絡先関連ヘッダーが追加されること","contact: { id: 1, email: 'test@example.com', phone_number: '+1234' }","custom_tool.add_contact_headers({}, contact)","X-Chatwoot-Contact-Id, Email, Phoneが含まれる","低","メタデータヘッダー"
"157","UT-CPT-157","Captain::CustomTool (Concerns::Toolable)","format_response","正常系","レスポンステンプレートで整形されること","response_template: '{{ response.data }}'","custom_tool.format_response('{""data"": ""value""}')","'value'が返却される","中","レスポンス整形"
"158","UT-CPT-158","Captain::CustomTool (Concerns::Toolable)","format_response","境界値","response_template空の場合はそのまま返却すること","response_template: nil","custom_tool.format_response('raw_response')","'raw_response'が返却される","中","レスポンス整形"
"159","UT-CPT-159","Captain::CustomTool (Concerns::Toolable)","render_template","異常系","Liquidテンプレートエラーの場合例外が発生すること","テンプレート構文エラー","custom_tool.send(:render_template, '{{ invalid }', {})","Template rendering failed例外が発生","中","テンプレートレンダリング"
"160","UT-CPT-160","Captain::CustomTool (Concerns::SafeEndpointValidatable)","validate_safe_endpoint_url","正常系","HTTPSの有効なURLの場合成功すること","endpoint_url: 'https://api.example.com'","custom_tool.valid?","バリデーション成功","高","エンドポイントバリデーション"
"161","UT-CPT-161","Captain::CustomTool (Concerns::SafeEndpointValidatable)","validate_safe_endpoint_url","異常系","HTTPの場合エラーになること","endpoint_url: 'http://api.example.com'","custom_tool.valid?","'must use HTTPS protocol'エラー","高","エンドポイントバリデーション"
"162","UT-CPT-162","Captain::CustomTool (Concerns::SafeEndpointValidatable)","validate_safe_endpoint_url","異常系","localhostの場合エラーになること","endpoint_url: 'https://localhost/api'","custom_tool.valid?","'cannot use disallowed hostname'エラー","高","エンドポイントバリデーション"
"163","UT-CPT-163","Captain::CustomTool (Concerns::SafeEndpointValidatable)","validate_safe_endpoint_url","異常系","IPアドレスの場合エラーになること","endpoint_url: 'https://192.168.1.1/api'","custom_tool.valid?","'cannot be an IP address'エラー","高","エンドポイントバリデーション"
"164","UT-CPT-164","Captain::CustomTool (Concerns::SafeEndpointValidatable)","validate_safe_endpoint_url","異常系","FRONTEND_URLと同じホストの場合エラーになること","endpoint_url: FRONTEND_URLと同じホスト","custom_tool.valid?","'cannot point to the application itself'エラー","高","エンドポイントバリデーション"
"165","UT-CPT-165","Captain::CustomTool (Concerns::SafeEndpointValidatable)","validate_safe_endpoint_url","異常系",".localで終わるホストの場合エラーになること","endpoint_url: 'https://myserver.local/api'","custom_tool.valid?","'cannot use disallowed hostname'エラー","中","エンドポイントバリデーション"
"166","UT-CPT-166","Captain::CustomTool (Concerns::SafeEndpointValidatable)","validate_safe_endpoint_url","異常系","非ASCII文字を含むホストの場合エラーになること","endpoint_url: 'https://例え.com/api'","custom_tool.valid?","'hostname cannot contain non-ASCII characters'エラー","中","エンドポイントバリデーション"
"167","UT-CPT-167","Captain::CustomTool (Concerns::SafeEndpointValidatable)","parse_endpoint_uri","正常系","Liquidテンプレート構文が除去されてパースされること","endpoint_url: 'https://api.example.com/{{id}}'","custom_tool.valid?","バリデーション成功（placeholderに置換されてパース）","中","URIパース"
"168","UT-CPT-168","Captain::CustomTool (Concerns::SafeEndpointValidatable)","parse_endpoint_uri","異常系","無効なURIの場合エラーになること","endpoint_url: 'not a valid url'","custom_tool.valid?","'must be a valid URL'エラー","中","URIパース"
