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
54 changes: 25 additions & 29 deletions backend/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
allow_headers=["*"],
)

def check_empty_text(v: str) -> str:
if not v or not v.strip():
raise ValueError("text must not be empty")
return v


# ============================================
# REQUEST/RESPONSE SCHEMAS
Expand All @@ -37,11 +42,28 @@ class GenerateRequest(BaseModel):
@field_validator("text")
@classmethod
def text_must_not_be_empty(cls, v: str) -> str:
if not v or not v.strip():
raise ValueError("text must not be empty")
return check_empty_text(v)


class StudyPackRequest(GenerateRequest):
"""
Request body for POST /generate-study-pack
- text: The user's study notes to process
"""
@field_validator("text")
@classmethod
def text_length_constraint(cls, v: str) -> str:
v = check_empty_text(v)
stripped = v.strip()
# validate length
if len(stripped) < 10:
raise ValueError(f"text must not be less than 10 characters")
if len(stripped) > 10000:
raise ValueError("text must not be more than 10000 characters")
return v
Comment on lines +53 to +63
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Length constraints now apply to /api/v1/generate too

Before this PR, GenerateRequest only validated that text was non-empty. Adding text_length_constraint here means the /api/v1/generate endpoint now also enforces min 10 / max 10,000 character limits — a breaking change for any existing callers that send short notes (< 10 chars) or very long notes (> 10,000 chars).

If this is intentional, the docstring for /api/v1/generate (line 141) should document the new constraints. If unintentional, consider keeping the length validator only in a subclass or applying it selectively to the /generate-study-pack route.

Comment on lines +48 to +63
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Child validator overrides parent's empty check

In Pydantic v2, StudyPackRequest.text_length_constraint completely replaces GenerateRequest.text_must_not_be_empty instead of running both. Empty/whitespace text will pass through StudyPackRequest validation unchecked.

Suggested change
class StudyPackRequest(GenerateRequest):
"""
Request body for POST /generate-study-pack
- text: The user's study notes to process
"""
@field_validator("text")
@classmethod
def text_length_constraint(cls, v: str) -> str:
stripped = v.strip()
# validate length
if len(stripped) < 10:
raise ValueError(f"text must not be less than 10 characters")
if len(stripped) > 10000:
raise ValueError("text must not be more than 10000 characters")
return v
class StudyPackRequest(GenerateRequest):
"""
Request body for POST /generate-study-pack
- text: The user's study notes to process
"""
@field_validator("text")
@classmethod
def text_length_constraint(cls, v: str) -> str:
# Must re-check emptiness since child validators override parent
stripped = v.strip()
if not v or not stripped:
raise ValueError("text must not be empty")
# validate length
if len(stripped) < 10:
raise ValueError(f"text must not be less than 10 characters")
if len(stripped) > 10000:
raise ValueError("text must not be more than 10000 characters")
return v




class QuizQuestion(BaseModel):
"""A single quiz question with multiple choice options"""
question: str
Expand All @@ -59,32 +81,6 @@ class GenerateResponse(BaseModel):
quiz: list[QuizQuestion]


# ============================================
# STUDY PACK REQUESTS
# ============================================

class StudyPackRequest(BaseModel):
"""
Request body for POST /generate-study-pack
- text: The user's study notes to process
"""
text: str

@field_validator('text')
@classmethod
def validate_text(cls, v: str) -> str:
# do not include whitespace
stripped = v.strip()
# validate emptiness
if not v or not stripped:
raise ValueError("text must not be empty")
# validate length
if len(stripped) < 10:
raise ValueError(f"text must not be less than 10 characters")
if len(stripped) > 10000:
raise ValueError("text must not be more than 10000 characters")
return v


# ============================================
# STUDY PACK HELPER FUNCTIONS
Expand Down Expand Up @@ -280,7 +276,7 @@ async def generate_study_pack(request: StudyPackRequest):
if response is None:
raise HTTPException(
status_code=500,
detail="Gemini API is unavailable. Please ensure GEMINI_API_KEY is set in .env file."
detail="Gemini unavailable. Please try again."
)

try:
Expand Down
4 changes: 2 additions & 2 deletions backend/tests/test_studypack_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ def test_handles_gemini_api_unavailable(self, mock_gemini, client):
)

assert response.status_code == 500
assert "GEMINI_API_KEY" in response.json()["detail"]
assert "Gemini unavailable" in response.json()["detail"]

@patch.object(GeminiService, 'call_gemini', new_callable=AsyncMock)
def test_handles_invalid_json_from_gemini(self, mock_gemini, client):
Expand Down Expand Up @@ -453,7 +453,7 @@ def test_various_input_formats(self, mock_gemini, client):
)
assert response.status_code == 200, f"Failed for input: {notes}"



if __name__ == "__main__":
pytest.main([__file__, "-v"])
15 changes: 15 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,18 @@ You can check out [the Next.js GitHub repository](https://github.com/vercel/next
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.

Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.

## Testing

Install dependencies:

```bash
npm install --save-dev @testing-library/react @testing-library/jest-dom @testing-library/user-event
npm install --save-dev ts-jest @types/jest
```

Run test:

```bash
npm test
```
Loading