# Dolt — инструкция для агентов

Ты подключаешь сервис к базе **dolt.aipika.tech:3306**. Это MySQL-совместимый сервер
(`SELECT VERSION()` → 8.0.31): `mysql2`, ORM и `mysql`-клиент работают как обычно. Сверх MySQL —
версионирование (каждая транзакция становится версией) и снапшоты через ветки: тесты получают копию
продакшен-данных мгновенно и не могут задеть продакшен. Человекочитаемая версия — https://dolt.aipika.tech.

## 1. Доступы

Оператор выдаёт три набора доступов. Пароли у всех разные; имена баз/пользователей — по схеме ниже,
где `<сервис>` — имя сервиса.

| Роль | База / пользователь | Можно | Нельзя |
|---|---|---|---|
| prod | `<сервис>` / `<сервис>` | всё в общей базе и в базе чувствительных данных (включая джойны между ними) | — |
| prod-secure | `<сервис>_secure` / `<сервис>_secure` | всё в базе чувствительных данных | общую базу не видит |
| test | `<сервис>_test` / `<сервис>_test` | всё в своей тестовой базе; снапшот **общей** базы и работа в нём | доступа к `<сервис>_secure` нет вообще; писать в прод-ветку нельзя |

Что куда класть:

* **общие данные** → `<сервис>`. Эта база снапшотится для тестов, значит её содержимое увидят тестовые прогоны.
* **чувствительные данные** (персональные, платёжные, токены, переписка) → `<сервис>_secure` —
  она не снапшотится, и тестовые доступы её не видят.
* **тестовые данные** → `<сервис>_test`.

## 2. Строки подключения

```
prod:        mysql://<сервис>:<пароль>@dolt.aipika.tech:3306/<сервис>
prod-secure: mysql://<сервис>_secure:<пароль>@dolt.aipika.tech:3306/<сервис>_secure
test:        mysql://<сервис>_test:<пароль>@dolt.aipika.tech:3306/<сервис>_test
снапшот:     mysql://<сервис>_test:<пароль>@dolt.aipika.tech:3306/<сервис>   (та же тестовая учётка, база общих данных)
```

Удобно развести по переменным окружения:

```dotenv
DATABASE_URL=mysql://<сервис>:<пароль>@dolt.aipika.tech:3306/<сервис>
SECURE_DATABASE_URL=mysql://<сервис>_secure:<пароль>@dolt.aipika.tech:3306/<сервис>_secure
TEST_DATABASE_URL=mysql://<сервис>_test:<пароль>@dolt.aipika.tech:3306/<сервис>_test
SNAPSHOT_DATABASE_URL=mysql://<сервис>_test:<пароль>@dolt.aipika.tech:3306/<сервис>
```

## 3. Как настраивать тесты

### 3.1. Обычный прогон — своя тестовая база

Тесты подключаются `TEST_DATABASE_URL` и живут в `<сервис>_test`: миграции, фикстуры, `TRUNCATE`,
`DROP TABLE` — всё разрешено. База уже создана; создавать базы самому нельзя (см. §5).

### 3.2. Прогон на прод-данных — снапшот

Нужно проверить поведение на реальных данных — снимаешь снапшот **тестовыми доступами**, подключившись
к базе общих данных. Снапшот — это ветка: copy-on-write, снимается мгновенно, данные не копируются,
размер базы не важен. Ветка у каждой сессии своя, поэтому продакшен снапшот не замечает.

```sql
USE `<сервис>`;
CALL DOLT_BRANCH('test-<run-id>');        -- снапшот общих данных на текущий момент
CALL DOLT_CHECKOUT('test-<run-id>');      -- переключается только эта сессия
-- дальше читаешь прод-данные и пишешь (миграции, фикстуры) внутри снапшота
CALL DOLT_CHECKOUT('main');
CALL DOLT_BRANCH('-D', 'test-<run-id>');  -- убрать снапшот после прогона
```

Разрешено на общей базе: читать данные, создавать/удалять ветки, переключаться в них, писать внутри
снапшота. Запрещено: писать в `main`, откатывать/сливать/коммитить прод-ветку, удалять или
переименовывать `main`.

Node-хелпер (mysql2, одна сессия на прогон):

```js
import mysql from 'mysql2/promise';

export async function withProdSnapshot(runId, fn) {
  const branch = `test-${runId}`;
  const conn = await mysql.createConnection(process.env.SNAPSHOT_DATABASE_URL);
  try {
    await conn.query(`CALL DOLT_BRANCH(?)`, [branch]);
    await conn.query(`CALL DOLT_CHECKOUT(?)`, [branch]);
    return await fn(conn);                       // тесты читают и пишут в снапшоте
  } finally {
    await conn.query(`CALL DOLT_CHECKOUT('main')`).catch(() => {});
    await conn.query(`CALL DOLT_BRANCH('-D', ?)`, [branch]).catch(() => {});
    await conn.end();
  }
}
```

Параллельные прогоны не мешают друг другу: у каждого свой `run-id` и своя ветка. Не делай
`DOLT_CHECKOUT` в общем пуле соединений без явного `run-id` — ветка привязана к сессии.

### 3.3. Чувствительные данные в тестах

Их там быть не должно. Тестовые доступы не имеют прав на `<сервис>_secure` (даже имя базы не видно),
а снапшоты снимаются только с общей базы. Нужны «похожие» данные — генерируй их в `<сервис>_test`.
Если данные не должны попасть в тестовый снапшот, их место в `<сервис>_secure`.

## 4. Проверка, что доступ настроен

```sh
# 1. тестовая база: создать и удалить таблицу
mysql -h dolt.aipika.tech -P 3306 -u <сервис>_test -p '<пароль>' <сервис>_test \
  -e 'CREATE TABLE t (id INT PRIMARY KEY); DROP TABLE t;'

# 2. снапшот общей базы
mysql -h dolt.aipika.tech -P 3306 -u <сервис>_test -p '<пароль>' <сервис> -e \
  "CALL DOLT_BRANCH('test-smoke'); CALL DOLT_CHECKOUT('test-smoke'); SELECT active_branch(); CALL DOLT_CHECKOUT('main'); CALL DOLT_BRANCH('-D','test-smoke');"

# 3. изоляция: запись в main прод-базы должна падать с
#    `does not have the correct permissions on branch main`
```

## 5. Правила и грабли

* **База всегда указана в строке подключения.** Сессия без выбранной базы не может трогать таблицы
  (`cannot dolt_commit with no database selected`); `SELECT 1` и `SHOW DATABASES` при этом работают —
  healthcheck не сломается.
* **Одна транзакция — одна база.** `Cannot commit changes on more than one branch / database`:
  не пиши в две базы из одной сессии. Для чтения с джойном между общей и чувствительной базой
  используй права prod — это работает.
* **Базы создаёт оператор, не сервис.** Нужна новая база или база под параллельные прогоны — попроси оператора.
* **Версионирование включено.** Каждая транзакция становится версией: есть `dolt_log`,
  `SELECT ... AS OF 'HEAD~5'`, `dolt_diff_<таблица>`. В `dolt_diff_*` подставляется хэш версии
  (`to_commit = (SELECT commit_hash FROM dolt_log ORDER BY commit_order DESC LIMIT 1)`),
  литерал `'HEAD'` там не работает.
* **Ветки-снапшоты удаляй после прогона** — иначе они накапливаются.
* **Права не правь вручную.** Нужно другое разделение доступов — попроси оператора.

## 6. Если что-то не так

* `command denied` / `does not have the correct permissions on branch main` — это ожидаемое поведение
  схемы из §1, а не поломка: сверься с таблицей ролей и не пытайся обойти;
* нет соединения с `dolt.aipika.tech:3306` — проверь доступность хоста из своего окружения;
* нужен доступ, новая база или дополнительные тестовые базы — оператору сервиса.
