Frontend — consistent HSL token usage across remaining pages: - Users: shared Card, Badge with success/error tokens, h2 typography, animate-fadeIn - Audit: shared Card, muted-foreground text, animate-fadeIn - Clients: shared Card, Badge active/inactive, hsl(--primary) icon color - Storage: shared Card, StatusBadge for status pills, hsl warning/primary bars replacing hardcoded amber/blue, all gray text → muted-foreground - Login: hsl(--surface) bg, hsl(--primary) submit button, brand mark icon, animate-scaleIn card entry, hsl(--warning) dev notice Backend tests — convert print-only stubs to real assertions: - test_pptx_creator: mkdir, deterministic save path, assert file exists + slide count - test_gemini_schema_support: direct google.genai client, skipif guard on GOOGLE_API_KEY, JSON parse + Pydantic model validation assertions - test_openai_schema_support: clean skip (OpenAI removed in Phase 6) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
71 lines
2.6 KiB
Python
71 lines
2.6 KiB
Python
import json
|
|
import os
|
|
import pytest
|
|
from typing import Optional
|
|
from pydantic import BaseModel, Field
|
|
|
|
|
|
class ColumnContentModel(BaseModel):
|
|
title: str = Field(min_length=3, max_length=100, description="Column title")
|
|
content: str = Field(min_length=10, max_length=800, description="Column content")
|
|
|
|
|
|
class TwoColumnSlideModel(BaseModel):
|
|
title: str = Field(
|
|
min_length=3,
|
|
max_length=100,
|
|
description="Title of the slide",
|
|
)
|
|
subtitle: Optional[str] = Field(
|
|
default=None,
|
|
min_length=3,
|
|
max_length=150,
|
|
description="Optional subtitle or description",
|
|
)
|
|
leftColumn: ColumnContentModel = Field(description="Left column content")
|
|
rightColumn: ColumnContentModel = Field(description="Right column content")
|
|
backgroundImage: Optional[str] = Field(
|
|
default=None,
|
|
description="URL to background image for the slide",
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
not os.getenv("GOOGLE_API_KEY"),
|
|
reason="GOOGLE_API_KEY not set — skipping Gemini API test",
|
|
)
|
|
def test_gemini_schema_support():
|
|
import google.genai as genai
|
|
from google.genai.types import GenerateContentConfig
|
|
|
|
client = genai.Client(api_key=os.environ["GOOGLE_API_KEY"])
|
|
model = os.getenv("GOOGLE_MODEL", "gemini-2.0-flash")
|
|
|
|
response = client.models.generate_content(
|
|
model=model,
|
|
contents=[
|
|
"Generate a slide for a presentation about renewable energy.",
|
|
"The slide should have a title, subtitle, and two columns (left and right).",
|
|
],
|
|
config=GenerateContentConfig(
|
|
response_mime_type="application/json",
|
|
response_schema=TwoColumnSlideModel.model_json_schema(),
|
|
),
|
|
)
|
|
|
|
assert response is not None, "Response from Gemini was None"
|
|
assert response.text, "Response text is empty"
|
|
|
|
data = json.loads(response.text)
|
|
assert "title" in data, "Response missing 'title' field"
|
|
assert "leftColumn" in data, "Response missing 'leftColumn' field"
|
|
assert "rightColumn" in data, "Response missing 'rightColumn' field"
|
|
assert isinstance(data["leftColumn"], dict), "'leftColumn' should be an object"
|
|
assert "title" in data["leftColumn"], "'leftColumn' missing 'title'"
|
|
assert "content" in data["leftColumn"], "'leftColumn' missing 'content'"
|
|
|
|
# Validate full model parse
|
|
slide = TwoColumnSlideModel.model_validate(data)
|
|
assert len(slide.title) >= 3, "Title too short"
|
|
assert len(slide.leftColumn.content) >= 10, "Left column content too short"
|
|
assert len(slide.rightColumn.content) >= 10, "Right column content too short"
|