252 lines
6.4 KiB
Python
252 lines
6.4 KiB
Python
'''
|
|
Storage command UX regressions.
|
|
|
|
'''
|
|
from collections.abc import (
|
|
AsyncIterator,
|
|
Iterator,
|
|
)
|
|
from contextlib import asynccontextmanager
|
|
import json
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from piker import tsp
|
|
from piker import config
|
|
from piker.data import def_iohlcv_fields
|
|
from piker.storage import cli as storage_cli
|
|
from piker.storage.cli import store
|
|
|
|
|
|
def test_store_group_shows_help_without_arguments() -> None:
|
|
'''
|
|
Bare ``piker store`` must present its command map immediately.
|
|
|
|
The Typer group emitted only a missing-command error and
|
|
required another ``--help`` invocation. Invoke the group
|
|
without arguments and prove complete help replaces that error.
|
|
|
|
'''
|
|
result = CliRunner().invoke(store, [])
|
|
|
|
assert result.exit_code == 2
|
|
assert 'Usage:' in result.output
|
|
assert 'Missing command' not in result.output
|
|
for command in (
|
|
'anal',
|
|
'audit',
|
|
'delete',
|
|
'ldshm',
|
|
'ls',
|
|
'series',
|
|
'shm',
|
|
):
|
|
assert command in result.output
|
|
|
|
|
|
def test_store_commands_show_help_without_arguments() -> None:
|
|
'''
|
|
Bare endpoints must not open runtimes or report missing args.
|
|
|
|
Every endpoint is discoverable by typing its name once. Exercise
|
|
both required-input and explicitly-triggered listing commands and
|
|
prove help rendering exits before any callback can run.
|
|
|
|
'''
|
|
runner = CliRunner()
|
|
for command in (
|
|
'anal',
|
|
'audit',
|
|
'delete',
|
|
'ldshm',
|
|
'ls',
|
|
'series',
|
|
'shm',
|
|
):
|
|
result = runner.invoke(store, [command])
|
|
assert result.exit_code == 2
|
|
assert 'Usage:' in result.output
|
|
assert 'Missing argument' not in result.output
|
|
|
|
|
|
def test_series_lists_exact_native_periods(
|
|
tmp_path: Path,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
'''
|
|
Durable listing must expose separate 1s and 60s identities.
|
|
|
|
The old FQME-only index hid one timeframe. Create canonical files
|
|
without storage or actor services, invoke the JSON endpoint,
|
|
and prove both exact paths and periods are returned.
|
|
|
|
'''
|
|
nativedb: Path = tmp_path / 'nativedb'
|
|
nativedb.mkdir()
|
|
one = nativedb / 'qqq.nasdaq.ib.ohlcv1s.parquet'
|
|
sixty = nativedb / 'qqq.nasdaq.ib.ohlcv60s.parquet'
|
|
one.write_bytes(b'1')
|
|
sixty.write_bytes(b'60')
|
|
monkeypatch.setattr(config, 'get_conf_dir', lambda: tmp_path)
|
|
|
|
result = CliRunner().invoke(
|
|
store,
|
|
['series', '--all', '--json'],
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
payload = json.loads(result.output)
|
|
assert [item['period_s'] for item in payload] == [1, 60]
|
|
assert [item['path'] for item in payload] == [
|
|
str(one),
|
|
str(sixty),
|
|
]
|
|
|
|
|
|
def test_shm_endpoint_reports_immutable_snapshot(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
'''
|
|
SHM diagnostics report exact buffers without storage mutation.
|
|
|
|
Build one RT identity and snapshot, then replace runtime
|
|
attachment with fakes. Invoke JSON output and prove it carries
|
|
generation, cadence, and invalid-slot
|
|
evidence without opening a storage client or writer.
|
|
|
|
'''
|
|
path = Path(
|
|
'piker.datad[aaaaaaaa-aaaa-aa].qqq.nasdaq.ib.rt'
|
|
)
|
|
ref = tsp.ShmBufferRef(
|
|
service='datad',
|
|
generation='aaaaaaaa-aaaa-aa',
|
|
fqme='qqq.nasdaq.ib',
|
|
kind='rt',
|
|
path=path,
|
|
)
|
|
frame = np.zeros(
|
|
4,
|
|
dtype=np.dtype(def_iohlcv_fields),
|
|
)
|
|
frame['index'] = np.arange(4)
|
|
frame['time'] = [1, 0, 3, 4]
|
|
|
|
@asynccontextmanager
|
|
async def open_runtime(
|
|
*args: Any,
|
|
**kwargs: Any,
|
|
) -> AsyncIterator[None]:
|
|
'''
|
|
Yield a runtime-free diagnostic context.
|
|
|
|
'''
|
|
yield
|
|
|
|
def iter_refs(
|
|
fqme: str|None = None,
|
|
) -> Iterator[tsp.ShmBufferRef]:
|
|
'''
|
|
Yield the selected exact SHM identity.
|
|
|
|
'''
|
|
yield ref
|
|
|
|
def iter_frames(
|
|
fqme: str,
|
|
shm_name: str|None = None,
|
|
refs: list[tsp.ShmBufferRef]|None = None,
|
|
) -> Iterator[tuple[Path, SimpleNamespace, None]]:
|
|
'''
|
|
Yield an immutable snapshot through the attachment interface.
|
|
|
|
'''
|
|
shm = SimpleNamespace(array=frame)
|
|
yield path, shm, None
|
|
|
|
def fail_storage(*args: Any, **kwargs: Any) -> None:
|
|
'''
|
|
Fail if read-only SHM inspection reaches storage.
|
|
|
|
'''
|
|
raise AssertionError('SHM diagnostics opened storage')
|
|
|
|
monkeypatch.setattr(
|
|
storage_cli,
|
|
'open_piker_runtime',
|
|
open_runtime,
|
|
)
|
|
monkeypatch.setattr(
|
|
storage_cli,
|
|
'open_storage_client',
|
|
fail_storage,
|
|
)
|
|
monkeypatch.setattr(tsp, 'iter_shm_buffer_refs', iter_refs)
|
|
monkeypatch.setattr(tsp, 'iter_dfs_from_shms', iter_frames)
|
|
|
|
result = CliRunner().invoke(
|
|
store,
|
|
['shm', 'qqq.nasdaq.ib', '--json'],
|
|
)
|
|
|
|
assert result.exit_code == 0
|
|
payload = json.loads(result.output)
|
|
assert len(payload) == 1
|
|
assert payload[0]['generation'] == 'aaaaaaaa-aaaa-aa'
|
|
assert payload[0]['period_s'] == 1
|
|
assert payload[0]['invalid_count'] == 1
|
|
|
|
|
|
def test_ldshm_rejects_unknown_exact_name_before_runtime(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
'''
|
|
A stale exact SHM selection must fail before opening services.
|
|
|
|
The old no-match path paused after opening runtime and storage
|
|
setup. Return no identity, install a runtime fail-spy,
|
|
and prove exact selection exits immediately
|
|
with a deterministic error.
|
|
|
|
'''
|
|
def iter_refs(
|
|
fqme: str|None = None,
|
|
) -> Iterator[tsp.ShmBufferRef]:
|
|
'''
|
|
Yield no matching SHM identities.
|
|
|
|
'''
|
|
return iter(())
|
|
|
|
def fail_runtime(*args: Any, **kwargs: Any) -> None:
|
|
'''
|
|
Fail if no-match handling opens a runtime.
|
|
|
|
'''
|
|
raise AssertionError('unknown SHM opened runtime')
|
|
|
|
monkeypatch.setattr(tsp, 'iter_shm_buffer_refs', iter_refs)
|
|
monkeypatch.setattr(
|
|
storage_cli,
|
|
'open_piker_runtime',
|
|
fail_runtime,
|
|
)
|
|
|
|
result = CliRunner().invoke(
|
|
store,
|
|
[
|
|
'ldshm',
|
|
'qqq.nasdaq.ib',
|
|
'--shm-name',
|
|
'missing',
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 2
|
|
assert 'No exact OHLCV SHM buffer' in result.output
|