---
name: check-allegro
description: >
  Manual QA testing of Allegro Lister product listings end-to-end.
  Use when testing upload flow, AI-generated content, photo generation, or full pipeline.
  Use when user says "check allegro", "QA test", "smoke test", "manual test", "прогони тесты".
  Do NOT use for unit tests (use pytest), deployment, or code changes.
---

# Check Allegro — Manual QA

## Overview

End-to-end QA проверка Allegro Lister: загрузка фото, AI-контент, photoshoot, генерация карт, валидация.
Прогоняем реальный продукт через весь пайплайн и проверяем каждый этап.

## Prerequisites

1. Сервер запущен на `localhost:8100`
2. `.env` настроен (GCP_PROJECT_ID, CLIPROXY_BASE_URL)
3. Фото продукта доступны на диске

Если сервер не запущен:
```bash
cd /Users/jarvis/claude/allegro-lister
kill $(lsof -ti:8100) 2>/dev/null
nohup .venv/bin/python -c "
import logging; logging.basicConfig(level=logging.INFO)
import uvicorn; uvicorn.run('api.app:app', host='0.0.0.0', port=8100, log_level='info')
" > /tmp/allegro-server.log 2>&1 &
sleep 3
```

## Phase 1: Unit Tests + Lint

Прогони перед мануальным тестом:

```bash
cd /Users/jarvis/claude/allegro-lister
.venv/bin/python -m pytest --tb=short -q
.venv/bin/python -m ruff check . && .venv/bin/python -m ruff format --check .
```

**Критерий:** все тесты зелёные, линтер чистый.

## Phase 2: HTTP Smoke Tests

Проверяем все endpoints:

```bash
# Основные страницы
curl -s -o /dev/null -w "upload: %{http_code}\n" http://localhost:8100/upload/
curl -s -o /dev/null -w "listings: %{http_code}\n" http://localhost:8100/listings/
curl -s -o /dev/null -w "home-redirect: %{http_code}\n" http://localhost:8100/

# Listing-specific (замени 1 на реальный ID)
curl -s -o /dev/null -w "photoshoot: %{http_code}\n" http://localhost:8100/listings/1/photoshoot
curl -s -o /dev/null -w "cards-download: %{http_code}\n" http://localhost:8100/listings/1/cards/download

# Error handling
curl -s -o /dev/null -w "404: %{http_code}\n" http://localhost:8100/listings/999
curl -s -o /dev/null -w "no-photo: %{http_code}\n" -X POST http://localhost:8100/upload/ -F "notes=test"
echo "not image" > /tmp/fake.txt
curl -s -o /dev/null -w "bad-ext: %{http_code}\n" -X POST http://localhost:8100/upload/ -F "photo_main=@/tmp/fake.txt"
```

**Ожидаемые результаты:**

| Endpoint | Код |
|----------|-----|
| GET /upload/ | 200 |
| GET /listings/ | 200 |
| GET / | 307 |
| GET /listings/1/photoshoot | 200 |
| GET /listings/1/cards/download | 200 (zip) |
| GET /listings/999 | 404 |
| POST /upload/ без фото | 422 |
| POST /upload/ с .txt | 400 |

## Phase 3: Upload Flow (Browser)

Открой `http://localhost:8100/upload/` в браузере.

### Checklist UI:
- [ ] Заголовок "Добавить новый продукт"
- [ ] 5 фото-слотов с русскими инструкциями
- [ ] Слот 1 "Главное фото" помечен как обязательный (красная звёздочка)
- [ ] Навигация на русском: "+ Новый", "Листинги", "Allegro Вход"
- [ ] Секция "Характеристики продукта (ограничения для AI)"
- [ ] Секция "Ключевые слова из Allegro Ads" (синий фон)
- [ ] Кнопка "Отправить и анализировать"

### Загрузка тестового продукта:

```bash
curl -s -w "\n%{http_code}" -L \
  -F "photo_main=@/путь/к/фото.jpg" \
  -F "product_name=Название на русском" \
  -F "material=Материал" \
  -F "color=Цвет" \
  -F "notes=Описание продукта на русском" \
  -F "price=39.99" \
  -F "ads_keywords_text=подушка на шею	400	0,84 zl	srednia" \
  http://localhost:8100/upload/
```

**Ожидаемый результат:** 303 redirect → листинг создан.

## Phase 4: Listing Detail

Открой созданный листинг `http://localhost:8100/listings/{id}`.

### Checklist:
- [ ] Заголовок на польском (12-75 символов)
- [ ] Статус "identified" или "described"
- [ ] Оригинальные фото отображаются
- [ ] Счётчик символов заголовка (XX/75)
- [ ] Описание HTML валидно (без `<strong>`, `<div>`, `<br>`)
- [ ] Цена отображается корректно
- [ ] Ссылка на Photoshoot (`/listings/{id}/photoshoot`)

### Проверка AI контента:

**Заголовок (Title):**
- Длина 12-75 символов
- На польском языке
- Нет запрещённых слов: "okazja", "nowość", "promocja", "hit", "super", "mega"
- Каждое слово с большой буквы
- Содержит ключевые слова продукта

**Описание (Description HTML):**
- Структура AIDA: h1 → p (hook) → ul/ol (benefits) → h2 "Specyfikacja" → ul → h2 "Zawartość" → ol → p (CTA)
- Только разрешённые теги: h1, h2, p, b, ul, ol, li
- `<b>` только внутри `<p>` или `<li>`, НИКОГДА в заголовках
- Нет голого текста вне тегов
- На польском (не на русском!)
- Содержит ВСЕ ключевые слова из Ads CSV
- Максимум 300 слов, 40KB

Валидация через код:
```bash
cd /Users/jarvis/claude/allegro-lister && .venv/bin/python -c "
from core.db import get_listing
from content.validator import validate_allegro_html
l = get_listing(LISTING_ID)
errors = validate_allegro_html(l['description_html'])
print('Title:', l['title'], f'({len(l[\"title\"])} chars)')
print('Errors:', errors if errors else 'VALID')
print('Keywords:', l.get('keywords', []))
"
```

## Phase 5: Photoshoot Form

Открой `http://localhost:8100/listings/{id}/photoshoot`.

### Checklist:
- [ ] 8 слотов — по одному на каждую карточку
- [ ] Каждый слот показывает `studio_instruction_ru` (инструкция от Vision LLM на русском)
- [ ] Слоты с `edit_strategy=collage` показывают `collage_count` (2-4 фото)
- [ ] Кнопка загрузки фото для каждого слота
- [ ] После загрузки — кнопка "Сгенерировать карты" (или SSE-поток стартует автоматически)

## Phase 6: Photo Generation

### Allegro Cards (8 штук):
Проверяем в БД:
```bash
cd /Users/jarvis/claude/allegro-lister && .venv/bin/python -c "
import sqlite3
conn = sqlite3.connect('data/allegro-lister.db')
rows = conn.execute('SELECT photo_type, count(*) FROM listing_photos WHERE listing_id = ? GROUP BY photo_type', (LISTING_ID,)).fetchall()
for r in rows: print(f'  {r[0]}: {r[1]}')
"
```

**Ожидаемый результат:**

| Тип | Кол-во | Описание |
|-----|--------|----------|
| original | 1-5 | Загруженные фото |
| card_source_* | 0-N | Per-card источники (если загружены через photoshoot) |
| allegro_cards | 8 | Готовые карточки |

### Просмотр карт:

```
http://localhost:8100/photos/{id}/allegro_cards/listing_{id}_card_01_hero.jpg
http://localhost:8100/photos/{id}/allegro_cards/listing_{id}_card_05_closeup.jpg
http://localhost:8100/photos/{id}/allegro_cards/listing_{id}_card_08_scene.jpg
```

### Checklist качества карт:

| Карточка | Метод | Что проверить |
|----------|-------|---------------|
| 01_hero | Gemini Edit | Чистый белый фон RGB(255,255,255), продукт без артефактов |
| 02_lifestyle | Gemini | Человек использует продукт, реалистичный контекст |
| 03_features | Gemini | Инфографика с callout-стрелками, текст на польском |
| 04_angles | Gemini | Три ракурса: спереди, сбоку, сзади |
| 05_closeup | Gemini | Макро-деталь текстуры и материала |
| 06_dimensions | Gemini | Стрелки + размеры, читаемые цифры |
| 07_contents | Gemini | Flat lay комплектации на белом фоне |
| 08_scene | Imagen 3 BGSWAP | Продукт в lifestyle-сцене, фон заменён, продукт сохранён 100% |

## Phase 7: Pipeline Log

Проверяем что все этапы пайплайна прошли:

```bash
cd /Users/jarvis/claude/allegro-lister && .venv/bin/python -c "
import sqlite3
conn = sqlite3.connect('data/allegro-lister.db')
rows = conn.execute('SELECT phase, status, details FROM pipeline_log WHERE listing_id = ? ORDER BY id', (LISTING_ID,)).fetchall()
for r in rows: print(f'  {r[0]:25s} {r[1]:10s} {(r[2] or \"\")[:60]}')
"
```

**Ожидаемые этапы:**

| Фаза | Статус | Примечание |
|------|--------|------------|
| vision | completed | Продукт идентифицирован |
| ads_csv | ok | N keywords загружено (если CSV загружен) |
| autocomplete | ok/empty | Может быть empty с localhost |
| keywords | completed | Ключи отобраны |
| content_title | done | Заголовок сгенерирован |
| content_description | done | Описание сгенерировано |
| pipeline | ok | Все фазы завершены |
| allegro_cards | ok | 8 images |

## Phase 8: Cross-browser Check

Открой в Chrome и проверь визуально:

- [ ] Upload форма: все 5 слотов видны, инструкции читаемы
- [ ] Listing detail: статус, заголовок, описание корректны
- [ ] Photoshoot: 8 слотов с инструкциями от Vision LLM
- [ ] Карты отображаются в сетке
- [ ] Описание preview: HTML рендерится корректно (заголовки, списки, bold)
- [ ] Навигация: все ссылки работают

## Phase 9: Publish (Production only)

Требует OAuth с production credentials (sandbox не поддерживает публикацию).

- Открой `/publish/` — проверь что кнопка видна для статуса `approved`
- После публикации: статус → `published`, `allegro_offer_id` записан в БД

```bash
cd /Users/jarvis/claude/allegro-lister && .venv/bin/python -c "
from core.db import get_listing
l = get_listing(LISTING_ID)
print('Status:', l['status'])
print('Offer ID:', l.get('allegro_offer_id'))
print('Offer URL:', l.get('allegro_offer_url'))
"
```

## QA Report Template

```
## QA Report — Allegro Lister
**Дата:** YYYY-MM-DD
**Продукт:** [название]
**Listing ID:** #N

### Unit Tests: X/X passed
### HTTP Smoke: X/X passed

### Upload Flow
| Проверка | Результат |
|----------|-----------|
| UI на русском | OK/FAIL |
| 5 фото-слотов | OK/FAIL |
| Загрузка фото | OK/FAIL |

### AI Content
| Проверка | Результат |
|----------|-----------|
| Title длина 12-75 | OK/FAIL |
| Title на польском | OK/FAIL |
| Description AIDA | OK/FAIL |
| Description HTML valid | OK/FAIL |
| Все ключевые слова | OK/FAIL |

### Photoshoot Form
| Проверка | Результат |
|----------|-----------|
| 8 слотов с инструкциями | OK/FAIL |
| Per-card загрузка | OK/FAIL |

### Photo Generation
| Проверка | Результат |
|----------|-----------|
| Allegro Cards (8) | X/8 |
| 01_hero белый фон | OK/FAIL |
| 08_scene BGSWAP | OK/FAIL |
| 03_features инфографика | OK/FAIL |

### Pipeline Log: все этапы completed

### ИТОГО: X/X passed, Y blockers
```

## Common Issues

| Проблема | Причина | Решение |
|----------|---------|---------|
| Карты не генерируются | GCP_PROJECT_ID не задан | Проверь `.env` |
| Title > 75 chars | LLM не соблюдает лимит | Retry или ручная правка |
| 502 от ClipProxy | Модель недоступна | Проверь AI_MODEL_LIGHT/HEAVY в `.env` |
| 403 autocomplete | Allegro блокирует с localhost | Нормально, Ads CSV компенсирует |
| Rate limit cards | Gemini/Imagen 429 | Подожди 2-3 мин, перегенерируй через retry |
| Description `<strong>` | LLM использует запрещённый тег | Sanitizer должен исправить автоматически |
| Генерация зависла | SSE-поток прерван | Открой `/listings/{id}/cards/stream`, перезапусти сервер |
