80 lines
3.9 KiB
Python
'''Проверки схемы сервиса и чтения SQLite без создания пустых баз менеджером.'''
| |||
| |||
import ast
| |||
import io
| |||
import re
| |||
import sqlite3
| |||
import tempfile
| |||
import unittest
| |||
from contextlib import closing, redirect_stdout
| |||
from pathlib import Path
| |||
| |||
from mt5_control import MT5_Control
| |||
| |||
| |||
class DatabaseTests(unittest.TestCase):
| |||
def setUp(self):
| |||
self.temp = tempfile.TemporaryDirectory()
| |||
self.root = Path(self.temp.name)
| |||
# Решётка и пробелы проверяют корректность URI файла SQLite.
| |||
self.database = self.root / 'состояние #1.sqlite'
| |||
self.control = MT5_Control({'One': {}}, str(self.root), str(self.database))
| |||
| |||
def tearDown(self):
| |||
self.temp.cleanup()
| |||
| |||
def create_service_schema(self, connection):
| |||
# Используем SQL из настоящего MQL5-исходника, чтобы проверять его
| |||
# совместимость с STATE_QUERY, а не отдельную копию схемы в тестах.
| |||
source = (Path(__file__).resolve().parent.parent / 'ServiceStateWriter.mq5').read_text(encoding='utf-8-sig')
| |||
definitions = re.findall(r'const string sql_(?:terminals|states)\s*=\s*(.*?);', source, re.S)
| |||
self.assertEqual(len(definitions), 2)
| |||
for definition in definitions:
| |||
sql = ''.join(ast.literal_eval(value) for value in re.findall(r'"(?:[^"\\]|\\.)*"', definition))
| |||
connection.execute(sql)
| |||
| |||
def test_missing_database_is_not_created_by_reader(self):
| |||
with redirect_stdout(io.StringIO()) as output:
| |||
self.assertIsNone(self.control.read_state('One'))
| |||
self.assertFalse(self.database.exists())
| |||
self.assertIn(str(self.database), output.getvalue())
| |||
| |||
def test_existing_empty_database_is_reported_without_modifying_it(self):
| |||
self.database.touch()
| |||
with redirect_stdout(io.StringIO()) as output:
| |||
self.assertIsNone(self.control.read_state('One'))
| |||
self.assertIn('no such table: terminals', output.getvalue())
| |||
self.assertIn(str(self.database), output.getvalue())
| |||
self.assertEqual(self.database.stat().st_size, 0)
| |||
| |||
def test_service_schema_initializes_existing_file_and_matches_python_query(self):
| |||
self.database.touch()
| |||
with closing(sqlite3.connect(self.database)) as connection:
| |||
self.create_service_schema(connection)
| |||
connection.execute('INSERT INTO terminals(id, folder, path) VALUES (1, ?, ?)',
| |||
('One', str(self.root / 'One')))
| |||
connection.execute('INSERT INTO states(terminal_id, updated_at, equity) VALUES (1, 123, 50000)')
| |||
connection.commit()
| |||
# Повторное создание не удаляет уже записанные данные.
| |||
self.create_service_schema(connection)
| |||
connection.commit()
| |||
row = self.control.read_state('One')
| |||
self.assertEqual(row['equity'], 50000)
| |||
self.assertEqual(row['terminal_path'], str(self.root / 'One'))
| |||
self.assertIn('holder_name', row.keys())
| |||
| |||
def test_readonly_connection_observes_committed_wal_updates(self):
| |||
with closing(sqlite3.connect(self.database)) as writer:
| |||
writer.execute('PRAGMA journal_mode=WAL')
| |||
self.create_service_schema(writer)
| |||
writer.execute('INSERT INTO terminals(id, folder) VALUES (1, ?)', ('One',))
| |||
writer.execute('INSERT INTO states(terminal_id, updated_at, equity) VALUES (1, 123, 50000)')
| |||
writer.commit()
| |||
self.assertEqual(self.control.read_state('One')['equity'], 50000)
| |||
writer.execute('UPDATE states SET equity=51000 WHERE terminal_id=1')
| |||
writer.commit()
| |||
self.assertEqual(self.control.read_state('One')['equity'], 51000)
| |||
| |||
| |||
if __name__ == '__main__':
| |||
unittest.main()
|