Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
backend-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: Install dependencies
run: pip install -r requirements.txt
- name: Run tests
env:
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

secrets を使ってAPIキーを渡すことで、CIでの認証エラーを解決。

run: pytest

frontend-test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Python
backend/venv/
backend/__pycache__/
__pycache__/
.pytest_cache/
*.pyc

# Environment variables
Expand Down
40 changes: 28 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
- 目的フェーズ間のコンテキスト引継ぎの補助となるjsonサマリー出力
- チャット履歴の管理
- Markdownおよびコードブロックの整形表示

---
## 技術スタック

### バックエンド
Expand All @@ -54,7 +54,7 @@
- **UIライブラリ**: shadcn/ui
- **スタイリング**: Tailwind CSS
- **Markdownレンダリング**: react-markdown, remark-gfm, rehype-highlight

---
## セットアップ手順

### 前提条件
Expand All @@ -72,7 +72,7 @@ cd castor
- プロジェクトルート (`castor/`) に `.env` ファイルを作成し、Google AI Studioから取得したAPIキーを設定します。
- APIを無料枠で使用する場合、2.5-flashモデルの仕様を推奨します。
```
GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY_HERE"
GEMINI_API_KEY="YOUR_API_KEY_HERE"
```

### 3. バックエンドのセットアップと起動
Expand Down Expand Up @@ -119,17 +119,28 @@ npm run dev
このスクリプトは、新しいウィンドウでバックエンドとフロントエンドをそれぞれ起動します。


---
## License

This project is licensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details.

---

## 現在の課題
- 要件定義は機能・非機能で行ったが、実装中の気付きに合わせた変更を言語化すべきだった。
- 業務的なチケット化・スコープ管理を意識したが、個人開発では難しい部分が多かった
- PoC的なポートフォリオのため更新が多く、バージョニング/リリース管理の必要性は検討中
- PoC的なポートフォリオのため細かい更新が多く、バージョニング管理の必要性は検討中
- コードアシストを受けた部分と手作業・目視でのフロー確認についての言及
- READMEへのアーキテクチャ図の追加。
- コードアシストを受けた部分と手作業・目視でのフロー確認についての言及。コード内への記載。
- 現在テストは最小限で、開発ではホットリロード+ログデバッグを使用。
- テストコードでの実施が課題。自動化も含め勉強の必要性を感じている。

- ~~業務的なチケット化・スコープ管理を意識したが、個人開発では難しい部分が多かった。~~
→セルフでIssueを立てるようにした。
- ~~テストコードでの実施が課題。自動化も含め勉強の必要性を感じている。~~
→Pytest,Vitestの導入とGithub Actionsを試している

## 履歴
---
## 開発履歴
<details>
<summary>(クリックしてください)</summary>

**2025-11-06**:
- プロジェクトの初期セットアップとディレクトリ構成の整理。
Expand All @@ -151,7 +162,7 @@ npm run dev
- フロントエンドにMarkdownレンダリングを追加。
- READMEを更新し、プロジェクトの目的、設計原則、セットアップ手順、技術スタックを明確化。
- READMEに動作イメージのスクリーンショットを追加。
-

**2025-11-20**:
- 履歴削除機能とUI改善、ダークテーマ/トグルを追加。
- 軽微なバグ修正を実施。
Expand All @@ -162,6 +173,11 @@ npm run dev
- `README.md`内の画像パスをローカルの`images`ディレクトリを参照するように更新。
- ウェルカム画面にフェーズ説明を追加。

## License
**2025-11-26**:
- ライセンスの追加。
- サイドバーのコンポーネントの最適化、メモパッド機能を追加。

This project is licensed under the Apache License, Version 2.0. See the [LICENSE](LICENSE) file for details.
**2025-11-27**:
- テストコードの導入
- GitHub ActionsのCIワークフローを追加
</details>
5 changes: 5 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Google AI Studio (https://aistudio.google.com/app/apikey) から取得したAPIキーを
# 以下の"YOUR_API_KEY_HERE"を実際のAPIキーに置き換えてください。

# GEMINI_API_KEY="YOUR_API_KEY_HERE"
# ファイル名を.envに変更して使用してください。
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ユーザー向けに環境変数のテンプレートを追加。

Empty file added backend/__init__.py
Empty file.
11 changes: 7 additions & 4 deletions backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import traceback
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from contextlib import asynccontextmanager
from .schemas import ChatRequest, HistoryRequest, CreateProjectRequest, NewSessionRequest
from .chat_logic import (
init_chat_on_startup,
Expand All @@ -17,10 +18,16 @@
delete_project, delete_history
)

# --- Lifespan pytestに推奨された ---
@asynccontextmanager
async def lifespan(app: FastAPI):
await init_chat_on_startup()
yield

# --- FastAPI ---
app = FastAPI()


# --- CORS(React/Vite) ---
origins = [
"http://localhost:5173", # Vite default port
Expand All @@ -35,10 +42,6 @@
)


# --- Init and Session Persistence ---
init_chat_on_startup()


# --- Endpoints ---
@app.get("/")
def root():
Expand Down
Binary file modified backend/requirements.txt
Binary file not shown.
90 changes: 90 additions & 0 deletions backend/tests/test_chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import pytest
from fastapi.testclient import TestClient
from app.main import app

# TestClientインスタンスを作成
client = TestClient(app)

# 正常系はGeminiが生成し、異常系は自分で書いてみた

# `send_chat_message` SDKでAPI呼び出しを行う
# `save_message` ユーザー入力とAI応答を保存する
CHAT_LOGIC_PATH = "app.main.send_chat_message"
STORAGE_LOGIC_PATH = "app.main.save_message"

@pytest.mark.asyncio
async def test_chat_endpoint_success(mocker):
"""
/chat エンドポイントの正常系テスト
- 外部関数をモック化する
- 正常なリクエストを送信し、200 OKが返ることを確認する
- モック化した関数が正しく呼び出されることを確認する
"""
# --- モックの設定 ---
# send_chat_message をモック化し、固定の応答を返すように設定
mock_send_chat = mocker.patch(CHAT_LOGIC_PATH, return_value="AIの応答メッセージ")

# save_message をモック化
mock_save_message = mocker.patch(STORAGE_LOGIC_PATH)

# --- テストデータ ---
test_request_body = {
"message": "テストメッセージ",
"project": "test_project",
"phase": "test_phase",
"session_id": "test_session_123"
}

# --- リクエストの実行 ---
# /chat エンドポイントにPOSTリクエストを送信
# TestClientは非同期エンドポイントも同期的に呼び出せる
response = client.post("/chat", json=test_request_body)

# --- 検証 ---
# ステータスコードが200 OKであることを確認
assert response.status_code == 200

# レスポンスボディが期待通りであることを確認
assert response.json() == {"response": "AIの応答メッセージ"}

# --- モックの呼び出し検証 ---
# send_chat_message が1回、指定されたメッセージで呼び出されたことを確認
mock_send_chat.assert_called_once_with(message="テストメッセージ")

# save_message が2回呼び出されたことを確認(ユーザーメッセージとAI応答)
assert mock_save_message.call_count == 2

# 1回目の呼び出し(ユーザーメッセージ)の引数を確認
mock_save_message.call_args_list[0].assert_called_with(
"test_project", "test_phase", "test_session_123", "user", "テストメッセージ"
)
# 2回目の呼び出し(AI応答)の引数を確認
mock_save_message.call_args_list[1].assert_called_with(
"test_project", "test_phase", "test_session_123", "model", "AIの応答メッセージ"
)


@pytest.mark.asyncio
async def test_chat_endpoint_invalid_body(mocker):
"""
/chat エンドポイントの異常系テスト
- 不正なリクエストボディ
- messageフィールドが欠落している場合、400 Bad Requestが返ることを確認する
"""
# --- テストデータ ---
invalid_request_body = {
"project": "test_project",
"phase": "test_phase",
"session_id": "test_session_123"
# "message" フィールドが欠落
}

# --- リクエストの実行 ---
response = client.post("/chat", json=invalid_request_body)

# --- 検証 ---
# ステータスコードが400 Bad Requestであることを確認
assert response.status_code == 422 # FastAPIはバリデーションエラーで422を返す

# エラーメッセージに 'message' フィールドの欠落が含まれていることを確認
assert "message" in response.json()["detail"][0]["loc"]
Loading