Экран говорит «сохранено» — база и журнал показывают, что сохранилось на самом деле
sqlite3 (sudo apt install sqlite3). Снимки — DB Browser 3.13.1 в Windows 11.Интерфейс и API показывают то, что программа решила показать. База хранит то, что программа на самом деле записала. Между ними бывают расхождения:
Такие проверки — серый ящик из раздела 02: код не читаем, но знаем, где и как хранятся данные. В реальных проектах доступ к базе тестового стенда обычно дают по запросу; к рабочей базе — почти никогда, и это правильно.
SQLite — база данных в одном файле, без сервера. «Задачник» хранит всё в tasker.db рядом с tasker.py. DB Browser for SQLite открывает такой файл и показывает таблицы, данные и результат запросов.
Открывайте базу только для чтения: Файл → «Открыть БД только для чтения…» (Ctrl+Shift+O). Если открыть обычно и начать править ячейки, DB Browser держит изменения в незавершённой транзакции, и «Задачник» в это время может получить ошибку «database is locked». Внизу окна должно быть написано «Только для чтения».
Вкладки DB Browser:
После действия в «Задачнике» данные в DB Browser сами не обновляются: на вкладке Browse Data нажмите кнопку обновления (две зелёные стрелки) или выполните запрос заново.
| Таблица | Столбцы | Что хранит |
|---|---|---|
users | id, login, password, created | Пользователи |
sessions | token, user_id, created | Действующие сессии и токены API; одна строка — один вход |
tasks | id, owner_id, title, due, priority, status, created, updated | Задачи; owner_id — id пользователя из users |
Даты хранятся текстом: срок — 2026-10-29, время создания и изменения — 2026-10-29 13:07:50. Статус — код: new, in_progress, done (на экране — «Новая», «В работе», «Готово»). Пустой срок — NULL.
Тестировщику хватает чтения: SELECT и несколько дополнений. Изменять данные запросами (UPDATE, DELETE) в этом курсе не нужно — для этого есть интерфейс, API и --reset.
| Что нужно | Запрос |
|---|---|
| Все задачи пользователя 2 | SELECT * FROM tasks WHERE owner_id = 2; |
| Только нужные столбцы, по порядку | SELECT id, title, due FROM tasks ORDER BY due; |
| Поиск по части названия | SELECT * FROM tasks WHERE title LIKE '%отчёт%'; |
| Задачи без срока | SELECT * FROM tasks WHERE due IS NULL; |
| Срок раньше сегодняшнего дня | SELECT * FROM tasks WHERE due < date('now', 'localtime'); |
| Сколько всего | SELECT COUNT(*) FROM tasks WHERE owner_id = 1; |
| Сколько в каждой группе | SELECT status, COUNT(*) FROM tasks GROUP BY status; |
| Данные из двух таблиц | SELECT u.login, t.title FROM tasks t JOIN users u ON u.id = t.owner_id; |
| Последняя созданная задача | SELECT * FROM tasks ORDER BY id DESC LIMIT 1; |
Пример — сколько задач у каждого пользователя в каждом статусе (JOIN нужен, чтобы вместо номера пользователя видеть логин):
То же в Linux консольной программой sqlite3 (Debian 13, sqlite3 3.46):
$ sqlite3 -header -column tasker.db "SELECT id, title, status, priority FROM tasks WHERE owner_id = 2;"
id title status priority
-- -------------------------------------- ----------- --------
9 Записаться к врачу new 2
10 Пароль от Wi-Fi переговорной: Neva2026 new 1
11 План командировки в Казань in_progress 3
Внимание к LIKE и регистру: в SQLite LIKE не различает регистр только латинских букв, а '%Отчёт%' и '%отчёт%' для кириллицы — разные запросы. Если проверяете поиск приложения запросом к базе, помните об этом, чтобы не принять особенность SQLite за дефект программы или наоборот.
Схема одна для любой операции: до — действие — после.
updated.| Операция | Что проверить в базе |
|---|---|
| Создание | Появилась ровно одна строка; поля совпадают с введёнными (пробелы по краям, регистр, кириллица); правильный владелец; created и updated заполнены |
| Изменение | Изменились только нужные поля; updated стал новее, created не изменился |
| Смена статуса | Новый код статуса; другие поля не тронуты |
| Удаление | Строки нет (или есть отметка об удалении, если так в требованиях) |
| Счётчики и итоги | Число на экране равно COUNT(*) по тем же условиям, что в требованиях |
| Регистрация | Строка пользователя; как хранится пароль (требование Т9.1) |
| Вход и выход | При входе появляется строка в sessions, при выходе — исчезает |
В баг-репорт о данных вставляйте запрос и его результат до и после — разработчик повторит их у себя.
Журнал (лог) — записи сервера о том, что он делал. «Задачник» пишет журнал в консоль и в файл tasker.log: время, метод, адрес и код ответа каждого запроса.
2026-10-29 13:07:50 Задачник 1.0: http://127.0.0.1:8000 — остановить: Ctrl+C
2026-10-29 13:07:53 GET /api/tasks 401
2026-10-29 13:07:53 POST /api/login 200
2026-10-29 13:07:54 GET /api/tasks?status=in_progress 200
Когда сервер отвечает 500, в журнал пишется трассировка (traceback) — цепочка вызовов, на которой программа упала. Пример из вымышленного интернет-магазина на Python:
2026-11-12 14:31:07 POST /api/cart/apply-coupon — внутренняя ошибка
Traceback (most recent call last):
File "shop/server.py", line 210, in dispatch
self.api(method, url.path)
File "shop/cart.py", line 88, in apply_coupon
discount = coupon["percent"] / 100 * cart.total
~~~~~~^^^^^^^^^^^
KeyError: 'percent'
2026-11-12 14:31:07 POST /api/cart/apply-coupon 500
Как читать: последняя строка трассировки — тип ошибки и подробность (KeyError: 'percent' — у купона нет поля «percent»); строки выше — где именно, от общего к частному. Исправлять тестировщику не нужно, но первую и последнюю строки трассировки вставляют в баг-репорт: по ним разработчик сразу найдёт место.
Как найти нужную запись: отметьте время действия (в Network DevTools у запроса есть время, в curl — заголовок Date), откройте журнал и ищите строки за эту секунду.
# Windows PowerShell: последние 20 строк и строки с ошибками
Get-Content tasker.log -Tail 20 -Encoding utf8
Select-String -Path tasker.log -Pattern "ошибка", "Traceback" -Encoding utf8
# Linux
tail -n 20 tasker.log
grep -n -A 12 "внутренняя ошибка" tasker.log
В Windows PowerShell 5.1 указывайте -Encoding utf8, иначе русские буквы журнала будут искажены. Журнал можно открыть и Блокнотом — он понимает UTF-8.
Результат проверки зависит от данных, поэтому их готовят так же внимательно, как шаги:
py tasker.py --reset; в своём проекте — скрипт, который создаёт тестовые записи, или резервная копия базы, которую восстанавливают перед прогоном. Для SQLite копия — просто копия файла при остановленном приложении.После прогона данные возвращают в исходное состояние — иначе следующий прогон начнётся с «грязной» базы, и его результаты нельзя будет сравнить.
updated должен стать новее, остальные поля — не измениться